From ef23faa3e6e003282e53fc08031ec4eb9a2e2aae Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 7 Apr 2025 00:46:26 -0400 Subject: Add Byte.to() method and improved micro optimization of iterating over fixed-width integer ranges --- src/compile.c | 64 ++++++++++++++++++++++++++++++++------------------ src/environment.c | 1 + src/stdlib/bytes.c | 25 ++++++++++++++++++++ src/stdlib/bytes.h | 9 ++----- src/stdlib/datatypes.h | 8 +++++++ src/typecheck.c | 12 +++++----- src/types.c | 2 +- 7 files changed, 84 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/compile.c b/src/compile.c index 40d9ed4e..3b382111 100644 --- a/src/compile.c +++ b/src/compile.c @@ -947,7 +947,7 @@ CORD optional_into_nonnone(type_t *t, CORD value) { if (t->tag == OptionalType) t = Match(t, OptionalType)->type; switch (t->tag) { - case IntType: + case IntType: case ByteType: return CORD_all(value, ".value"); case StructType: if (t == PATH_TYPE || t == PATH_TYPE_TYPE) @@ -1500,52 +1500,70 @@ static CORD _compile_statement(env_t *env, ast_t *ast) // Special case for improving performance for numeric iteration: if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "to") && - get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) { + is_int_type(get_type(env, Match(for_->iter, MethodCall)->self))) { // TODO: support other integer types arg_ast_t *args = Match(for_->iter, MethodCall)->args; if (!args) code_err(for_->iter, "to() needs at least one argument"); + type_t *int_type = get_type(env, Match(for_->iter, MethodCall)->self); + type_t *step_type = int_type->tag == ByteType ? Type(IntType, .bits=TYPE_IBITS8) : int_type; + CORD last = CORD_EMPTY, step = CORD_EMPTY, optional_step = CORD_EMPTY; if (!args->name || streq(args->name, "last")) { - last = compile_to_type(env, args->value, INT_TYPE); + last = compile_to_type(env, args->value, int_type); if (args->next) { if (args->next->name && !streq(args->next->name, "step")) code_err(args->next->value, "Invalid argument name: ", args->next->name); if (get_type(env, args->next->value)->tag == OptionalType) - optional_step = compile_to_type(env, args->next->value, Type(OptionalType, .type=INT_TYPE)); + optional_step = compile_to_type(env, args->next->value, Type(OptionalType, step_type)); else - step = compile_to_type(env, args->next->value, INT_TYPE); + step = compile_to_type(env, args->next->value, step_type); } } else if (streq(args->name, "step")) { if (get_type(env, args->value)->tag == OptionalType) - optional_step = compile_to_type(env, args->value, Type(OptionalType, .type=INT_TYPE)); + optional_step = compile_to_type(env, args->value, Type(OptionalType, step_type)); else - step = compile_to_type(env, args->value, INT_TYPE); + step = compile_to_type(env, args->value, step_type); if (args->next) { if (args->next->name && !streq(args->next->name, "last")) code_err(args->next->value, "Invalid argument name: ", args->next->name); - last = compile_to_type(env, args->next->value, INT_TYPE); + last = compile_to_type(env, args->next->value, int_type); } } if (!last) code_err(for_->iter, "No `last` argument was given"); - if (step && optional_step) - step = CORD_all("({ OptionalInt_t maybe_step = ", optional_step, "; maybe_step->small == 0 ? (Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)) : (Int_t)maybe_step; })"); - else if (!step) - step = "Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)"; - + CORD type_code = compile_type(int_type); CORD value = for_->vars ? compile(body_scope, for_->vars->ast) : "i"; - return CORD_all( - "for (Int_t first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", - value, " = first, " - "last = ", last, ", " - "step = ", step, "; " - "Int$compare_value(", value, ", last) != Int$compare_value(step, I_small(0)); ", value, " = Int$plus(", value, ", step)) {\n" - "\t", naked_body, - "}", - stop); + if (int_type->tag == BigIntType) { + if (optional_step) + step = CORD_all("({ OptionalInt_t maybe_step = ", optional_step, "; maybe_step->small == 0 ? (Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)) : (Int_t)maybe_step; })"); + else if (!step) + step = "Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)"; + return CORD_all( + "for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + value, " = first, last = ", last, ", step = ", step, "; " + "Int$compare_value(", value, ", last) != Int$compare_value(step, I_small(0)); ", + value, " = Int$plus(", value, ", step)) {\n" + "\t", naked_body, + "}", + stop); + } else { + if (optional_step) + step = CORD_all("({ ", compile_type(Type(OptionalType, step_type)), " maybe_step = ", optional_step, "; " + "maybe_step.is_none ? (", type_code, ")(last >= first ? 1 : -1) : maybe_step.value; })"); + else if (!step) + step = CORD_all("(", type_code, ")(last >= first ? 1 : -1)"); + return CORD_all( + "for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + value, " = first, last = ", last, ", step = ", step, "; " + "step > 0 ? ", value, " <= last : ", value, " >= last; ", + value, " += step) {\n" + "\t", naked_body, + "}", + stop); + } } else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "onward") && get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) { // Special case for Int.onward() @@ -1974,7 +1992,7 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) { - if (ast->tag == Int && is_numeric_type(t)) { + if (ast->tag == Int && is_numeric_type(non_optional(t))) { return compile_int_to_type(env, ast, t); } else if (ast->tag == Num && t->tag == NumType) { double n = Match(ast, Num)->n; diff --git a/src/environment.c b/src/environment.c index 0d55be9c..927f2e87 100644 --- a/src/environment.c +++ b/src/environment.c @@ -88,6 +88,7 @@ env_t *global_env(void) {"max", "Byte$max", "Byte"}, {"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"}, {"min", "Byte$min", "Byte"}, + {"to", "Byte$to", "func(first:Byte,last:Byte,step:Int8?=none -> func(->Byte?))"}, )}, {"Int", Type(BigIntType), "Int_t", "Int$info", TypedList(ns_entry_t, {"abs", "Int$abs", "func(x:Int -> Int)"}, diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index 09e19c40..1203aa55 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -30,6 +30,31 @@ public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { return text; } +typedef struct { + OptionalByte_t current, last; + Int8_t step; +} ByteRange_t; + +static OptionalByte_t _next_Byte(ByteRange_t *info) { + OptionalByte_t i = info->current; + if (!i.is_none) { + Byte_t next; bool overflow = __builtin_add_overflow(i.value, info->step, &next); + if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value))) + info->current = (OptionalByte_t){.is_none=true}; + else + info->current = (OptionalByte_t){.value=next}; + } + return i; +} + +public CONSTFUNC Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step) { + ByteRange_t *range = GC_MALLOC(sizeof(ByteRange_t)); + range->current = (OptionalByte_t){.value=first}; + range->last = (OptionalByte_t){.value=last}; + range->step = step.is_none ? (last >= first ? 1 : -1) : step.value; + return (Closure_t){.fn=_next_Byte, .userdata=range}; +} + public PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) { if unlikely (truncate && Int$compare_value(i, I_small(0xFF)) > 0) fail("This value is too large to convert to a byte without truncation: ", i); diff --git a/src/stdlib/bytes.h b/src/stdlib/bytes.h index ac1b61a3..f875e7cb 100644 --- a/src/stdlib/bytes.h +++ b/src/stdlib/bytes.h @@ -6,6 +6,7 @@ #include #include "datatypes.h" +#include "integers.h" #include "stdlib.h" #include "types.h" #include "util.h" @@ -18,6 +19,7 @@ Byte_t Byte$from_int(Int_t i, bool truncate); Byte_t Byte$from_int64(int64_t i, bool truncate); Byte_t Byte$from_int32(int32_t i, bool truncate); Byte_t Byte$from_int16(int16_t i, bool truncate); +Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step); MACROLIKE Byte_t Byte$from_int8(int8_t i) { return (Byte_t)i; } MACROLIKE Byte_t Byte$from_bool(bool b) { return (Byte_t)b; } @@ -28,11 +30,4 @@ extern const TypeInfo_t Byte$info; Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix); -typedef struct { - Byte_t value; - bool has_value:1; -} OptionalByte_t; - -#define NONE_BYTE ((OptionalByte_t){.has_value=false}) - // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h index 950c9457..77c09ddb 100644 --- a/src/stdlib/datatypes.h +++ b/src/stdlib/datatypes.h @@ -111,4 +111,12 @@ typedef struct { #define OptionalText_t Text_t #define OptionalClosure_t Closure_t +typedef struct { + Byte_t value; + bool is_none:1; +} OptionalByte_t; + +#define NONE_BYTE ((OptionalByte_t){.is_none=true}) + + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/typecheck.c b/src/typecheck.c index ce8b4276..fedc7c96 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -1056,9 +1056,9 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); - if (binop.lhs->tag == Int && (is_int_type(rhs_t) || rhs_t->tag == ByteType)) + if (binop.lhs->tag == Int && is_int_type(rhs_t)) return rhs_t; - else if (binop.rhs->tag == Int && (is_int_type(lhs_t) || lhs_t->tag == ByteType)) + else if (binop.rhs->tag == Int && is_int_type(lhs_t)) return lhs_t; // `opt? or (x == y)` / `(x == y) or opt?` is a boolean conditional: @@ -1102,9 +1102,9 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); - if (binop.lhs->tag == Int && (is_int_type(rhs_t) || rhs_t->tag == ByteType)) + if (binop.lhs->tag == Int && is_int_type(rhs_t)) return rhs_t; - else if (binop.rhs->tag == Int && (is_int_type(lhs_t) || lhs_t->tag == ByteType)) + else if (binop.rhs->tag == Int && is_int_type(lhs_t)) return lhs_t; // `and` between optionals/bools is a boolean expression like `if opt? and opt?:` or `if x > 0 and opt?:` @@ -1136,9 +1136,9 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); - if (binop.lhs->tag == Int && (is_int_type(rhs_t) || rhs_t->tag == ByteType)) + if (binop.lhs->tag == Int && is_int_type(rhs_t)) return rhs_t; - else if (binop.rhs->tag == Int && (is_int_type(lhs_t) || lhs_t->tag == ByteType)) + else if (binop.rhs->tag == Int && is_int_type(lhs_t)) return lhs_t; // `xor` between optionals/bools is a boolean expression like `if opt? xor opt?:` or `if x > 0 xor opt?:` diff --git a/src/types.c b/src/types.c index 83c3e702..efd83477 100644 --- a/src/types.c +++ b/src/types.c @@ -420,7 +420,7 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) PUREFUNC bool is_int_type(type_t *t) { - return t->tag == IntType || t->tag == BigIntType; + return t->tag == IntType || t->tag == BigIntType || t->tag == ByteType; } PUREFUNC bool is_numeric_type(type_t *t) -- cgit v1.2.3