authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-06 13:48:04-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-06 13:48:04-05:00
logb1775ca168e0bcfba6753346c5226881da49c6c4
treedc8a194940eca72dc20b692d170a9c1d2a6a4a31
parent8c6fa982cd0a02775264b616c37da9907cc603bb
signature Commit is signed but in an unrecognized format.

thread local storage working for linux x86_64


20 files changed, 306 insertions(+), 103 deletions(-)

CMakeLists.txt+1
...@@ -587,6 +587,7 @@ set(ZIG_STD_FILES...@@ -587,6 +587,7 @@ set(ZIG_STD_FILES
587 "os/linux/vdso.zig"587 "os/linux/vdso.zig"
588 "os/linux/x86_64.zig"588 "os/linux/x86_64.zig"
589 "os/path.zig"589 "os/path.zig"
590 "os/startup.zig"
590 "os/time.zig"591 "os/time.zig"
591 "os/uefi.zig"592 "os/uefi.zig"
592 "os/windows/advapi32.zig"593 "os/windows/advapi32.zig"
src/all_types.hpp+8-5
...@@ -544,12 +544,7 @@ struct AstNodeDefer {...@@ -544,12 +544,7 @@ struct AstNodeDefer {
544};544};
545545
546struct AstNodeVariableDeclaration {546struct AstNodeVariableDeclaration {
547 VisibMod visib_mod;
548 Buf *symbol;547 Buf *symbol;
549 bool is_const;
550 bool is_comptime;
551 bool is_export;
552 bool is_extern;
553 // one or both of type and expr will be non null548 // one or both of type and expr will be non null
554 AstNode *type;549 AstNode *type;
555 AstNode *expr;550 AstNode *expr;
...@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {...@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {
559 AstNode *align_expr;554 AstNode *align_expr;
560 // populated if the "section(S)" is present555 // populated if the "section(S)" is present
561 AstNode *section_expr;556 AstNode *section_expr;
557 Token *threadlocal_tok;
558
559 VisibMod visib_mod;
560 bool is_const;
561 bool is_comptime;
562 bool is_export;
563 bool is_extern;
562};564};
563565
564struct AstNodeTestDecl {566struct AstNodeTestDecl {
...@@ -1873,6 +1875,7 @@ struct ZigVar {...@@ -1873,6 +1875,7 @@ struct ZigVar {
1873 bool shadowable;1875 bool shadowable;
1874 bool src_is_const;1876 bool src_is_const;
1875 bool gen_is_const;1877 bool gen_is_const;
1878 bool is_thread_local;
1876};1879};
18771880
1878struct ErrorTableEntry {1881struct ErrorTableEntry {
src/analyze.cpp+45-24
...@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum...@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum
28static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);28static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
29static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);29static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
3030
31ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {31static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token,
32 if (node->owner->c_import_node != nullptr) {32 Buf *msg)
33 // if this happens, then translate_c generated code that33{
34 // failed semantic analysis, which isn't supposed to happen34 if (owner->c_import_node != nullptr) {
35 ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
36 buf_sprintf("compiler bug: @cImport generated invalid zig code"));
37
38 add_error_note(g, err, node, msg);
39
40 g->errors.append(err);
41 return err;
42 }
43
44 ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
45 node->owner->source_code, node->owner->line_offsets, msg);
46
47 g->errors.append(err);
48 return err;
49}
50
51ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
52 if (node->owner->c_import_node != nullptr) {
53 // if this happens, then translate_c generated code that35 // if this happens, then translate_c generated code that
54 // failed semantic analysis, which isn't supposed to happen36 // failed semantic analysis, which isn't supposed to happen
5537
...@@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m...@@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
64 return note;46 return note;
65 }47 }
6648
67 ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,49 ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
68 node->owner->source_code, node->owner->line_offsets, msg);50 owner->source_code, owner->line_offsets, msg);
6951
70 err_msg_add_note(parent_msg, err);52 err_msg_add_note(parent_msg, err);
71 return err;53 return err;
72}54}
7355
56ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) {
57 if (owner->c_import_node != nullptr) {
58 // if this happens, then translate_c generated code that
59 // failed semantic analysis, which isn't supposed to happen
60 ErrorMsg *err = add_node_error(g, owner->c_import_node,
61 buf_sprintf("compiler bug: @cImport generated invalid zig code"));
62
63 add_error_note_token(g, err, owner, token, msg);
64
65 g->errors.append(err);
66 return err;
67 }
68 ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
69 owner->source_code, owner->line_offsets, msg);
70
71 g->errors.append(err);
72 return err;
73}
74
75ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
76 Token fake_token;
77 fake_token.start_line = node->line;
78 fake_token.start_column = node->column;
79 return add_token_error(g, node->owner, &fake_token, msg);
80}
81
82ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
83 Token fake_token;
84 fake_token.start_line = node->line;
85 fake_token.start_column = node->column;
86 return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
87}
88
74ZigType *new_type_table_entry(ZigTypeId id) {89ZigType *new_type_table_entry(ZigTypeId id) {
75 ZigType *entry = allocate<ZigType>(1);90 ZigType *entry = allocate<ZigType>(1);
76 entry->id = id;91 entry->id = id;
...@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {...@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
3668 bool is_const = var_decl->is_const;3683 bool is_const = var_decl->is_const;
3669 bool is_extern = var_decl->is_extern;3684 bool is_extern = var_decl->is_extern;
3670 bool is_export = var_decl->is_export;3685 bool is_export = var_decl->is_export;
3686 bool is_thread_local = var_decl->threadlocal_tok != nullptr;
36713687
3672 ZigType *explicit_type = nullptr;3688 ZigType *explicit_type = nullptr;
3673 if (var_decl->type) {3689 if (var_decl->type) {
...@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {...@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
3727 tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,3743 tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
3728 is_const, init_val, &tld_var->base, type);3744 is_const, init_val, &tld_var->base, type);
3729 tld_var->var->linkage = linkage;3745 tld_var->var->linkage = linkage;
3746 tld_var->var->is_thread_local = is_thread_local;
37303747
3731 if (implicit_type != nullptr && type_is_invalid(implicit_type)) {3748 if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
3732 tld_var->var->var_type = g->builtin_types.entry_invalid;3749 tld_var->var->var_type = g->builtin_types.entry_invalid;
...@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {...@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
3747 }3764 }
3748 }3765 }
37493766
3767 if (is_thread_local && is_const) {
3768 add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
3769 }
3770
3750 g->global_vars.append(tld_var);3771 g->global_vars.append(tld_var);
3751}3772}
37523773
src/analyze.hpp+1
...@@ -12,6 +12,7 @@...@@ -12,6 +12,7 @@
1212
13void semantic_analyze(CodeGen *g);13void semantic_analyze(CodeGen *g);
14ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);14ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
15ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg);
15ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);16ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
16ZigType *new_type_table_entry(ZigTypeId id);17ZigType *new_type_table_entry(ZigTypeId id);
17ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);18ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
src/ast_render.cpp+6-1
...@@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) {...@@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) {
132 return is_const ? "const" : "var";132 return is_const ? "const" : "var";
133}133}
134134
135static const char *thread_local_string(Token *tok) {
136 return (tok == nullptr) ? "" : "threadlocal ";
137}
138
135const char *container_string(ContainerKind kind) {139const char *container_string(ContainerKind kind) {
136 switch (kind) {140 switch (kind) {
137 case ContainerKindEnum: return "enum";141 case ContainerKindEnum: return "enum";
...@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
554 {558 {
555 const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);559 const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
556 const char *extern_str = extern_string(node->data.variable_declaration.is_extern);560 const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
561 const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
557 const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);562 const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
558 fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var);563 fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
559 print_symbol(ar, node->data.variable_declaration.symbol);564 print_symbol(ar, node->data.variable_declaration.symbol);
560565
561 if (node->data.variable_declaration.type) {566 if (node->data.variable_declaration.type) {
src/codegen.cpp+7
...@@ -6445,6 +6445,9 @@ static void do_code_gen(CodeGen *g) {...@@ -6445,6 +6445,9 @@ static void do_code_gen(CodeGen *g) {
6445 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);6445 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
6446 LLVMSetAlignment(global_value, var->align_bytes);6446 LLVMSetAlignment(global_value, var->align_bytes);
6447 LLVMSetGlobalConstant(global_value, var->gen_is_const);6447 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6448 if (var->is_thread_local && !g->is_single_threaded) {
6449 LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
6450 }
6448 }6451 }
6449 } else {6452 } else {
6450 bool exported = (var->linkage == VarLinkageExport);6453 bool exported = (var->linkage == VarLinkageExport);
...@@ -6470,6 +6473,9 @@ static void do_code_gen(CodeGen *g) {...@@ -6470,6 +6473,9 @@ static void do_code_gen(CodeGen *g) {
6470 }6473 }
64716474
6472 LLVMSetGlobalConstant(global_value, var->gen_is_const);6475 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6476 if (var->is_thread_local && !g->is_single_threaded) {
6477 LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
6478 }
6473 }6479 }
64746480
6475 var->value_ref = global_value;6481 var->value_ref = global_value;
...@@ -7520,6 +7526,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {...@@ -7520,6 +7526,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
7520 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);7526 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
7521 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);7527 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
7522 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);7528 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
7529 g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
7523 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);7530 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
7524 scan_import(g, g->compile_var_import);7531 scan_import(g, g->compile_var_import);
75257532
src/ir.cpp+4
...@@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
5204 add_node_error(irb->codegen, variable_declaration->section_expr,5204 add_node_error(irb->codegen, variable_declaration->section_expr,
5205 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));5205 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
5206 }5206 }
5207 if (variable_declaration->threadlocal_tok != nullptr) {
5208 add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
5209 buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
5210 }
52075211
5208 // Temporarily set the name of the IrExecutable to the VariableDeclaration5212 // Temporarily set the name of the IrExecutable to the VariableDeclaration
5209 // so that the struct or enum from the init expression inherits the name.5213 // so that the struct or enum from the init expression inherits the name.
src/parser.cpp+14-8
...@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {...@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
844844
845// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON845// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
846static AstNode *ast_parse_var_decl(ParseContext *pc) {846static AstNode *ast_parse_var_decl(ParseContext *pc) {
847 Token *first = eat_token_if(pc, TokenIdKeywordConst);847 Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
848 if (first == nullptr)848 Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
849 first = eat_token_if(pc, TokenIdKeywordVar);849 if (mut_kw == nullptr)
850 if (first == nullptr)850 mut_kw = eat_token_if(pc, TokenIdKeywordVar);
851 return nullptr;851 if (mut_kw == nullptr) {
852852 if (thread_local_kw == nullptr) {
853 return nullptr;
854 } else {
855 ast_invalid_token_error(pc, peek_token(pc));
856 }
857 }
853 Token *identifier = expect_token(pc, TokenIdSymbol);858 Token *identifier = expect_token(pc, TokenIdSymbol);
854 AstNode *type_expr = nullptr;859 AstNode *type_expr = nullptr;
855 if (eat_token_if(pc, TokenIdColon) != nullptr)860 if (eat_token_if(pc, TokenIdColon) != nullptr)
...@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {...@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
863868
864 expect_token(pc, TokenIdSemicolon);869 expect_token(pc, TokenIdSemicolon);
865870
866 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first);871 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
867 res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst;872 res->data.variable_declaration.threadlocal_tok = thread_local_kw;
873 res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
868 res->data.variable_declaration.symbol = token_buf(identifier);874 res->data.variable_declaration.symbol = token_buf(identifier);
869 res->data.variable_declaration.type = type_expr;875 res->data.variable_declaration.type = type_expr;
870 res->data.variable_declaration.align_expr = align_expr;876 res->data.variable_declaration.align_expr = align_expr;
src/tokenizer.cpp+2
...@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {...@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {
146 {"suspend", TokenIdKeywordSuspend},146 {"suspend", TokenIdKeywordSuspend},
147 {"switch", TokenIdKeywordSwitch},147 {"switch", TokenIdKeywordSwitch},
148 {"test", TokenIdKeywordTest},148 {"test", TokenIdKeywordTest},
149 {"threadlocal", TokenIdKeywordThreadLocal},
149 {"true", TokenIdKeywordTrue},150 {"true", TokenIdKeywordTrue},
150 {"try", TokenIdKeywordTry},151 {"try", TokenIdKeywordTry},
151 {"undefined", TokenIdKeywordUndefined},152 {"undefined", TokenIdKeywordUndefined},
...@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {...@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {
1586 case TokenIdKeywordStruct: return "struct";1587 case TokenIdKeywordStruct: return "struct";
1587 case TokenIdKeywordSwitch: return "switch";1588 case TokenIdKeywordSwitch: return "switch";
1588 case TokenIdKeywordTest: return "test";1589 case TokenIdKeywordTest: return "test";
1590 case TokenIdKeywordThreadLocal: return "threadlocal";
1589 case TokenIdKeywordTrue: return "true";1591 case TokenIdKeywordTrue: return "true";
1590 case TokenIdKeywordTry: return "try";1592 case TokenIdKeywordTry: return "try";
1591 case TokenIdKeywordUndefined: return "undefined";1593 case TokenIdKeywordUndefined: return "undefined";
src/tokenizer.hpp+1
...@@ -88,6 +88,7 @@ enum TokenId {...@@ -88,6 +88,7 @@ enum TokenId {
88 TokenIdKeywordSuspend,88 TokenIdKeywordSuspend,
89 TokenIdKeywordSwitch,89 TokenIdKeywordSwitch,
90 TokenIdKeywordTest,90 TokenIdKeywordTest,
91 TokenIdKeywordThreadLocal,
91 TokenIdKeywordTrue,92 TokenIdKeywordTrue,
92 TokenIdKeywordTry,93 TokenIdKeywordTry,
93 TokenIdKeywordUndefined,94 TokenIdKeywordUndefined,
std/debug/index.zig-1
...@@ -37,7 +37,6 @@ const Module = struct {...@@ -37,7 +37,6 @@ const Module = struct {
37var stderr_file: os.File = undefined;37var stderr_file: os.File = undefined;
38var stderr_file_out_stream: os.File.OutStream = undefined;38var stderr_file_out_stream: os.File.OutStream = undefined;
3939
40/// TODO multithreaded awareness
41var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;40var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
42var stderr_mutex = std.Mutex.init();41var stderr_mutex = std.Mutex.init();
43pub fn warn(comptime fmt: []const u8, args: ...) void {42pub fn warn(comptime fmt: []const u8, args: ...) void {
std/heap.zig+2-5
...@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {...@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
106 };106 };
107 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;107 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
108 const root_addr = @ptrToInt(ptr);108 const root_addr = @ptrToInt(ptr);
109 const rem = @rem(root_addr, alignment);109 const adjusted_addr = mem.alignForward(root_addr, alignment);
110 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
111 const adjusted_addr = root_addr + march_forward_bytes;
112 const record_addr = adjusted_addr + n;110 const record_addr = adjusted_addr + n;
113 @intToPtr(*align(1) usize, record_addr).* = root_addr;111 @intToPtr(*align(1) usize, record_addr).* = root_addr;
114 return @intToPtr([*]u8, adjusted_addr)[0..n];112 return @intToPtr([*]u8, adjusted_addr)[0..n];
...@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {...@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
126 const base_addr = @ptrToInt(old_mem.ptr);124 const base_addr = @ptrToInt(old_mem.ptr);
127 const old_addr_end = base_addr + old_mem.len;125 const old_addr_end = base_addr + old_mem.len;
128 const new_addr_end = base_addr + new_size;126 const new_addr_end = base_addr + new_size;
129 const rem = @rem(new_addr_end, os.page_size);127 const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
130 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
131 if (old_addr_end > new_addr_end_rounded) {128 if (old_addr_end > new_addr_end_rounded) {
132 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);129 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
133 }130 }
std/index.zig+2-1
...@@ -33,8 +33,8 @@ pub const io = @import("io.zig");...@@ -33,8 +33,8 @@ pub const io = @import("io.zig");
33pub const json = @import("json.zig");33pub const json = @import("json.zig");
34pub const macho = @import("macho.zig");34pub const macho = @import("macho.zig");
35pub const math = @import("math/index.zig");35pub const math = @import("math/index.zig");
36pub const meta = @import("meta/index.zig");
37pub const mem = @import("mem.zig");36pub const mem = @import("mem.zig");
37pub const meta = @import("meta/index.zig");
38pub const net = @import("net.zig");38pub const net = @import("net.zig");
39pub const os = @import("os/index.zig");39pub const os = @import("os/index.zig");
40pub const pdb = @import("pdb.zig");40pub const pdb = @import("pdb.zig");
...@@ -45,6 +45,7 @@ pub const unicode = @import("unicode.zig");...@@ -45,6 +45,7 @@ pub const unicode = @import("unicode.zig");
45pub const zig = @import("zig/index.zig");45pub const zig = @import("zig/index.zig");
4646
47pub const lazyInit = @import("lazy_init.zig").lazyInit;47pub const lazyInit = @import("lazy_init.zig").lazyInit;
48pub const startup = @import("os/startup.zig");
4849
49test "std" {50test "std" {
50 // run tests from these51 // run tests from these
std/mem.zig+20
...@@ -1366,3 +1366,23 @@ test "std.mem.subArrayPtr" {...@@ -1366,3 +1366,23 @@ test "std.mem.subArrayPtr" {
1366 sub2[1] = 'X';1366 sub2[1] = 'X';
1367 debug.assert(std.mem.eql(u8, a2, "abcXef"));1367 debug.assert(std.mem.eql(u8, a2, "abcXef"));
1368}1368}
1369
1370/// Round an address up to the nearest aligned address
1371pub fn alignForward(addr: usize, alignment: usize) usize {
1372 return (addr + alignment - 1) & ~(alignment - 1);
1373}
1374
1375test "std.mem.alignForward" {
1376 debug.assertOrPanic(alignForward(1, 1) == 1);
1377 debug.assertOrPanic(alignForward(2, 1) == 2);
1378 debug.assertOrPanic(alignForward(1, 2) == 2);
1379 debug.assertOrPanic(alignForward(2, 2) == 2);
1380 debug.assertOrPanic(alignForward(3, 2) == 4);
1381 debug.assertOrPanic(alignForward(4, 2) == 4);
1382 debug.assertOrPanic(alignForward(7, 8) == 8);
1383 debug.assertOrPanic(alignForward(8, 8) == 8);
1384 debug.assertOrPanic(alignForward(9, 8) == 16);
1385 debug.assertOrPanic(alignForward(15, 8) == 16);
1386 debug.assertOrPanic(alignForward(16, 8) == 16);
1387 debug.assertOrPanic(alignForward(17, 8) == 24);
1388}
std/os/index.zig+66-53
...@@ -8,6 +8,9 @@ const is_posix = switch (builtin.os) {...@@ -8,6 +8,9 @@ const is_posix = switch (builtin.os) {
8};8};
9const os = @This();9const os = @This();
1010
11// See the comment in startup.zig for why this does not use the `std` global above.
12const startup = @import("std").startup;
13
11test "std.os" {14test "std.os" {
12 _ = @import("child_process.zig");15 _ = @import("child_process.zig");
13 _ = @import("darwin.zig");16 _ = @import("darwin.zig");
...@@ -667,14 +670,11 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {...@@ -667,14 +670,11 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
667 }670 }
668}671}
669672
670pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
671pub var posix_environ_raw: [][*]u8 = undefined;
672
673/// See std.elf for the constants.673/// See std.elf for the constants.
674pub fn linuxGetAuxVal(index: usize) usize {674pub fn linuxGetAuxVal(index: usize) usize {
675 if (builtin.link_libc) {675 if (builtin.link_libc) {
676 return usize(std.c.getauxval(index));676 return usize(std.c.getauxval(index));
677 } else if (linux_elf_aux_maybe) |auxv| {677 } else if (startup.linux_elf_aux_maybe) |auxv| {
678 var i: usize = 0;678 var i: usize = 0;
679 while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {679 while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
680 if (auxv[i].a_type == index)680 if (auxv[i].a_type == index)
...@@ -692,12 +692,7 @@ pub fn getBaseAddress() usize {...@@ -692,12 +692,7 @@ pub fn getBaseAddress() usize {
692 return base;692 return base;
693 }693 }
694 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);694 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
695 const ElfHeader = switch (@sizeOf(usize)) {695 return phdr - @sizeOf(std.elf.Ehdr);
696 4 => std.elf.Elf32_Ehdr,
697 8 => std.elf.Elf64_Ehdr,
698 else => @compileError("Unsupported architecture"),
699 };
700 return phdr - @sizeOf(ElfHeader);
701 },696 },
702 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),697 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
703 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),698 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
...@@ -739,7 +734,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {...@@ -739,7 +734,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
739 try result.setMove(key, value);734 try result.setMove(key, value);
740 }735 }
741 } else {736 } else {
742 for (posix_environ_raw) |ptr| {737 for (startup.posix_environ_raw) |ptr| {
743 var line_i: usize = 0;738 var line_i: usize = 0;
744 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}739 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
745 const key = ptr[0..line_i];740 const key = ptr[0..line_i];
...@@ -761,7 +756,7 @@ test "os.getEnvMap" {...@@ -761,7 +756,7 @@ test "os.getEnvMap" {
761756
762/// TODO make this go through libc when we have it757/// TODO make this go through libc when we have it
763pub fn getEnvPosix(key: []const u8) ?[]const u8 {758pub fn getEnvPosix(key: []const u8) ?[]const u8 {
764 for (posix_environ_raw) |ptr| {759 for (startup.posix_environ_raw) |ptr| {
765 var line_i: usize = 0;760 var line_i: usize = 0;
766 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}761 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
767 const this_key = ptr[0..line_i];762 const this_key = ptr[0..line_i];
...@@ -1942,14 +1937,14 @@ pub const ArgIteratorPosix = struct {...@@ -1942,14 +1937,14 @@ pub const ArgIteratorPosix = struct {
1942 pub fn init() ArgIteratorPosix {1937 pub fn init() ArgIteratorPosix {
1943 return ArgIteratorPosix{1938 return ArgIteratorPosix{
1944 .index = 0,1939 .index = 0,
1945 .count = raw.len,1940 .count = startup.posix_argv_raw.len,
1946 };1941 };
1947 }1942 }
19481943
1949 pub fn next(self: *ArgIteratorPosix) ?[]const u8 {1944 pub fn next(self: *ArgIteratorPosix) ?[]const u8 {
1950 if (self.index == self.count) return null;1945 if (self.index == self.count) return null;
19511946
1952 const s = raw[self.index];1947 const s = startup.posix_argv_raw[self.index];
1953 self.index += 1;1948 self.index += 1;
1954 return cstr.toSlice(s);1949 return cstr.toSlice(s);
1955 }1950 }
...@@ -1960,10 +1955,6 @@ pub const ArgIteratorPosix = struct {...@@ -1960,10 +1955,6 @@ pub const ArgIteratorPosix = struct {
1960 self.index += 1;1955 self.index += 1;
1961 return true;1956 return true;
1962 }1957 }
1963
1964 /// This is marked as public but actually it's only meant to be used
1965 /// internally by zig's startup code.
1966 pub var raw: [][*]u8 = undefined;
1967};1958};
19681959
1969pub const ArgIteratorWindows = struct {1960pub const ArgIteratorWindows = struct {
...@@ -2908,14 +2899,15 @@ pub const Thread = struct {...@@ -2908,14 +2899,15 @@ pub const Thread = struct {
2908 pub const Data = if (use_pthreads)2899 pub const Data = if (use_pthreads)
2909 struct {2900 struct {
2910 handle: Thread.Handle,2901 handle: Thread.Handle,
2911 stack_addr: usize,2902 mmap_addr: usize,
2912 stack_len: usize,2903 mmap_len: usize,
2913 }2904 }
2914 else switch (builtin.os) {2905 else switch (builtin.os) {
2915 builtin.Os.linux => struct {2906 builtin.Os.linux => struct {
2916 handle: Thread.Handle,2907 handle: Thread.Handle,
2917 stack_addr: usize,2908 mmap_addr: usize,
2918 stack_len: usize,2909 mmap_len: usize,
2910 tls_end_addr: usize,
2919 },2911 },
2920 builtin.Os.windows => struct {2912 builtin.Os.windows => struct {
2921 handle: Thread.Handle,2913 handle: Thread.Handle,
...@@ -2955,7 +2947,7 @@ pub const Thread = struct {...@@ -2955,7 +2947,7 @@ pub const Thread = struct {
2955 posix.EDEADLK => unreachable,2947 posix.EDEADLK => unreachable,
2956 else => unreachable,2948 else => unreachable,
2957 }2949 }
2958 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);2950 assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
2959 } else switch (builtin.os) {2951 } else switch (builtin.os) {
2960 builtin.Os.linux => {2952 builtin.Os.linux => {
2961 while (true) {2953 while (true) {
...@@ -2969,7 +2961,7 @@ pub const Thread = struct {...@@ -2969,7 +2961,7 @@ pub const Thread = struct {
2969 else => unreachable,2961 else => unreachable,
2970 }2962 }
2971 }2963 }
2972 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);2964 assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
2973 },2965 },
2974 builtin.Os.windows => {2966 builtin.Os.windows => {
2975 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);2967 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
...@@ -3097,42 +3089,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -3097,42 +3089,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
30973089
3098 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;3090 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
30993091
3100 const mmap_len = default_stack_size;3092 var stack_end_offset: usize = undefined;
3101 const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);3093 var thread_start_offset: usize = undefined;
3102 if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;3094 var context_start_offset: usize = undefined;
3103 errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);3095 var tls_start_offset: usize = undefined;
3096 const mmap_len = blk: {
3097 // First in memory will be the stack, which grows downwards.
3098 var l: usize = mem.alignForward(default_stack_size, os.page_size);
3099 stack_end_offset = l;
3100 // Above the stack, so that it can be in the same mmap call, put the Thread object.
3101 l = mem.alignForward(l, @alignOf(Thread));
3102 thread_start_offset = l;
3103 l += @sizeOf(Thread);
3104 // Next, the Context object.
3105 if (@sizeOf(Context) != 0) {
3106 l = mem.alignForward(l, @alignOf(Context));
3107 context_start_offset = l;
3108 l += @sizeOf(Context);
3109 }
3110 // Finally, the Thread Local Storage, if any.
3111 if (!Thread.use_pthreads) {
3112 if (startup.linux_tls_phdr) |tls_phdr| {
3113 l = mem.alignForward(l, tls_phdr.p_align);
3114 tls_start_offset = l;
3115 l += tls_phdr.p_memsz;
3116 }
3117 }
3118 break :blk l;
3119 };
3120 const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
3121 if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
3122 errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
3123
3124 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
3125 thread_ptr.data.mmap_addr = mmap_addr;
3126 thread_ptr.data.mmap_len = mmap_len;
31043127
3105 var stack_end: usize = stack_addr + mmap_len;
3106 var arg: usize = undefined;3128 var arg: usize = undefined;
3107 if (@sizeOf(Context) != 0) {3129 if (@sizeOf(Context) != 0) {
3108 stack_end -= @sizeOf(Context);3130 arg = mmap_addr + context_start_offset;
3109 stack_end -= stack_end % @alignOf(Context);3131 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
3110 assert(stack_end >= stack_addr);
3111 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
3112 context_ptr.* = context;3132 context_ptr.* = context;
3113 arg = stack_end;
3114 }3133 }
31153134
3116 stack_end -= @sizeOf(Thread);3135 if (Thread.use_pthreads) {
3117 stack_end -= stack_end % @alignOf(Thread);
3118 assert(stack_end >= stack_addr);
3119 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end));
3120
3121 thread_ptr.data.stack_addr = stack_addr;
3122 thread_ptr.data.stack_len = mmap_len;
3123
3124 if (builtin.os == builtin.Os.windows) {
3125 // use windows API directly
3126 @compileError("TODO support spawnThread for Windows");
3127 } else if (Thread.use_pthreads) {
3128 // use pthreads3136 // use pthreads
3129 var attr: c.pthread_attr_t = undefined;3137 var attr: c.pthread_attr_t = undefined;
3130 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;3138 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
3131 defer assert(c.pthread_attr_destroy(&attr) == 0);3139 defer assert(c.pthread_attr_destroy(&attr) == 0);
31323140
3133 // align to page3141 assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
3134 stack_end -= stack_end % os.page_size;
3135 assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
31363142
3137 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));3143 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
3138 switch (err) {3144 switch (err) {
...@@ -3143,10 +3149,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -3143,10 +3149,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
3143 else => return unexpectedErrorPosix(@intCast(usize, err)),3149 else => return unexpectedErrorPosix(@intCast(usize, err)),
3144 }3150 }
3145 } else if (builtin.os == builtin.Os.linux) {3151 } else if (builtin.os == builtin.Os.linux) {
3146 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly3152 var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
3147 const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;3153 posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
3148 const newtls: usize = 0;3154 posix.CLONE_DETACHED;
3149 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);3155 var newtls: usize = undefined;
3156 if (startup.linux_tls_phdr) |tls_phdr| {
3157 @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), startup.linux_tls_img_src, tls_phdr.p_filesz);
3158 thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
3159 newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
3160 flags |= posix.CLONE_SETTLS;
3161 }
3162 const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
3150 const err = posix.getErrno(rc);3163 const err = posix.getErrno(rc);
3151 switch (err) {3164 switch (err) {
3152 0 => return thread_ptr,3165 0 => return thread_ptr,
std/os/startup.zig created+26
...@@ -0,0 +1,26 @@
1// This file contains global variables that are initialized on startup from
2// std/special/bootstrap.zig. There are a few things to be aware of here.
3//
4// First, when building an object or library, and no entry point is defined
5// (such as pub fn main), std/special/bootstrap.zig is not included in the
6// compilation. And so these global variables will remain set to the values
7// you see here.
8//
9// Second, when using `zig test` to test the standard library, note that
10// `zig test` is self-hosted. This means that it uses std/special/bootstrap.zig
11// and an @import("std") from the install directory, which is distinct from
12// the standard library files that we are directly testing with `zig test`.
13// This means that these global variables would not get set. So the workaround
14// here is that references to these globals from the standard library must
15// use `@import("std").startup` rather than
16// `@import("path/to/std/index.zig").startup` (and rather than the file path of
17// this file directly). We also put "std" as a reference to itself in the
18// standard library package so that this can work.
19
20const std = @import("../index.zig");
21
22pub var linux_tls_phdr: ?*std.elf.Phdr = null;
23pub var linux_tls_img_src: [*]const u8 = undefined; // defined when linux_tls_phdr is non-null
24pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
25pub var posix_environ_raw: [][*]u8 = undefined;
26pub var posix_argv_raw: [][*]u8 = undefined;
std/os/test.zig+16
...@@ -105,3 +105,19 @@ test "AtomicFile" {...@@ -105,3 +105,19 @@ test "AtomicFile" {
105105
106 try os.deleteFile(test_out_file);106 try os.deleteFile(test_out_file);
107}107}
108
109test "thread local storage" {
110 if (builtin.single_threaded) return error.SkipZigTest;
111 const thread1 = try std.os.spawnThread({}, testTls);
112 const thread2 = try std.os.spawnThread({}, testTls);
113 testTls({});
114 thread1.wait();
115 thread2.wait();
116}
117
118threadlocal var x: i32 = 1234;
119fn testTls(context: void) void {
120 if (x != 1234) @panic("bad start value");
121 x += 1;
122 if (x != 1235) @panic("bad end value");
123}
std/special/bootstrap.zig+58-5
...@@ -4,6 +4,7 @@...@@ -4,6 +4,7 @@
4const root = @import("@root");4const root = @import("@root");
5const std = @import("std");5const std = @import("std");
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const assert = std.debug.assert;
78
8var argc_ptr: [*]usize = undefined;9var argc_ptr: [*]usize = undefined;
910
...@@ -61,9 +62,23 @@ fn posixCallMainAndExit() noreturn {...@@ -61,9 +62,23 @@ fn posixCallMainAndExit() noreturn {
61 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}62 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
62 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];63 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
63 if (builtin.os == builtin.Os.linux) {64 if (builtin.os == builtin.Os.linux) {
64 const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);65 // Scan auxiliary vector.
65 std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);66 const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
66 std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);67 std.startup.linux_elf_aux_maybe = auxv;
68 var i: usize = 0;
69 var at_phdr: usize = 0;
70 var at_phnum: usize = 0;
71 var at_phent: usize = 0;
72 while (auxv[i].a_un.a_val != 0) : (i += 1) {
73 switch (auxv[i].a_type) {
74 std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
75 std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
76 std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
77 std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
78 else => {},
79 }
80 }
81 if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
67 }82 }
6883
69 std.os.posix.exit(callMainWithArgs(argc, argv, envp));84 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
...@@ -72,8 +87,8 @@ fn posixCallMainAndExit() noreturn {...@@ -72,8 +87,8 @@ fn posixCallMainAndExit() noreturn {
72// This is marked inline because for some reason LLVM in release mode fails to inline it,87// This is marked inline because for some reason LLVM in release mode fails to inline it,
73// and we want fewer call frames in stack traces.88// and we want fewer call frames in stack traces.
74inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {89inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
75 std.os.ArgIteratorPosix.raw = argv[0..argc];90 std.startup.posix_argv_raw = argv[0..argc];
76 std.os.posix_environ_raw = envp;91 std.startup.posix_environ_raw = envp;
77 return callMain();92 return callMain();
78}93}
7994
...@@ -116,3 +131,41 @@ inline fn callMain() u8 {...@@ -116,3 +131,41 @@ inline fn callMain() u8 {
116 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),131 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
117 }132 }
118}133}
134
135var tls_end_addr: usize = undefined;
136const main_thread_tls_align = 32;
137var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
138
139fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
140 var phdr_addr = at_phdr;
141 var n = at_phnum;
142 var base: usize = 0;
143 while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) {
144 const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
145 // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
146 switch (phdr.p_type) {
147 std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
148 std.elf.PT_TLS => std.startup.linux_tls_phdr = phdr,
149 else => continue,
150 }
151 }
152 const tls_phdr = std.startup.linux_tls_phdr orelse return;
153 std.startup.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
154 assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
155 assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
156 @memcpy(&main_thread_tls_bytes, std.startup.linux_tls_img_src, tls_phdr.p_filesz);
157 tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
158 linuxSetThreadArea(@ptrToInt(&tls_end_addr));
159}
160
161fn linuxSetThreadArea(addr: usize) void {
162 switch (builtin.arch) {
163 builtin.Arch.x86_64 => {
164 const ARCH_SET_FS = 0x1002;
165 const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
166 // acrh_prctl is documented to never fail
167 assert(rc == 0);
168 },
169 else => @compileError("Unsupported architecture"),
170 }
171}
test/compile_errors.zig+19
...@@ -1,6 +1,25 @@...@@ -1,6 +1,25 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.add(
5 "threadlocal qualifier on const",
6 \\threadlocal const x: i32 = 1234;
7 \\export fn entry() i32 {
8 \\ return x;
9 \\}
10 ,
11 ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant",
12 );
13
14 cases.add(
15 "threadlocal qualifier on local variable",
16 \\export fn entry() void {
17 \\ threadlocal var x: i32 = 1234;
18 \\}
19 ,
20 ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal",
21 );
22
4 cases.add(23 cases.add(
5 "@bitCast same size but bit count mismatch",24 "@bitCast same size but bit count mismatch",
6 \\export fn entry(byte: u8) void {25 \\export fn entry(byte: u8) void {
test/stage1/behavior/misc.zig+8
...@@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" {...@@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" {
685fn getNull() ?*i32 {685fn getNull() ?*i32 {
686 return null;686 return null;
687}687}
688
689test "thread local variable" {
690 const S = struct {
691 threadlocal var t: i32 = 1234;
692 };
693 S.t += 1;
694 assertOrPanic(S.t == 1235);
695}