Skip to content

Commit 731ef9e

Browse files
committed
added: #stack_alloc modifier to closures
1 parent 6488da0 commit 731ef9e

6 files changed

Lines changed: 113 additions & 19 deletions

File tree

compiler/include/astnodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,7 @@ struct AstCaptureBlock {
15121512
bh_arr(AstCaptureLocal *) captures;
15131513

15141514
u32 total_size_in_bytes;
1515+
b32 alloc_on_stack : 1;
15151516
};
15161517

15171518
struct AstCaptureLocal {

compiler/include/wasm_emit.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,8 +683,16 @@ typedef struct DeferredStmt {
683683
};
684684
} DeferredStmt;
685685

686+
typedef enum AllocatedSpaceKind {
687+
Allocated_Space_Kind_Local,
688+
Allocated_Space_Kind_Raw
689+
} AllocatedSpaceKind;
690+
686691
typedef struct AllocatedSpace {
692+
AllocatedSpaceKind kind;
687693
u64 depth;
694+
u32 size;
695+
u32 alignment;
688696
AstTyped *expr;
689697
} AllocatedSpace;
690698

compiler/src/parser.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3479,8 +3479,11 @@ static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* tok
34793479
}
34803480

34813481
if (consume_token_if_next(parser, Token_Type_Keyword_Use)) {
3482+
b32 stack_alloc = parse_possible_directive(parser, "stack_alloc");
3483+
34823484
expect_token(parser, '(');
34833485
func_def->captures = parse_capture_list(parser, ')');
3486+
if (func_def->captures) func_def->captures->alloc_on_stack = stack_alloc;
34843487
consume_token_if_next(parser, ',');
34853488

34863489
if (bh_arr_length(parser->current_function_stack) > 1) {
@@ -3683,8 +3686,11 @@ static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped
36833686
}
36843687

36853688
if (consume_token_if_next(parser, Token_Type_Keyword_Use)) {
3689+
b32 stack_alloc = parse_possible_directive(parser, "stack_alloc");
3690+
36863691
expect_token(parser, '(');
36873692
captures = parse_capture_list(parser, ')');
3693+
if (captures) captures->alloc_on_stack = stack_alloc;
36883694
}
36893695
}
36903696

compiler/src/wasm_emit.c

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,7 @@ static void local_raw_free(LocalAllocator* la, WasmType wt) {
150150
la->freed[idx]++;
151151
}
152152

