From: Brendan Hansen Date: Sun, 17 Oct 2021 23:33:58 +0000 (-0500) Subject: added #threadlocal storage X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=7d5a249f43dd7259fb5108507beccea7f5ef1f84;p=onyx.git added #threadlocal storage --- diff --git a/bin/onyx b/bin/onyx index f843ade9..c80c6ba0 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/core/runtime/common.onyx b/core/runtime/common.onyx index 5064c717..d63f573a 100644 --- a/core/runtime/common.onyx +++ b/core/runtime/common.onyx @@ -29,8 +29,6 @@ __runtime_initialize :: () { context.temp_allocator = alloc.temp_allocator; context.assert_handler = __assert_handler; - __stdio_init(); - #if Multi_Threading_Enabled { thread.__initialize(); } diff --git a/core/runtime/js.onyx b/core/runtime/js.onyx index 083641fa..bfddb133 100644 --- a/core/runtime/js.onyx +++ b/core/runtime/js.onyx @@ -12,6 +12,9 @@ __exit :: (status: i32) -> void #foreign "host" "exit" --- #export "_start" () { __runtime_initialize(); + __tls_base = raw_alloc(context.allocator, __tls_size); + __stdio_init(); + args: [] cstr = .{ null, 0 }; (package main).main(args); @@ -24,14 +27,18 @@ __exit :: (status: i32) -> void #foreign "host" "exit" --- #export "_thread_start" (func: (data: rawptr) -> void, data: rawptr) { __stack_top = raw_alloc(context.allocator, 1 << 20); - // Need to initialize thread-local variables here + __tls_base = raw_alloc(context.allocator, __tls_size); + __stdio_init(); func(data); + + __flush_stdio(); } #export "_thread_exit" (id: i32) { // raw_free(context.allocator, __stack_top); - + raw_free(context.allocator, __tls_base); + thread.__exited(id); } } \ No newline at end of file diff --git a/core/runtime/wasi.onyx b/core/runtime/wasi.onyx index be8f7fad..9f8b4420 100644 --- a/core/runtime/wasi.onyx +++ b/core/runtime/wasi.onyx @@ -22,6 +22,9 @@ __exit :: (status: i32) do proc_exit(status); // Sets up everything needed for execution. #export "_start" () { __runtime_initialize(); + + __tls_base = raw_alloc(context.allocator, __tls_size); + __stdio_init(); args : [] cstr; argv_buf_size : Size; diff --git a/core/stdio.onyx b/core/stdio.onyx index 424b8ff2..72fe20dd 100644 --- a/core/stdio.onyx +++ b/core/stdio.onyx @@ -100,7 +100,7 @@ byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) { // Private and internal things // -stdio : struct { +#threadlocal stdio : struct { print_stream : io.DynamicStringStream; print_writer : io.Writer; } diff --git a/include/astnodes.h b/include/astnodes.h index 4f6b1ab5..24c18836 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -867,8 +867,14 @@ struct AstTypeOf { // Top level nodes struct AstBinding { AstTyped_base; AstNode* node; }; struct AstAlias { AstTyped_base; AstTyped* alias; }; -struct AstMemRes { AstTyped_base; u64 addr; AstTyped *initial_value; }; struct AstInclude { AstNode_base; char* name; }; +struct AstMemRes { + AstTyped_base; + u64 addr; + AstTyped *initial_value; + + b32 threadlocal : 1; +}; struct AstGlobal { AstTyped_base; @@ -1337,6 +1343,8 @@ extern AstBasicType basic_type_auto_return; extern OnyxToken builtin_package_token; extern AstNumLit builtin_heap_start; extern AstGlobal builtin_stack_top; +extern AstGlobal builtin_tls_base; +extern AstGlobal builtin_tls_size; extern AstType *builtin_string_type; extern AstType *builtin_range_type; extern Type *builtin_range_type_type; diff --git a/include/wasm.h b/include/wasm.h index 22863784..45c9824d 100644 --- a/include/wasm.h +++ b/include/wasm.h @@ -658,11 +658,13 @@ typedef struct OnyxWasmModule { u32 next_func_idx; u32 next_global_idx; u32 next_datum_offset; + u32 next_tls_offset; u32 next_elem_idx; u32 foreign_function_count; u32 foreign_global_count; i32 *stack_top_ptr; + i32 *tls_size_ptr; u64 stack_base_idx; CallingConvention curr_cc; i32 null_proc_func_idx; diff --git a/src/builtins.c b/src/builtins.c index 14c257ee..6376397b 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -40,8 +40,12 @@ 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 } }; AstNumLit builtin_heap_start = { Ast_Kind_NumLit, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 }; AstGlobal builtin_stack_top = { Ast_Kind_Global, Ast_Flag_Global_Stack_Top, &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 }; AstType *builtin_string_type; AstType *builtin_range_type; @@ -83,6 +87,8 @@ const BuiltinSymbol builtin_symbols[] = { { "builtin", "__heap_start", (AstNode *) &builtin_heap_start }, { "builtin", "__stack_top", (AstNode *) &builtin_stack_top }, + { "builtin", "__tls_base", (AstNode *) &builtin_tls_base }, + { "builtin", "__tls_size", (AstNode *) &builtin_tls_size }, { NULL, NULL, NULL }, }; diff --git a/src/checker.c b/src/checker.c index 3f754285..f4d2d26d 100644 --- a/src/checker.c +++ b/src/checker.c @@ -2048,6 +2048,11 @@ CheckStatus check_memres_type(AstMemRes* memres) { CheckStatus check_memres(AstMemRes* memres) { if (memres->initial_value != NULL) { + if (memres->threadlocal) { + onyx_report_error(memres->token->pos, "'#threadlocal' variables cannot have an initializer at the moment."); + return Check_Error; + } + CHECK(expression, &memres->initial_value); if (memres->type != NULL) { diff --git a/src/onyx.c b/src/onyx.c index 48b3f4e4..0532dd6b 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -223,6 +223,7 @@ static void context_init(CompileOptions* opts) { })); add_entities_for_node(NULL, (AstNode *) &builtin_stack_top, context.global_scope, NULL); + add_entities_for_node(NULL, (AstNode *) &builtin_tls_base, context.global_scope, NULL); // NOTE: Add all files passed by command line to the queue bh_arr_each(const char *, filename, opts->files) { diff --git a/src/parser.c b/src/parser.c index c6fa7063..643c7b5b 100644 --- a/src/parser.c +++ b/src/parser.c @@ -65,6 +65,7 @@ static AstTyped* parse_global_declaration(OnyxParser* parser); static AstEnumType* parse_enum_declaration(OnyxParser* parser); static AstMacro* parse_macro(OnyxParser* parser); static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements); +static AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 thread_local); static AstTyped* parse_top_level_expression(OnyxParser* parser); static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol); static void parse_top_level_statement(OnyxParser* parser); @@ -1478,19 +1479,10 @@ static AstNode* parse_statement(OnyxParser* parser) { } if (parse_possible_directive(parser, "persist")) { - // :Duplicated from parse_top_level_statement - AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres); - memres->token = expect_token(parser, Token_Type_Symbol); - expect_token(parser, ':'); - - if (parser->curr->type != '=') - memres->type_node = parse_type(parser); - - if (consume_token_if_next(parser, '=')) - memres->initial_value = parse_expression(parser, 1); - + b32 thread_local = parse_possible_directive(parser, "threadlocal"); - ENTITY_SUBMIT(memres); + OnyxToken* symbol = expect_token(parser, Token_Type_Symbol); + AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local); AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding); binding->token = memres->token; @@ -2575,6 +2567,23 @@ static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statem return static_if_node; } +static AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 threadlocal) { + expect_token(parser, ':'); + + AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres); + memres->threadlocal = threadlocal; + memres->token = symbol; + + if (parser->curr->type != '=') + memres->type_node = parse_type(parser); + + if (consume_token_if_next(parser, '=')) + memres->initial_value = parse_expression(parser, 1); + + ENTITY_SUBMIT(memres); + return memres; +} + static AstMacro* parse_macro(OnyxParser* parser) { AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro); macro->token = expect_token(parser, Token_Type_Keyword_Macro); @@ -2759,9 +2768,10 @@ static void parse_top_level_statement(OnyxParser* parser) { case Token_Type_Symbol: { OnyxToken* symbol = expect_token(parser, Token_Type_Symbol); - expect_token(parser, ':'); - if (parser->curr->type == ':') { + if (next_tokens_are(parser, 2, ':', ':')) { + expect_token(parser, ':'); + bh_arr_push(parser->current_symbol_stack, symbol); binding = parse_top_level_binding(parser, symbol); bh_arr_pop(parser->current_symbol_stack); @@ -2771,17 +2781,7 @@ static void parse_top_level_statement(OnyxParser* parser) { goto submit_binding_to_entities; } - AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres); - memres->token = symbol; - - if (parser->curr->type != '=') - memres->type_node = parse_type(parser); - - if (consume_token_if_next(parser, '=')) - memres->initial_value = parse_expression(parser, 1); - - - ENTITY_SUBMIT(memres); + AstMemRes* memres = parse_memory_reservation(parser, symbol, 0); binding = make_node(AstBinding, Ast_Kind_Binding); binding->token = symbol; @@ -2897,6 +2897,17 @@ static void parse_top_level_statement(OnyxParser* parser) { ENTITY_SUBMIT(tag); return; } + else if (parse_possible_directive(parser, "threadlocal")) { + OnyxToken* symbol = expect_token(parser, Token_Type_Symbol); + AstMemRes* memres = parse_memory_reservation(parser, symbol, 1); + + binding = make_node(AstBinding, Ast_Kind_Binding); + binding->token = symbol; + binding->flags |= private_kind; + binding->node = (AstNode *) memres; + + goto submit_binding_to_entities; + } else { OnyxToken* directive_token = expect_token(parser, '#'); OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol); diff --git a/src/wasm.c b/src/wasm.c index 51498d5e..197eb3dd 100644 --- a/src/wasm.c +++ b/src/wasm.c @@ -2117,6 +2117,13 @@ EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) { EMIT_FUNC(memory_reservation_location, AstMemRes* memres) { bh_arr(WasmInstruction) code = *pcode; WID(WI_PTR_CONST, memres->addr); + + if (memres->threadlocal) { + u64 tls_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_tls_base); + WIL(WI_GLOBAL_GET, tls_base_idx); + WI(WI_PTR_ADD); + } + *pcode = code; } @@ -2480,7 +2487,7 @@ EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return) { case Ast_Kind_Memres: { AstMemRes* memres = (AstMemRes *) expr; - WID(WI_PTR_CONST, memres->addr); + emit_memory_reservation_location(mod, &code, memres); *offset_return = 0; break; } @@ -2778,7 +2785,7 @@ EMIT_FUNC(expression, AstTyped* expr) { case Ast_Kind_Memres: { AstMemRes* memres = (AstMemRes *) expr; - WID(WI_I32_CONST, memres->addr); + emit_memory_reservation_location(mod, &code, memres); emit_load_instruction(mod, &code, memres->type, 0); break; } @@ -3321,8 +3328,10 @@ static void emit_global(OnyxWasmModule* module, AstGlobal* global) { bh_arr_set_at(module->globals, global_idx - module->foreign_global_count, glob); - if (global->flags & Ast_Flag_Global_Stack_Top) + if (global == &builtin_stack_top) module->stack_top_ptr = &module->globals[global_idx - module->foreign_global_count].initial_value[0].data.i1; + if (global == &builtin_tls_size) + module->tls_size_ptr = &module->globals[global_idx - module->foreign_global_count].initial_value[0].data.i1; } static void emit_foreign_global(OnyxWasmModule* module, AstGlobal* global) { @@ -3538,24 +3547,31 @@ static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) { return; } - u32 offset = mod->next_datum_offset; - bh_align(offset, alignment); + if (memres->threadlocal) { + memres->addr = mod->next_tls_offset; + bh_align(memres->addr, alignment); + mod->next_tls_offset = memres->addr + size; + + } else { + memres->addr = mod->next_datum_offset; + bh_align(memres->addr, alignment); + mod->next_datum_offset = memres->addr + size; + } if (memres->initial_value != NULL) { + assert(!memres->threadlocal); + u8* data = bh_alloc(global_heap_allocator, size); emit_raw_data(mod, data, memres->initial_value); WasmDatum datum = { - .offset = offset, + .offset = memres->addr, .length = size, .data = data, }; bh_arr_push(mod->data, datum); } - - memres->addr = offset; - mod->next_datum_offset = offset + size; } static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) { @@ -3649,6 +3665,8 @@ OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) { .next_datum_offset = 32, // Starting offset so null pointers don't immediately // break constant data. - brendanfh 2020/12/16 + .next_tls_offset = 0, + .elems = NULL, .next_elem_idx = 0,