Add support for building without nan-boxing

This commit is contained in:
K. Lange 2024-01-11 13:36:27 +09:00
parent df4435efdc
commit b162ed889b
8 changed files with 128 additions and 61 deletions

View File

@ -84,6 +84,10 @@ ifdef KRK_HEAP_TAG_BYTE
CFLAGS += -DKRK_HEAP_TAG_BYTE=${KRK_HEAP_TAG_BYTE}
endif
ifdef KRK_NO_NAN_BOXING
CFLAGS += -DKRK_NO_NAN_BOXING=1
endif
ifdef KRK_BUNDLE_LIBS
KRK_BUNDLE_LIBS_CFLAG=$(patsubst %,BUNDLED(%);,${KRK_BUNDLE_LIBS})
KRK_BUNDLE_LIBS_BOBJS=$(patsubst %,src/modules/module_%.o,${KRK_BUNDLE_LIBS})

View File

@ -476,7 +476,7 @@ KrkValue krk_runtimeError(KrkClass * type, const char * fmt, ...) {
/* Allocate an exception object of the requested type. */
KrkInstance * exceptionObject = krk_newInstance(type);
krk_push(OBJECT_VAL(exceptionObject));
krk_attachNamedValue(&exceptionObject->fields, "arg", msg == KWARGS_VAL(0) ? finishStringBuilder(&sb) : msg);
krk_attachNamedValue(&exceptionObject->fields, "arg", krk_valuesSame(msg,KWARGS_VAL(0)) ? finishStringBuilder(&sb) : msg);
krk_attachNamedValue(&exceptionObject->fields, "__cause__", NONE_VAL());
krk_attachNamedValue(&exceptionObject->fields, "__context__", NONE_VAL());
krk_pop();

View File

@ -7,6 +7,7 @@
#include <string.h>
#include "kuroko.h"
#ifndef KRK_NO_NAN_BOXING
/**
* @brief Tag enum for basic value types.
*
@ -23,28 +24,6 @@ typedef enum {
KRK_VAL_NOTIMPL = 0x7FFE,
} KrkValueType;
/*
* The following poorly-named macros define bit patterns for identifying
* various boxed types.
*
* Boxing is done by first setting all of the bits of MASK_NAN. If all of
* these bits are set, a value is not a float. If any of them are not set,
* then a value is a float - and possibly a real NaN.
*
* Three other bits - one before and two after the MASK_NAN bits - determine
* what type the value actually is. KWARGS sets none of the identifying bits,
* NONE sets all of them.
*/
#define KRK_VAL_MASK_BOOLEAN ((uint64_t)0xFFFC000000000000) /* 1..1100 */
#define KRK_VAL_MASK_INTEGER ((uint64_t)0xFFFD000000000000) /* 1..1101 */
#define KRK_VAL_MASK_HANDLER ((uint64_t)0xFFFE000000000000) /* 1..1110 */
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000) /* 1..1111 */
#define KRK_VAL_MASK_KWARGS ((uint64_t)0x7FFC000000000000) /* 0..1100 */
#define KRK_VAL_MASK_OBJECT ((uint64_t)0x7FFD000000000000) /* 0..1101 */
#define KRK_VAL_MASK_NOTIMPL ((uint64_t)0x7FFE000000000000) /* 0..1110 */
#define KRK_VAL_MASK_NAN ((uint64_t)0x7FFC000000000000)
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
/**
* @struct KrkValue
* @brief Stack reference or primative value.
@ -61,6 +40,32 @@ typedef enum {
*/
typedef uint64_t KrkValue;
#define _krk_valuesSame(a,b) (a == b)
#else
/*
* Tagged union, but without the union fun.
*/
typedef enum {
KRK_VAL_NONE = 0,
KRK_VAL_INTEGER = 1,
KRK_VAL_BOOLEAN = 2,
KRK_VAL_HANDLER = 4,
KRK_VAL_KWARGS = 8,
KRK_VAL_OBJECT = 16,
KRK_VAL_NOTIMPL = 32,
KRK_VAL_FLOATING = 64,
} KrkValueType;
typedef struct {
uint64_t tag;
uint64_t val;
} KrkValue;
#define _krk_valuesSame(a,b) (memcmp(&(a),&(b),sizeof(KrkValue)) == 0)
#endif
/**
* @brief Flexible vector of stack references.
*
@ -133,7 +138,7 @@ extern int krk_valuesEqual(KrkValue a, KrkValue b);
*
* @return 1 if values represent the same object or value, 0 otherwise.
*/
extern int krk_valuesSame(KrkValue a, KrkValue b);
static inline int krk_valuesSame(KrkValue a, KrkValue b) { return _krk_valuesSame(a,b); }
/**
* @brief Compare two values by identity, then by equality.
@ -147,11 +152,35 @@ extern int krk_valuesSameOrEqual(KrkValue a, KrkValue b);
extern KrkValue krk_parse_int(const char * start, size_t width, unsigned int base);
#ifndef KRK_NO_NAN_BOXING
typedef union {
KrkValue val;
double dbl;
} KrkValueDbl;
/*
* The following poorly-named macros define bit patterns for identifying
* various boxed types.
*
* Boxing is done by first setting all of the bits of MASK_NAN. If all of
* these bits are set, a value is not a float. If any of them are not set,
* then a value is a float - and possibly a real NaN.
*
* Three other bits - one before and two after the MASK_NAN bits - determine
* what type the value actually is. KWARGS sets none of the identifying bits,
* NONE sets all of them.
*/
#define KRK_VAL_MASK_BOOLEAN ((uint64_t)0xFFFC000000000000) /* 1..1100 */
#define KRK_VAL_MASK_INTEGER ((uint64_t)0xFFFD000000000000) /* 1..1101 */
#define KRK_VAL_MASK_HANDLER ((uint64_t)0xFFFE000000000000) /* 1..1110 */
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000) /* 1..1111 */
#define KRK_VAL_MASK_KWARGS ((uint64_t)0x7FFC000000000000) /* 0..1100 */
#define KRK_VAL_MASK_OBJECT ((uint64_t)0x7FFD000000000000) /* 0..1101 */
#define KRK_VAL_MASK_NOTIMPL ((uint64_t)0x7FFE000000000000) /* 0..1110 */
#define KRK_VAL_MASK_NAN ((uint64_t)0x7FFC000000000000)
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
#ifdef KRK_SANITIZE_OBJECT_POINTERS
/**
* Debugging tool for verifying we aren't trying to box NULL, which is not a valid object.
@ -213,6 +242,47 @@ static inline uintptr_t _krk_sanitize(uintptr_t input) {
/* ... and as we said above, if any of the MASK_NAN bits are unset, it's a float. */
#define IS_FLOATING(value) (((value) & KRK_VAL_MASK_NAN) != KRK_VAL_MASK_NAN)
#else
typedef union {
uint64_t val;
double dbl;
} KrkValueDbl;
#define NONE_VAL() ((KrkValue){KRK_VAL_NONE,-1})
#define NOTIMPL_VAL() ((KrkValue){KRK_VAL_NOTIMPL,0})
#define BOOLEAN_VAL(value) ((KrkValue){KRK_VAL_BOOLEAN,!!(value)})
#define INTEGER_VAL(value) ((KrkValue){KRK_VAL_INTEGER,((uint64_t)(value)) & 0xFFFFffffFFFFULL})
#define KWARGS_VAL(value) ((KrkValue){KRK_VAL_KWARGS,((uint32_t)(value))})
#define OBJECT_VAL(value) ((KrkValue){KRK_VAL_OBJECT,((uintptr_t)(value))})
#define HANDLER_VAL(ty,ta) ((KrkValue){KRK_VAL_HANDLER,((uint64_t)((((uint64_t)ty) << 32) | ((uint32_t)ta)))})
#define FLOATING_VAL(value) ((KrkValue){KRK_VAL_FLOATING,(((KrkValueDbl){.dbl = (value)}).val)})
#define KRK_VAL_TYPE(value) ((value).tag)
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000)
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
#define KRK_IX(value) ((uint64_t)((value).val & KRK_VAL_MASK_LOW))
#define KRK_SX(value) ((uint64_t)((value).val & 0x800000000000))
#define AS_INTEGER(value) ((krk_integer_type)(KRK_SX(value) ? (KRK_IX(value) | KRK_VAL_MASK_NONE) : (KRK_IX(value))))
#define AS_BOOLEAN(value) AS_INTEGER(value)
#define AS_HANDLER(value) ((uint64_t)((value)).val)
#define AS_OBJECT(value) ((KrkObj*)((uintptr_t)((value).val)))
#define AS_FLOATING(value) (((KrkValueDbl){.val = ((value)).val}).dbl)
#define IS_INTEGER(value) (!!(((value)).tag & (KRK_VAL_INTEGER|KRK_VAL_BOOLEAN)))
#define IS_BOOLEAN(value) (((value)).tag == KRK_VAL_BOOLEAN)
#define IS_NONE(value) (((value)).tag == KRK_VAL_NONE)
#define IS_HANDLER(value) (((value)).tag == KRK_VAL_HANDLER)
#define IS_OBJECT(value) (((value)).tag == KRK_VAL_OBJECT)
#define IS_KWARGS(value) (((value)).tag == KRK_VAL_KWARGS)
#define IS_NOTIMPL(value) (((value)).tag == KRK_VAL_NOTIMPL)
#define IS_FLOATING(value) (((value)).tag == KRK_VAL_FLOATING)
#endif
#define AS_HANDLER_TYPE(value) (AS_HANDLER(value) >> 32)
#define AS_HANDLER_TARGET(value) (AS_HANDLER(value) & 0xFFFFFFFF)
#define IS_HANDLER_TYPE(value,type) (IS_HANDLER(value) && AS_HANDLER_TYPE(value) == type)

