context.temp_allocator = alloc.temp_allocator;
context.assert_handler = __assert_handler;
- __stdio_init();
-
#if Multi_Threading_Enabled {
thread.__initialize();
}
#export "_start" () {
__runtime_initialize();
+ __tls_base = raw_alloc(context.allocator, __tls_size);
+ __stdio_init();
+
args: [] cstr = .{ null, 0 };
(package main).main(args);
#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
// 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;
// Private and internal things
//
-stdio : struct {
+#threadlocal stdio : struct {
print_stream : io.DynamicStringStream;
print_writer : io.Writer;
}
// 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;
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;
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;
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;
{ "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 },
};
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) {
}));
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) {
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);
}
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;
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);
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);
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;
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);
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;
}
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;
}
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;
}
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) {
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) {
.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,