From 4f91bf48f31fdca562090e7493b66a5fdbca0055 Mon Sep 17 00:00:00 2001 From: William Bell <62452284+Ugric@users.noreply.github.com> Date: Sat, 30 Aug 2025 03:29:02 +0100 Subject: [PATCH] improve performance for integers --- app.py | 7 +- src/arobject.h | 9 +- src/main.c | 2 +- src/parser/while/while.c | 2 + src/runtime/call/call.c | 1 + src/runtime/objects/number/number.c | 274 ++++++++++++++++++++++----- src/runtime/objects/number/number.h | 4 +- src/runtime/objects/object.c | 1 + src/runtime/objects/string/string.c | 2 +- src/runtime/runtime.c | 231 ++++++++++++++-------- src/shell.c | 2 +- src/translator/bytecode_spec.md | 22 +-- src/translator/number/number.c | 8 + src/translator/operation/operation.c | 66 +++---- src/translator/translator.c | 1 + src/translator/translator.h | 11 +- 16 files changed, 456 insertions(+), 187 deletions(-) diff --git a/app.py b/app.py index 56404f2..3a5094d 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,3 @@ -def f(): - return f() - -f() \ No newline at end of file +i = 1000000 +while i: + i=i-1 \ No newline at end of file diff --git a/src/arobject.h b/src/arobject.h index d51564d..fb05f2b 100644 --- a/src/arobject.h +++ b/src/arobject.h @@ -40,6 +40,7 @@ typedef struct { typedef struct { uint8_t registerCount; + uint8_t registerAssignment; DArray *return_jumps; DArray bytecode; ConstantArena constants; @@ -74,7 +75,13 @@ struct ArgonObject { struct hashmap_GC *dict; bool as_bool; union { - mpq_t *as_number; + struct as_number { + bool is_int64; + union { + mpq_t *mpq; + int64_t i64; + } n; + }as_number; struct string_struct as_str; native_fn native_fn; struct argon_function_struct argon_fn; diff --git a/src/main.c b/src/main.c index 8588913..3389cae 100644 --- a/src/main.c +++ b/src/main.c @@ -427,7 +427,7 @@ Translated load_argon_file(char *path, ArErr *err) { } hashmap_free(translated.constants.hashmap, NULL); Translated gc_translated = { - translated.registerCount, NULL, {}, {}, translated.path}; + translated.registerCount, translated.registerAssignment,NULL, {}, {}, translated.path}; gc_translated.bytecode.data = ar_alloc_atomic(translated.bytecode.capacity); memcpy(gc_translated.bytecode.data, translated.bytecode.data, translated.bytecode.capacity); diff --git a/src/parser/while/while.c b/src/parser/while/while.c index 064fd9d..6a34782 100644 --- a/src/parser/while/while.c +++ b/src/parser/while/while.c @@ -94,6 +94,8 @@ void free_parsed_while(void *ptr) { ParsedValue *parsedValue = ptr; ParsedWhile *parsed_while = parsedValue->data; free_parsed(parsed_while->condition); + free(parsed_while->condition); free_parsed(parsed_while->content); + free(parsed_while->content); free(parsed_while); } \ No newline at end of file diff --git a/src/runtime/call/call.c b/src/runtime/call/call.c index 6f53ac7..1c2954a 100644 --- a/src/runtime/call/call.c +++ b/src/runtime/call/call.c @@ -134,6 +134,7 @@ ArErr run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv, } StackFrame new_stackFrame = { {object->value.argon_fn.translated.registerCount, + object->value.argon_fn.translated.registerAssignment, NULL, {object->value.argon_fn.bytecode, sizeof(uint8_t), object->value.argon_fn.bytecode_length, diff --git a/src/runtime/objects/number/number.c b/src/runtime/objects/number/number.c index 68109bd..fa266ab 100644 --- a/src/runtime/objects/number/number.c +++ b/src/runtime/objects/number/number.c @@ -7,6 +7,7 @@ #include "number.h" #include "../functions/functions.h" #include "../string/string.h" +#include #include #include #include @@ -72,8 +73,7 @@ ArgonObject *ARGON_NUMBER_TYPE___boolean__(size_t argc, ArgonObject **argv, "__boolean__ expects 1 arguments, got %" PRIu64, argc); return ARGON_NULL; } - return mpq_cmp_si(*argv[0]->value.as_number, 0, 1) == 0 ? ARGON_FALSE - : ARGON_TRUE; + return argv[0]->as_bool ? ARGON_FALSE : ARGON_TRUE; } ArgonObject *ARGON_NUMBER_TYPE___add__(size_t argc, ArgonObject **argv, @@ -87,22 +87,60 @@ ArgonObject *ARGON_NUMBER_TYPE___add__(size_t argc, ArgonObject **argv, if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_field_for_class( get_field(argv[1], "__class__", false, false), "__name__", argv[1]); - *err = create_err(0, 0, 0, "", "Runtime Error", - "__add__ cannot perform addition between a number and %.*s", - type_name->value.as_str.length, - type_name->value.as_str.data); + *err = create_err( + 0, 0, 0, "", "Runtime Error", + "__add__ cannot perform addition between a number and %.*s", + type_name->value.as_str.length, type_name->value.as_str.data); return ARGON_NULL; } - mpq_t r; - mpq_init(r); - mpq_add(r, *argv[0]->value.as_number, *argv[1]->value.as_number); - ArgonObject *result = new_number_object(r); - mpq_clear(r); - return result; + if (argv[0]->value.as_number.is_int64 && argv[1]->value.as_number.is_int64) { + int64_t a = argv[0]->value.as_number.n.i64; + int64_t b = argv[1]->value.as_number.n.i64; + bool gonna_overflow = (a > 0 && b > 0 && a > INT64_MAX - b) || + (a < 0 && b < 0 && a < INT64_MIN - b); + if (!gonna_overflow) { + return new_number_object_from_num_and_den(a + b, 1); + } + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + mpq_set_si(a_GMP, a, 1); + mpq_set_si(b_GMP, b, 1); + mpq_add(a_GMP, a_GMP, b_GMP); + ArgonObject *result = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + return result; + } else if (!argv[0]->value.as_number.is_int64 && + !argv[1]->value.as_number.is_int64) { + mpq_t r; + mpq_init(r); + mpq_add(r, *argv[0]->value.as_number.n.mpq, + *argv[1]->value.as_number.n.mpq); + ArgonObject *result = new_number_object(r); + mpq_clear(r); + return result; + } else { + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + if (argv[0]->value.as_number.is_int64) { + mpq_set_si(a_GMP, argv[0]->value.as_number.n.i64, 1); + mpq_set(b_GMP, *argv[1]->value.as_number.n.mpq); + } else { + mpq_set(a_GMP, *argv[0]->value.as_number.n.mpq); + mpq_set_si(b_GMP, argv[1]->value.as_number.n.i64, 1); + } + mpq_add(a_GMP, a_GMP, b_GMP); + ArgonObject *result = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + return result; + } } ArgonObject *ARGON_NUMBER_TYPE___subtract__(size_t argc, ArgonObject **argv, - ArErr *err, RuntimeState *state) { + ArErr *err, RuntimeState *state) { (void)state; if (argc != 2) { *err = create_err(0, 0, 0, "", "Runtime Error", @@ -114,20 +152,61 @@ ArgonObject *ARGON_NUMBER_TYPE___subtract__(size_t argc, ArgonObject **argv, if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_field_for_class( get_field(argv[1], "__class__", false, false), "__name__", argv[1]); - *err = create_err(0, 0, 0, "", "Runtime Error", - "__subtract__ cannot perform subtraction between number and %.*s", - type_name->value.as_str.length, - type_name->value.as_str.data); + *err = create_err( + 0, 0, 0, "", "Runtime Error", + "__subtract__ cannot perform subtraction between number and %.*s", + type_name->value.as_str.length, type_name->value.as_str.data); return ARGON_NULL; } - mpq_sub(r, *argv[0]->value.as_number, *argv[1]->value.as_number); - ArgonObject *result = new_number_object(r); - mpq_clear(r); - return result; + if (argv[0]->value.as_number.is_int64 && argv[1]->value.as_number.is_int64) { + int64_t a = argv[0]->value.as_number.n.i64; + int64_t b = argv[1]->value.as_number.n.i64; + int64_t neg_a = -a; + bool gonna_overflow = (neg_a > 0 && b > 0 && b > INT64_MAX - neg_a) || + (neg_a < 0 && b < 0 && b < INT64_MIN - neg_a); + if (!gonna_overflow) { + return new_number_object_from_num_and_den(a - b, 1); + } + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + mpq_set_si(a_GMP, a, 1); + mpq_set_si(b_GMP, b, 1); + mpq_sub(a_GMP, a_GMP, b_GMP); + ArgonObject *result = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + return result; + } else if (!argv[0]->value.as_number.is_int64 && + !argv[1]->value.as_number.is_int64) { + mpq_t r; + mpq_init(r); + mpq_sub(r, *argv[0]->value.as_number.n.mpq, + *argv[1]->value.as_number.n.mpq); + ArgonObject *result = new_number_object(r); + mpq_clear(r); + return result; + } else { + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + if (argv[0]->value.as_number.is_int64) { + mpq_set_si(a_GMP, argv[0]->value.as_number.n.i64, 1); + mpq_set(b_GMP, *argv[1]->value.as_number.n.mpq); + } else { + mpq_set(a_GMP, *argv[0]->value.as_number.n.mpq); + mpq_set_si(b_GMP, argv[1]->value.as_number.n.i64, 1); + } + mpq_sub(a_GMP, a_GMP, b_GMP); + ArgonObject *result = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + return result; + } } ArgonObject *ARGON_NUMBER_TYPE___multiply__(size_t argc, ArgonObject **argv, - ArErr *err, RuntimeState *state) { + ArErr *err, RuntimeState *state) { (void)state; if (argc != 2) { *err = create_err(0, 0, 0, "", "Runtime Error", @@ -139,20 +218,20 @@ ArgonObject *ARGON_NUMBER_TYPE___multiply__(size_t argc, ArgonObject **argv, if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_field_for_class( get_field(argv[1], "__class__", false, false), "__name__", argv[1]); - *err = create_err(0, 0, 0, "", "Runtime Error", - "__multiply__ cannot perform multiplication between number and %.*s", - type_name->value.as_str.length, - type_name->value.as_str.data); + *err = create_err( + 0, 0, 0, "", "Runtime Error", + "__multiply__ cannot perform multiplication between number and %.*s", + type_name->value.as_str.length, type_name->value.as_str.data); return ARGON_NULL; } - mpq_mul(r, *argv[0]->value.as_number, *argv[1]->value.as_number); + mpq_mul(r, *argv[0]->value.as_number.n.mpq, *argv[1]->value.as_number.n.mpq); ArgonObject *result = new_number_object(r); mpq_clear(r); return result; } ArgonObject *ARGON_NUMBER_TYPE___division__(size_t argc, ArgonObject **argv, - ArErr *err, RuntimeState *state) { + ArErr *err, RuntimeState *state) { (void)state; if (argc != 2) { *err = create_err(0, 0, 0, "", "Runtime Error", @@ -164,13 +243,13 @@ ArgonObject *ARGON_NUMBER_TYPE___division__(size_t argc, ArgonObject **argv, if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_field_for_class( get_field(argv[1], "__class__", false, false), "__name__", argv[1]); - *err = create_err(0, 0, 0, "", "Runtime Error", - "__division__ cannot perform division between number and %.*s", - type_name->value.as_str.length, - type_name->value.as_str.data); + *err = create_err( + 0, 0, 0, "", "Runtime Error", + "__division__ cannot perform division between number and %.*s", + type_name->value.as_str.length, type_name->value.as_str.data); return ARGON_NULL; } - mpq_div(r, *argv[0]->value.as_number, *argv[1]->value.as_number); + mpq_div(r, *argv[0]->value.as_number.n.mpq, *argv[1]->value.as_number.n.mpq); ArgonObject *result = new_number_object(r); mpq_clear(r); return result; @@ -185,7 +264,13 @@ ArgonObject *ARGON_NUMBER_TYPE___string__(size_t argc, ArgonObject **argv, return NULL; } - mpq_t *num = argv[0]->value.as_number; + if (argv[0]->value.as_number.is_int64) { + char buf[32]; + snprintf(buf, sizeof(buf), "%" PRId64, argv[0]->value.as_number.n.i64); + return new_string_object_null_terminated(buf); + } + + mpq_t *num = argv[0]->value.as_number.n.mpq; /* If denominator == 1, print numerator as full integer */ if (mpz_cmp_ui(mpq_denref(*num), 1) == 0) { @@ -327,6 +412,20 @@ ArgonObject *ARGON_NUMBER_TYPE___string__(size_t argc, ArgonObject **argv, return result; } +ArgonObject small_ints[UINT8_MAX * 2]; + +void init_small_ints() { + for (uint64_t i = 0; i < UINT8_MAX * 2; i++) { + small_ints[i].type = TYPE_NUMBER; + small_ints[i].dict = createHashmap_GC(); + add_field(&small_ints[i], "__class__", ARGON_NUMBER_TYPE); + add_field(&small_ints[i], "__base__", BASE_CLASS); + small_ints[i].value.as_number.is_int64 = true; + small_ints[i].value.as_number.n.i64 = i; + small_ints[i].as_bool = i; + } +} + void create_ARGON_NUMBER_TYPE() { ARGON_NUMBER_TYPE = new_object(); add_field(ARGON_NUMBER_TYPE, "__name__", @@ -343,8 +442,7 @@ void create_ARGON_NUMBER_TYPE() { create_argon_native_function("__boolean__", ARGON_NUMBER_TYPE___boolean__)); add_field(ARGON_NUMBER_TYPE, "__add__", - create_argon_native_function("__add__", - ARGON_NUMBER_TYPE___add__)); + create_argon_native_function("__add__", ARGON_NUMBER_TYPE___add__)); add_field(ARGON_NUMBER_TYPE, "__subtract__", create_argon_native_function("__subtract__", ARGON_NUMBER_TYPE___subtract__)); @@ -354,6 +452,7 @@ void create_ARGON_NUMBER_TYPE() { add_field(ARGON_NUMBER_TYPE, "__division__", create_argon_native_function("__division__", ARGON_NUMBER_TYPE___division__)); + init_small_ints(); } void mpz_init_gc_managed(mpz_t z, size_t limbs_count) { @@ -393,43 +492,116 @@ mpq_t *mpq_new_gc_from(const mpq_t src) { return dest; } +bool mpq_to_int64(mpq_t q, int64_t *out) { + // Check denominator == 1 + if (mpz_cmp_ui(mpq_denref(q), 1) != 0) { + return false; + } + + // Get numerator + mpz_t num; + mpz_init(num); + mpz_set(num, mpq_numref(q)); + + // Check bounds + if (mpz_cmp_si(num, INT64_MIN) < 0 || mpz_cmp_si(num, INT64_MAX) > 0) { + mpz_clear(num); + return false; + } + + *out = mpz_get_si(num); // safe because we checked range + mpz_clear(num); + return true; +} + +bool double_to_int64(double x, int64_t *out) { + if (x < (double)INT64_MIN || x > (double)INT64_MAX) { + return false; + } + + int64_t i = (int64_t)x; + if ((double)i == x) { // no fractional part + *out = i; + return true; + } + return false; +} + ArgonObject *new_number_object(mpq_t number) { + int64_t i64 = 0; + bool is_int64 = mpq_to_int64(number, &i64); + if (is_int64 && i64 >= INT8_MIN * 2 && i64 <= INT8_MAX * 2) { + return &small_ints[i64]; + } ArgonObject *object = new_object(); add_field(object, "__class__", ARGON_NUMBER_TYPE); object->type = TYPE_NUMBER; - object->value.as_number = mpq_new_gc_from(number); - object->as_bool = mpq_cmp_si(number, 0, 1) != 0; + object->value.as_number.n.i64 = i64; + object->value.as_number.is_int64 = is_int64; + if (object->value.as_number.is_int64) { + object->as_bool = object->value.as_number.n.i64; + } else { + object->value.as_number.n.mpq = mpq_new_gc_from(number); + object->as_bool = mpq_cmp_si(number, 0, 1) != 0; + } return object; } -ArgonObject *new_number_object_from_long(long n, unsigned long d) { +ArgonObject *new_number_object_from_num_and_den(int64_t n, uint64_t d) { + if (d == 1 && n >= INT8_MIN && n <= INT8_MAX) { + return &small_ints[(int8_t)n]; + } ArgonObject *object = new_object(); add_field(object, "__class__", ARGON_NUMBER_TYPE); - mpq_t r; - mpq_init(r); - mpq_set_si(r, n, d); object->type = TYPE_NUMBER; - object->value.as_number = mpq_new_gc_from(r); - object->as_bool = n!=0; - mpq_clear(r); + if (d == 1) { + object->value.as_number.is_int64 = true; + object->value.as_number.n.i64 = n; + object->as_bool = n; + } else { + object->value.as_number.is_int64 = false; + mpq_t r; + mpq_init(r); + mpq_set_si(r, n, d); + object->value.as_number.n.mpq = mpq_new_gc_from(r); + object->as_bool = n != 0; + mpq_clear(r); + } return object; } ArgonObject *new_number_object_from_double(double d) { + int64_t i64 = 0; + bool is_int64 = double_to_int64(d, &i64); + if (is_int64 && i64 >= INT8_MIN && i64 <= INT8_MAX) { + return &small_ints[(int8_t)i64]; + } ArgonObject *object = new_object(); add_field(object, "__class__", ARGON_NUMBER_TYPE); - mpq_t r; - mpq_init(r); - mpq_set_d(r, d); object->type = TYPE_NUMBER; - object->value.as_number = mpq_new_gc_from(r); - object->as_bool = d!=0; - mpq_clear(r); + object->value.as_number.n.i64 = i64; + object->value.as_number.is_int64 = is_int64; + if (object->value.as_number.is_int64) { + object->as_bool = object->value.as_number.n.i64; + } else { + mpq_t r; + mpq_init(r); + mpq_set_d(r, d); + object->value.as_number.n.mpq = mpq_new_gc_from(r); + object->as_bool = d != 0; + mpq_clear(r); + } return object; } void load_number(Translated *translated, RuntimeState *state) { uint8_t to_register = pop_byte(translated, state); + uint8_t is_int64 = pop_byte(translated, state); + if (is_int64) { + state->registers[to_register] = + new_number_object_from_num_and_den(pop_bytecode(translated, state), 1); + return; + } size_t num_size = pop_bytecode(translated, state); size_t num_pos = pop_bytecode(translated, state); mpq_t r; diff --git a/src/runtime/objects/number/number.h b/src/runtime/objects/number/number.h index 62f5d03..9b933a0 100644 --- a/src/runtime/objects/number/number.h +++ b/src/runtime/objects/number/number.h @@ -14,10 +14,12 @@ void create_ARGON_NUMBER_TYPE(); ArgonObject *new_number_object(mpq_t number); +bool mpq_to_int64(mpq_t q, int64_t *out); + void load_number(Translated *translated, RuntimeState *state); ArgonObject *new_number_object_from_double(double d); -ArgonObject *new_number_object_from_long(long n, unsigned long d); +ArgonObject *new_number_object_from_num_and_den(int64_t n, uint64_t d); #endif // RUNTIME_NUMBER_H \ No newline at end of file diff --git a/src/runtime/objects/object.c b/src/runtime/objects/object.c index 98e74e8..6071e32 100644 --- a/src/runtime/objects/object.c +++ b/src/runtime/objects/object.c @@ -15,6 +15,7 @@ ArgonObject *BASE_CLASS = NULL; + ArgonObject *new_object() { ArgonObject *object = ar_alloc(sizeof(ArgonObject)); object->type = TYPE_OBJECT; diff --git a/src/runtime/objects/string/string.c b/src/runtime/objects/string/string.c index 57344a7..d643ed8 100644 --- a/src/runtime/objects/string/string.c +++ b/src/runtime/objects/string/string.c @@ -17,7 +17,7 @@ ArgonObject *ARGON_STRING_TYPE = NULL; ArgonObject *new_string_object(char*data, size_t length) { ArgonObject * object = new_object(); add_field(object, "__class__", ARGON_STRING_TYPE); - add_field(object, "length", new_number_object_from_long(length, 1)); + add_field(object, "length", new_number_object_from_num_and_den(length, 1)); object->type = TYPE_STRING; object->value.as_str.data = ar_alloc_atomic(length); memcpy(object->value.as_str.data, data, length); diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index bf24582..0084e34 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -376,7 +376,7 @@ ArgonObject *ARGON_BOOL_TYPE___number__(size_t argc, ArgonObject **argv, "__number__ expects 1 arguments, got %" PRIu64, argc); return ARGON_NULL; } - return new_number_object_from_long(argv[0] == ARGON_TRUE, 1); + return new_number_object_from_num_and_den(argv[0] == ARGON_TRUE, 1); } ArgonObject *ARGON_STRING_TYPE___string__(size_t argc, ArgonObject **argv, @@ -451,7 +451,7 @@ ArgonObject *ARGON_NULL_TYPE___number__(size_t argc, ArgonObject **argv, *err = create_err(0, 0, 0, "", "Runtime Error", "__boolean__ expects 1 arguments, got %" PRIu64, argc); } - return new_number_object_from_long(0, 1); + return new_number_object_from_num_and_den(0, 1); } ArgonObject *ARGON_NULL_TYPE___string__(size_t argc, ArgonObject **argv, ArErr *err, RuntimeState *state) { @@ -655,29 +655,28 @@ static inline ArErr load_variable(Translated *translated, RuntimeState *state, static inline ArErr run_instruction(Translated *translated, RuntimeState *state, struct Stack **stack) { - static void *dispatch_table[] = { - [OP_LOAD_STRING] = &&DO_LOAD_STRING, - [OP_DECLARE] = &&DO_DECLARE, - [OP_LOAD_NULL] = &&DO_LOAD_NULL, - [OP_LOAD_FUNCTION] = &&DO_LOAD_FUNCTION, - [OP_IDENTIFIER] = &&DO_IDENTIFIER, - [OP_BOOL] = &&DO_BOOL, - [OP_JUMP_IF_FALSE] = &&DO_JUMP_IF_FALSE, - [OP_JUMP] = &&DO_JUMP, - [OP_NEW_SCOPE] = &&DO_NEW_SCOPE, - [OP_POP_SCOPE] = &&DO_POP_SCOPE, - [OP_INIT_CALL] = &&DO_INIT_CALL, - [OP_INSERT_ARG] = &&DO_INSERT_ARG, - [OP_CALL] = &&DO_CALL, - [OP_SOURCE_LOCATION] = &&DO_SOURCE_LOCATION, - [OP_LOAD_ACCESS_FUNCTION] = &&DO_LOAD_ACCESS_FUNCTION, - [OP_LOAD_BOOL] = &&DO_LOAD_BOOL, - [OP_LOAD_NUMBER] = &&DO_LOAD_NUMBER, - [OP_LOAD_ADDITION_FUNCTION] = &&DO_LOAD_ADDITION_FUNCTION, - [OP_LOAD_SUBTRACTION_FUNCTION] = &&DO_LOAD_SUBTRACTION_FUNCTION, - [OP_LOAD_MULTIPLY_FUNCTION] = &&DO_LOAD_MULTIPLY_FUNCTION, - [OP_LOAD_DIVISION_FUNCTION] = &&DO_LOAD_DIVISION_FUNCTION, - [OP_ASSIGN] = &&DO_ASSIGN}; + static void *dispatch_table[] = {[OP_LOAD_STRING] = &&DO_LOAD_STRING, + [OP_DECLARE] = &&DO_DECLARE, + [OP_LOAD_NULL] = &&DO_LOAD_NULL, + [OP_LOAD_FUNCTION] = &&DO_LOAD_FUNCTION, + [OP_IDENTIFIER] = &&DO_IDENTIFIER, + [OP_BOOL] = &&DO_BOOL, + [OP_JUMP_IF_FALSE] = &&DO_JUMP_IF_FALSE, + [OP_JUMP] = &&DO_JUMP, + [OP_NEW_SCOPE] = &&DO_NEW_SCOPE, + [OP_POP_SCOPE] = &&DO_POP_SCOPE, + [OP_INIT_CALL] = &&DO_INIT_CALL, + [OP_INSERT_ARG] = &&DO_INSERT_ARG, + [OP_CALL] = &&DO_CALL, + [OP_SOURCE_LOCATION] = &&DO_SOURCE_LOCATION, + [OP_LOAD_BOOL] = &&DO_LOAD_BOOL, + [OP_LOAD_NUMBER] = &&DO_LOAD_NUMBER, + [OP_ASSIGN] = &&DO_ASSIGN, + [OP_COPY_TO_REGISTER] = + &&DO_COPY_TO_REGISTER, + [OP_ADDITION] = &&DO_ADDITION, + [OP_SUBTRACTION] = &&DO_SUBTRACTION, + [OP_LOAD_ACCESS_FUNCTION] = &&DO_LOAD_ACCESS_FUNCTION}; goto *dispatch_table[pop_byte(translated, state)]; DO_LOAD_NULL: state->registers[pop_byte(translated, state)] = ARGON_NULL; @@ -723,28 +722,7 @@ DO_JUMP: DO_NEW_SCOPE: *stack = create_scope(*stack); goto BREAK; -DO_POP_SCOPE:; - // struct node_GC *array = - // checked_malloc(sizeof(struct node_GC) * (*stack)->scope->count); - // size_t j = 0; - // for (size_t i = 0; i < (*stack)->scope->size; i++) { - // struct node_GC *temp = (*stack)->scope->list[i]; - // while (temp) { - // array[j++] = *temp; - // temp = temp->next; - // } - // } - // qsort(array, (*stack)->scope->count, sizeof(struct node_GC), - // compare_by_order); - // for (size_t i = 0; i < (*stack)->scope->count; i++) { - // struct node_GC temp = array[i]; - // printf("%.*s = %.*s\n", - // (int)((ArgonObject *)temp.key)->value.as_str.length, - // ((ArgonObject *)temp.key)->value.as_str.data, - // (int)((ArgonObject *)temp.val)->value.as_str.length, - // ((ArgonObject *)temp.val)->value.as_str.data); - // } - // free(array); +DO_POP_SCOPE: *stack = (*stack)->prev; goto BREAK; DO_INIT_CALL: { @@ -758,7 +736,7 @@ DO_INIT_CALL: { } DO_INSERT_ARG:; size_t index = pop_bytecode(translated, state); - (state->call_instance->args)[index] = state->registers[0]; + state->call_instance->args[index] = state->registers[0]; goto BREAK; DO_CALL: { ArErr err = @@ -766,25 +744,6 @@ DO_CALL: { state->call_instance->args, state, false); state->call_instance = (*state->call_instance).previous; return err; - // ArgonObject *object = state->registers[from_register]; - // char *field = "__class__"; - // uint64_t hash = siphash64_bytes(field, strlen(field), siphash_key); - // ArgonObject *class = hashmap_lookup_GC(object->dict, hash); - // field = "__name__"; - // hash = siphash64_bytes(field, strlen(field), siphash_key); - // ArgonObject *class_name = hashmap_lookup_GC(class->dict, hash); - // hash = siphash64_bytes(field, strlen(field), siphash_key); - // ArgonObject *object_name = hashmap_lookup_GC(object->dict, hash); - // if (object_name) { - // printf("call <%.*s %.*s at %p>\n", - // (int)class_name->value.as_str.length, - // class_name->value.as_str.data,(int)object_name->value.as_str.length, - // object_name->value.as_str.data, object); - // } else { - // printf("call <%.*s object at %p>\n", - // (int)class_name->value.as_str.length, class_name->value.as_str.data, - // object); - // } } DO_SOURCE_LOCATION: state->source_location = (SourceLocation){pop_bytecode(translated, state), @@ -797,18 +756,134 @@ DO_LOAD_BOOL: DO_LOAD_ACCESS_FUNCTION: state->registers[0] = ACCESS_FUNCTION; goto BREAK; -DO_LOAD_ADDITION_FUNCTION: - state->registers[0] = ADDITION_FUNCTION; - goto BREAK; -DO_LOAD_SUBTRACTION_FUNCTION: - state->registers[0] = SUBTRACTION_FUNCTION; - goto BREAK; -DO_LOAD_MULTIPLY_FUNCTION: - state->registers[0] = MULTIPLY_FUNCTION; - goto BREAK; -DO_LOAD_DIVISION_FUNCTION: - state->registers[0] = DIVISION_FUNCTION; +DO_COPY_TO_REGISTER: { + uint8_t from_register = pop_byte(translated, state); + uint64_t to_register = pop_byte(translated, state); + state->registers[to_register] = state->registers[from_register]; goto BREAK; +} +DO_ADDITION: { + uint8_t registerA = pop_byte(translated, state); + uint64_t registerB = pop_byte(translated, state); + uint64_t registerC = pop_byte(translated, state); + + ArgonObject *valueA = state->registers[registerA]; + ArgonObject *valueB = state->registers[registerB]; + + if (valueA->type == TYPE_NUMBER && valueB->type == TYPE_NUMBER) { + if (valueA->value.as_number.is_int64 && valueB->value.as_number.is_int64) { + int64_t a = valueA->value.as_number.n.i64; + int64_t b = valueB->value.as_number.n.i64; + bool gonna_overflow = (a > 0 && b > 0 && a > INT64_MAX - b) || + (a < 0 && b < 0 && a < INT64_MIN - b); + if (!gonna_overflow) { + state->registers[registerC] = + new_number_object_from_num_and_den(a + b, 1); + goto BREAK; + } + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + mpq_set_si(a_GMP, a, 1); + mpq_set_si(b_GMP, b, 1); + mpq_add(a_GMP, a_GMP, b_GMP); + state->registers[registerC] = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + } else if (!valueA->value.as_number.is_int64 && + !valueB->value.as_number.is_int64) { + mpq_t r; + mpq_init(r); + mpq_add(r, *valueA->value.as_number.n.mpq, + *valueB->value.as_number.n.mpq); + state->registers[registerC] = new_number_object(r); + mpq_clear(r); + } else { + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + if (valueA->value.as_number.is_int64) { + mpq_set_si(a_GMP, valueA->value.as_number.n.i64, 1); + mpq_set(b_GMP, *valueB->value.as_number.n.mpq); + } else { + mpq_set(a_GMP, *valueA->value.as_number.n.mpq); + mpq_set_si(b_GMP, valueB->value.as_number.n.i64, 1); + } + mpq_add(a_GMP, a_GMP, b_GMP); + state->registers[registerC] = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + } + goto BREAK; + } + + ArErr err = no_err; + ArgonObject *args[] = {valueA, valueB}; + state->registers[registerC] = ARGON_ADDITION_FUNCTION(2, args, &err, state); + return err; +} +DO_SUBTRACTION: { + uint8_t registerA = pop_byte(translated, state); + uint64_t registerB = pop_byte(translated, state); + uint64_t registerC = pop_byte(translated, state); + + ArgonObject *valueA = state->registers[registerA]; + ArgonObject *valueB = state->registers[registerB]; + + if (valueA->type == TYPE_NUMBER && valueB->type == TYPE_NUMBER) { + if (valueA->value.as_number.is_int64 && valueB->value.as_number.is_int64) { + int64_t a = valueA->value.as_number.n.i64; + int64_t b = valueB->value.as_number.n.i64; + int64_t neg_a = -a; + bool gonna_overflow = (neg_a > 0 && b > 0 && b > INT64_MAX - neg_a) || + (neg_a < 0 && b < 0 && b < INT64_MIN - neg_a); + if (!gonna_overflow) { + state->registers[registerC] = + new_number_object_from_num_and_den(a - b, 1); + goto BREAK; + } + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + mpq_set_si(a_GMP, a, 1); + mpq_set_si(b_GMP, b, 1); + mpq_sub(a_GMP, a_GMP, b_GMP); + state->registers[registerC] = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + } else if (!valueA->value.as_number.is_int64 && + !valueB->value.as_number.is_int64) { + mpq_t r; + mpq_init(r); + mpq_sub(r, *valueA->value.as_number.n.mpq, + *valueB->value.as_number.n.mpq); + state->registers[registerC] = new_number_object(r); + mpq_clear(r); + } else { + mpq_t a_GMP, b_GMP; + mpq_init(a_GMP); + mpq_init(b_GMP); + if (valueA->value.as_number.is_int64) { + mpq_set_si(a_GMP, valueA->value.as_number.n.i64, 1); + mpq_set(b_GMP, *valueB->value.as_number.n.mpq); + } else { + mpq_set(a_GMP, *valueA->value.as_number.n.mpq); + mpq_set_si(b_GMP, valueB->value.as_number.n.i64, 1); + } + mpq_sub(a_GMP, a_GMP, b_GMP); + state->registers[registerC] = new_number_object(a_GMP); + mpq_clear(a_GMP); + mpq_clear(b_GMP); + } + goto BREAK; + } + + ArErr err = no_err; + ArgonObject *args[] = {valueA, valueB}; + state->registers[registerC] = ARGON_ADDITION_FUNCTION(2, args, &err, state); + return err; +} + BREAK: return no_err; } diff --git a/src/shell.c b/src/shell.c index 41c8fa1..3ab199d 100644 --- a/src/shell.c +++ b/src/shell.c @@ -91,7 +91,7 @@ int execute_code(FILE *stream, char *path, Stack *scope, hashmap_free(__translated.constants.hashmap, NULL); Translated translated = { - __translated.registerCount, NULL, {}, {}, __translated.path}; + __translated.registerCount, __translated.registerAssignment, NULL, {}, {}, __translated.path}; translated.bytecode.data = ar_alloc(__translated.bytecode.capacity); memcpy(translated.bytecode.data, __translated.bytecode.data, __translated.bytecode.capacity); diff --git a/src/translator/bytecode_spec.md b/src/translator/bytecode_spec.md index 730a92f..9495985 100644 --- a/src/translator/bytecode_spec.md +++ b/src/translator/bytecode_spec.md @@ -131,27 +131,27 @@ loads a boolean into register 1 ## OP_LOAD_NUMBER -loads a mpq_t number into memory +loads a mpq_t / int64 number into memory 1. the register to write to. (*) +1. is int64 (*) 1. the size of the numerator in the constant buffer. 1. the offset in the constant buffer of the numerator. 1. is integer. (*) 1. the size of the denominator in the constant buffer. 1. the offset in the constant buffer of the denominator. -## OP_LOAD_ADDITION_FUNCTION +## OP_COPY_TO_REGISTER -loads the addition function into register 1 +copies the value from one register to another -## OP_LOAD_SUBTRACTION_FUNCTION +1. the register to copy from (*) +2. the register to write to (*) -loads the subtraction function into register 1 +## OP_ADDITION -## OP_LOAD_MULTIPLY_FUNCTION +performs an addition between register A and register B, storing the result in register C -loads the multiply function into register 1 - -## OP_LOAD_DIVISION_FUNCTION - -loads the division function into register 1 \ No newline at end of file +1. the register A (*) +2. the register B (*) +2. the register C (*) \ No newline at end of file diff --git a/src/translator/number/number.c b/src/translator/number/number.c index 2355c18..954b996 100644 --- a/src/translator/number/number.c +++ b/src/translator/number/number.c @@ -5,6 +5,7 @@ */ #include "number.h" +#include "../../runtime/objects/number/number.h" #include "../translator.h" #include #include @@ -16,6 +17,13 @@ size_t translate_parsed_number(Translated *translated, mpq_t *number, set_registers(translated, to_register + 1); size_t start = push_instruction_byte(translated, OP_LOAD_NUMBER); push_instruction_byte(translated, to_register); + int64_t i64; + uint8_t is_int64 = mpq_to_int64(*number, &i64); + push_instruction_byte(translated, is_int64); + if (is_int64) { + push_instruction_code(translated, i64); + return start; + } size_t num_size; void *num_data = mpz_export(NULL, &num_size, 1, 1, 0, 0, mpq_numref(*number)); size_t numerator_pos = arena_push(&translated->constants, num_data, num_size); diff --git a/src/translator/operation/operation.c b/src/translator/operation/operation.c index 357c9e6..021069f 100644 --- a/src/translator/operation/operation.c +++ b/src/translator/operation/operation.c @@ -6,42 +6,44 @@ #include "operation.h" #include +#include +#include size_t translate_operation(Translated *translated, ParsedOperation *operation, ArErr *err) { - set_registers(translated, 1); - uint64_t first; - switch (operation->operation) { - case TOKEN_PLUS:; - first = push_instruction_byte(translated, OP_LOAD_ADDITION_FUNCTION); - break; - case TOKEN_MINUS: - first = push_instruction_byte(translated, OP_LOAD_SUBTRACTION_FUNCTION); - break; - case TOKEN_STAR: - first = push_instruction_byte(translated, OP_LOAD_MULTIPLY_FUNCTION); - break; - case TOKEN_SLASH: - first = push_instruction_byte(translated, OP_LOAD_DIVISION_FUNCTION); - break; - default: - *err = create_err(operation->line, operation->column, operation->length, - translated->path, "Syntax Error", "unknown operation"); - return 0; - } - push_instruction_byte(translated, OP_INIT_CALL); - push_instruction_code(translated, operation->to_operate_on.size); - - for (size_t i = 0; i < operation->to_operate_on.size; i++) { + uint8_t registerA = translated->registerAssignment++; + uint8_t registerB = translated->registerAssignment++; + set_registers(translated, translated->registerAssignment); + uint64_t first = translate_parsed(translated, darray_get(&operation->to_operate_on, 0), err); + push_instruction_byte(translated, OP_COPY_TO_REGISTER); + push_instruction_byte(translated, 0); + push_instruction_byte(translated, registerA); + for (size_t i = 1; i < operation->to_operate_on.size; i++) { translate_parsed(translated, darray_get(&operation->to_operate_on, i), err); - push_instruction_byte(translated, OP_INSERT_ARG); - push_instruction_code(translated, i); + push_instruction_byte(translated, OP_COPY_TO_REGISTER); + push_instruction_byte(translated, 0); + push_instruction_byte(translated, registerB); + switch (operation->operation) { + case TOKEN_PLUS:; + push_instruction_byte(translated, OP_ADDITION); + break; + case TOKEN_MINUS:; + push_instruction_byte(translated, OP_SUBTRACTION); + break; + default: + *err = create_err(operation->line, operation->column, operation->length, + translated->path, "Syntax Error", "unknown operation"); + return 0; + } + push_instruction_byte(translated, registerA); + push_instruction_byte(translated, registerB); + push_instruction_byte( + translated, operation->to_operate_on.size - 1 == i ? 0 : registerA); } - - push_instruction_byte(translated, OP_SOURCE_LOCATION); - push_instruction_code(translated, operation->line); - push_instruction_code(translated, operation->column); - push_instruction_code(translated, operation->length); - push_instruction_byte(translated, OP_CALL); + push_instruction_byte(translated, OP_LOAD_NULL); + push_instruction_byte(translated, registerA); + push_instruction_byte(translated, OP_LOAD_NULL); + push_instruction_byte(translated, registerB); + translated->registerAssignment -= 2; return first; } \ No newline at end of file diff --git a/src/translator/translator.c b/src/translator/translator.c index 5fe3f19..b362283 100644 --- a/src/translator/translator.c +++ b/src/translator/translator.c @@ -86,6 +86,7 @@ Translated init_translator(char *path) { Translated translated; translated.path = path; translated.registerCount = 1; + translated.registerAssignment = 1; translated.return_jumps = NULL; darray_init(&translated.bytecode, sizeof(uint8_t)); arena_init(&translated.constants); diff --git a/src/translator/translator.h b/src/translator/translator.h index 8cbe9af..0657f9f 100644 --- a/src/translator/translator.h +++ b/src/translator/translator.h @@ -29,14 +29,13 @@ typedef enum { OP_INSERT_ARG, OP_CALL, OP_SOURCE_LOCATION, - OP_LOAD_ACCESS_FUNCTION, OP_LOAD_BOOL, OP_LOAD_NUMBER, - OP_LOAD_ADDITION_FUNCTION, - OP_LOAD_SUBTRACTION_FUNCTION, - OP_LOAD_MULTIPLY_FUNCTION, - OP_LOAD_DIVISION_FUNCTION, - OP_ASSIGN + OP_ASSIGN, + OP_COPY_TO_REGISTER, + OP_ADDITION, + OP_SUBTRACTION, + OP_LOAD_ACCESS_FUNCTION } OperationType; void arena_resize(ConstantArena *arena, size_t new_size);