View File

@ -188,7 +188,7 @@ int krk_parseVArgs(
* still want to have all the type checking and automatic parsing. */
fmt++;
int * out = va_arg(args, int*);
*out = arg != KWARGS_VAL(0);
*out = !krk_valuesSame(arg, KWARGS_VAL(0));
}
if (*fmt == '!') {
@ -198,7 +198,7 @@ int krk_parseVArgs(
* Maybe if you want @c p to only be a bool this could be useful? */
fmt++;
KrkClass * type = va_arg(args, KrkClass*);
if (arg != KWARGS_VAL(0) && !krk_isInstanceOf(arg, type)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0)) && !krk_isInstanceOf(arg, type)) {
raise_TypeError(_method_name, type ? type->name->chars : "unknown type", arg, names[oarg]);
goto _error;
}
@ -216,7 +216,7 @@ int krk_parseVArgs(
*/
case 'O': {
KrkObj ** out = va_arg(args, KrkObj**);
if (arg != KWARGS_VAL(0)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
if (IS_NONE(arg)) {
*out = NULL;
} else if (!IS_OBJECT(arg)) {
@ -240,7 +240,7 @@ int krk_parseVArgs(
*/
case 'V': {
KrkValue * out = va_arg(args, KrkValue*);
if (arg != KWARGS_VAL(0)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
*out = arg;
}
break;
@ -260,8 +260,8 @@ int krk_parseVArgs(
fmt++;
size = va_arg(args, size_t*);
}
if (arg != KWARGS_VAL(0)) {
if (arg == NONE_VAL()) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
if (IS_NONE(arg)) {
*out = NULL;
if (size) *size = 0;
} else if (IS_STRING(arg)) {
@ -285,7 +285,7 @@ int krk_parseVArgs(
fmt++;
size = va_arg(args, size_t*);
}
if (arg != KWARGS_VAL(0)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
if (IS_STRING(arg)) {
*out = AS_CSTRING(arg);
if (size) *size = AS_STRING(arg)->length;
@ -306,7 +306,7 @@ int krk_parseVArgs(
* both for future compatibility and to make intent clear, but have no
* functional difference at this point.
*/
#define NUMERIC(c,type) case c: { type * out = va_arg(args, type*); if (arg != KWARGS_VAL(0)) { if (!krk_long_to_int(arg, sizeof(type), out)) goto _error; } break; }
#define NUMERIC(c,type) case c: { type * out = va_arg(args, type*); if (!krk_valuesSame(arg, KWARGS_VAL(0))) { if (!krk_long_to_int(arg, sizeof(type), out)) goto _error; } break; }
NUMERIC('b',unsigned char)
NUMERIC('h',short)
NUMERIC('H',unsigned short)
@ -325,7 +325,7 @@ int krk_parseVArgs(
*/
case 'C': {
int * out = va_arg(args, int*);
if (arg != KWARGS_VAL(0)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
if (!IS_STRING(arg) || AS_STRING(arg)->codesLength != 1) {
raise_TypeError(_method_name, "str of length 1", arg, names[oarg]);
goto _error;
@ -341,7 +341,7 @@ int krk_parseVArgs(
*/
case 'f': {
float * out = va_arg(args, float*);
if (arg != KWARGS_VAL(0)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
if (!IS_FLOATING(arg)) {
KrkClass * type = krk_getType(arg);
krk_push(arg);
@ -362,7 +362,7 @@ int krk_parseVArgs(
*/
case 'd': {
double * out = va_arg(args, double*);
if (arg != KWARGS_VAL(0)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
if (!IS_FLOATING(arg)) {
KrkClass * type = krk_getType(arg);
krk_push(arg);
@ -392,7 +392,7 @@ int krk_parseVArgs(
*/
case 'p': {
int * out = va_arg(args, int*);
if (arg != KWARGS_VAL(0)) {
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
*out = !krk_isFalsey(arg);
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _error;
}

View File

@ -72,9 +72,9 @@ KrkTableEntry * krk_findEntry(KrkTableEntry * entries, size_t capacity, KrkValue
KrkTableEntry * tombstone = NULL;
for (;;) {
KrkTableEntry * entry = &entries[index];
if (entry->key == KWARGS_VAL(0)) {
if (krk_valuesSame(entry->key, KWARGS_VAL(0))) {
return tombstone != NULL ? tombstone : entry;
} else if (entry->key == KWARGS_VAL(1)) {
} else if (krk_valuesSame(entry->key, KWARGS_VAL(1))) {
if (tombstone == entry) return tombstone;
if (tombstone == NULL) tombstone = entry;
} else if (krk_valuesSameOrEqual(entry->key, key)) {
@ -93,9 +93,9 @@ KrkTableEntry * krk_findEntryExact(KrkTableEntry * entries, size_t capacity, Krk
KrkTableEntry * tombstone = NULL;
for (;;) {
KrkTableEntry * entry = &entries[index];
if (entry->key == KWARGS_VAL(0)) {
if (krk_valuesSame(entry->key, KWARGS_VAL(0))) {
return tombstone != NULL ? tombstone : entry;
} else if (entry->key == KWARGS_VAL(1)) {
} else if (krk_valuesSame(entry->key, KWARGS_VAL(1))) {
if (tombstone == entry) return tombstone;
if (tombstone == NULL) tombstone = entry;
} else if (krk_valuesSame(entry->key, key)) {
@ -192,12 +192,12 @@ int krk_tableGet_fast(KrkTable * table, KrkString * str, KrkValue * value) {
KrkTableEntry * tombstone = NULL;
for (;;) {
KrkTableEntry * entry = &table->entries[index];
if (entry->key == KWARGS_VAL(0)) {
if (krk_valuesSame(entry->key, KWARGS_VAL(0))) {
return 0;
} else if (entry->key == KWARGS_VAL(1)) {
} else if (krk_valuesSame(entry->key, KWARGS_VAL(1))) {
if (tombstone == entry) return 0;
if (tombstone == NULL) tombstone = entry;
} else if (entry->key == OBJECT_VAL(str)) {
} else if (krk_valuesSame(entry->key, OBJECT_VAL(str))) {
*value = entry->value;
return 1;
}
@ -236,9 +236,9 @@ KrkString * krk_tableFindString(KrkTable * table, const char * chars, size_t len
KrkTableEntry * tombstone = NULL;
for (;;) {
KrkTableEntry * entry = &table->entries[index];
if (entry->key == KWARGS_VAL(0)) {
if (krk_valuesSame(entry->key, KWARGS_VAL(0))) {
return NULL;
} else if (entry->key == KWARGS_VAL(1)) {
} else if (krk_valuesSame(entry->key, KWARGS_VAL(1))) {
if (tombstone == entry) return NULL;
if (tombstone == NULL) tombstone = entry;
} else if (AS_STRING(entry->key)->length == length &&

View File

@ -30,13 +30,6 @@ void krk_freeValueArray(KrkValueArray * array) {
krk_initValueArray(array);
}
/**
* Identity really should be the simple...
*/
int krk_valuesSame(KrkValue a, KrkValue b) {
return a == b;
}
static inline int _krk_method_equivalence(KrkValue a, KrkValue b) {
KrkClass * type = krk_getType(a);
if (likely(type && type->_eq)) {
@ -69,7 +62,7 @@ static inline int _krk_same_type_equivalence(uint16_t valtype, KrkValue a, KrkVa
case KRK_VAL_NOTIMPL:
case KRK_VAL_KWARGS:
case KRK_VAL_HANDLER:
return a == b;
return krk_valuesSame(a,b);
case KRK_VAL_OBJECT:
default:
return _krk_method_equivalence(a,b);
@ -101,7 +94,7 @@ static inline int _krk_diff_type_equivalence(uint16_t val_a, uint16_t val_b, Krk
_hot
int krk_valuesSameOrEqual(KrkValue a, KrkValue b) {
if (a == b) return 1;
if (krk_valuesSame(a,b)) return 1;
uint16_t val_a = KRK_VAL_TYPE(a);
uint16_t val_b = KRK_VAL_TYPE(b);
return (val_a == val_b)

View File

@ -1652,7 +1652,7 @@ static void clearCache(KrkClass * type) {
type->cacheIndex = 0;
for (size_t i = 0; i < type->subclasses.capacity; ++i) {
KrkTableEntry * entry = &type->subclasses.entries[i];
if (entry->key == KWARGS_VAL(0)) continue;
if (krk_valuesSame(entry->key, KWARGS_VAL(0))) continue;
clearCache(AS_CLASS(entry->key));
}
}
@ -2448,13 +2448,13 @@ _finishReturn: (void)0;
*/
case OP_JUMP_IF_FALSE_OR_POP: {
TWO_BYTE_OPERAND;
if (krk_peek(0) == BOOLEAN_VAL(0) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
if (krk_valuesSame(krk_peek(0), BOOLEAN_VAL(0)) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
else krk_pop();
break;
}
case OP_POP_JUMP_IF_FALSE: {
TWO_BYTE_OPERAND;
if (krk_peek(0) == BOOLEAN_VAL(0) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
if (krk_valuesSame(krk_peek(0), BOOLEAN_VAL(0)) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
krk_pop();
break;
}
@ -2542,7 +2542,7 @@ _finishReturn: (void)0;
krk_push(iter);
krk_push(krk_callStack(0));
/* krk_valuesSame() */
if (iter == krk_peek(0)) frame->ip += OPERAND;
if (krk_valuesSame(iter, krk_peek(0))) frame->ip += OPERAND;
break;
}
case OP_LOOP_ITER: {
@ -2550,12 +2550,12 @@ _finishReturn: (void)0;
KrkValue iter = krk_peek(0);
krk_push(iter);
krk_push(krk_callStack(0));
if (iter != krk_peek(0)) frame->ip -= OPERAND;
if (!krk_valuesSame(iter, krk_peek(0))) frame->ip -= OPERAND;
break;
}
case OP_TEST_ARG: {
TWO_BYTE_OPERAND;
if (krk_pop() != KWARGS_VAL(0)) frame->ip += OPERAND;
if (!krk_valuesSame(krk_pop(), KWARGS_VAL(0))) frame->ip += OPERAND;
break;
}
case OP_FILTER_EXCEPT: {

View File

@ -318,7 +318,7 @@ static int doSecondPass(FILE * out) {
WRITE_FUNCTION(AS_codeobject(*val));
break;
default:
if (*val == Ellipsis) {
if (krk_valuesSame(*val,Ellipsis)) {
fwrite("e",1,1,out);
break;
}