From: Brendan Hansen Date: Mon, 10 Aug 2020 19:45:33 +0000 (-0500) Subject: started work on reducing compile time known values X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=8c156cd4411aebb3c57a143d162b7e56a3238666;p=onyx.git started work on reducing compile time known values --- diff --git a/Makefile b/Makefile index 0e514db6..6a575b28 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -RELEASE=1 +RELEASE=0 OBJ_FILES=\ build/onyxlex.o \ diff --git a/docs/plan b/docs/plan index 838906d8..f025768a 100644 --- a/docs/plan +++ b/docs/plan @@ -172,7 +172,7 @@ HOW: - Checking which things are allowed to cast to/from should be checked in the checker, not in the wasm generatation - [ ] Start work on evaluating compile time known values. + [X] Start work on evaluating compile time known values. - An expression marked COMPTIME will be reduced to its value in the parse tree. [ ] All code paths return correct value diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 6d242e73..41ffdc94 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -493,6 +493,7 @@ extern const BuiltinSymbol builtin_symbols[]; // NOTE: Useful not inlined functions +AstTyped* ast_reduce(bh_allocator a, AstTyped* node); void promote_numlit_to_larger(AstNumLit* num); // NOTE: Useful inlined functions diff --git a/include/onyxtypes.h b/include/onyxtypes.h index 7d17297c..ce7e9d0c 100644 --- a/include/onyxtypes.h +++ b/include/onyxtypes.h @@ -123,6 +123,7 @@ b32 type_is_pointer(Type* type); b32 type_is_array(Type* tyoe); b32 type_is_struct(Type* type); b32 type_is_bool(Type* type); +b32 type_is_small_integer(Type* type); b32 type_is_integer(Type* type); b32 type_is_numeric(Type* type); b32 type_is_compound(Type* type); diff --git a/include/onyxwasm.h b/include/onyxwasm.h index 3ee2c499..911b63a7 100644 --- a/include/onyxwasm.h +++ b/include/onyxwasm.h @@ -230,7 +230,7 @@ typedef enum WasmInstructionType { typedef union { struct { - u32 i1, i2; + i32 i1, i2; }; i64 l; float f; diff --git a/onyx b/onyx index 144ca80f..1b601253 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/stack_based.onyx b/progs/stack_based.onyx index 5483f7c9..bae44419 100644 --- a/progs/stack_based.onyx +++ b/progs/stack_based.onyx @@ -44,7 +44,7 @@ vec_add :: proc (v: Vec3, u: Vec3, use out: ^Vec3) { z = v.z + u.z; } -some_value := #char "A"; +some_value := 20 + 30 * 4 + 15 / 5; start :: proc #export { heap_init(); @@ -57,6 +57,8 @@ start :: proc #export { print_bin(42l); print_hex(42l); + print(cast(u32) (#char "a" + #char "0")); + for i: #char "a", #char "f" do print_hex(cast(u64) i); a := 12345; diff --git a/src/onyxchecker.c b/src/onyxchecker.c index 1a32875c..973f8cb8 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -13,7 +13,8 @@ CHECK(if, AstIf* ifnode); CHECK(while, AstWhile* whilenode); CHECK(for, AstFor* fornode); CHECK(call, AstCall* call); -CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok); +CHECK(binaryop, AstBinaryOp** pbinop, b32 assignment_is_ok); +CHECK(unaryop, AstUnaryOp** punop); CHECK(expression, AstTyped** expr); CHECK(address_of, AstAddressOf* aof); CHECK(dereference, AstDereference* deref); @@ -426,7 +427,7 @@ CHECK(binop_assignment, AstBinaryOp* binop, b32 assignment_is_ok) { binop->right = (AstTyped *) binop_node; binop->operation = Binary_Op_Assign; - if (check_binaryop(binop_node, 0)) return 1; + if (check_binaryop(&binop_node, 0)) return 1; } if (!types_are_compatible(binop->left->type, binop->right->type)) { @@ -442,7 +443,9 @@ CHECK(binop_assignment, AstBinaryOp* binop, b32 assignment_is_ok) { return 0; } -CHECK(binaryop_compare, AstBinaryOp* binop) { +CHECK(binaryop_compare, AstBinaryOp** pbinop) { + AstBinaryOp* binop = *pbinop; + if (binop->left->type == NULL) { onyx_message_add(Msg_Type_Unresolved_Type, binop->token->pos, @@ -469,7 +472,9 @@ CHECK(binaryop_compare, AstBinaryOp* binop) { return 0; } -CHECK(binaryop_bool, AstBinaryOp* binop) { +CHECK(binaryop_bool, AstBinaryOp** pbinop) { + AstBinaryOp* binop = *pbinop; + if (binop->left->type == NULL) { onyx_message_add(Msg_Type_Unresolved_Type, binop->token->pos, @@ -495,15 +500,21 @@ CHECK(binaryop_bool, AstBinaryOp* binop) { return 0; } -CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) { +CHECK(binaryop, AstBinaryOp** pbinop, b32 assignment_is_ok) { + AstBinaryOp* binop = *pbinop; + if (check_expression(&binop->left)) return 1; if (check_expression(&binop->right)) return 1; + if ((binop->left->flags & Ast_Flag_Comptime) && (binop->right->flags & Ast_Flag_Comptime)) { + binop->flags |= Ast_Flag_Comptime; + } + if (binop_is_assignment(binop)) return check_binop_assignment(binop, assignment_is_ok); - if (binop_is_compare(binop)) return check_binaryop_compare(binop); + if (binop_is_compare(binop)) return check_binaryop_compare(pbinop); if (binop->operation == Binary_Op_Bool_And || binop->operation == Binary_Op_Bool_Or) - return check_binaryop_bool(binop); + return check_binaryop_bool(pbinop); if (binop->left->type == NULL) { onyx_message_add(Msg_Type_Unresolved_Type, @@ -585,7 +596,7 @@ CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) { binop_node->type = binop->right->type; binop_node->operation = Binary_Op_Multiply; - if (check_binaryop(binop_node, 0)) return 1; + if (check_binaryop(&binop_node, 0)) return 1; binop->right = (AstTyped *) binop_node; binop->type = binop->left->type; @@ -601,6 +612,30 @@ CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) { } binop->type = binop->left->type; + + if (binop->flags & Ast_Flag_Comptime) { + // NOTE: Not a binary op + *pbinop = (AstBinaryOp *) ast_reduce(semstate.node_allocator, (AstTyped *) binop); + } + + return 0; +} + +CHECK(unaryop, AstUnaryOp** punop) { + AstUnaryOp* unaryop = *punop; + + if (check_expression(&unaryop->expr)) return 1; + + if (unaryop->operation != Unary_Op_Cast) { + unaryop->type = unaryop->expr->type; + } + + if (unaryop->expr->flags & Ast_Flag_Comptime) { + unaryop->flags |= Ast_Flag_Comptime; + // NOTE: Not a unary op + *punop = (AstUnaryOp *) ast_reduce(semstate.node_allocator, (AstTyped *) unaryop); + } + return 0; } @@ -748,15 +783,8 @@ CHECK(expression, AstTyped** pexpr) { i32 retval = 0; switch (expr->kind) { - case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp *) expr, 0); break; - - case Ast_Kind_Unary_Op: - retval = check_expression(&((AstUnaryOp *) expr)->expr); - - if (((AstUnaryOp *) expr)->operation != Unary_Op_Cast) { - expr->type = ((AstUnaryOp *) expr)->expr->type; - } - break; + case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp **) pexpr, 0); break; + case Ast_Kind_Unary_Op: retval = check_unaryop((AstUnaryOp **) pexpr); break; case Ast_Kind_Call: retval = check_call((AstCall *) expr); break; case Ast_Kind_Block: retval = check_block((AstBlock *) expr); break; @@ -868,7 +896,7 @@ CHECK(statement, AstNode* stmt) { case Ast_Kind_Binary_Op: stmt->flags |= Ast_Flag_Expr_Ignored; - return check_binaryop((AstBinaryOp *) stmt, 1); + return check_binaryop((AstBinaryOp **) &stmt, 1); default: stmt->flags |= Ast_Flag_Expr_Ignored; @@ -1077,6 +1105,14 @@ CHECK(memres, AstMemRes* memres) { if (memres->initial_value != NULL) { fill_in_type(memres->initial_value); + check_expression(&memres->initial_value); + + if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) { + onyx_message_add(Msg_Type_Literal, + memres->initial_value->token->pos, + "top level expressions must be compile time known"); + return 1; + } Type* memres_type = memres->type; if (!type_is_compound(memres_type)) memres_type = memres_type->Pointer.elem; @@ -1102,7 +1138,7 @@ CHECK(node, AstNode* node) { case Ast_Kind_If: return check_if((AstIf *) node); case Ast_Kind_While: return check_while((AstWhile *) node); case Ast_Kind_Call: return check_call((AstCall *) node); - case Ast_Kind_Binary_Op: return check_binaryop((AstBinaryOp *) node, 1); + case Ast_Kind_Binary_Op: return check_binaryop((AstBinaryOp **) &node, 1); default: return check_expression((AstTyped **) &node); } } diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 8a095b04..f5c3e4bd 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -228,6 +228,9 @@ static void symres_unaryop(AstUnaryOp** unaryop) { } symres_expression(&(*unaryop)->expr); + + if ((*unaryop)->type_node == NULL) + (*unaryop)->type_node = ((AstUnaryOp *)(*unaryop))->expr->type_node; } static void symres_expression(AstTyped** expr) { @@ -239,6 +242,8 @@ static void symres_expression(AstTyped** expr) { case Ast_Kind_Binary_Op: symres_expression(&((AstBinaryOp *)(*expr))->left); symres_expression(&((AstBinaryOp *)(*expr))->right); + + (*expr)->type_node = ((AstBinaryOp *)(*expr))->left->type_node; break; case Ast_Kind_Unary_Op: symres_unaryop((AstUnaryOp **) expr); break; @@ -498,13 +503,6 @@ static void symres_memres(AstMemRes** memres) { if ((*memres)->initial_value != NULL) { symres_expression(&(*memres)->initial_value); - if (((*memres)->initial_value->flags & Ast_Flag_Comptime) == 0) { - onyx_message_add(Msg_Type_Literal, - (*memres)->initial_value->token->pos, - "top level expressions must be compile time known"); - return; - } - if ((*memres)->type_node == NULL) (*memres)->type_node = (*memres)->initial_value->type_node; diff --git a/src/onyxtypes.c b/src/onyxtypes.c index b61e05fd..8134b4be 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -471,6 +471,12 @@ b32 type_is_bool(Type* type) { return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool; } +b32 type_is_small_integer(Type* type) { + if (type->kind != Type_Kind_Basic) return 0; + + return type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U32; +} + b32 type_is_integer(Type* type) { if (type->kind != Type_Kind_Basic) return 0; diff --git a/src/onyxutils.c b/src/onyxutils.c index 4a1c20fe..72dde5fd 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -2,6 +2,7 @@ #include "onyxlex.h" #include "onyxastnodes.h" #include "onyxmsgs.h" +#include "onyxparser.h" bh_scratch global_scratch; bh_allocator global_scratch_allocator; @@ -179,9 +180,129 @@ AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) { return res; } +#define REDUCE_BINOP_ALL(op) \ + if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \ + res->value.i = left->value.i op right->value.i; \ + } else if (type_is_integer(res->type)) { \ + res->value.l = left->value.l op right->value.l; \ + } else if (res->type->Basic.kind == Basic_Kind_F32) { \ + res->value.f = left->value.f op right->value.f; \ + } else if (res->type->Basic.kind == Basic_Kind_F64) { \ + res->value.d = left->value.d op right->value.d; \ + } \ + break; + +#define REDUCE_BINOP_INT(op) \ + if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \ + res->value.i = left->value.i op right->value.i; \ + } else if (type_is_integer(res->type)) { \ + res->value.l = left->value.l op right->value.l; \ + } \ + break; + +#define REDUCE_BINOP_BOOL(op) \ + if (type_is_bool(res->type)) { \ + res->value.i = left->value.i op right->value.i; \ + } \ + break; + + +AstTyped* ast_reduce(bh_allocator a, AstTyped* node); + +AstNumLit* ast_reduce_binop(bh_allocator a, AstBinaryOp* node) { + AstNumLit* left = (AstNumLit *) ast_reduce(a, node->left); + AstNumLit* right = (AstNumLit *) ast_reduce(a, node->right); + + if (left->kind != Ast_Kind_NumLit || right->kind != Ast_Kind_NumLit) { + node->left = (AstTyped *) left; + node->right = (AstTyped *) right; + return (AstNumLit *) node; + } + + AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit); + res->token = node->token; + res->flags |= Ast_Flag_Comptime; + res->type_node = node->type_node; + res->type = node->type; + + switch (node->operation) { + case Binary_Op_Add: REDUCE_BINOP_ALL(+); + case Binary_Op_Minus: REDUCE_BINOP_ALL(-); + case Binary_Op_Multiply: REDUCE_BINOP_ALL(*); + case Binary_Op_Divide: REDUCE_BINOP_ALL(/); + case Binary_Op_Modulus: REDUCE_BINOP_INT(%); + + case Binary_Op_Equal: REDUCE_BINOP_ALL(==); + case Binary_Op_Not_Equal: REDUCE_BINOP_ALL(!=); + case Binary_Op_Less: REDUCE_BINOP_ALL(<); + case Binary_Op_Less_Equal: REDUCE_BINOP_ALL(<=); + case Binary_Op_Greater: REDUCE_BINOP_ALL(>); + case Binary_Op_Greater_Equal: REDUCE_BINOP_ALL(>=); + + case Binary_Op_And: REDUCE_BINOP_INT(&); + case Binary_Op_Or: REDUCE_BINOP_INT(|); + case Binary_Op_Xor: REDUCE_BINOP_INT(^); + case Binary_Op_Shl: REDUCE_BINOP_INT(<<); + case Binary_Op_Shr: REDUCE_BINOP_INT(>>); + case Binary_Op_Sar: REDUCE_BINOP_INT(>>); + + case Binary_Op_Bool_And: REDUCE_BINOP_BOOL(&&); + case Binary_Op_Bool_Or: REDUCE_BINOP_BOOL(||); + + default: break; + } + + return res; +} +#define REDUCE_UNOP(op) \ + if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \ + res->value.i = op ((AstNumLit *) unop->expr)->value.i; \ + } else if (type_is_integer(unop->type)) { \ + res->value.l = op ((AstNumLit *) unop->expr)->value.l; \ + } else if (unop->type->Basic.kind == Basic_Kind_F32) { \ + res->value.f = op ((AstNumLit *) unop->expr)->value.f; \ + } else if (unop->type->Basic.kind == Basic_Kind_F64) { \ + res->value.d = op ((AstNumLit *) unop->expr)->value.d; \ + } \ + break; + +AstTyped* ast_reduce_unaryop(bh_allocator a, AstUnaryOp* unop) { + unop->expr = ast_reduce(a, unop->expr); + + if (unop->expr->kind != Ast_Kind_NumLit) { + return (AstTyped *) unop; + } + AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit); + res->token = unop->token; + res->flags |= Ast_Flag_Comptime; + res->type_node = unop->type_node; + res->type = unop->type; + + switch (unop->operation) { + case Unary_Op_Negate: REDUCE_UNOP(-); + case Unary_Op_Not: { + if (type_is_bool(res->type)) res->value.i = ! ((AstNumLit *) unop->expr)->value.i; + break; + } + default: return (AstTyped *) unop; + } + + return (AstTyped *) res; +} + +AstTyped* ast_reduce(bh_allocator a, AstTyped* node) { + assert(node->flags & Ast_Flag_Comptime); + + switch (node->kind) { + case Ast_Kind_Binary_Op: return (AstTyped *) ast_reduce_binop(a, (AstBinaryOp *) node); + case Ast_Kind_Unary_Op: return (AstTyped *) ast_reduce_unaryop(a, (AstUnaryOp *) node); + case Ast_Kind_NumLit: return node; + default: return NULL; + } +} void promote_numlit_to_larger(AstNumLit* num) { assert(num->type != NULL);