\
NODE(CaptureBlock) \
NODE(CaptureLocal) \
+ NODE(CaptureBuilder) \
\
NODE(ForeignBlock) \
\
Ast_Kind_Capture_Block,
Ast_Kind_Capture_Local,
+ Ast_Kind_Capture_Builder,
Ast_Kind_Foreign_Block,
u32 offset;
};
+struct AstCaptureBuilder {
+ AstTyped_base;
+
+ AstTyped *func;
+ AstCaptureBlock *captures;
+
+ bh_arr(AstTyped *) capture_values;
+};
+
struct AstPolyQuery {
AstNode_base;
extern AstGlobal builtin_stack_top;
extern AstGlobal builtin_tls_base;
extern AstGlobal builtin_tls_size;
+extern AstGlobal builtin_closure_base;
extern AstType *builtin_string_type;
extern AstType *builtin_cstring_type;
extern AstType *builtin_range_type;
extern AstTyped *tagged_procedures_node;
extern AstFunction *builtin_initialize_data_segments;
extern AstFunction *builtin_run_init_procedures;
+extern AstFunction *builtin_closure_block_allocate;
extern bh_arr(AstFunction *) init_procedures;
extern AstOverloadedFunction *builtin_implicit_bool_cast;
|| (node->kind == Ast_Kind_Subscript)
|| (node->kind == Ast_Kind_Field_Access)
|| (node->kind == Ast_Kind_Memres)
+ || (node->kind == Ast_Kind_Capture_Local)
|| (node->kind == Ast_Kind_Constraint_Sentinel)) // Bit of a hack, but this makes constraints like 'T->foo()' work.
return 1;
char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym);
char *find_closest_symbol_in_node(AstNode *node, char *sym);
+b32 maybe_create_capture_builder_for_function_expression(AstTyped **pexpr);
+
extern AstTyped node_that_signals_a_yield;
extern AstTyped node_that_signals_failure;
i32 *tls_size_ptr;
i32 *heap_start_ptr;
u64 stack_base_idx;
+ u64 closure_base_idx;
CallingConvention curr_cc;
i32 null_proc_func_idx;
"CAPTURE BLOCK",
"CAPTURE LOCAL",
+ "CAPTURE BUILDER",
"FOREIGN BLOCK",
"ZERO VALUE",
OnyxToken builtin_package_token = { Token_Type_Symbol, 7, "builtin ", { 0 } };
-static OnyxToken builtin_heap_start_token = { Token_Type_Symbol, 12, "__heap_start ", { 0 } };
-static OnyxToken builtin_stack_top_token = { Token_Type_Symbol, 11, "__stack_top ", { 0 } };
-static OnyxToken builtin_tls_base_token = { Token_Type_Symbol, 10, "__tls_base ", { 0 } };
-static OnyxToken builtin_tls_size_token = { Token_Type_Symbol, 10, "__tls_size ", { 0 } };
+static OnyxToken builtin_heap_start_token = { Token_Type_Symbol, 12, "__heap_start ", { 0 } };
+static OnyxToken builtin_stack_top_token = { Token_Type_Symbol, 11, "__stack_top ", { 0 } };
+static OnyxToken builtin_tls_base_token = { Token_Type_Symbol, 10, "__tls_base ", { 0 } };
+static OnyxToken builtin_tls_size_token = { Token_Type_Symbol, 10, "__tls_size ", { 0 } };
+static OnyxToken builtin_closure_base_token = { Token_Type_Symbol, 14, "__closure_base ", { 0 } };
AstGlobal builtin_heap_start = { Ast_Kind_Global, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
AstGlobal builtin_stack_top = { Ast_Kind_Global, 0, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
AstGlobal builtin_tls_base = { Ast_Kind_Global, 0, &builtin_tls_base_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
AstGlobal builtin_tls_size = { Ast_Kind_Global, 0, &builtin_tls_size_token, NULL, NULL, (AstType *) &basic_type_u32, NULL };
+AstGlobal builtin_closure_base = { Ast_Kind_Global, 0, &builtin_closure_base_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
AstType *builtin_string_type;
AstType *builtin_cstring_type;
AstTyped *tagged_procedures_node = NULL;
AstFunction *builtin_initialize_data_segments = NULL;
AstFunction *builtin_run_init_procedures = NULL;
+AstFunction *builtin_closure_block_allocate = NULL;
bh_arr(AstFunction *) init_procedures = NULL;
AstOverloadedFunction *builtin_implicit_bool_cast;
{ "builtin", "__stack_top", (AstNode *) &builtin_stack_top },
{ "builtin", "__tls_base", (AstNode *) &builtin_tls_base },
{ "builtin", "__tls_size", (AstNode *) &builtin_tls_size },
+ { "builtin", "__closure_base", (AstNode *) &builtin_closure_base },
{ NULL, NULL, NULL },
};
return;
}
+ builtin_closure_block_allocate = (AstFunction *) symbol_raw_resolve(p->scope, "__closure_block_allocate");
+ if (builtin_closure_block_allocate == NULL || builtin_closure_block_allocate->kind != Ast_Kind_Function) {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__closure_block_allocate' procedure not found.");
+ return;
+ }
+ // HACK
+ builtin_closure_block_allocate->flags |= Ast_Flag_Function_Used;
+
+
builtin_link_options_type = (AstType *) symbol_raw_resolve(p->scope, "Link_Options");
if (builtin_link_options_type == NULL) {
onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Link_Options' type not found.");
&& expr->kind != Ast_Kind_Field_Access
&& expr->kind != Ast_Kind_Memres
&& expr->kind != Ast_Kind_Local
+ && expr->kind != Ast_Kind_Capture_Local
&& expr->kind != Ast_Kind_Constraint_Sentinel
&& !node_is_addressable_literal((AstNode *) expr))
|| (expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
YIELD(expr->token->pos, "Waiting for function type to be resolved.");
expr->flags |= Ast_Flag_Function_Used;
+ if (maybe_create_capture_builder_for_function_expression(pexpr)) {
+ retval = Check_Return_To_Symres;
+ }
break;
case Ast_Kind_Directive_Solidify:
YIELD(expr->token->pos, "Waiting to resolve #this_package.");
break;
+ case Ast_Kind_Capture_Builder: {
+ AstCaptureBuilder *builder = (void *) expr;
+
+ fori (i, 0, bh_arr_length(builder->capture_values)) {
+ if (!builder->captures->captures[i]->type) {
+ YIELD(expr->token->pos, "Waiting to know capture value types.");
+ }
+
+ TYPE_CHECK(&builder->capture_values[i], builder->captures->captures[i]->type) {
+ ERROR_(builder->captures->captures[i]->token->pos, "Type mismatch for this captured value. Expected '%s', got '%s'.",
+ type_get_name(builder->captures->captures[i]->type), type_get_name(builder->capture_values[i]->type));
+ }
+ }
+ break;
+ }
+
case Ast_Kind_File_Contents: break;
case Ast_Kind_Overloaded_Function: break;
case Ast_Kind_Enum_Value: break;
bh_arr_each(AstCaptureLocal *, capture, block->captures) {
CHECK(expression, (AstTyped **) capture);
-
- assert((*capture)->type);
+ if (!(*capture)->type) YIELD((*capture)->token->pos, "Waiting to resolve captures type.");
(*capture)->offset = block->total_size_in_bytes;
block->total_size_in_bytes += type_size_of((*capture)->type);
add_entities_for_node(NULL, (AstNode *) &builtin_heap_start, context.global_scope, NULL);
add_entities_for_node(NULL, (AstNode *) &builtin_tls_base, context.global_scope, NULL);
add_entities_for_node(NULL, (AstNode *) &builtin_tls_size, context.global_scope, NULL);
+ add_entities_for_node(NULL, (AstNode *) &builtin_closure_base, context.global_scope, NULL);
// NOTE: Add all files passed by command line to the queue
bh_arr_each(const char *, filename, opts->files) {
static SymresStatus symres_macro(AstMacro* macro);
static SymresStatus symres_constraint(AstConstraint* constraint);
static SymresStatus symres_polyquery(AstPolyQuery *query);
+static SymresStatus symres_capture_builder(AstCaptureBuilder *builder);
static void scope_enter(Scope* new_scope) {
current_scope = new_scope;
break;
}
+ case Ast_Kind_Capture_Builder: SYMRES(capture_builder, (AstCaptureBuilder *) *expr); break;
+
default: break;
}
return Symres_Success;
}
+static SymresStatus symres_capture_builder(AstCaptureBuilder *builder) {
+ fori (i, bh_arr_length(builder->capture_values), bh_arr_length(builder->captures->captures)) {
+ OnyxToken *token = builder->captures->captures[i]->token;
+ AstTyped *resolved = (AstTyped *) symbol_resolve(current_scope, token);
+ if (!resolved) {
+ // Should this do a yield? In there any case that that would make sense?
+ onyx_report_error(token->pos, Error_Critical, "'%b' is not found in the enclosing scope.",
+ token->text, token->length);
+ return Symres_Error;
+ }
+
+ bh_arr_push(builder->capture_values, resolved);
+ }
+
+ return Symres_Success;
+}
+
static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
if (remove) *remove = 0;
bh_arr_push(syminfo->symbols_resolutions, res);
}
+
+
+
+b32 maybe_create_capture_builder_for_function_expression(AstTyped **pexpr) {
+ AstFunction *func = (void *) *pexpr;
+
+ if (!(func->flags & Ast_Flag_Function_Is_Lambda)) return 0;
+ if (!func->captures) return 0;
+
+ AstCaptureBuilder *builder = onyx_ast_node_new(context.ast_alloc, sizeof(AstCaptureBuilder), Ast_Kind_Capture_Builder);
+ builder->token = func->captures->token - 1;
+
+ builder->func = (void *) func;
+ builder->type = builder->func->type;
+ builder->captures = func->captures;
+
+ bh_arr_new(context.ast_alloc, builder->capture_values, bh_arr_length(builder->captures->captures));
+
+ *((void **) pexpr) = builder;
+ return 1;
+}
+
EMIT_FUNC(subscript_location, AstSubscript* sub, u64* offset_return);
EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return);
EMIT_FUNC(local_location, AstLocal* local, u64* offset_return);
+EMIT_FUNC(capture_local_location, AstCaptureLocal *capture, u64 *offset_return);
EMIT_FUNC(memory_reservation_location, AstMemRes* memres);
EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return);
EMIT_FUNC(location, AstTyped* expr);
} else {
emit_expression(mod, &code, call->callee);
- WI(NULL, WI_DROP);
+
+ u64 global_closure_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_closure_base);
+ WIL(NULL, WI_GLOBAL_SET, global_closure_base_idx);
i32 type_idx = generate_type_idx(mod, call->callee->type);
WID(NULL, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
*pcode = code;
}
+EMIT_FUNC(capture_local_location, AstCaptureLocal *capture, u64 *offset_return) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ WIL(NULL, WI_LOCAL_GET, mod->closure_base_idx);
+
+ *offset_return = capture->offset;
+
+ *pcode = code;
+}
+
EMIT_FUNC(compound_load, Type* type, u64 offset, i32 ignored_value_count) {
bh_arr(WasmInstruction) code = *pcode;
i32 mem_count = type_linear_member_count(type);
break;
}
+ case Ast_Kind_Capture_Local: {
+ AstCaptureLocal *capture = (AstCaptureLocal *) expr;
+ emit_capture_local_location(mod, &code, capture, offset_return);
+ break;
+ }
+
default: {
if (expr->token) {
onyx_report_error(expr->token->pos, Error_Critical, "Unable to generate location for '%s'.", onyx_ast_node_kind_string(expr->kind));
break;
}
+ case Ast_Kind_Capture_Builder: {
+ AstCaptureBuilder *builder = (AstCaptureBuilder *) expr;
+
+ assert(builder->func->kind == Ast_Kind_Function);
+ i32 elemidx = get_element_idx(mod, (AstFunction *) builder->func);
+ WID(NULL, WI_I32_CONST, elemidx);
+
+ // Allocate the block
+ WIL(NULL, WI_I32_CONST, builder->captures->total_size_in_bytes);
+ i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) builtin_closure_block_allocate);
+ WIL(NULL, WI_CALL, func_idx);
+
+ u64 capture_block_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ WIL(NULL, WI_LOCAL_TEE, capture_block_ptr);
+
+ // Populate the block
+ fori (i, 0, bh_arr_length(builder->capture_values)) {
+ WIL(NULL, WI_LOCAL_GET, capture_block_ptr);
+ emit_expression(mod, &code, builder->capture_values[i]);
+ emit_store_instruction(mod, &code, builder->capture_values[i]->type, builder->captures->captures[i]->offset);
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ break;
+ }
+
case Ast_Kind_Block: emit_block(mod, &code, (AstBlock *) expr, 1); break;
case Ast_Kind_Do_Block: emit_do_block(mod, &code, (AstDoBlock *) expr); break;
case Ast_Kind_Call: emit_call(mod, &code, (AstCall *) expr); break;
}
case Ast_Kind_Capture_Local: {
- printf("HANDLE CAPTURE LOCAL!!!\n");
- assert(0);
+ AstCaptureLocal* capture = (AstCaptureLocal *) expr;
+ u64 offset = 0;
+ emit_capture_local_location(mod, &code, capture, &offset);
+ emit_load_instruction(mod, &code, capture->type, offset);
break;
}
mod->stack_base_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
debug_function_set_ptr_idx(mod, func_idx, mod->stack_base_idx);
+ if (fd->captures) {
+ mod->closure_base_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ debug_emit_instruction(mod, NULL);
+ debug_emit_instruction(mod, NULL);
+
+ u64 global_closure_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_closure_base);
+ bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_GLOBAL_GET, { .l = global_closure_base_idx } }));
+ bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_LOCAL_SET, { .l = mod->closure_base_idx } }));
+ }
+
// Generate code
emit_function_body(mod, &wasm_func.code, fd);
.stack_top_ptr = NULL,
.stack_base_idx = 0,
+ .closure_base_idx = 0,
+
.foreign_function_count = 0,
.null_proc_func_idx = -1,
"""
__implicit_bool_cast :: #match -> bool {}
+#doc """
+ Internal procedure to allocate space for the captures in a closure. This will be soon
+ changed to a configurable way, but for now it simply allocates out of the heap allocator.
+"""
+__closure_block_allocate :: (size: i32) -> rawptr {
+ return raw_alloc(context.allocator, size);
+}
+
+
#doc """
Defines all options for changing the memory layout, imports and exports,
and more of an Onyx binary.