From af3596ec1d614f4ee22ac179d241dd76aa4fdc0e Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 31 Mar 2022 13:38:15 -0500 Subject: [PATCH] added '#remove' directive in for-loops --- core/builtin.onyx | 7 +++--- core/container/iter.onyx | 40 ++++++++++++++++++++++++++++++++ include/astnodes.h | 6 +++++ include/wasm_emit.h | 10 ++++++++ src/astnodes.c | 1 + src/checker.c | 17 ++++++++++++++ src/clone.c | 1 + src/parser.c | 8 +++++++ src/symres.c | 1 + src/wasm_emit.c | 49 ++++++++++++++++++++++++++++++++++++---- 10 files changed, 133 insertions(+), 7 deletions(-) diff --git a/core/builtin.onyx b/core/builtin.onyx index abd390c9..99dcf209 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -183,9 +183,10 @@ cfree :: (ptr: rawptr) do raw_free(context.allocator, ptr); Iterator :: struct (Iter_Type: type_expr) { - data: rawptr; - next: (data: rawptr) -> (Iter_Type, bool); - close: (data: rawptr) -> void = null_proc; + data: rawptr; + next: (data: rawptr) -> (Iter_Type, bool); + close: (data: rawptr) -> void = null_proc; + remove: (data: rawptr) -> void = null_proc; } diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 43c52871..b7b3efe5 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -437,6 +437,46 @@ from_array :: (arr: [] $T) -> Iterator(^T) { }; } +#match as_iterator (x: ^[..] $T) -> Iterator(^T) { + Context :: struct (T: type_expr) { + arr: ^[..] T; + current: u32; + } + + c := make(Context(T)); + c.arr = x; + c.current = 0; + + next :: (use _: ^Context($T)) -> (^T, bool) { + if current < arr.count { + defer current += 1; + return ^arr.data[current], true; + + } else { + return null, false; + } + } + + close :: (data: rawptr) { + cfree(data); + } + + remove :: (use _: ^Context($T)) { + // + // This is current - 1 because current will have already + // been incremented by the time this element calls #remove. + array :: package core.array + array.delete(arr, current - 1); + } + + return .{ + data = c, + next = #solidify next { T = T }, + close = close, + remove = #solidify remove { T = T }, + }; +} + #match as_iterator (r: range) -> Iterator(i32) { Context :: struct { r: range; diff --git a/include/astnodes.h b/include/astnodes.h index 4f052b13..94ea66f5 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -42,6 +42,7 @@ NODE(DirectiveTag) \ NODE(DirectiveInit) \ NODE(DirectiveLibrary) \ + NODE(DirectiveRemove) \ \ NODE(Return) \ NODE(Jump) \ @@ -213,6 +214,7 @@ typedef enum AstKind { Ast_Kind_Directive_Tag, Ast_Kind_Directive_Init, Ast_Kind_Directive_Library, + Ast_Kind_Directive_Remove, Ast_Kind_Call_Site, Ast_Kind_Code_Block, @@ -1259,6 +1261,10 @@ struct AstDirectiveTag { AstTyped* tag; }; +struct AstDirectiveRemove { + AstNode_base; +}; + struct AstNote { AstNode_base; }; diff --git a/include/wasm_emit.h b/include/wasm_emit.h index a369bb2f..71141188 100644 --- a/include/wasm_emit.h +++ b/include/wasm_emit.h @@ -613,6 +613,14 @@ typedef struct PatchInfo { u32 instruction_index; } PatchInfo; +typedef struct ForRemoveInfo { + // These are WASM locals + u64 iterator_remove_func; + u64 iterator_data_ptr; + + i32 remove_func_type_idx; +} ForRemoveInfo; + typedef struct OnyxWasmModule { bh_allocator allocator; @@ -635,6 +643,8 @@ typedef struct OnyxWasmModule { bh_arr(PatchInfo) stack_leave_patches; + bh_arr(ForRemoveInfo) for_remove_info; + // NOTE: Used internally as a map from strings that represent function types, // 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 ) // to the function type index if it has been created. diff --git a/src/astnodes.c b/src/astnodes.c index d354d520..9a3819a8 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -96,6 +96,7 @@ static const char* ast_node_names[] = { "TAG", "INIT", "LIBRARY", + "REMOVE", "CALL SITE", "CODE BLOCK", diff --git a/src/checker.c b/src/checker.c index 0d510a41..fa0e3300 100644 --- a/src/checker.c +++ b/src/checker.c @@ -112,6 +112,7 @@ CheckStatus check_polyquery(AstPolyQuery *query); // HACK HACK HACK b32 expression_types_must_be_known = 0; b32 all_checks_are_final = 1; +b32 inside_for_iterator = 0; #define STATEMENT_LEVEL 1 #define EXPRESSION_LEVEL 2 @@ -307,9 +308,16 @@ CheckStatus check_for(AstFor* fornode) { fornode->flags |= Ast_Flag_Has_Been_Checked; fornode_expr_checked: + b32 old_inside_for_iterator = inside_for_iterator; + inside_for_iterator = 0; + iter_type = fornode->iter->type; + if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) { + inside_for_iterator = 1; + } CHECK(block, fornode->stmt); + inside_for_iterator = old_inside_for_iterator; return Check_Success; } @@ -1965,6 +1973,14 @@ CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid) { return Check_Success; } +CheckStatus check_remove_directive(AstDirectiveRemove *remove) { + if (!inside_for_iterator) { + ERROR(remove->token->pos, "#remove is only allowed in the body of a for-loop over an iterator."); + } + + return Check_Success; +} + CheckStatus check_statement(AstNode** pstmt) { AstNode* stmt = *pstmt; @@ -1981,6 +1997,7 @@ CheckStatus check_statement(AstNode** pstmt) { case Ast_Kind_Switch: return check_switch((AstSwitch *) stmt); case Ast_Kind_Block: return check_block((AstBlock *) stmt); case Ast_Kind_Defer: return check_statement(&((AstDefer *) stmt)->stmt); + case Ast_Kind_Directive_Remove: return check_remove_directive((AstDirectiveRemove *) stmt); case Ast_Kind_Call: { CHECK(call, (AstCall **) pstmt); (*pstmt)->flags |= Ast_Flag_Expr_Ignored; diff --git a/src/clone.c b/src/clone.c index 324ddee3..55ffc3fd 100644 --- a/src/clone.c +++ b/src/clone.c @@ -113,6 +113,7 @@ static inline i32 ast_kind_to_size(AstNode* node) { case Ast_Kind_Directive_Defined: return sizeof(AstDirectiveDefined); case Ast_Kind_Do_Block: return sizeof(AstDoBlock); case Ast_Kind_Constraint: return sizeof(AstConstraint); + case Ast_Kind_Directive_Remove: return sizeof(AstDirectiveRemove); case Ast_Kind_Count: return 0; } diff --git a/src/parser.c b/src/parser.c index 83b722cd..4c8195eb 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1545,6 +1545,14 @@ static AstNode* parse_statement(OnyxParser* parser) { break; } + if (parse_possible_directive(parser, "remove")) { + // :LinearTokenDependent + AstDirectiveRemove *remove = make_node(AstDirectiveRemove, Ast_Kind_Directive_Remove); + remove->token = parser->curr - 2; + retval = (AstNode *) remove; + break; + } + if (next_tokens_are(parser, 2, '#', Token_Type_Symbol)) { retval = (AstNode *) parse_factor(parser); break; diff --git a/src/symres.c b/src/symres.c index 88b7e5b8..9b501aca 100644 --- a/src/symres.c +++ b/src/symres.c @@ -849,6 +849,7 @@ static SymresStatus symres_statement(AstNode** stmt, b32 *remove) { case Ast_Kind_Defer: SYMRES(statement, &((AstDefer *) *stmt)->stmt, NULL); break; case Ast_Kind_Switch_Case: SYMRES(case, (AstSwitchCase *) *stmt); break; case Ast_Kind_Jump: break; + case Ast_Kind_Directive_Remove: break; case Ast_Kind_Local: // if (remove) *remove = 1; diff --git a/src/wasm_emit.c b/src/wasm_emit.c index 5b076fb4..237507d9 100644 --- a/src/wasm_emit.c +++ b/src/wasm_emit.c @@ -243,6 +243,7 @@ EMIT_FUNC(defer, AstDefer* defer); EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count); EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt); EMIT_FUNC_NO_ARGS(deferred_stmts); +EMIT_FUNC(remove_directive, AstDirectiveRemove* remove); EMIT_FUNC(binop, AstBinaryOp* binop); EMIT_FUNC(unaryop, AstUnaryOp* unop); EMIT_FUNC(call, AstCall* call); @@ -430,6 +431,7 @@ EMIT_FUNC(statement, AstNode* stmt) { case Ast_Kind_Defer: emit_defer(mod, &code, (AstDefer *) stmt); break; case Ast_Kind_Local: emit_local_allocation(mod, &code, (AstTyped *) stmt); break; + case Ast_Kind_Directive_Remove: emit_remove_directive(mod, &code, (AstDirectiveRemove *) stmt); break; case Ast_Kind_Directive_Insert: break; default: emit_expression(mod, &code, (AstTyped *) stmt); break; @@ -1024,14 +1026,32 @@ EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) { bh_arr(WasmInstruction) code = *pcode; // Allocate temporaries for iterator contents - u64 iterator_data_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); - u64 iterator_next_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); - u64 iterator_close_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); - u64 iterator_done_bool = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); + u64 iterator_data_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); + u64 iterator_next_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); + u64 iterator_close_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); + u64 iterator_remove_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); + u64 iterator_done_bool = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); + WIL(WI_LOCAL_SET, iterator_remove_func); WIL(WI_LOCAL_SET, iterator_close_func); WIL(WI_LOCAL_SET, iterator_next_func); WIL(WI_LOCAL_SET, iterator_data_ptr); + + { + // + // This pushes an entry onto the stack of for loops that have + // are iterator that can have a '#remove' directive in them. + ForRemoveInfo remove_info; + remove_info.iterator_data_ptr = iterator_data_ptr; + remove_info.iterator_remove_func = iterator_remove_func; + + TypeWithOffset remove_func_type; + type_linear_member_lookup(for_node->iter->type, 3, &remove_func_type); + remove_info.remove_func_type_idx = generate_type_idx(mod, remove_func_type.type); + + bh_arr_push(mod->for_remove_info, remove_info); + } + AstLocal* var = for_node->var; b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0); u64 offset = 0; @@ -1115,6 +1135,8 @@ EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) { emit_deferred_stmts(mod, &code); emit_leave_structured_block(mod, &code); + bh_arr_pop(mod->for_remove_info); + local_raw_free(mod->local_alloc, WASM_TYPE_PTR); local_raw_free(mod->local_alloc, WASM_TYPE_FUNC); local_raw_free(mod->local_alloc, WASM_TYPE_FUNC); @@ -1296,6 +1318,25 @@ EMIT_FUNC_NO_ARGS(deferred_stmts) { } } +EMIT_FUNC(remove_directive, AstDirectiveRemove* remove) { + assert(bh_arr_length(mod->for_remove_info) > 0); + + bh_arr(WasmInstruction) code = *pcode; + + ForRemoveInfo remove_info = bh_arr_last(mod->for_remove_info); + + WIL(WI_LOCAL_GET, remove_info.iterator_remove_func); + WIL(WI_I32_CONST, mod->null_proc_func_idx); + WI(WI_I32_NE); + WID(WI_IF_START, 0x40); + WIL(WI_LOCAL_GET, remove_info.iterator_data_ptr); + WIL(WI_LOCAL_GET, remove_info.iterator_remove_func); + WIL(WI_CALL_INDIRECT, remove_info.remove_func_type_idx); + WI(WI_IF_END); + + *pcode = code; +} + // NOTE: These need to be in the same order as // the OnyxBinaryOp enum static const WasmInstructionType binop_map[][4] = { -- 2.25.1