authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-06 20:21:13-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-02-06 20:21:13-05:00
log8a5d3e2eaf72195993e4932bfda66d08a36c6064
tree6a09c68ee70d00e97feeae55b213203fbaddc295
parent3abf293a84b4dc052c0d735018db520eade6274b
parent89ffb5819703c81514e4f29d2cadb5b74ea1f840
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1924 from ziglang/tls

Implement Thread Local Variables

27 files changed, 376 insertions(+), 101 deletions(-)

CMakeLists.txt+1
...@@ -596,6 +596,7 @@ set(ZIG_STD_FILES...@@ -596,6 +596,7 @@ set(ZIG_STD_FILES
596 "os/windows/ntdll.zig"596 "os/windows/ntdll.zig"
597 "os/windows/ole32.zig"597 "os/windows/ole32.zig"
598 "os/windows/shell32.zig"598 "os/windows/shell32.zig"
599 "os/windows/tls.zig"
599 "os/windows/util.zig"600 "os/windows/util.zig"
600 "os/zen.zig"601 "os/zen.zig"
601 "pdb.zig"602 "pdb.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+22-5
...@@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {...@@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {
88};88};
8989
90CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,90CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
91 Buf *zig_lib_dir)91 Buf *zig_lib_dir, Buf *override_std_dir)
92{92{
93 CodeGen *g = allocate<CodeGen>(1);93 CodeGen *g = allocate<CodeGen>(1);
9494
...@@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out...@@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
9696
97 g->zig_lib_dir = zig_lib_dir;97 g->zig_lib_dir = zig_lib_dir;
9898
99 g->zig_std_dir = buf_alloc();99 if (override_std_dir == nullptr) {
100 os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);100 g->zig_std_dir = buf_alloc();
101 os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
102 } else {
103 g->zig_std_dir = override_std_dir;
104 }
101105
102 g->zig_c_headers_dir = buf_alloc();106 g->zig_c_headers_dir = buf_alloc();
103 os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);107 os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);
...@@ -6341,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) {...@@ -6341,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) {
6341 report_errors_and_maybe_exit(g);6345 report_errors_and_maybe_exit(g);
6342}6346}
63436347
6348static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
6349 if (var->is_thread_local && !g->is_single_threaded) {
6350 LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
6351 }
6352}
6353
6344static void do_code_gen(CodeGen *g) {6354static void do_code_gen(CodeGen *g) {
6345 assert(!g->errors.length);6355 assert(!g->errors.length);
63466356
...@@ -6425,6 +6435,7 @@ static void do_code_gen(CodeGen *g) {...@@ -6425,6 +6435,7 @@ static void do_code_gen(CodeGen *g) {
6425 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);6435 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
6426 LLVMSetAlignment(global_value, var->align_bytes);6436 LLVMSetAlignment(global_value, var->align_bytes);
6427 LLVMSetGlobalConstant(global_value, var->gen_is_const);6437 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6438 set_global_tls(g, var, global_value);
6428 }6439 }
6429 } else {6440 } else {
6430 bool exported = (var->linkage == VarLinkageExport);6441 bool exported = (var->linkage == VarLinkageExport);
...@@ -6450,6 +6461,7 @@ static void do_code_gen(CodeGen *g) {...@@ -6450,6 +6461,7 @@ static void do_code_gen(CodeGen *g) {
6450 }6461 }
64516462
6452 LLVMSetGlobalConstant(global_value, var->gen_is_const);6463 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6464 set_global_tls(g, var, global_value);
6453 }6465 }
64546466
6455 var->value_ref = global_value;6467 var->value_ref = global_value;
...@@ -7500,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {...@@ -7500,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
7500 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);7512 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
7501 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);7513 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
7502 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);7514 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
7515 g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
7503 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);7516 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
7504 scan_import(g, g->compile_var_import);7517 scan_import(g, g->compile_var_import);
75057518
...@@ -8329,8 +8342,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {...@@ -8329,8 +8342,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {
8329 if (!entry)8342 if (!entry)
8330 break;8343 break;
83318344
8332 cache_buf(ch, entry->key);8345 // TODO: I think we need a more sophisticated detection of
8333 add_cache_pkg(g, ch, entry->value);8346 // packages we have already seen
8347 if (entry->value != pkg) {
8348 cache_buf(ch, entry->key);
8349 add_cache_pkg(g, ch, entry->value);
8350 }
8334 }8351 }
8335}8352}
83368353
src/codegen.hpp+1-1
...@@ -15,7 +15,7 @@...@@ -15,7 +15,7 @@
15#include <stdio.h>15#include <stdio.h>
1616
17CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,17CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
18 Buf *zig_lib_dir);18 Buf *zig_lib_dir, Buf *override_std_dir);
1919
20void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);20void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
21void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);21void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
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/link.cpp+1-1
...@@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)...@@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
42 }42 }
4343
44 CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type,44 CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type,
45 parent_gen->build_mode, parent_gen->zig_lib_dir);45 parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir);
4646
47 child_gen->out_h_path = nullptr;47 child_gen->out_h_path = nullptr;
48 child_gen->verbose_tokenize = parent_gen->verbose_tokenize;48 child_gen->verbose_tokenize = parent_gen->verbose_tokenize;
src/main.cpp+9-3
...@@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) {...@@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) {
74 " -dirafter [dir] same as -isystem but do it last\n"74 " -dirafter [dir] same as -isystem but do it last\n"
75 " -isystem [dir] add additional search path for other .h files\n"75 " -isystem [dir] add additional search path for other .h files\n"
76 " -mllvm [arg] forward an arg to LLVM's option processing\n"76 " -mllvm [arg] forward an arg to LLVM's option processing\n"
77 " --override-std-dir [arg] use an alternate Zig standard library\n"
77 "\n"78 "\n"
78 "Link Options:\n"79 "Link Options:\n"
79 " --dynamic-linker [path] set the path to ld.so\n"80 " --dynamic-linker [path] set the path to ld.so\n"
...@@ -395,6 +396,7 @@ int main(int argc, char **argv) {...@@ -395,6 +396,7 @@ int main(int argc, char **argv) {
395 bool system_linker_hack = false;396 bool system_linker_hack = false;
396 TargetSubsystem subsystem = TargetSubsystemAuto;397 TargetSubsystem subsystem = TargetSubsystemAuto;
397 bool is_single_threaded = false;398 bool is_single_threaded = false;
399 Buf *override_std_dir = nullptr;
398400
399 if (argc >= 2 && strcmp(argv[1], "build") == 0) {401 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
400 Buf zig_exe_path_buf = BUF_INIT;402 Buf zig_exe_path_buf = BUF_INIT;
...@@ -430,7 +432,8 @@ int main(int argc, char **argv) {...@@ -430,7 +432,8 @@ int main(int argc, char **argv) {
430 Buf *build_runner_path = buf_alloc();432 Buf *build_runner_path = buf_alloc();
431 os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);433 os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);
432434
433 CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir());435 CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
436 override_std_dir);
434 g->enable_time_report = timing_info;437 g->enable_time_report = timing_info;
435 buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);438 buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
436 codegen_set_out_name(g, buf_create_from_str("build"));439 codegen_set_out_name(g, buf_create_from_str("build"));
...@@ -645,6 +648,8 @@ int main(int argc, char **argv) {...@@ -645,6 +648,8 @@ int main(int argc, char **argv) {
645 clang_argv.append(argv[i]);648 clang_argv.append(argv[i]);
646649
647 llvm_argv.append(argv[i]);650 llvm_argv.append(argv[i]);
651 } else if (strcmp(arg, "--override-std-dir") == 0) {
652 override_std_dir = buf_create_from_str(argv[i]);
648 } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {653 } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
649 lib_dirs.append(argv[i]);654 lib_dirs.append(argv[i]);
650 } else if (strcmp(arg, "--library") == 0) {655 } else if (strcmp(arg, "--library") == 0) {
...@@ -819,7 +824,7 @@ int main(int argc, char **argv) {...@@ -819,7 +824,7 @@ int main(int argc, char **argv) {
819824
820 switch (cmd) {825 switch (cmd) {
821 case CmdBuiltin: {826 case CmdBuiltin: {
822 CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());827 CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir);
823 g->is_single_threaded = is_single_threaded;828 g->is_single_threaded = is_single_threaded;
824 Buf *builtin_source = codegen_generate_builtin_source(g);829 Buf *builtin_source = codegen_generate_builtin_source(g);
825 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {830 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
...@@ -878,7 +883,8 @@ int main(int argc, char **argv) {...@@ -878,7 +883,8 @@ int main(int argc, char **argv) {
878 if (cmd == CmdRun && buf_out_name == nullptr) {883 if (cmd == CmdRun && buf_out_name == nullptr) {
879 buf_out_name = buf_create_from_str("run");884 buf_out_name = buf_create_from_str("run");
880 }885 }
881 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir());886 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(),
887 override_std_dir);
882 g->subsystem = subsystem;888 g->subsystem = subsystem;
883889
884 if (disable_pic) {890 if (disable_pic) {
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/build.zig+10
...@@ -1686,6 +1686,7 @@ pub const TestStep = struct {...@@ -1686,6 +1686,7 @@ pub const TestStep = struct {
1686 no_rosegment: bool,1686 no_rosegment: bool,
1687 output_path: ?[]const u8,1687 output_path: ?[]const u8,
1688 system_linker_hack: bool,1688 system_linker_hack: bool,
1689 override_std_dir: ?[]const u8,
16891690
1690 pub fn init(builder: *Builder, root_src: []const u8) TestStep {1691 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
1691 const step_name = builder.fmt("test {}", root_src);1692 const step_name = builder.fmt("test {}", root_src);
...@@ -1707,6 +1708,7 @@ pub const TestStep = struct {...@@ -1707,6 +1708,7 @@ pub const TestStep = struct {
1707 .no_rosegment = false,1708 .no_rosegment = false,
1708 .output_path = null,1709 .output_path = null,
1709 .system_linker_hack = false,1710 .system_linker_hack = false,
1711 .override_std_dir = null,
1710 };1712 };
1711 }1713 }
17121714
...@@ -1737,6 +1739,10 @@ pub const TestStep = struct {...@@ -1737,6 +1739,10 @@ pub const TestStep = struct {
1737 self.build_mode = mode;1739 self.build_mode = mode;
1738 }1740 }
17391741
1742 pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void {
1743 self.override_std_dir = dir_path;
1744 }
1745
1740 pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {1746 pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {
1741 self.output_path = file_path;1747 self.output_path = file_path;
17421748
...@@ -1914,6 +1920,10 @@ pub const TestStep = struct {...@@ -1914,6 +1920,10 @@ pub const TestStep = struct {
1914 if (self.system_linker_hack) {1920 if (self.system_linker_hack) {
1915 try zig_args.append("--system-linker-hack");1921 try zig_args.append("--system-linker-hack");
1916 }1922 }
1923 if (self.override_std_dir) |dir| {
1924 try zig_args.append("--override-std-dir");
1925 try zig_args.append(builder.pathFromRoot(dir));
1926 }
19171927
1918 try builder.spawnChild(zig_args.toSliceConst());1928 try builder.spawnChild(zig_args.toSliceConst());
1919 }1929 }
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+1-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");
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+65-41
...@@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) {...@@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) {
8};8};
9const os = @This();9const os = @This();
1010
11comptime {
12 assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
13}
14
11test "std.os" {15test "std.os" {
12 _ = @import("child_process.zig");16 _ = @import("child_process.zig");
13 _ = @import("darwin.zig");17 _ = @import("darwin.zig");
...@@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {...@@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {
692 return base;696 return base;
693 }697 }
694 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);698 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
695 const ElfHeader = switch (@sizeOf(usize)) {699 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 },700 },
702 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),701 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
703 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),702 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
...@@ -2908,14 +2907,15 @@ pub const Thread = struct {...@@ -2908,14 +2907,15 @@ pub const Thread = struct {
2908 pub const Data = if (use_pthreads)2907 pub const Data = if (use_pthreads)
2909 struct {2908 struct {
2910 handle: Thread.Handle,2909 handle: Thread.Handle,
2911 stack_addr: usize,2910 mmap_addr: usize,
2912 stack_len: usize,2911 mmap_len: usize,
2913 }2912 }
2914 else switch (builtin.os) {2913 else switch (builtin.os) {
2915 builtin.Os.linux => struct {2914 builtin.Os.linux => struct {
2916 handle: Thread.Handle,2915 handle: Thread.Handle,
2917 stack_addr: usize,2916 mmap_addr: usize,
2918 stack_len: usize,2917 mmap_len: usize,
2918 tls_end_addr: usize,
2919 },2919 },
2920 builtin.Os.windows => struct {2920 builtin.Os.windows => struct {
2921 handle: Thread.Handle,2921 handle: Thread.Handle,
...@@ -2955,7 +2955,7 @@ pub const Thread = struct {...@@ -2955,7 +2955,7 @@ pub const Thread = struct {
2955 posix.EDEADLK => unreachable,2955 posix.EDEADLK => unreachable,
2956 else => unreachable,2956 else => unreachable,
2957 }2957 }
2958 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);2958 assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
2959 } else switch (builtin.os) {2959 } else switch (builtin.os) {
2960 builtin.Os.linux => {2960 builtin.Os.linux => {
2961 while (true) {2961 while (true) {
...@@ -2969,7 +2969,7 @@ pub const Thread = struct {...@@ -2969,7 +2969,7 @@ pub const Thread = struct {
2969 else => unreachable,2969 else => unreachable,
2970 }2970 }
2971 }2971 }
2972 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);2972 assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
2973 },2973 },
2974 builtin.Os.windows => {2974 builtin.Os.windows => {
2975 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);2975 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
...@@ -3008,6 +3008,9 @@ pub const SpawnThreadError = error{...@@ -3008,6 +3008,9 @@ pub const SpawnThreadError = error{
3008 Unexpected,3008 Unexpected,
3009};3009};
30103010
3011pub var linux_tls_phdr: ?*std.elf.Phdr = null;
3012pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is
3013
3011/// caller must call wait on the returned thread3014/// caller must call wait on the returned thread
3012/// fn startFn(@typeOf(context)) T3015/// fn startFn(@typeOf(context)) T
3013/// where T is u8, noreturn, void, or !void3016/// where T is u8, noreturn, void, or !void
...@@ -3097,42 +3100,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -3097,42 +3100,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
30973100
3098 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;3101 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
30993102
3100 const mmap_len = default_stack_size;3103 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);3104 var thread_start_offset: usize = undefined;
3102 if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;3105 var context_start_offset: usize = undefined;
3103 errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);3106 var tls_start_offset: usize = undefined;
3107 const mmap_len = blk: {
3108 // First in memory will be the stack, which grows downwards.
3109 var l: usize = mem.alignForward(default_stack_size, os.page_size);
3110 stack_end_offset = l;
3111 // Above the stack, so that it can be in the same mmap call, put the Thread object.
3112 l = mem.alignForward(l, @alignOf(Thread));
3113 thread_start_offset = l;
3114 l += @sizeOf(Thread);
3115 // Next, the Context object.
3116 if (@sizeOf(Context) != 0) {
3117 l = mem.alignForward(l, @alignOf(Context));
3118 context_start_offset = l;
3119 l += @sizeOf(Context);
3120 }
3121 // Finally, the Thread Local Storage, if any.
3122 if (!Thread.use_pthreads) {
3123 if (linux_tls_phdr) |tls_phdr| {
3124 l = mem.alignForward(l, tls_phdr.p_align);
3125 tls_start_offset = l;
3126 l += tls_phdr.p_memsz;
3127 }
3128 }
3129 break :blk l;
3130 };
3131 const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
3132 if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
3133 errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
3134
3135 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
3136 thread_ptr.data.mmap_addr = mmap_addr;
3137 thread_ptr.data.mmap_len = mmap_len;
31043138
3105 var stack_end: usize = stack_addr + mmap_len;
3106 var arg: usize = undefined;3139 var arg: usize = undefined;
3107 if (@sizeOf(Context) != 0) {3140 if (@sizeOf(Context) != 0) {
3108 stack_end -= @sizeOf(Context);3141 arg = mmap_addr + context_start_offset;
3109 stack_end -= stack_end % @alignOf(Context);3142 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;3143 context_ptr.* = context;
3113 arg = stack_end;
3114 }3144 }
31153145
3116 stack_end -= @sizeOf(Thread);3146 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 pthreads3147 // use pthreads
3129 var attr: c.pthread_attr_t = undefined;3148 var attr: c.pthread_attr_t = undefined;
3130 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;3149 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
3131 defer assert(c.pthread_attr_destroy(&attr) == 0);3150 defer assert(c.pthread_attr_destroy(&attr) == 0);
31323151
3133 // align to page3152 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);
31363153
3137 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));3154 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
3138 switch (err) {3155 switch (err) {
...@@ -3143,10 +3160,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -3143,10 +3160,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
3143 else => return unexpectedErrorPosix(@intCast(usize, err)),3160 else => return unexpectedErrorPosix(@intCast(usize, err)),
3144 }3161 }
3145 } else if (builtin.os == builtin.Os.linux) {3162 } else if (builtin.os == builtin.Os.linux) {
3146 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly3163 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;3164 posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
3148 const newtls: usize = 0;3165 posix.CLONE_DETACHED;
3149 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);3166 var newtls: usize = undefined;
3167 if (linux_tls_phdr) |tls_phdr| {
3168 @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz);
3169 thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
3170 newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
3171 flags |= posix.CLONE_SETTLS;
3172 }
3173 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);3174 const err = posix.getErrno(rc);
3151 switch (err) {3175 switch (err) {
3152 0 => return thread_ptr,3176 0 => return thread_ptr,
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/os/windows/index.zig+18-1
...@@ -49,7 +49,10 @@ pub const UNICODE = false;...@@ -49,7 +49,10 @@ pub const UNICODE = false;
49pub const WCHAR = u16;49pub const WCHAR = u16;
50pub const WORD = u16;50pub const WORD = u16;
51pub const LARGE_INTEGER = i64;51pub const LARGE_INTEGER = i64;
52pub const LONG = c_long;52pub const ULONG = u32;
53pub const LONG = i32;
54pub const ULONGLONG = u64;
55pub const LONGLONG = i64;
5356
54pub const TRUE = 1;57pub const TRUE = 1;
55pub const FALSE = 0;58pub const FALSE = 0;
...@@ -380,3 +383,17 @@ pub const COORD = extern struct {...@@ -380,3 +383,17 @@ pub const COORD = extern struct {
380};383};
381384
382pub const CREATE_UNICODE_ENVIRONMENT = 1024;385pub const CREATE_UNICODE_ENVIRONMENT = 1024;
386
387pub const TLS_OUT_OF_INDEXES = 4294967295;
388pub const IMAGE_TLS_DIRECTORY = extern struct {
389 StartAddressOfRawData: usize,
390 EndAddressOfRawData: usize,
391 AddressOfIndex: usize,
392 AddressOfCallBacks: usize,
393 SizeOfZeroFill: u32,
394 Characteristics: u32,
395};
396pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
397pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
398
399pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void;
std/os/windows/kernel32.zig+4
...@@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;...@@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
164164
165pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;165pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
166166
167pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD;
168
169pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL;
170
167pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;171pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
168172
169pub extern "kernel32" stdcallcc fn WriteFile(173pub extern "kernel32" stdcallcc fn WriteFile(
std/os/windows/tls.zig created+36
...@@ -0,0 +1,36 @@
1const std = @import("../../index.zig");
2
3export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
4export var _tls_start: u8 linksection(".tls") = 0;
5export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
6export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
7export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
8
9// TODO this is how I would like it to be expressed
10// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
11// why they do that.
12//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
13// .StartAddressOfRawData = @ptrToInt(&_tls_start),
14// .EndAddressOfRawData = @ptrToInt(&_tls_end),
15// .AddressOfIndex = @ptrToInt(&_tls_index),
16// .AddressOfCallBacks = @ptrToInt(__xl_a),
17// .SizeOfZeroFill = 0,
18// .Characteristics = 0,
19//};
20// This is the workaround because we can't do @ptrToInt at comptime like that.
21pub const IMAGE_TLS_DIRECTORY = extern struct {
22 StartAddressOfRawData: *c_void,
23 EndAddressOfRawData: *c_void,
24 AddressOfIndex: *c_void,
25 AddressOfCallBacks: *c_void,
26 SizeOfZeroFill: u32,
27 Characteristics: u32,
28};
29export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY {
30 .StartAddressOfRawData = &_tls_start,
31 .EndAddressOfRawData = &_tls_end,
32 .AddressOfIndex = &_tls_index,
33 .AddressOfCallBacks = &__xl_a,
34 .SizeOfZeroFill = 0,
35 .Characteristics = 0,
36};
std/special/bootstrap.zig+59-4
...@@ -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
...@@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {...@@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {
4445
45extern fn WinMainCRTStartup() noreturn {46extern fn WinMainCRTStartup() noreturn {
46 @setAlignStack(16);47 @setAlignStack(16);
4748 if (!builtin.single_threaded) {
49 _ = @import("../os/windows/tls.zig");
50 }
48 std.os.windows.ExitProcess(callMain());51 std.os.windows.ExitProcess(callMain());
49}52}
5053
...@@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {...@@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {
61 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}64 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
62 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];65 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
63 if (builtin.os == builtin.Os.linux) {66 if (builtin.os == builtin.Os.linux) {
64 const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);67 // Scan auxiliary vector.
65 std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);68 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);69 std.os.linux_elf_aux_maybe = auxv;
70 var i: usize = 0;
71 var at_phdr: usize = 0;
72 var at_phnum: usize = 0;
73 var at_phent: usize = 0;
74 while (auxv[i].a_un.a_val != 0) : (i += 1) {
75 switch (auxv[i].a_type) {
76 std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
77 std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
78 std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
79 std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
80 else => {},
81 }
82 }
83 if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
67 }84 }
6885
69 std.os.posix.exit(callMainWithArgs(argc, argv, envp));86 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
...@@ -116,3 +133,41 @@ inline fn callMain() u8 {...@@ -116,3 +133,41 @@ inline fn callMain() u8 {
116 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),133 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
117 }134 }
118}135}
136
137var tls_end_addr: usize = undefined;
138const main_thread_tls_align = 32;
139var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
140
141fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
142 var phdr_addr = at_phdr;
143 var n = at_phnum;
144 var base: usize = 0;
145 while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) {
146 const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
147 // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
148 switch (phdr.p_type) {
149 std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
150 std.elf.PT_TLS => std.os.linux_tls_phdr = phdr,
151 else => continue,
152 }
153 }
154 const tls_phdr = std.os.linux_tls_phdr orelse return;
155 std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
156 assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
157 assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
158 @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz);
159 tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
160 linuxSetThreadArea(@ptrToInt(&tls_end_addr));
161}
162
163fn linuxSetThreadArea(addr: usize) void {
164 switch (builtin.arch) {
165 builtin.Arch.x86_64 => {
166 const ARCH_SET_FS = 0x1002;
167 const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
168 // acrh_prctl is documented to never fail
169 assert(rc == 0);
170 },
171 else => @compileError("Unsupported architecture"),
172 }
173}
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}
test/tests.zig+3
...@@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons...@@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
194 if (link_libc) {194 if (link_libc) {
195 these_tests.linkSystemLibrary("c");195 these_tests.linkSystemLibrary("c");
196 }196 }
197 if (mem.eql(u8, name, "std")) {
198 these_tests.overrideStdDir("std");
199 }
197 step.dependOn(&these_tests.step);200 step.dependOn(&these_tests.step);
198 }201 }
199 }202 }