diff --git a/Makefile b/Makefile index 73bd43b..064862a 100644 --- a/Makefile +++ b/Makefile @@ -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}) diff --git a/src/exceptions.c b/src/exceptions.c index 51fcdb3..0551584 100644 --- a/src/exceptions.c +++ b/src/exceptions.c @@ -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(); diff --git a/src/kuroko/value.h b/src/kuroko/value.h index 6707d14..e0c2deb 100644 --- a/src/kuroko/value.h +++ b/src/kuroko/value.h @@ -7,6 +7,7 @@ #include #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) diff --git a/src/parseargs.c b/src/parseargs.c index 9724f0c..de6ea19 100644 --- a/src/parseargs.c +++ b/src/parseargs.c @@ -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; } diff --git a/src/table.c b/src/table.c index a75aa63..9969fc0 100644 --- a/src/table.c +++ b/src/table.c @@ -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 && diff --git a/src/value.c b/src/value.c index 434ace2..cb3da38 100644 --- a/src/value.c +++ b/src/value.c @@ -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) diff --git a/src/vm.c b/src/vm.c index f161990..7a61b59 100644 --- a/src/vm.c +++ b/src/vm.c @@ -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: { diff --git a/tools/compile.c b/tools/compile.c index b4d920c..1b6ce7d 100644 --- a/tools/compile.c +++ b/tools/compile.c @@ -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; }