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
signaturebadge-question-mark 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
596596 "os/windows/ntdll.zig"
597597 "os/windows/ole32.zig"
598598 "os/windows/shell32.zig"
599 "os/windows/tls.zig"
599600 "os/windows/util.zig"
600601 "os/zen.zig"
601602 "pdb.zig"
src/all_types.hpp+8-5
......@@ -544,12 +544,7 @@ struct AstNodeDefer {
544544};
545545
546546struct AstNodeVariableDeclaration {
547 VisibMod visib_mod;
548547 Buf *symbol;
549 bool is_const;
550 bool is_comptime;
551 bool is_export;
552 bool is_extern;
553548 // one or both of type and expr will be non null
554549 AstNode *type;
555550 AstNode *expr;
......@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {
559554 AstNode *align_expr;
560555 // populated if the "section(S)" is present
561556 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;
562564};
563565
564566struct AstNodeTestDecl {
......@@ -1873,6 +1875,7 @@ struct ZigVar {
18731875 bool shadowable;
18741876 bool src_is_const;
18751877 bool gen_is_const;
1878 bool is_thread_local;
18761879};
18771880
18781881struct ErrorTableEntry {
src/analyze.cpp+45-24
......@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum
2828static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
2929static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
3030
31ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
32 if (node->owner->c_import_node != nullptr) {
33 // if this happens, then translate_c generated code that
34 // failed semantic analysis, which isn't supposed to happen
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) {
31static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token,
32 Buf *msg)
33{
34 if (owner->c_import_node != nullptr) {
5335 // if this happens, then translate_c generated code that
5436 // 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
6446 return note;
6547 }
6648
67 ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
68 node->owner->source_code, node->owner->line_offsets, msg);
49 ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
50 owner->source_code, owner->line_offsets, msg);
6951
7052 err_msg_add_note(parent_msg, err);
7153 return err;
7254}
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
7489ZigType *new_type_table_entry(ZigTypeId id) {
7590 ZigType *entry = allocate<ZigType>(1);
7691 entry->id = id;
......@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
36683683 bool is_const = var_decl->is_const;
36693684 bool is_extern = var_decl->is_extern;
36703685 bool is_export = var_decl->is_export;
3686 bool is_thread_local = var_decl->threadlocal_tok != nullptr;
36713687
36723688 ZigType *explicit_type = nullptr;
36733689 if (var_decl->type) {
......@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
37273743 tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
37283744 is_const, init_val, &tld_var->base, type);
37293745 tld_var->var->linkage = linkage;
3746 tld_var->var->is_thread_local = is_thread_local;
37303747
37313748 if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
37323749 tld_var->var->var_type = g->builtin_types.entry_invalid;
......@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
37473764 }
37483765 }
37493766
3767 if (is_thread_local && is_const) {
3768 add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
3769 }
3770
37503771 g->global_vars.append(tld_var);
37513772}
37523773
src/analyze.hpp+1
......@@ -12,6 +12,7 @@
1212
1313void semantic_analyze(CodeGen *g);
1414ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
15ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg);
1516ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
1617ZigType *new_type_table_entry(ZigTypeId id);
1718ZigType *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) {
132132 return is_const ? "const" : "var";
133133}
134134
135static const char *thread_local_string(Token *tok) {
136 return (tok == nullptr) ? "" : "threadlocal ";
137}
138
135139const char *container_string(ContainerKind kind) {
136140 switch (kind) {
137141 case ContainerKindEnum: return "enum";
......@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
554558 {
555559 const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
556560 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);
557562 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);
559564 print_symbol(ar, node->data.variable_declaration.symbol);
560565
561566 if (node->data.variable_declaration.type) {
src/codegen.cpp+22-5
......@@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {
8888};
8989
9090CodeGen *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)
9292{
9393 CodeGen *g = allocate<CodeGen>(1);
9494
......@@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
9696
9797 g->zig_lib_dir = zig_lib_dir;
9898
99 g->zig_std_dir = buf_alloc();
100 os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
99 if (override_std_dir == nullptr) {
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
102106 g->zig_c_headers_dir = buf_alloc();
103107 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) {
63416345 report_errors_and_maybe_exit(g);
63426346}
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
63446354static void do_code_gen(CodeGen *g) {
63456355 assert(!g->errors.length);
63466356
......@@ -6425,6 +6435,7 @@ static void do_code_gen(CodeGen *g) {
64256435 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
64266436 LLVMSetAlignment(global_value, var->align_bytes);
64276437 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6438 set_global_tls(g, var, global_value);
64286439 }
64296440 } else {
64306441 bool exported = (var->linkage == VarLinkageExport);
......@@ -6450,6 +6461,7 @@ static void do_code_gen(CodeGen *g) {
64506461 }
64516462
64526463 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6464 set_global_tls(g, var, global_value);
64536465 }
64546466
64556467 var->value_ref = global_value;
......@@ -7500,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
75007512 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
75017513 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
75027514 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);
75037516 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
75047517 scan_import(g, g->compile_var_import);
75057518
......@@ -8329,8 +8342,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {
83298342 if (!entry)
83308343 break;
83318344
8332 cache_buf(ch, entry->key);
8333 add_cache_pkg(g, ch, entry->value);
8345 // TODO: I think we need a more sophisticated detection of
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 }
83348351 }
83358352}
83368353
src/codegen.hpp+1-1
......@@ -15,7 +15,7 @@
1515#include <stdio.h>
1616
1717CodeGen *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
2020void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
2121void 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
52045204 add_node_error(irb->codegen, variable_declaration->section_expr,
52055205 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
52065206 }
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
52085212 // Temporarily set the name of the IrExecutable to the VariableDeclaration
52095213 // 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)
4242 }
4343
4444 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
4747 child_gen->out_h_path = nullptr;
4848 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) {
7474 " -dirafter [dir] same as -isystem but do it last\n"
7575 " -isystem [dir] add additional search path for other .h files\n"
7676 " -mllvm [arg] forward an arg to LLVM's option processing\n"
77 " --override-std-dir [arg] use an alternate Zig standard library\n"
7778 "\n"
7879 "Link Options:\n"
7980 " --dynamic-linker [path] set the path to ld.so\n"
......@@ -395,6 +396,7 @@ int main(int argc, char **argv) {
395396 bool system_linker_hack = false;
396397 TargetSubsystem subsystem = TargetSubsystemAuto;
397398 bool is_single_threaded = false;
399 Buf *override_std_dir = nullptr;
398400
399401 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
400402 Buf zig_exe_path_buf = BUF_INIT;
......@@ -430,7 +432,8 @@ int main(int argc, char **argv) {
430432 Buf *build_runner_path = buf_alloc();
431433 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);
434437 g->enable_time_report = timing_info;
435438 buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
436439 codegen_set_out_name(g, buf_create_from_str("build"));
......@@ -645,6 +648,8 @@ int main(int argc, char **argv) {
645648 clang_argv.append(argv[i]);
646649
647650 llvm_argv.append(argv[i]);
651 } else if (strcmp(arg, "--override-std-dir") == 0) {
652 override_std_dir = buf_create_from_str(argv[i]);
648653 } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
649654 lib_dirs.append(argv[i]);
650655 } else if (strcmp(arg, "--library") == 0) {
......@@ -819,7 +824,7 @@ int main(int argc, char **argv) {
819824
820825 switch (cmd) {
821826 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);
823828 g->is_single_threaded = is_single_threaded;
824829 Buf *builtin_source = codegen_generate_builtin_source(g);
825830 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) {
878883 if (cmd == CmdRun && buf_out_name == nullptr) {
879884 buf_out_name = buf_create_from_str("run");
880885 }
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);
882888 g->subsystem = subsystem;
883889
884890 if (disable_pic) {
src/parser.cpp+14-8
......@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
844844
845845// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
846846static AstNode *ast_parse_var_decl(ParseContext *pc) {
847 Token *first = eat_token_if(pc, TokenIdKeywordConst);
848 if (first == nullptr)
849 first = eat_token_if(pc, TokenIdKeywordVar);
850 if (first == nullptr)
851 return nullptr;
852
847 Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
848 Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
849 if (mut_kw == nullptr)
850 mut_kw = eat_token_if(pc, TokenIdKeywordVar);
851 if (mut_kw == nullptr) {
852 if (thread_local_kw == nullptr) {
853 return nullptr;
854 } else {
855 ast_invalid_token_error(pc, peek_token(pc));
856 }
857 }
853858 Token *identifier = expect_token(pc, TokenIdSymbol);
854859 AstNode *type_expr = nullptr;
855860 if (eat_token_if(pc, TokenIdColon) != nullptr)
......@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
863868
864869 expect_token(pc, TokenIdSemicolon);
865870
866 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first);
867 res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst;
871 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
872 res->data.variable_declaration.threadlocal_tok = thread_local_kw;
873 res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
868874 res->data.variable_declaration.symbol = token_buf(identifier);
869875 res->data.variable_declaration.type = type_expr;
870876 res->data.variable_declaration.align_expr = align_expr;
src/tokenizer.cpp+2
......@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {
146146 {"suspend", TokenIdKeywordSuspend},
147147 {"switch", TokenIdKeywordSwitch},
148148 {"test", TokenIdKeywordTest},
149 {"threadlocal", TokenIdKeywordThreadLocal},
149150 {"true", TokenIdKeywordTrue},
150151 {"try", TokenIdKeywordTry},
151152 {"undefined", TokenIdKeywordUndefined},
......@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {
15861587 case TokenIdKeywordStruct: return "struct";
15871588 case TokenIdKeywordSwitch: return "switch";
15881589 case TokenIdKeywordTest: return "test";
1590 case TokenIdKeywordThreadLocal: return "threadlocal";
15891591 case TokenIdKeywordTrue: return "true";
15901592 case TokenIdKeywordTry: return "try";
15911593 case TokenIdKeywordUndefined: return "undefined";
src/tokenizer.hpp+1
......@@ -88,6 +88,7 @@ enum TokenId {
8888 TokenIdKeywordSuspend,
8989 TokenIdKeywordSwitch,
9090 TokenIdKeywordTest,
91 TokenIdKeywordThreadLocal,
9192 TokenIdKeywordTrue,
9293 TokenIdKeywordTry,
9394 TokenIdKeywordUndefined,
std/build.zig+10
......@@ -1686,6 +1686,7 @@ pub const TestStep = struct {
16861686 no_rosegment: bool,
16871687 output_path: ?[]const u8,
16881688 system_linker_hack: bool,
1689 override_std_dir: ?[]const u8,
16891690
16901691 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
16911692 const step_name = builder.fmt("test {}", root_src);
......@@ -1707,6 +1708,7 @@ pub const TestStep = struct {
17071708 .no_rosegment = false,
17081709 .output_path = null,
17091710 .system_linker_hack = false,
1711 .override_std_dir = null,
17101712 };
17111713 }
17121714
......@@ -1737,6 +1739,10 @@ pub const TestStep = struct {
17371739 self.build_mode = mode;
17381740 }
17391741
1742 pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void {
1743 self.override_std_dir = dir_path;
1744 }
1745
17401746 pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {
17411747 self.output_path = file_path;
17421748
......@@ -1914,6 +1920,10 @@ pub const TestStep = struct {
19141920 if (self.system_linker_hack) {
19151921 try zig_args.append("--system-linker-hack");
19161922 }
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
19181928 try builder.spawnChild(zig_args.toSliceConst());
19191929 }
std/debug/index.zig-1
......@@ -37,7 +37,6 @@ const Module = struct {
3737var stderr_file: os.File = undefined;
3838var stderr_file_out_stream: os.File.OutStream = undefined;
3939
40/// TODO multithreaded awareness
4140var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
4241var stderr_mutex = std.Mutex.init();
4342pub fn warn(comptime fmt: []const u8, args: ...) void {
std/heap.zig+2-5
......@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
106106 };
107107 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
108108 const root_addr = @ptrToInt(ptr);
109 const rem = @rem(root_addr, alignment);
110 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
111 const adjusted_addr = root_addr + march_forward_bytes;
109 const adjusted_addr = mem.alignForward(root_addr, alignment);
112110 const record_addr = adjusted_addr + n;
113111 @intToPtr(*align(1) usize, record_addr).* = root_addr;
114112 return @intToPtr([*]u8, adjusted_addr)[0..n];
......@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
126124 const base_addr = @ptrToInt(old_mem.ptr);
127125 const old_addr_end = base_addr + old_mem.len;
128126 const new_addr_end = base_addr + new_size;
129 const rem = @rem(new_addr_end, os.page_size);
130 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
127 const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
131128 if (old_addr_end > new_addr_end_rounded) {
132129 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
133130 }
std/index.zig+1-1
......@@ -33,8 +33,8 @@ pub const io = @import("io.zig");
3333pub const json = @import("json.zig");
3434pub const macho = @import("macho.zig");
3535pub const math = @import("math/index.zig");
36pub const meta = @import("meta/index.zig");
3736pub const mem = @import("mem.zig");
37pub const meta = @import("meta/index.zig");
3838pub const net = @import("net.zig");
3939pub const os = @import("os/index.zig");
4040pub const pdb = @import("pdb.zig");
std/mem.zig+20
......@@ -1366,3 +1366,23 @@ test "std.mem.subArrayPtr" {
13661366 sub2[1] = 'X';
13671367 debug.assert(std.mem.eql(u8, a2, "abcXef"));
13681368}
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) {
88};
99const os = @This();
1010
11comptime {
12 assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
13}
14
1115test "std.os" {
1216 _ = @import("child_process.zig");
1317 _ = @import("darwin.zig");
......@@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {
692696 return base;
693697 }
694698 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
695 const ElfHeader = switch (@sizeOf(usize)) {
696 4 => std.elf.Elf32_Ehdr,
697 8 => std.elf.Elf64_Ehdr,
698 else => @compileError("Unsupported architecture"),
699 };
700 return phdr - @sizeOf(ElfHeader);
699 return phdr - @sizeOf(std.elf.Ehdr);
701700 },
702701 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
703702 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
......@@ -2908,14 +2907,15 @@ pub const Thread = struct {
29082907 pub const Data = if (use_pthreads)
29092908 struct {
29102909 handle: Thread.Handle,
2911 stack_addr: usize,
2912 stack_len: usize,
2910 mmap_addr: usize,
2911 mmap_len: usize,
29132912 }
29142913 else switch (builtin.os) {
29152914 builtin.Os.linux => struct {
29162915 handle: Thread.Handle,
2917 stack_addr: usize,
2918 stack_len: usize,
2916 mmap_addr: usize,
2917 mmap_len: usize,
2918 tls_end_addr: usize,
29192919 },
29202920 builtin.Os.windows => struct {
29212921 handle: Thread.Handle,
......@@ -2955,7 +2955,7 @@ pub const Thread = struct {
29552955 posix.EDEADLK => unreachable,
29562956 else => unreachable,
29572957 }
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);
29592959 } else switch (builtin.os) {
29602960 builtin.Os.linux => {
29612961 while (true) {
......@@ -2969,7 +2969,7 @@ pub const Thread = struct {
29692969 else => unreachable,
29702970 }
29712971 }
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);
29732973 },
29742974 builtin.Os.windows => {
29752975 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
......@@ -3008,6 +3008,9 @@ pub const SpawnThreadError = error{
30083008 Unexpected,
30093009};
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
30113014/// caller must call wait on the returned thread
30123015/// fn startFn(@typeOf(context)) T
30133016/// where T is u8, noreturn, void, or !void
......@@ -3097,42 +3100,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
30973100
30983101 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
30993102
3100 const mmap_len = default_stack_size;
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);
3102 if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
3103 errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
3103 var stack_end_offset: usize = undefined;
3104 var thread_start_offset: usize = undefined;
3105 var context_start_offset: usize = undefined;
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;
31063139 var arg: usize = undefined;
31073140 if (@sizeOf(Context) != 0) {
3108 stack_end -= @sizeOf(Context);
3109 stack_end -= stack_end % @alignOf(Context);
3110 assert(stack_end >= stack_addr);
3111 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
3141 arg = mmap_addr + context_start_offset;
3142 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
31123143 context_ptr.* = context;
3113 arg = stack_end;
31143144 }
31153145
3116 stack_end -= @sizeOf(Thread);
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) {
3146 if (Thread.use_pthreads) {
31283147 // use pthreads
31293148 var attr: c.pthread_attr_t = undefined;
31303149 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
31313150 defer assert(c.pthread_attr_destroy(&attr) == 0);
31323151
3133 // align to page
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);
3152 assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
31363153
31373154 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
31383155 switch (err) {
......@@ -3143,10 +3160,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
31433160 else => return unexpectedErrorPosix(@intCast(usize, err)),
31443161 }
31453162 } else if (builtin.os == builtin.Os.linux) {
3146 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
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;
3148 const newtls: usize = 0;
3149 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
3163 var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
3164 posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
3165 posix.CLONE_DETACHED;
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);
31503174 const err = posix.getErrno(rc);
31513175 switch (err) {
31523176 0 => return thread_ptr,
std/os/test.zig+16
......@@ -105,3 +105,19 @@ test "AtomicFile" {
105105
106106 try os.deleteFile(test_out_file);
107107}
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;
4949pub const WCHAR = u16;
5050pub const WORD = u16;
5151pub 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
5457pub const TRUE = 1;
5558pub const FALSE = 0;
......@@ -380,3 +383,17 @@ pub const COORD = extern struct {
380383};
381384
382385pub 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;
164164
165165pub 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
167171pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
168172
169173pub 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 @@
44const root = @import("@root");
55const std = @import("std");
66const builtin = @import("builtin");
7const assert = std.debug.assert;
78
89var argc_ptr: [*]usize = undefined;
910
......@@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {
4445
4546extern fn WinMainCRTStartup() noreturn {
4647 @setAlignStack(16);
47
48 if (!builtin.single_threaded) {
49 _ = @import("../os/windows/tls.zig");
50 }
4851 std.os.windows.ExitProcess(callMain());
4952}
5053
......@@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {
6164 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
6265 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
6366 if (builtin.os == builtin.Os.linux) {
64 const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
65 std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);
66 std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);
67 // Scan auxiliary vector.
68 const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
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);
6784 }
6885
6986 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
......@@ -116,3 +133,41 @@ inline fn callMain() u8 {
116133 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
117134 }
118135}
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 @@
11const tests = @import("tests.zig");
22
33pub 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
423 cases.add(
524 "@bitCast same size but bit count mismatch",
625 \\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" {
685685fn getNull() ?*i32 {
686686 return null;
687687}
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
194194 if (link_libc) {
195195 these_tests.linkSystemLibrary("c");
196196 }
197 if (mem.eql(u8, name, "std")) {
198 these_tests.overrideStdDir("std");
199 }
197200 step.dependOn(&these_tests.step);
198201 }
199202 }