153-
static u64 local_allocate_type_in_memory(LocalAllocator* la, Type *type) {
154-
u32 size = type_size_of(type);
155-
u32 alignment = type_alignment_of(type);
156-
153+
static u64 local_allocate_bytes_in_memory(LocalAllocator *la, u32 size, u32 alignment) {
157154
bh_align(la->curr_stack, alignment);
158155

159156
if (la->max_stack < la->curr_stack)
@@ -172,12 +169,24 @@ static u64 local_allocate_type_in_memory(LocalAllocator* la, Type *type) {
172169
return la->curr_stack - size;
173170
}
174171

172+
static void local_free_bytes_in_memory(LocalAllocator* la, u32 size, u32 alignment) {
173+
bh_align(size, alignment);
174+
175+
la->curr_stack -= size;
176+
}
177+
178+
static u64 local_allocate_type_in_memory(LocalAllocator* la, Type *type) {
179+
u32 size = type_size_of(type);
180+
u32 alignment = type_alignment_of(type);
181+
182+
return local_allocate_bytes_in_memory(la, size, alignment);
183+
}
184+
175185
static void local_free_type_in_memory(LocalAllocator* la, Type* type) {
176186
u32 size = type_size_of(type);
177187
u32 alignment = type_alignment_of(type);
178-
bh_align(size, alignment);
179188

180-
la->curr_stack -= size;
189+
local_free_bytes_in_memory(la, size, alignment);
181190
}
182191

183192
static u64 local_allocate(LocalAllocator* la, AstTyped* local) {
@@ -769,6 +778,19 @@ EMIT_FUNC(statement, AstNode* stmt) {
769778
*pcode = code;
770779
}
771780

781+
EMIT_FUNC_RETURNING(u64, stack_alloc, u32 size, u32 alignment) {
782+
u32 local_idx = local_allocate_bytes_in_memory(mod->local_alloc, size, alignment);
783+
784+
bh_arr_push(mod->local_allocations, ((AllocatedSpace) {
785+
.kind = Allocated_Space_Kind_Raw,
786+
.depth = bh_arr_length(mod->structured_jump_target),
787+
.size = size,
788+
.alignment = alignment
789+
}));
790+
791+
return local_idx;
792+
}
793+
772794
EMIT_FUNC_RETURNING(u64, local_allocation, AstTyped* stmt) {
773795
//
774796
// If the statement does not have a type, it should not
@@ -809,6 +831,7 @@ EMIT_FUNC_RETURNING(u64, local_allocation, AstTyped* stmt) {
809831
}
810832

811833
bh_arr_push(mod->local_allocations, ((AllocatedSpace) {
834+
.kind = Allocated_Space_Kind_Local,
812835
.depth = bh_arr_length(mod->structured_jump_target),
813836
.expr = stmt,
814837
}));
@@ -821,10 +844,19 @@ EMIT_FUNC_NO_ARGS(free_local_allocations) {
821844

822845
u64 depth = bh_arr_length(mod->structured_jump_target);
823846
while (bh_arr_length(mod->local_allocations) > 0 && bh_arr_last(mod->local_allocations).depth >= depth) {
824-
// CHECK: Not sure this next line is okay to be here...
825-
bh_imap_delete(&mod->local_map, (u64) bh_arr_last(mod->local_allocations).expr);
847+
AllocatedSpace alloc = bh_arr_last(mod->local_allocations);
848+
switch (alloc.kind) {
849+
case Allocated_Space_Kind_Local:
850+
// CHECK: Not sure this next line is okay to be here...
851+
bh_imap_delete(&mod->local_map, (u64) alloc.expr);
852+
local_free(mod->local_alloc, alloc.expr);
853+
break;
854+
855+
case Allocated_Space_Kind_Raw:
856+
local_free_bytes_in_memory(mod->local_alloc, alloc.size, alloc.alignment);
857+
break;
858+
}
826859

827-
local_free(mod->local_alloc, bh_arr_last(mod->local_allocations).expr);
828860
bh_arr_pop(mod->local_allocations);
829861
}
830862
}
@@ -2085,7 +2117,7 @@ EMIT_FUNC(call, AstCall* call) {
20852117
WIL(NULL, WI_GLOBAL_SET, stack_trace_pass_global);
20862118
}
20872119

2088-
if (call->callee->kind == Ast_Kind_Function) {
2120+
if (call->callee->kind == Ast_Kind_Function && !((AstFunction *) call->callee)->captures) {
20892121
CodePatchInfo code_patch;
20902122
code_patch.kind = Code_Patch_Callee;
20912123
code_patch.func_idx = mod->current_func_idx;
@@ -3349,17 +3381,23 @@ EMIT_FUNC(expression, AstTyped* expr) {
33493381
}
33503382

33513383
// Allocate the block
3352-
WIL(NULL, WI_I32_CONST, func->captures->total_size_in_bytes);
3384+
if (func->captures->alloc_on_stack) {
3385+
u32 address = emit_stack_alloc(mod, &code, func->captures->total_size_in_bytes, 16);
3386+
emit_stack_address(mod, &code, address, NULL);
33533387

3354-
CodePatchInfo code_patch;
3355-
code_patch.kind = Code_Patch_Callee;
3356-
code_patch.func_idx = mod->current_func_idx;
3357-
code_patch.instr = bh_arr_length(code);
3358-
code_patch.node_related_to_patch = (AstNode *) mod->context->builtins.closure_block_allocate;
3359-
bh_arr_push(mod->code_patches, code_patch);
3360-
WIL(NULL, WI_CALL, 0);
3388+
} else {
3389+
WIL(NULL, WI_I32_CONST, func->captures->total_size_in_bytes);
3390+
3391+
CodePatchInfo code_patch;
3392+
code_patch.kind = Code_Patch_Callee;
3393+
code_patch.func_idx = mod->current_func_idx;
3394+
code_patch.instr = bh_arr_length(code);
3395+
code_patch.node_related_to_patch = (AstNode *) mod->context->builtins.closure_block_allocate;
3396+
bh_arr_push(mod->code_patches, code_patch);
3397+
WIL(NULL, WI_CALL, 0);
33613398

3362-
ensure_node_has_been_submitted_for_emission(mod->context, (AstNode *) mod->context->builtins.closure_block_allocate);
3399+
ensure_node_has_been_submitted_for_emission(mod->context, (AstNode *) mod->context->builtins.closure_block_allocate);
3400+
}
33633401

33643402
u64 capture_block_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
33653403
WIL(NULL, WI_LOCAL_TEE, capture_block_ptr);

tests/closure_stack_alloc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
1
2+
4
3+
6
4+
8
5+
9
6+
10
7+
12
8+
13
9+
14
10+
15
11+
16
12+
17
13+
18
14+
19

tests/closure_stack_alloc.onyx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use core {*}
2+
3+
main :: () {
4+
// Ensure that any use of closures will break the program
5+
context.closure_allocate = n => {
6+
panic("LAND MINE")
7+
return cast(rawptr, 0)
8+
}
9+
10+
a := Array.make(.[2, 3, 5, 7, 11])
11+
12+
res := Iterator.from(1 .. 20)
13+
// While the Iterator itself will allocate in order to
14+
// store its context, the closure will not allocate.
15+
|> Iterator.filter((x) use #stack_alloc (a) => {
16+
// This tests using a stack allocated closure in a macro,
17+
// that is called directly (() use ...)(), which caused
18+
// issues in the initial implementation.
19+
return !Slice.some(a, (y) use #stack_alloc (x) => {
20+
return x == y
21+
})
22+
})
23+
24+
for i in res {
25+
println(i)
26+
}
27+
}

0 commit comments

Comments
 (0)