added #threadlocal storage
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 17 Oct 2021 23:33:58 +0000 (18:33 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 17 Oct 2021 23:33:58 +0000 (18:33 -0500)
12 files changed:
bin/onyx
core/runtime/common.onyx
core/runtime/js.onyx
core/runtime/wasi.onyx
core/stdio.onyx
include/astnodes.h
include/wasm.h
src/builtins.c
src/checker.c
src/onyx.c
src/parser.c
src/wasm.c

index f843ade9b013b3269d87e4fd65c623223f3d38a1..c80c6ba0896a0365a656f0d060b485d9c2c8af30 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 5064c717f033ad98048e4b07bb665565577780c8..d63f573ac9c3b26a3d4459d6d15c10264fef61fe 100644 (file)
@@ -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();
     }
index 083641fa7ee809d8d746796b63a4a8e7fc94eb74..bfddb133b39c4b4881ee2afcb4af712250134461 100644 (file)
@@ -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
index be8f7fad57fedd562b421a48ea3935a4a0d3f516..9f8b44202c2e2023afa142ea30097c5c7cc1a03f 100644 (file)
@@ -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;
index 424b8ff2b5f8e09e731d3f40bd72b0c263051288..72fe20dd5fab9f0a130dcca0787a8bc58d3fb9ba 100644 (file)
@@ -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;
 }
index 4f6b1ab50da4249d9f57f5895567b1808f6aa2e4..24c18836ea22eb50ae674d43b30ff26afb39ac05 100644 (file)
@@ -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;
index 22863784801c31d02af5810679a26b3397ab552b..45c9824d93657f34c8cece4c3a6b226049972d4e 100644 (file)
@@ -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;
index 14c257ee0583808db537d044e4190c0692f2ffe4..6376397b309e67833cfce0f102e7c8debea59fad 100644 (file)
@@ -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 },
 };
index 3f7542855486af5820ff45f31a864be157da4bbd..f4d2d26d0852f6126fcbf98f02eef00d96fe4919 100644 (file)
@@ -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) {
index 48b3f4e40bc8a5de39844ec83fab9d8e2865d673..0532dd6b7e696ee7ca95e71761fd95aa810a834c 100644 (file)
@@ -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) {
index c6fa70633fb0b09db492b7373b5f0c1d7998a3cf..643c7b5b7105d5d0873a74870c3406a5de6d6693 100644 (file)
@@ -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);
index 51498d5e63681545a5df7b58f94e1cc0769a8dd2..197eb3dd879e6a69c3e045ebf4eff95846c447b5 100644 (file)
@@ -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,