authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 00:43:12-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 00:43:12-04:00
logdd9728c5a03844267bc378c326c353fd2b0e084e
tree5786bd228312976ee482a58463a798bc426d64af
parent558b0b87913dfb6e6b76f5dbe2c36b920302faab
parent10bdf73a02c90dc375985e49b08b5020cfc20b93

Merge remote-tracking branch 'origin/master' into llvm7


76 files changed, 7872 insertions(+), 1812 deletions(-)

CMakeLists.txt+12-1
...@@ -426,6 +426,7 @@ set(ZIG_SOURCES...@@ -426,6 +426,7 @@ set(ZIG_SOURCES
426)426)
427set(ZIG_CPP_SOURCES427set(ZIG_CPP_SOURCES
428 "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"428 "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
429 "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
429)430)
430431
431set(ZIG_STD_FILES432set(ZIG_STD_FILES
...@@ -479,6 +480,7 @@ set(ZIG_STD_FILES...@@ -479,6 +480,7 @@ set(ZIG_STD_FILES
479 "index.zig"480 "index.zig"
480 "io.zig"481 "io.zig"
481 "json.zig"482 "json.zig"
483 "lazy_init.zig"
482 "linked_list.zig"484 "linked_list.zig"
483 "macho.zig"485 "macho.zig"
484 "math/acos.zig"486 "math/acos.zig"
...@@ -488,6 +490,7 @@ set(ZIG_STD_FILES...@@ -488,6 +490,7 @@ set(ZIG_STD_FILES
488 "math/atan.zig"490 "math/atan.zig"
489 "math/atan2.zig"491 "math/atan2.zig"
490 "math/atanh.zig"492 "math/atanh.zig"
493 "math/big/index.zig"
491 "math/big/int.zig"494 "math/big/int.zig"
492 "math/cbrt.zig"495 "math/cbrt.zig"
493 "math/ceil.zig"496 "math/ceil.zig"
...@@ -553,9 +556,10 @@ set(ZIG_STD_FILES...@@ -553,9 +556,10 @@ set(ZIG_STD_FILES
553 "net.zig"556 "net.zig"
554 "os/child_process.zig"557 "os/child_process.zig"
555 "os/darwin.zig"558 "os/darwin.zig"
556 "os/darwin_errno.zig"559 "os/darwin/errno.zig"
557 "os/epoch.zig"560 "os/epoch.zig"
558 "os/file.zig"561 "os/file.zig"
562 "os/get_app_data_dir.zig"
559 "os/get_user_id.zig"563 "os/get_user_id.zig"
560 "os/index.zig"564 "os/index.zig"
561 "os/linux/errno.zig"565 "os/linux/errno.zig"
...@@ -564,8 +568,14 @@ set(ZIG_STD_FILES...@@ -564,8 +568,14 @@ set(ZIG_STD_FILES
564 "os/linux/x86_64.zig"568 "os/linux/x86_64.zig"
565 "os/path.zig"569 "os/path.zig"
566 "os/time.zig"570 "os/time.zig"
571 "os/windows/advapi32.zig"
567 "os/windows/error.zig"572 "os/windows/error.zig"
568 "os/windows/index.zig"573 "os/windows/index.zig"
574 "os/windows/kernel32.zig"
575 "os/windows/ole32.zig"
576 "os/windows/shell32.zig"
577 "os/windows/shlwapi.zig"
578 "os/windows/user32.zig"
569 "os/windows/util.zig"579 "os/windows/util.zig"
570 "os/zen.zig"580 "os/zen.zig"
571 "rand/index.zig"581 "rand/index.zig"
...@@ -614,6 +624,7 @@ set(ZIG_STD_FILES...@@ -614,6 +624,7 @@ set(ZIG_STD_FILES
614 "zig/ast.zig"624 "zig/ast.zig"
615 "zig/index.zig"625 "zig/index.zig"
616 "zig/parse.zig"626 "zig/parse.zig"
627 "zig/parse_string_literal.zig"
617 "zig/render.zig"628 "zig/render.zig"
618 "zig/tokenizer.zig"629 "zig/tokenizer.zig"
619)630)
README.md+5-5
...@@ -21,19 +21,19 @@ clarity....@@ -21,19 +21,19 @@ clarity.
21 * Compatible with C libraries with no wrapper necessary. Directly include21 * Compatible with C libraries with no wrapper necessary. Directly include
22 C .h files and get access to the functions and symbols therein.22 C .h files and get access to the functions and symbols therein.
23 * Provides standard library which competes with the C standard library and is23 * Provides standard library which competes with the C standard library and is
24 always compiled against statically in source form. Compile units do not24 always compiled against statically in source form. Zig binaries do not
25 depend on libc unless explicitly linked.25 depend on libc unless explicitly linked.
26 * Nullable type instead of null pointers.26 * Optional type instead of null pointers.
27 * Safe unions, tagged unions, and C ABI compatible unions.27 * Safe unions, tagged unions, and C ABI compatible unions.
28 * Generics so that one can write efficient data structures that work for any28 * Generics so that one can write efficient data structures that work for any
29 data type.29 data type.
30 * No header files required. Top level declarations are entirely30 * No header files required. Top level declarations are entirely
31 order-independent.31 order-independent.
32 * Compile-time code execution. Compile-time reflection.32 * Compile-time code execution. Compile-time reflection.
33 * Partial compile-time function evaluation with eliminates the need for33 * Partial compile-time function evaluation which eliminates the need for
34 a preprocessor or macros.34 a preprocessor or macros.
35 * The binaries produced by Zig have complete debugging information so you can,35 * The binaries produced by Zig have complete debugging information so you can,
36 for example, use GDB or MSVC to debug your software.36 for example, use GDB, MSVC, or LLDB to debug your software.
37 * Built-in unit tests with `zig test`.37 * Built-in unit tests with `zig test`.
38 * Friendly toward package maintainers. Reproducible build, bootstrapping38 * Friendly toward package maintainers. Reproducible build, bootstrapping
39 process carefully documented. Issues filed by package maintainers are39 process carefully documented. Issues filed by package maintainers are
...@@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table....@@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table.
7070
71## Community71## Community
7272
73 * IRC: `#zig` on Freenode.73 * IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)).
74 * Reddit: [/r/zig](https://www.reddit.com/r/zig)74 * Reddit: [/r/zig](https://www.reddit.com/r/zig)
75 * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)75 * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)
7676
build.zig+1-1
...@@ -92,7 +92,7 @@ pub fn build(b: *Builder) !void {...@@ -92,7 +92,7 @@ pub fn build(b: *Builder) !void {
92 test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes));92 test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes));
9393
94 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));94 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
95 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));95 test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes));
96 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));96 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
97 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));97 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
98 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));98 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
doc/langref.html.in+3-3
...@@ -1087,7 +1087,7 @@ unwrapped == 1234</code></pre>...@@ -1087,7 +1087,7 @@ unwrapped == 1234</code></pre>
1087 </td>1087 </td>
1088 <td>1088 <td>
1089 If <code>a</code> is <code>false</code>, returns <code>false</code>1089 If <code>a</code> is <code>false</code>, returns <code>false</code>
1090 without evaluating <code>b</code>. Otherwise, retuns <code>b</code>.1090 without evaluating <code>b</code>. Otherwise, returns <code>b</code>.
1091 </td>1091 </td>
1092 <td>1092 <td>
1093 <pre><code class="zig">false and true == false</code></pre>1093 <pre><code class="zig">false and true == false</code></pre>
...@@ -1102,7 +1102,7 @@ unwrapped == 1234</code></pre>...@@ -1102,7 +1102,7 @@ unwrapped == 1234</code></pre>
1102 </td>1102 </td>
1103 <td>1103 <td>
1104 If <code>a</code> is <code>true</code>, returns <code>true</code>1104 If <code>a</code> is <code>true</code>, returns <code>true</code>
1105 without evaluating <code>b</code>. Otherwise, retuns <code>b</code>.1105 without evaluating <code>b</code>. Otherwise, returns <code>b</code>.
1106 </td>1106 </td>
1107 <td>1107 <td>
1108 <pre><code class="zig">false or true == true</code></pre>1108 <pre><code class="zig">false or true == true</code></pre>
...@@ -1483,7 +1483,7 @@ test "pointer array access" {...@@ -1483,7 +1483,7 @@ test "pointer array access" {
1483}1483}
14841484
1485test "pointer slicing" {1485test "pointer slicing" {
1486 // In Zig, we prefer using slices over null-terminated pointers.1486 // In Zig, we prefer slices over pointers to null-terminated arrays.
1487 // You can turn an array into a slice using slice syntax:1487 // You can turn an array into a slice using slice syntax:
1488 var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };1488 var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
1489 const slice = array[2..4];1489 const slice = array[2..4];
src-self-hosted/c.zig+1
...@@ -4,4 +4,5 @@ pub use @cImport({...@@ -4,4 +4,5 @@ pub use @cImport({
4 @cInclude("inttypes.h");4 @cInclude("inttypes.h");
5 @cInclude("config.h");5 @cInclude("config.h");
6 @cInclude("zig_llvm.h");6 @cInclude("zig_llvm.h");
7 @cInclude("windows_sdk.h");
7});8});
src-self-hosted/c_int.zig created+68
...@@ -0,0 +1,68 @@
1pub const CInt = struct {
2 id: Id,
3 zig_name: []const u8,
4 c_name: []const u8,
5 is_signed: bool,
6
7 pub const Id = enum {
8 Short,
9 UShort,
10 Int,
11 UInt,
12 Long,
13 ULong,
14 LongLong,
15 ULongLong,
16 };
17
18 pub const list = []CInt{
19 CInt{
20 .id = Id.Short,
21 .zig_name = "c_short",
22 .c_name = "short",
23 .is_signed = true,
24 },
25 CInt{
26 .id = Id.UShort,
27 .zig_name = "c_ushort",
28 .c_name = "unsigned short",
29 .is_signed = false,
30 },
31 CInt{
32 .id = Id.Int,
33 .zig_name = "c_int",
34 .c_name = "int",
35 .is_signed = true,
36 },
37 CInt{
38 .id = Id.UInt,
39 .zig_name = "c_uint",
40 .c_name = "unsigned int",
41 .is_signed = false,
42 },
43 CInt{
44 .id = Id.Long,
45 .zig_name = "c_long",
46 .c_name = "long",
47 .is_signed = true,
48 },
49 CInt{
50 .id = Id.ULong,
51 .zig_name = "c_ulong",
52 .c_name = "unsigned long",
53 .is_signed = false,
54 },
55 CInt{
56 .id = Id.LongLong,
57 .zig_name = "c_longlong",
58 .c_name = "long long",
59 .is_signed = true,
60 },
61 CInt{
62 .id = Id.ULongLong,
63 .zig_name = "c_ulonglong",
64 .c_name = "unsigned long long",
65 .is_signed = false,
66 },
67 };
68};
src-self-hosted/codegen.zig+90-10
...@@ -1,19 +1,22 @@...@@ -1,19 +1,22 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const Compilation = @import("compilation.zig").Compilation;3const Compilation = @import("compilation.zig").Compilation;
3// we go through llvm instead of c for 2 reasons:
4// 1. to avoid accidentally calling the non-thread-safe functions
5// 2. patch up some of the types to remove nullability
6const llvm = @import("llvm.zig");4const llvm = @import("llvm.zig");
5const c = @import("c.zig");
7const ir = @import("ir.zig");6const ir = @import("ir.zig");
8const Value = @import("value.zig").Value;7const Value = @import("value.zig").Value;
9const Type = @import("type.zig").Type;8const Type = @import("type.zig").Type;
10const event = std.event;9const event = std.event;
11const assert = std.debug.assert;10const assert = std.debug.assert;
11const DW = std.dwarf;
1212
13pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void {13pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void {
14 fn_val.base.ref();14 fn_val.base.ref();
15 defer fn_val.base.deref(comp);15 defer fn_val.base.deref(comp);
16 defer code.destroy(comp.a());16 defer code.destroy(comp.gpa());
17
18 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
19 errdefer output_path.deinit();
1720
18 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();21 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
19 defer llvm_handle.release(comp.event_loop_local);22 defer llvm_handle.release(comp.event_loop_local);
...@@ -23,15 +26,59 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)...@@ -23,15 +26,59 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
23 const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;26 const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
24 defer llvm.DisposeModule(module);27 defer llvm.DisposeModule(module);
2528
29 llvm.SetTarget(module, comp.llvm_triple.ptr());
30 llvm.SetDataLayout(module, comp.target_layout_str);
31
32 if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) {
33 llvm.AddModuleCodeViewFlag(module);
34 } else {
35 llvm.AddModuleDebugInfoFlag(module);
36 }
37
26 const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;38 const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
27 defer llvm.DisposeBuilder(builder);39 defer llvm.DisposeBuilder(builder);
2840
41 const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory;
42 defer llvm.DisposeDIBuilder(dibuilder);
43
44 // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes
45 // the git revision.
46 const producer = try std.Buffer.allocPrint(
47 &code.arena.allocator,
48 "zig {}.{}.{}",
49 u32(c.ZIG_VERSION_MAJOR),
50 u32(c.ZIG_VERSION_MINOR),
51 u32(c.ZIG_VERSION_PATCH),
52 );
53 const flags = c"";
54 const runtime_version = 0;
55 const compile_unit_file = llvm.CreateFile(
56 dibuilder,
57 comp.name.ptr(),
58 comp.root_package.root_src_dir.ptr(),
59 ) orelse return error.OutOfMemory;
60 const is_optimized = comp.build_mode != builtin.Mode.Debug;
61 const compile_unit = llvm.CreateCompileUnit(
62 dibuilder,
63 DW.LANG_C99,
64 compile_unit_file,
65 producer.ptr(),
66 is_optimized,
67 flags,
68 runtime_version,
69 c"",
70 0,
71 !comp.strip,
72 ) orelse return error.OutOfMemory;
73
29 var ofile = ObjectFile{74 var ofile = ObjectFile{
30 .comp = comp,75 .comp = comp,
31 .module = module,76 .module = module,
32 .builder = builder,77 .builder = builder,
78 .dibuilder = dibuilder,
33 .context = context,79 .context = context,
34 .lock = event.Lock.init(comp.loop),80 .lock = event.Lock.init(comp.loop),
81 .arena = &code.arena.allocator,
35 };82 };
3683
37 try renderToLlvmModule(&ofile, fn_val, code);84 try renderToLlvmModule(&ofile, fn_val, code);
...@@ -41,10 +88,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)...@@ -41,10 +88,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
41 // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));88 // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
42 //}89 //}
4390
44 // TODO91 llvm.DIBuilderFinalize(dibuilder);
45 //ZigLLVMDIBuilderFinalize(g->dbuilder);
4692
47 if (comp.verbose_llvm_ir) {93 if (comp.verbose_llvm_ir) {
94 std.debug.warn("raw module:\n");
48 llvm.DumpModule(ofile.module);95 llvm.DumpModule(ofile.module);
49 }96 }
5097
...@@ -53,23 +100,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)...@@ -53,23 +100,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
53 var error_ptr: ?[*]u8 = null;100 var error_ptr: ?[*]u8 = null;
54 _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);101 _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
55 }102 }
103
104 assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types
105
106 const is_small = comp.build_mode == builtin.Mode.ReleaseSmall;
107 const is_debug = comp.build_mode == builtin.Mode.Debug;
108
109 var err_msg: [*]u8 = undefined;
110 // TODO integrate this with evented I/O
111 if (llvm.TargetMachineEmitToFile(
112 comp.target_machine,
113 module,
114 output_path.ptr(),
115 llvm.EmitBinary,
116 &err_msg,
117 is_debug,
118 is_small,
119 )) {
120 if (std.debug.runtime_safety) {
121 std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg);
122 }
123 return error.WritingObjectFileFailed;
124 }
125 //validate_inline_fns(g); TODO
126 fn_val.containing_object = output_path;
127 if (comp.verbose_llvm_ir) {
128 std.debug.warn("optimized module:\n");
129 llvm.DumpModule(ofile.module);
130 }
131 if (comp.verbose_link) {
132 std.debug.warn("created {}\n", output_path.toSliceConst());
133 }
56}134}
57135
58pub const ObjectFile = struct {136pub const ObjectFile = struct {
59 comp: *Compilation,137 comp: *Compilation,
60 module: llvm.ModuleRef,138 module: llvm.ModuleRef,
61 builder: llvm.BuilderRef,139 builder: llvm.BuilderRef,
140 dibuilder: *llvm.DIBuilder,
62 context: llvm.ContextRef,141 context: llvm.ContextRef,
63 lock: event.Lock,142 lock: event.Lock,
143 arena: *std.mem.Allocator,
64144
65 fn a(self: *ObjectFile) *std.mem.Allocator {145 fn gpa(self: *ObjectFile) *std.mem.Allocator {
66 return self.comp.a();146 return self.comp.gpa();
67 }147 }
68};148};
69149
70pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {150pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
71 // TODO audit more of codegen.cpp:fn_llvm_value and port more logic151 // TODO audit more of codegen.cpp:fn_llvm_value and port more logic
72 const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile);152 const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
73 const llvm_fn = llvm.AddFunction(153 const llvm_fn = llvm.AddFunction(
74 ofile.module,154 ofile.module,
75 fn_val.symbol_name.ptr(),155 fn_val.symbol_name.ptr(),
...@@ -87,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)...@@ -87,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
87 // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);167 // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
88 //}168 //}
89169
90 const fn_type = fn_val.base.typeof.cast(Type.Fn).?;170 const fn_type = fn_val.base.typ.cast(Type.Fn).?;
91171
92 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");172 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
93 //add_uwtable_attr(g, fn_table_entry->llvm_value);173 //add_uwtable_attr(g, fn_table_entry->llvm_value);
src-self-hosted/compilation.zig+719-229
...@@ -21,24 +21,47 @@ const Scope = @import("scope.zig").Scope;...@@ -21,24 +21,47 @@ const Scope = @import("scope.zig").Scope;
21const Decl = @import("decl.zig").Decl;21const Decl = @import("decl.zig").Decl;
22const ir = @import("ir.zig");22const ir = @import("ir.zig");
23const Visib = @import("visib.zig").Visib;23const Visib = @import("visib.zig").Visib;
24const ParsedFile = @import("parsed_file.zig").ParsedFile;
25const Value = @import("value.zig").Value;24const Value = @import("value.zig").Value;
26const Type = Value.Type;25const Type = Value.Type;
27const Span = errmsg.Span;26const Span = errmsg.Span;
27const Msg = errmsg.Msg;
28const codegen = @import("codegen.zig");28const codegen = @import("codegen.zig");
29const Package = @import("package.zig").Package;
30const link = @import("link.zig").link;
31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
32const CInt = @import("c_int.zig").CInt;
2933
30/// Data that is local to the event loop.34/// Data that is local to the event loop.
31pub const EventLoopLocal = struct {35pub const EventLoopLocal = struct {
32 loop: *event.Loop,36 loop: *event.Loop,
33 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),37 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
3438
35 fn init(loop: *event.Loop) EventLoopLocal {39 /// TODO pool these so that it doesn't have to lock
40 prng: event.Locked(std.rand.DefaultPrng),
41
42 native_libc: event.Future(LibCInstallation),
43
44 var lazy_init_targets = std.lazyInit(void);
45
46 fn init(loop: *event.Loop) !EventLoopLocal {
47 lazy_init_targets.get() orelse {
48 Target.initializeAll();
49 lazy_init_targets.resolve();
50 };
51
52 var seed_bytes: [@sizeOf(u64)]u8 = undefined;
53 try std.os.getRandomBytes(seed_bytes[0..]);
54 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
55
36 return EventLoopLocal{56 return EventLoopLocal{
37 .loop = loop,57 .loop = loop,
38 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),58 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
59 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
60 .native_libc = event.Future(LibCInstallation).init(loop),
39 };61 };
40 }62 }
4163
64 /// Must be called only after EventLoop.run completes.
42 fn deinit(self: *EventLoopLocal) void {65 fn deinit(self: *EventLoopLocal) void {
43 while (self.llvm_handle_pool.pop()) |node| {66 while (self.llvm_handle_pool.pop()) |node| {
44 c.LLVMContextDispose(node.data);67 c.LLVMContextDispose(node.data);
...@@ -62,6 +85,13 @@ pub const EventLoopLocal = struct {...@@ -62,6 +85,13 @@ pub const EventLoopLocal = struct {
6285
63 return LlvmHandle{ .node = node };86 return LlvmHandle{ .node = node };
64 }87 }
88
89 pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
90 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
91 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
92 self.native_libc.resolve();
93 return &self.native_libc.data;
94 }
65};95};
6696
67pub const LlvmHandle = struct {97pub const LlvmHandle = struct {
...@@ -76,23 +106,22 @@ pub const Compilation = struct {...@@ -76,23 +106,22 @@ pub const Compilation = struct {
76 event_loop_local: *EventLoopLocal,106 event_loop_local: *EventLoopLocal,
77 loop: *event.Loop,107 loop: *event.Loop,
78 name: Buffer,108 name: Buffer,
109 llvm_triple: Buffer,
79 root_src_path: ?[]const u8,110 root_src_path: ?[]const u8,
80 target: Target,111 target: Target,
112 llvm_target: llvm.TargetRef,
81 build_mode: builtin.Mode,113 build_mode: builtin.Mode,
82 zig_lib_dir: []const u8,114 zig_lib_dir: []const u8,
115 zig_std_dir: []const u8,
116
117 /// lazily created when we need it
118 tmp_dir: event.Future(BuildError![]u8),
83119
84 version_major: u32,120 version_major: u32,
85 version_minor: u32,121 version_minor: u32,
86 version_patch: u32,122 version_patch: u32,
87123
88 linker_script: ?[]const u8,124 linker_script: ?[]const u8,
89 cache_dir: []const u8,
90 libc_lib_dir: ?[]const u8,
91 libc_static_lib_dir: ?[]const u8,
92 libc_include_dir: ?[]const u8,
93 msvc_lib_dir: ?[]const u8,
94 kernel32_lib_dir: ?[]const u8,
95 dynamic_linker: ?[]const u8,
96 out_h_path: ?[]const u8,125 out_h_path: ?[]const u8,
97126
98 is_test: bool,127 is_test: bool,
...@@ -106,8 +135,16 @@ pub const Compilation = struct {...@@ -106,8 +135,16 @@ pub const Compilation = struct {
106 lib_dirs: []const []const u8,135 lib_dirs: []const []const u8,
107 rpath_list: []const []const u8,136 rpath_list: []const []const u8,
108 assembly_files: []const []const u8,137 assembly_files: []const []const u8,
138
139 /// paths that are explicitly provided by the user to link against
109 link_objects: []const []const u8,140 link_objects: []const []const u8,
110141
142 /// functions that have their own objects that we need to link
143 /// it uses an optional pointer so that tombstone removals are possible
144 fn_link_set: event.Locked(FnLinkSet),
145
146 pub const FnLinkSet = std.LinkedList(?*Value.Fn);
147
111 windows_subsystem_windows: bool,148 windows_subsystem_windows: bool,
112 windows_subsystem_console: bool,149 windows_subsystem_console: bool,
113150
...@@ -141,7 +178,7 @@ pub const Compilation = struct {...@@ -141,7 +178,7 @@ pub const Compilation = struct {
141178
142 /// Before code generation starts, must wait on this group to make sure179 /// Before code generation starts, must wait on this group to make sure
143 /// the build is complete.180 /// the build is complete.
144 build_group: event.Group(BuildError!void),181 prelink_group: event.Group(BuildError!void),
145182
146 compile_errors: event.Locked(CompileErrList),183 compile_errors: event.Locked(CompileErrList),
147184
...@@ -149,13 +186,49 @@ pub const Compilation = struct {...@@ -149,13 +186,49 @@ pub const Compilation = struct {
149 void_type: *Type.Void,186 void_type: *Type.Void,
150 bool_type: *Type.Bool,187 bool_type: *Type.Bool,
151 noreturn_type: *Type.NoReturn,188 noreturn_type: *Type.NoReturn,
189 comptime_int_type: *Type.ComptimeInt,
190 u8_type: *Type.Int,
152191
153 void_value: *Value.Void,192 void_value: *Value.Void,
154 true_value: *Value.Bool,193 true_value: *Value.Bool,
155 false_value: *Value.Bool,194 false_value: *Value.Bool,
156 noreturn_value: *Value.NoReturn,195 noreturn_value: *Value.NoReturn,
157196
158 const CompileErrList = std.ArrayList(*errmsg.Msg);197 target_machine: llvm.TargetMachineRef,
198 target_data_ref: llvm.TargetDataRef,
199 target_layout_str: [*]u8,
200 target_ptr_bits: u32,
201
202 /// for allocating things which have the same lifetime as this Compilation
203 arena_allocator: std.heap.ArenaAllocator,
204
205 root_package: *Package,
206 std_package: *Package,
207
208 override_libc: ?*LibCInstallation,
209
210 /// need to wait on this group before deinitializing
211 deinit_group: event.Group(void),
212
213 destroy_handle: promise,
214
215 have_err_ret_tracing: bool,
216
217 /// not locked because it is read-only
218 primitive_type_table: TypeTable,
219
220 int_type_table: event.Locked(IntTypeTable),
221 array_type_table: event.Locked(ArrayTypeTable),
222 ptr_type_table: event.Locked(PtrTypeTable),
223
224 c_int_types: [CInt.list.len]*Type.Int,
225
226 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
227 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
228 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
229 const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
230
231 const CompileErrList = std.ArrayList(*Msg);
159232
160 // TODO handle some of these earlier and report them in a way other than error codes233 // TODO handle some of these earlier and report them in a way other than error codes
161 pub const BuildError = error{234 pub const BuildError = error{
...@@ -195,12 +268,21 @@ pub const Compilation = struct {...@@ -195,12 +268,21 @@ pub const Compilation = struct {
195 BufferTooSmall,268 BufferTooSmall,
196 Unimplemented, // TODO remove this one269 Unimplemented, // TODO remove this one
197 SemanticAnalysisFailed, // TODO remove this one270 SemanticAnalysisFailed, // TODO remove this one
271 ReadOnlyFileSystem,
272 LinkQuotaExceeded,
273 EnvironmentVariableNotFound,
274 AppDataDirUnavailable,
275 LinkFailed,
276 LibCRequiredButNotProvidedOrFound,
277 LibCMissingDynamicLinker,
278 InvalidDarwinVersionString,
279 UnsupportedLinkArchitecture,
198 };280 };
199281
200 pub const Event = union(enum) {282 pub const Event = union(enum) {
201 Ok,283 Ok,
202 Error: BuildError,284 Error: BuildError,
203 Fail: []*errmsg.Msg,285 Fail: []*Msg,
204 };286 };
205287
206 pub const DarwinVersionMin = union(enum) {288 pub const DarwinVersionMin = union(enum) {
...@@ -234,31 +316,29 @@ pub const Compilation = struct {...@@ -234,31 +316,29 @@ pub const Compilation = struct {
234 event_loop_local: *EventLoopLocal,316 event_loop_local: *EventLoopLocal,
235 name: []const u8,317 name: []const u8,
236 root_src_path: ?[]const u8,318 root_src_path: ?[]const u8,
237 target: *const Target,319 target: Target,
238 kind: Kind,320 kind: Kind,
239 build_mode: builtin.Mode,321 build_mode: builtin.Mode,
322 is_static: bool,
240 zig_lib_dir: []const u8,323 zig_lib_dir: []const u8,
241 cache_dir: []const u8,
242 ) !*Compilation {324 ) !*Compilation {
243 const loop = event_loop_local.loop;325 const loop = event_loop_local.loop;
244326 const comp = try event_loop_local.loop.allocator.create(Compilation{
245 var name_buffer = try Buffer.init(loop.allocator, name);
246 errdefer name_buffer.deinit();
247
248 const events = try event.Channel(Event).create(loop, 0);
249 errdefer events.destroy();
250
251 const comp = try loop.allocator.create(Compilation{
252 .loop = loop,327 .loop = loop,
328 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
253 .event_loop_local = event_loop_local,329 .event_loop_local = event_loop_local,
254 .events = events,330 .events = undefined,
255 .name = name_buffer,
256 .root_src_path = root_src_path,331 .root_src_path = root_src_path,
257 .target = target.*,332 .target = target,
333 .llvm_target = undefined,
258 .kind = kind,334 .kind = kind,
259 .build_mode = build_mode,335 .build_mode = build_mode,
260 .zig_lib_dir = zig_lib_dir,336 .zig_lib_dir = zig_lib_dir,
261 .cache_dir = cache_dir,337 .zig_std_dir = undefined,
338 .tmp_dir = event.Future(BuildError![]u8).init(loop),
339
340 .name = undefined,
341 .llvm_triple = undefined,
262342
263 .version_major = 0,343 .version_major = 0,
264 .version_minor = 0,344 .version_minor = 0,
...@@ -273,17 +353,11 @@ pub const Compilation = struct {...@@ -273,17 +353,11 @@ pub const Compilation = struct {
273 .verbose_link = false,353 .verbose_link = false,
274354
275 .linker_script = null,355 .linker_script = null,
276 .libc_lib_dir = null,
277 .libc_static_lib_dir = null,
278 .libc_include_dir = null,
279 .msvc_lib_dir = null,
280 .kernel32_lib_dir = null,
281 .dynamic_linker = null,
282 .out_h_path = null,356 .out_h_path = null,
283 .is_test = false,357 .is_test = false,
284 .each_lib_rpath = false,358 .each_lib_rpath = false,
285 .strip = false,359 .strip = false,
286 .is_static = false,360 .is_static = is_static,
287 .linker_rdynamic = false,361 .linker_rdynamic = false,
288 .clang_argv = [][]const u8{},362 .clang_argv = [][]const u8{},
289 .llvm_argv = [][]const u8{},363 .llvm_argv = [][]const u8{},
...@@ -291,9 +365,10 @@ pub const Compilation = struct {...@@ -291,9 +365,10 @@ pub const Compilation = struct {
291 .rpath_list = [][]const u8{},365 .rpath_list = [][]const u8{},
292 .assembly_files = [][]const u8{},366 .assembly_files = [][]const u8{},
293 .link_objects = [][]const u8{},367 .link_objects = [][]const u8{},
368 .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
294 .windows_subsystem_windows = false,369 .windows_subsystem_windows = false,
295 .windows_subsystem_console = false,370 .windows_subsystem_console = false,
296 .link_libs_list = ArrayList(*LinkLib).init(loop.allocator),371 .link_libs_list = undefined,
297 .libc_link_lib = null,372 .libc_link_lib = null,
298 .err_color = errmsg.Color.Auto,373 .err_color = errmsg.Color.Auto,
299 .darwin_frameworks = [][]const u8{},374 .darwin_frameworks = [][]const u8{},
...@@ -303,8 +378,13 @@ pub const Compilation = struct {...@@ -303,8 +378,13 @@ pub const Compilation = struct {
303 .emit_file_type = Emit.Binary,378 .emit_file_type = Emit.Binary,
304 .link_out_file = null,379 .link_out_file = null,
305 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),380 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
306 .build_group = event.Group(BuildError!void).init(loop),381 .prelink_group = event.Group(BuildError!void).init(loop),
382 .deinit_group = event.Group(void).init(loop),
307 .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),383 .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
384 .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
385 .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
386 .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
387 .c_int_types = undefined,
308388
309 .meta_type = undefined,389 .meta_type = undefined,
310 .void_type = undefined,390 .void_type = undefined,
...@@ -314,120 +394,307 @@ pub const Compilation = struct {...@@ -314,120 +394,307 @@ pub const Compilation = struct {
314 .false_value = undefined,394 .false_value = undefined,
315 .noreturn_type = undefined,395 .noreturn_type = undefined,
316 .noreturn_value = undefined,396 .noreturn_value = undefined,
397 .comptime_int_type = undefined,
398 .u8_type = undefined,
399
400 .target_machine = undefined,
401 .target_data_ref = undefined,
402 .target_layout_str = undefined,
403 .target_ptr_bits = target.getArchPtrBitWidth(),
404
405 .root_package = undefined,
406 .std_package = undefined,
407
408 .override_libc = null,
409 .destroy_handle = undefined,
410 .have_err_ret_tracing = false,
411 .primitive_type_table = undefined,
317 });412 });
413 errdefer {
414 comp.int_type_table.private_data.deinit();
415 comp.array_type_table.private_data.deinit();
416 comp.ptr_type_table.private_data.deinit();
417 comp.arena_allocator.deinit();
418 comp.loop.allocator.destroy(comp);
419 }
420
421 comp.name = try Buffer.init(comp.arena(), name);
422 comp.llvm_triple = try target.getTriple(comp.arena());
423 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
424 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
425 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
426 comp.primitive_type_table = TypeTable.init(comp.arena());
427
428 const opt_level = switch (build_mode) {
429 builtin.Mode.Debug => llvm.CodeGenLevelNone,
430 else => llvm.CodeGenLevelAggressive,
431 };
432
433 const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
434
435 // LLVM creates invalid binaries on Windows sometimes.
436 // See https://github.com/ziglang/zig/issues/508
437 // As a workaround we do not use target native features on Windows.
438 var target_specific_cpu_args: ?[*]u8 = null;
439 var target_specific_cpu_features: ?[*]u8 = null;
440 errdefer llvm.DisposeMessage(target_specific_cpu_args);
441 errdefer llvm.DisposeMessage(target_specific_cpu_features);
442 if (target == Target.Native and !target.isWindows()) {
443 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
444 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
445 }
446
447 comp.target_machine = llvm.CreateTargetMachine(
448 comp.llvm_target,
449 comp.llvm_triple.ptr(),
450 target_specific_cpu_args orelse c"",
451 target_specific_cpu_features orelse c"",
452 opt_level,
453 reloc_mode,
454 llvm.CodeModelDefault,
455 ) orelse return error.OutOfMemory;
456 errdefer llvm.DisposeTargetMachine(comp.target_machine);
457
458 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
459 errdefer llvm.DisposeTargetData(comp.target_data_ref);
460
461 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
462 errdefer llvm.DisposeMessage(comp.target_layout_str);
463
464 comp.events = try event.Channel(Event).create(comp.loop, 0);
465 errdefer comp.events.destroy();
466
467 if (root_src_path) |root_src| {
468 const dirname = std.os.path.dirname(root_src) orelse ".";
469 const basename = std.os.path.basename(root_src);
470
471 comp.root_package = try Package.create(comp.arena(), dirname, basename);
472 comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig");
473 try comp.root_package.add("std", comp.std_package);
474 } else {
475 comp.root_package = try Package.create(comp.arena(), ".", "");
476 }
477
318 try comp.initTypes();478 try comp.initTypes();
479
480 comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
481
319 return comp;482 return comp;
320 }483 }
321484
485 /// it does ref the result because it could be an arbitrary integer size
486 pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
487 if (name.len >= 2) {
488 switch (name[0]) {
489 'i', 'u' => blk: {
490 for (name[1..]) |byte|
491 switch (byte) {
492 '0'...'9' => {},
493 else => break :blk,
494 };
495 const is_signed = name[0] == 'i';
496 const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
497 error.Overflow => return error.Overflow,
498 error.InvalidCharacter => unreachable, // we just checked the characters above
499 };
500 const int_type = try await (async Type.Int.get(comp, Type.Int.Key{
501 .bit_count = bit_count,
502 .is_signed = is_signed,
503 }) catch unreachable);
504 errdefer int_type.base.base.deref();
505 return &int_type.base;
506 },
507 else => {},
508 }
509 }
510
511 if (comp.primitive_type_table.get(name)) |entry| {
512 entry.value.base.ref();
513 return entry.value;
514 }
515
516 return null;
517 }
518
322 fn initTypes(comp: *Compilation) !void {519 fn initTypes(comp: *Compilation) !void {
323 comp.meta_type = try comp.a().create(Type.MetaType{520 comp.meta_type = try comp.arena().create(Type.MetaType{
324 .base = Type{521 .base = Type{
522 .name = "type",
325 .base = Value{523 .base = Value{
326 .id = Value.Id.Type,524 .id = Value.Id.Type,
327 .typeof = undefined,525 .typ = undefined,
328 .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice526 .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
329 },527 },
330 .id = builtin.TypeId.Type,528 .id = builtin.TypeId.Type,
529 .abi_alignment = Type.AbiAlignment.init(comp.loop),
331 },530 },
332 .value = undefined,531 .value = undefined,
333 });532 });
334 comp.meta_type.value = &comp.meta_type.base;533 comp.meta_type.value = &comp.meta_type.base;
335 comp.meta_type.base.base.typeof = &comp.meta_type.base;534 comp.meta_type.base.base.typ = &comp.meta_type.base;
336 errdefer comp.a().destroy(comp.meta_type);535 assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
337536
338 comp.void_type = try comp.a().create(Type.Void{537 comp.void_type = try comp.arena().create(Type.Void{
339 .base = Type{538 .base = Type{
539 .name = "void",
340 .base = Value{540 .base = Value{
341 .id = Value.Id.Type,541 .id = Value.Id.Type,
342 .typeof = &Type.MetaType.get(comp).base,542 .typ = &Type.MetaType.get(comp).base,
343 .ref_count = std.atomic.Int(usize).init(1),543 .ref_count = std.atomic.Int(usize).init(1),
344 },544 },
345 .id = builtin.TypeId.Void,545 .id = builtin.TypeId.Void,
546 .abi_alignment = Type.AbiAlignment.init(comp.loop),
346 },547 },
347 });548 });
348 errdefer comp.a().destroy(comp.void_type);549 assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
349550
350 comp.noreturn_type = try comp.a().create(Type.NoReturn{551 comp.noreturn_type = try comp.arena().create(Type.NoReturn{
351 .base = Type{552 .base = Type{
553 .name = "noreturn",
352 .base = Value{554 .base = Value{
353 .id = Value.Id.Type,555 .id = Value.Id.Type,
354 .typeof = &Type.MetaType.get(comp).base,556 .typ = &Type.MetaType.get(comp).base,
355 .ref_count = std.atomic.Int(usize).init(1),557 .ref_count = std.atomic.Int(usize).init(1),
356 },558 },
357 .id = builtin.TypeId.NoReturn,559 .id = builtin.TypeId.NoReturn,
560 .abi_alignment = Type.AbiAlignment.init(comp.loop),
561 },
562 });
563 assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
564
565 comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{
566 .base = Type{
567 .name = "comptime_int",
568 .base = Value{
569 .id = Value.Id.Type,
570 .typ = &Type.MetaType.get(comp).base,
571 .ref_count = std.atomic.Int(usize).init(1),
572 },
573 .id = builtin.TypeId.ComptimeInt,
574 .abi_alignment = Type.AbiAlignment.init(comp.loop),
358 },575 },
359 });576 });
360 errdefer comp.a().destroy(comp.noreturn_type);577 assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
361578
362 comp.bool_type = try comp.a().create(Type.Bool{579 comp.bool_type = try comp.arena().create(Type.Bool{
363 .base = Type{580 .base = Type{
581 .name = "bool",
364 .base = Value{582 .base = Value{
365 .id = Value.Id.Type,583 .id = Value.Id.Type,
366 .typeof = &Type.MetaType.get(comp).base,584 .typ = &Type.MetaType.get(comp).base,
367 .ref_count = std.atomic.Int(usize).init(1),585 .ref_count = std.atomic.Int(usize).init(1),
368 },586 },
369 .id = builtin.TypeId.Bool,587 .id = builtin.TypeId.Bool,
588 .abi_alignment = Type.AbiAlignment.init(comp.loop),
370 },589 },
371 });590 });
372 errdefer comp.a().destroy(comp.bool_type);591 assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
373592
374 comp.void_value = try comp.a().create(Value.Void{593 comp.void_value = try comp.arena().create(Value.Void{
375 .base = Value{594 .base = Value{
376 .id = Value.Id.Void,595 .id = Value.Id.Void,
377 .typeof = &Type.Void.get(comp).base,596 .typ = &Type.Void.get(comp).base,
378 .ref_count = std.atomic.Int(usize).init(1),597 .ref_count = std.atomic.Int(usize).init(1),
379 },598 },
380 });599 });
381 errdefer comp.a().destroy(comp.void_value);
382600
383 comp.true_value = try comp.a().create(Value.Bool{601 comp.true_value = try comp.arena().create(Value.Bool{
384 .base = Value{602 .base = Value{
385 .id = Value.Id.Bool,603 .id = Value.Id.Bool,
386 .typeof = &Type.Bool.get(comp).base,604 .typ = &Type.Bool.get(comp).base,
387 .ref_count = std.atomic.Int(usize).init(1),605 .ref_count = std.atomic.Int(usize).init(1),
388 },606 },
389 .x = true,607 .x = true,
390 });608 });
391 errdefer comp.a().destroy(comp.true_value);
392609
393 comp.false_value = try comp.a().create(Value.Bool{610 comp.false_value = try comp.arena().create(Value.Bool{
394 .base = Value{611 .base = Value{
395 .id = Value.Id.Bool,612 .id = Value.Id.Bool,
396 .typeof = &Type.Bool.get(comp).base,613 .typ = &Type.Bool.get(comp).base,
397 .ref_count = std.atomic.Int(usize).init(1),614 .ref_count = std.atomic.Int(usize).init(1),
398 },615 },
399 .x = false,616 .x = false,
400 });617 });
401 errdefer comp.a().destroy(comp.false_value);
402618
403 comp.noreturn_value = try comp.a().create(Value.NoReturn{619 comp.noreturn_value = try comp.arena().create(Value.NoReturn{
404 .base = Value{620 .base = Value{
405 .id = Value.Id.NoReturn,621 .id = Value.Id.NoReturn,
406 .typeof = &Type.NoReturn.get(comp).base,622 .typ = &Type.NoReturn.get(comp).base,
407 .ref_count = std.atomic.Int(usize).init(1),623 .ref_count = std.atomic.Int(usize).init(1),
408 },624 },
409 });625 });
410 errdefer comp.a().destroy(comp.noreturn_value);626
627 for (CInt.list) |cint, i| {
628 const c_int_type = try comp.arena().create(Type.Int{
629 .base = Type{
630 .name = cint.zig_name,
631 .base = Value{
632 .id = Value.Id.Type,
633 .typ = &Type.MetaType.get(comp).base,
634 .ref_count = std.atomic.Int(usize).init(1),
635 },
636 .id = builtin.TypeId.Int,
637 .abi_alignment = Type.AbiAlignment.init(comp.loop),
638 },
639 .key = Type.Int.Key{
640 .is_signed = cint.is_signed,
641 .bit_count = comp.target.cIntTypeSizeInBits(cint.id),
642 },
643 .garbage_node = undefined,
644 });
645 comp.c_int_types[i] = c_int_type;
646 assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
647 }
648 comp.u8_type = try comp.arena().create(Type.Int{
649 .base = Type{
650 .name = "u8",
651 .base = Value{
652 .id = Value.Id.Type,
653 .typ = &Type.MetaType.get(comp).base,
654 .ref_count = std.atomic.Int(usize).init(1),
655 },
656 .id = builtin.TypeId.Int,
657 .abi_alignment = Type.AbiAlignment.init(comp.loop),
658 },
659 .key = Type.Int.Key{
660 .is_signed = false,
661 .bit_count = 8,
662 },
663 .garbage_node = undefined,
664 });
665 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
411 }666 }
412667
413 pub fn destroy(self: *Compilation) void {668 /// This function can safely use async/await, because it manages Compilation's lifetime,
414 self.noreturn_value.base.deref(self);669 /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
415 self.void_value.base.deref(self);670 async fn internalDeinit(self: *Compilation) void {
416 self.false_value.base.deref(self);671 suspend;
417 self.true_value.base.deref(self);672
418 self.noreturn_type.base.base.deref(self);673 await (async self.deinit_group.wait() catch unreachable);
419 self.void_type.base.base.deref(self);674 if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
420 self.meta_type.base.base.deref(self);675 // TODO evented I/O?
676 os.deleteTree(self.arena(), tmp_dir) catch {};
677 } else |_| {};
421678
422 self.events.destroy();679 self.events.destroy();
423 self.name.deinit();
424680
425 self.a().destroy(self);681 llvm.DisposeMessage(self.target_layout_str);
682 llvm.DisposeTargetData(self.target_data_ref);
683 llvm.DisposeTargetMachine(self.target_machine);
684
685 self.primitive_type_table.deinit();
686
687 self.arena_allocator.deinit();
688 self.gpa().destroy(self);
689 }
690
691 pub fn destroy(self: *Compilation) void {
692 resume self.destroy_handle;
426 }693 }
427694
428 pub fn build(self: *Compilation) !void {695 pub fn build(self: *Compilation) !void {
429 if (self.llvm_argv.len != 0) {696 if (self.llvm_argv.len != 0) {
430 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{697 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
431 [][]const u8{"zig (LLVM option parsing)"},698 [][]const u8{"zig (LLVM option parsing)"},
432 self.llvm_argv,699 self.llvm_argv,
433 });700 });
...@@ -436,14 +703,13 @@ pub const Compilation = struct {...@@ -436,14 +703,13 @@ pub const Compilation = struct {
436 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);703 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
437 }704 }
438705
439 _ = try async<self.a()> self.buildAsync();706 _ = try async<self.gpa()> self.buildAsync();
440 }707 }
441708
442 async fn buildAsync(self: *Compilation) void {709 async fn buildAsync(self: *Compilation) void {
443 while (true) {710 while (true) {
444 // TODO directly awaiting async should guarantee memory allocation elision711 // TODO directly awaiting async should guarantee memory allocation elision
445 // TODO also async before suspending should guarantee memory allocation elision712 const build_result = await (async self.compileAndLink() catch unreachable);
446 const build_result = await (async self.addRootSrc() catch unreachable);
447713
448 // this makes a handy error return trace and stack trace in debug mode714 // this makes a handy error return trace and stack trace in debug mode
449 if (std.debug.runtime_safety) {715 if (std.debug.runtime_safety) {
...@@ -464,7 +730,7 @@ pub const Compilation = struct {...@@ -464,7 +730,7 @@ pub const Compilation = struct {
464 }730 }
465 } else |err| {731 } else |err| {
466 // if there's an error then the compile errors have dangling references732 // if there's an error then the compile errors have dangling references
467 self.a().free(compile_errors);733 self.gpa().free(compile_errors);
468734
469 await (async self.events.put(Event{ .Error = err }) catch unreachable);735 await (async self.events.put(Event{ .Error = err }) catch unreachable);
470 }736 }
...@@ -474,111 +740,215 @@ pub const Compilation = struct {...@@ -474,111 +740,215 @@ pub const Compilation = struct {
474 }740 }
475 }741 }
476742
477 async fn addRootSrc(self: *Compilation) !void {743 async fn compileAndLink(self: *Compilation) !void {
478 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");744 if (self.root_src_path) |root_src_path| {
479 // TODO async/await os.path.real745 // TODO async/await os.path.real
480 const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {746 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
481 try printError("unable to get real path '{}': {}", root_src_path, err);747 try printError("unable to get real path '{}': {}", root_src_path, err);
482 return err;748 return err;
749 };
750 const root_scope = blk: {
751 errdefer self.gpa().free(root_src_real_path);
752
753 // TODO async/await readFileAlloc()
754 const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
755 try printError("unable to open '{}': {}", root_src_real_path, err);
756 return err;
757 };
758 errdefer self.gpa().free(source_code);
759
760 const tree = try self.gpa().createOne(ast.Tree);
761 tree.* = try std.zig.parse(self.gpa(), source_code);
762 errdefer {
763 tree.deinit();
764 self.gpa().destroy(tree);
765 }
766
767 break :blk try Scope.Root.create(self, tree, root_src_real_path);
768 };
769 defer root_scope.base.deref(self);
770 const tree = root_scope.tree;
771
772 var error_it = tree.errors.iterator(0);
773 while (error_it.next()) |parse_error| {
774 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
775 errdefer msg.destroy();
776
777 try await (async self.addCompileErrorAsync(msg) catch unreachable);
778 }
779 if (tree.errors.len != 0) {
780 return;
781 }
782
783 const decls = try Scope.Decls.create(self, &root_scope.base);
784 defer decls.base.deref(self);
785
786 var decl_group = event.Group(BuildError!void).init(self.loop);
787 var decl_group_consumed = false;
788 errdefer if (!decl_group_consumed) decl_group.cancelAll();
789
790 var it = tree.root_node.decls.iterator(0);
791 while (it.next()) |decl_ptr| {
792 const decl = decl_ptr.*;
793 switch (decl.id) {
794 ast.Node.Id.Comptime => {
795 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
796
797 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
798 },
799 ast.Node.Id.VarDecl => @panic("TODO"),
800 ast.Node.Id.FnProto => {
801 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
802
803 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
804 try self.addCompileError(root_scope, Span{
805 .first = fn_proto.fn_token,
806 .last = fn_proto.fn_token + 1,
807 }, "missing function name");
808 continue;
809 };
810
811 const fn_decl = try self.gpa().create(Decl.Fn{
812 .base = Decl{
813 .id = Decl.Id.Fn,
814 .name = name,
815 .visib = parseVisibToken(tree, fn_proto.visib_token),
816 .resolution = event.Future(BuildError!void).init(self.loop),
817 .parent_scope = &decls.base,
818 },
819 .value = Decl.Fn.Val{ .Unresolved = {} },
820 .fn_proto = fn_proto,
821 });
822 errdefer self.gpa().destroy(fn_decl);
823
824 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
825 },
826 ast.Node.Id.TestDecl => @panic("TODO"),
827 else => unreachable,
828 }
829 }
830 decl_group_consumed = true;
831 try await (async decl_group.wait() catch unreachable);
832
833 // Now other code can rely on the decls scope having a complete list of names.
834 decls.name_future.resolve();
835 }
836
837 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
838 error.SemanticAnalysisFailed => {},
839 else => return err,
483 };840 };
484 errdefer self.a().free(root_src_real_path);
485841
486 // TODO async/await readFileAlloc()842 const any_prelink_errors = blk: {
487 const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {843 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
488 try printError("unable to open '{}': {}", root_src_real_path, err);844 defer compile_errors.release();
489 return err;845
846 break :blk compile_errors.value.len != 0;
490 };847 };
491 errdefer self.a().free(source_code);
492848
493 const parsed_file = try self.a().create(ParsedFile{849 if (!any_prelink_errors) {
494 .tree = undefined,850 try await (async link(self) catch unreachable);
495 .realpath = root_src_real_path,851 }
496 });852 }
497 errdefer self.a().destroy(parsed_file);
498
499 parsed_file.tree = try std.zig.parse(self.a(), source_code);
500 errdefer parsed_file.tree.deinit();
501
502 const tree = &parsed_file.tree;
503
504 // create empty struct for it
505 const decls = try Scope.Decls.create(self, null);
506 defer decls.base.deref(self);
507
508 var decl_group = event.Group(BuildError!void).init(self.loop);
509 errdefer decl_group.cancelAll();
510
511 var it = tree.root_node.decls.iterator(0);
512 while (it.next()) |decl_ptr| {
513 const decl = decl_ptr.*;
514 switch (decl.id) {
515 ast.Node.Id.Comptime => @panic("TODO"),
516 ast.Node.Id.VarDecl => @panic("TODO"),
517 ast.Node.Id.FnProto => {
518 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
519
520 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
521 try self.addCompileError(parsed_file, Span{
522 .first = fn_proto.fn_token,
523 .last = fn_proto.fn_token + 1,
524 }, "missing function name");
525 continue;
526 };
527853
528 const fn_decl = try self.a().create(Decl.Fn{854 /// caller takes ownership of resulting Code
529 .base = Decl{855 async fn genAndAnalyzeCode(
530 .id = Decl.Id.Fn,856 comp: *Compilation,
531 .name = name,857 scope: *Scope,
532 .visib = parseVisibToken(tree, fn_proto.visib_token),858 node: *ast.Node,
533 .resolution = event.Future(BuildError!void).init(self.loop),859 expected_type: ?*Type,
534 .resolution_in_progress = 0,860 ) !*ir.Code {
535 .parsed_file = parsed_file,861 const unanalyzed_code = try await (async ir.gen(
536 .parent_scope = &decls.base,862 comp,
537 },863 node,
538 .value = Decl.Fn.Val{ .Unresolved = {} },864 scope,
539 .fn_proto = fn_proto,865 ) catch unreachable);
540 });866 defer unanalyzed_code.destroy(comp.gpa());
541 errdefer self.a().destroy(fn_decl);867
542868 if (comp.verbose_ir) {
543 try decl_group.call(addTopLevelDecl, self, &fn_decl.base);869 std.debug.warn("unanalyzed:\n");
544 },870 unanalyzed_code.dump();
545 ast.Node.Id.TestDecl => @panic("TODO"),871 }
546 else => unreachable,872
547 }873 const analyzed_code = try await (async ir.analyze(
874 comp,
875 unanalyzed_code,
876 expected_type,
877 ) catch unreachable);
878 errdefer analyzed_code.destroy(comp.gpa());
879
880 if (comp.verbose_ir) {
881 std.debug.warn("analyzed:\n");
882 analyzed_code.dump();
548 }883 }
549 try await (async decl_group.wait() catch unreachable);884
550 try await (async self.build_group.wait() catch unreachable);885 return analyzed_code;
886 }
887
888 async fn addCompTimeBlock(
889 comp: *Compilation,
890 scope: *Scope,
891 comptime_node: *ast.Node.Comptime,
892 ) !void {
893 const void_type = Type.Void.get(comp);
894 defer void_type.base.base.deref(comp);
895
896 const analyzed_code = (await (async genAndAnalyzeCode(
897 comp,
898 scope,
899 comptime_node.expr,
900 &void_type.base,
901 ) catch unreachable)) catch |err| switch (err) {
902 // This poison value should not cause the errdefers to run. It simply means
903 // that comp.compile_errors is populated.
904 error.SemanticAnalysisFailed => return {},
905 else => return err,
906 };
907 analyzed_code.destroy(comp.gpa());
551 }908 }
552909
553 async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {910 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
554 const is_export = decl.isExported(&decl.parsed_file.tree);911 const tree = decl.findRootScope().tree;
912 const is_export = decl.isExported(tree);
913
914 var add_to_table_resolved = false;
915 const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
916 errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
555917
556 if (is_export) {918 if (is_export) {
557 try self.build_group.call(verifyUniqueSymbol, self, decl);919 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
558 try self.build_group.call(resolveDecl, self, decl);920 try self.prelink_group.call(resolveDecl, self, decl);
921 }
922
923 add_to_table_resolved = true;
924 try await add_to_table;
925 }
926
927 async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
928 const held = await (async decls.table.acquire() catch unreachable);
929 defer held.release();
930
931 if (try held.value.put(decl.name, decl)) |other_decl| {
932 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
933 // TODO note: other definition here
559 }934 }
560 }935 }
561936
562 fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {937 fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
563 const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);938 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
564 errdefer self.loop.allocator.free(text);939 errdefer self.gpa().free(text);
940
941 const msg = try Msg.createFromScope(self, root, span, text);
942 errdefer msg.destroy();
565943
566 try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text);944 try self.prelink_group.call(addCompileErrorAsync, self, msg);
567 }945 }
568946
569 async fn addCompileErrorAsync(947 async fn addCompileErrorAsync(
570 self: *Compilation,948 self: *Compilation,
571 parsed_file: *ParsedFile,949 msg: *Msg,
572 span: Span,
573 text: []u8,
574 ) !void {950 ) !void {
575 const msg = try self.loop.allocator.create(errmsg.Msg{951 errdefer msg.destroy();
576 .path = parsed_file.realpath,
577 .text = text,
578 .span = span,
579 .tree = &parsed_file.tree,
580 });
581 errdefer self.loop.allocator.destroy(msg);
582952
583 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);953 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
584 defer compile_errors.release();954 defer compile_errors.release();
...@@ -592,7 +962,7 @@ pub const Compilation = struct {...@@ -592,7 +962,7 @@ pub const Compilation = struct {
592962
593 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {963 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
594 try self.addCompileError(964 try self.addCompileError(
595 decl.parsed_file,965 decl.findRootScope(),
596 decl.getSpan(),966 decl.getSpan(),
597 "exported symbol collision: '{}'",967 "exported symbol collision: '{}'",
598 decl.name,968 decl.name,
...@@ -601,11 +971,6 @@ pub const Compilation = struct {...@@ -601,11 +971,6 @@ pub const Compilation = struct {
601 }971 }
602 }972 }
603973
604 pub fn link(self: *Compilation, out_file: ?[]const u8) !void {
605 warn("TODO link");
606 return error.Todo;
607 }
608
609 pub fn haveLibC(self: *Compilation) bool {974 pub fn haveLibC(self: *Compilation) bool {
610 return self.libc_link_lib != null;975 return self.libc_link_lib != null;
611 }976 }
...@@ -625,22 +990,127 @@ pub const Compilation = struct {...@@ -625,22 +990,127 @@ pub const Compilation = struct {
625 }990 }
626 }991 }
627992
628 const link_lib = try self.a().create(LinkLib{993 const link_lib = try self.gpa().create(LinkLib{
629 .name = name,994 .name = name,
630 .path = null,995 .path = null,
631 .provided_explicitly = provided_explicitly,996 .provided_explicitly = provided_explicitly,
632 .symbols = ArrayList([]u8).init(self.a()),997 .symbols = ArrayList([]u8).init(self.gpa()),
633 });998 });
634 try self.link_libs_list.append(link_lib);999 try self.link_libs_list.append(link_lib);
635 if (is_libc) {1000 if (is_libc) {
636 self.libc_link_lib = link_lib;1001 self.libc_link_lib = link_lib;
1002
1003 // get a head start on looking for the native libc
1004 if (self.target == Target.Native and self.override_libc == null) {
1005 try self.deinit_group.call(startFindingNativeLibC, self);
1006 }
637 }1007 }
638 return link_lib;1008 return link_lib;
639 }1009 }
6401010
641 fn a(self: Compilation) *mem.Allocator {1011 /// cancels itself so no need to await or cancel the promise.
1012 async fn startFindingNativeLibC(self: *Compilation) void {
1013 await (async self.loop.yield() catch unreachable);
1014 // we don't care if it fails, we're just trying to kick off the future resolution
1015 _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
1016 }
1017
1018 /// General Purpose Allocator. Must free when done.
1019 fn gpa(self: Compilation) *mem.Allocator {
642 return self.loop.allocator;1020 return self.loop.allocator;
643 }1021 }
1022
1023 /// Arena Allocator. Automatically freed when the Compilation is destroyed.
1024 fn arena(self: *Compilation) *mem.Allocator {
1025 return &self.arena_allocator.allocator;
1026 }
1027
1028 /// If the temporary directory for this compilation has not been created, it creates it.
1029 /// Then it creates a random file name in that dir and returns it.
1030 pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
1031 const tmp_dir = try await (async self.getTmpDir() catch unreachable);
1032 const file_prefix = await (async self.getRandomFileName() catch unreachable);
1033
1034 const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);
1035 defer self.gpa().free(file_name);
1036
1037 const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]);
1038 errdefer self.gpa().free(full_path);
1039
1040 return Buffer.fromOwnedSlice(self.gpa(), full_path);
1041 }
1042
1043 /// If the temporary directory for this Compilation has not been created, creates it.
1044 /// Then returns it. The directory is unique to this Compilation and cleaned up when
1045 /// the Compilation deinitializes.
1046 async fn getTmpDir(self: *Compilation) ![]const u8 {
1047 if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*;
1048 self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable);
1049 self.tmp_dir.resolve();
1050 return self.tmp_dir.data;
1051 }
1052
1053 async fn getTmpDirImpl(self: *Compilation) ![]u8 {
1054 const comp_dir_name = await (async self.getRandomFileName() catch unreachable);
1055 const zig_dir_path = try getZigDir(self.gpa());
1056 defer self.gpa().free(zig_dir_path);
1057
1058 const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]);
1059 try os.makePath(self.gpa(), tmp_dir);
1060 return tmp_dir;
1061 }
1062
1063 async fn getRandomFileName(self: *Compilation) [12]u8 {
1064 // here we replace the standard +/ with -_ so that it can be used in a file name
1065 const b64_fs_encoder = std.base64.Base64Encoder.init(
1066 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
1067 std.base64.standard_pad_char,
1068 );
1069
1070 var rand_bytes: [9]u8 = undefined;
1071
1072 {
1073 const held = await (async self.event_loop_local.prng.acquire() catch unreachable);
1074 defer held.release();
1075
1076 held.value.random.bytes(rand_bytes[0..]);
1077 }
1078
1079 var result: [12]u8 = undefined;
1080 b64_fs_encoder.encode(result[0..], rand_bytes);
1081 return result;
1082 }
1083
1084 fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
1085 // TODO put the garbage somewhere
1086 }
1087
1088 /// Returns a value which has been ref()'d once
1089 async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {
1090 const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);
1091 defer analyzed_code.destroy(comp.gpa());
1092
1093 return analyzed_code.getCompTimeResult(comp);
1094 }
1095
1096 async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {
1097 const meta_type = &Type.MetaType.get(comp).base;
1098 defer meta_type.base.deref(comp);
1099
1100 const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);
1101 errdefer result_val.base.deref(comp);
1102
1103 return result_val.cast(Type).?;
1104 }
1105
1106 /// This declaration has been blessed as going into the final code generation.
1107 pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
1108 if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
1109
1110 decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable);
1111 decl.resolution.resolve();
1112 return decl.resolution.data;
1113 }
644};1114};
6451115
646fn printError(comptime format: []const u8, args: ...) !void {1116fn printError(comptime format: []const u8, args: ...) !void {
...@@ -660,17 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib...@@ -660,17 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
660 }1130 }
661}1131}
6621132
663/// This declaration has been blessed as going into the final code generation.
664pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
665 if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
666 decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
667 decl.resolution.resolve();
668 return decl.resolution.data;
669 } else {
670 return (await (async decl.resolution.get() catch unreachable)).*;
671 }
672}
673
674/// The function that actually does the generation.1133/// The function that actually does the generation.
675async fn generateDecl(comp: *Compilation, decl: *Decl) !void {1134async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
676 switch (decl.id) {1135 switch (decl.id) {
...@@ -684,68 +1143,99 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {...@@ -684,68 +1143,99 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
684}1143}
6851144
686async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {1145async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
687 const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");1146 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
6881147
689 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);1148 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
690 defer fndef_scope.base.deref(comp);1149 defer fndef_scope.base.deref(comp);
6911150
692 // TODO actually look at the return type of the AST1151 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
693 const return_type = &Type.Void.get(comp).base;
694 defer return_type.base.deref(comp);
695
696 const is_var_args = false;
697 const params = ([*]Type.Fn.Param)(undefined)[0..0];
698 const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
699 defer fn_type.base.base.deref(comp);1152 defer fn_type.base.base.deref(comp);
7001153
701 var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name);1154 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
702 errdefer symbol_name.deinit();1155 var symbol_name_consumed = false;
1156 errdefer if (!symbol_name_consumed) symbol_name.deinit();
7031157
1158 // The Decl.Fn owns the initial 1 reference count
704 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);1159 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
1160 fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
1161 symbol_name_consumed = true;
1162
1163 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1164 &fndef_scope.base,
1165 body_node,
1166 fn_type.return_type,
1167 ) catch unreachable);
1168 errdefer analyzed_code.destroy(comp.gpa());
1169
1170 // Kick off rendering to LLVM module, but it doesn't block the fn decl
1171 // analysis from being complete.
1172 try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
1173 try comp.prelink_group.call(addFnToLinkSet, comp, fn_val);
1174}
1175
1176async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
1177 fn_val.base.ref();
705 defer fn_val.base.deref(comp);1178 defer fn_val.base.deref(comp);
7061179
707 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };1180 fn_val.link_set_node.data = fn_val;
7081181
709 const unanalyzed_code = (await (async ir.gen(1182 const held = await (async comp.fn_link_set.acquire() catch unreachable);
710 comp,1183 defer held.release();
711 body_node,1184
712 &fndef_scope.base,1185 held.value.append(fn_val.link_set_node);
713 Span.token(body_node.lastToken()),1186}
714 fn_decl.base.parsed_file,1187
715 ) catch unreachable)) catch |err| switch (err) {1188fn getZigDir(allocator: *mem.Allocator) ![]u8 {
716 // This poison value should not cause the errdefers to run. It simply means1189 return os.getAppDataDir(allocator, "zig");
717 // that self.compile_errors is populated.1190}
718 // TODO https://github.com/ziglang/zig/issues/7691191
719 error.SemanticAnalysisFailed => return {},1192async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
720 else => return err,1193 const return_type_node = switch (fn_proto.return_type) {
1194 ast.Node.FnProto.ReturnType.Explicit => |n| n,
1195 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
721 };1196 };
722 defer unanalyzed_code.destroy(comp.a());1197 const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
7231198 return_type.base.deref(comp);
724 if (comp.verbose_ir) {1199
725 std.debug.warn("unanalyzed:\n");1200 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
726 unanalyzed_code.dump();1201 var params_consumed = false;
727 }1202 defer if (params_consumed) {
7281203 for (params.toSliceConst()) |param| {
729 const analyzed_code = (await (async ir.analyze(1204 param.typ.base.deref(comp);
730 comp,1205 }
731 fn_decl.base.parsed_file,1206 params.deinit();
732 unanalyzed_code,
733 null,
734 ) catch unreachable)) catch |err| switch (err) {
735 // This poison value should not cause the errdefers to run. It simply means
736 // that self.compile_errors is populated.
737 // TODO https://github.com/ziglang/zig/issues/769
738 error.SemanticAnalysisFailed => return {},
739 else => return err,
740 };1207 };
741 errdefer analyzed_code.destroy(comp.a());
7421208
743 if (comp.verbose_ir) {1209 const is_var_args = false;
744 std.debug.warn("analyzed:\n");1210 {
745 analyzed_code.dump();1211 var it = fn_proto.params.iterator(0);
1212 while (it.next()) |param_node_ptr| {
1213 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
1214 const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
1215 errdefer param_type.base.deref(comp);
1216 try params.append(Type.Fn.Param{
1217 .typ = param_type,
1218 .is_noalias = param_node.noalias_token != null,
1219 });
1220 }
746 }1221 }
1222 const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);
1223 params_consumed = true;
1224 errdefer fn_type.base.base.deref(comp);
7471225
748 // Kick off rendering to LLVM module, but it doesn't block the fn decl1226 return fn_type;
749 // analysis from being complete.1227}
750 try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);1228
1229async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1230 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
1231 defer fn_type.base.base.deref(comp);
1232
1233 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
1234 var symbol_name_consumed = false;
1235 defer if (!symbol_name_consumed) symbol_name.deinit();
1236
1237 // The Decl.Fn owns the initial 1 reference count
1238 const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
1239 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
1240 symbol_name_consumed = true;
751}1241}
src-self-hosted/decl.zig+8-6
...@@ -3,7 +3,6 @@ const Allocator = mem.Allocator;...@@ -3,7 +3,6 @@ const Allocator = mem.Allocator;
3const mem = std.mem;3const mem = std.mem;
4const ast = std.zig.ast;4const ast = std.zig.ast;
5const Visib = @import("visib.zig").Visib;5const Visib = @import("visib.zig").Visib;
6const ParsedFile = @import("parsed_file.zig").ParsedFile;
7const event = std.event;6const event = std.event;
8const Value = @import("value.zig").Value;7const Value = @import("value.zig").Value;
9const Token = std.zig.Token;8const Token = std.zig.Token;
...@@ -16,8 +15,6 @@ pub const Decl = struct {...@@ -16,8 +15,6 @@ pub const Decl = struct {
16 name: []const u8,15 name: []const u8,
17 visib: Visib,16 visib: Visib,
18 resolution: event.Future(Compilation.BuildError!void),17 resolution: event.Future(Compilation.BuildError!void),
19 resolution_in_progress: u8,
20 parsed_file: *ParsedFile,
21 parent_scope: *Scope,18 parent_scope: *Scope,
2219
23 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);20 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
...@@ -48,6 +45,10 @@ pub const Decl = struct {...@@ -48,6 +45,10 @@ pub const Decl = struct {
48 }45 }
49 }46 }
5047
48 pub fn findRootScope(base: *const Decl) *Scope.Root {
49 return base.parent_scope.findRoot();
50 }
51
51 pub const Id = enum {52 pub const Id = enum {
52 Var,53 Var,
53 Fn,54 Fn,
...@@ -61,12 +62,13 @@ pub const Decl = struct {...@@ -61,12 +62,13 @@ pub const Decl = struct {
61 pub const Fn = struct {62 pub const Fn = struct {
62 base: Decl,63 base: Decl,
63 value: Val,64 value: Val,
64 fn_proto: *const ast.Node.FnProto,65 fn_proto: *ast.Node.FnProto,
6566
66 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous67 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
67 pub const Val = union {68 pub const Val = union(enum) {
68 Unresolved: void,69 Unresolved: void,
69 Ok: *Value.Fn,70 Fn: *Value.Fn,
71 FnProto: *Value.FnProto,
70 };72 };
7173
72 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {74 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
src-self-hosted/errmsg.zig+203-66
...@@ -4,6 +4,8 @@ const os = std.os;...@@ -4,6 +4,8 @@ const os = std.os;
4const Token = std.zig.Token;4const Token = std.zig.Token;
5const ast = std.zig.ast;5const ast = std.zig.ast;
6const TokenIndex = std.zig.ast.TokenIndex;6const TokenIndex = std.zig.ast.TokenIndex;
7const Compilation = @import("compilation.zig").Compilation;
8const Scope = @import("scope.zig").Scope;
79
8pub const Color = enum {10pub const Color = enum {
9 Auto,11 Auto,
...@@ -16,85 +18,220 @@ pub const Span = struct {...@@ -16,85 +18,220 @@ pub const Span = struct {
16 last: ast.TokenIndex,18 last: ast.TokenIndex,
1719
18 pub fn token(i: TokenIndex) Span {20 pub fn token(i: TokenIndex) Span {
19 return Span {21 return Span{
20 .first = i,22 .first = i,
21 .last = i,23 .last = i,
22 };24 };
23 }25 }
26
27 pub fn node(n: *ast.Node) Span {
28 return Span{
29 .first = n.firstToken(),
30 .last = n.lastToken(),
31 };
32 }
24};33};
2534
26pub const Msg = struct {35pub const Msg = struct {
27 path: []const u8,
28 text: []u8,
29 span: Span,36 span: Span,
30 tree: *ast.Tree,37 text: []u8,
31};38 data: Data,
39
40 const Data = union(enum) {
41 PathAndTree: PathAndTree,
42 ScopeAndComp: ScopeAndComp,
43 };
44
45 const PathAndTree = struct {
46 realpath: []const u8,
47 tree: *ast.Tree,
48 allocator: *mem.Allocator,
49 };
50
51 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,
53 compilation: *Compilation,
54 };
55
56 pub fn destroy(self: *Msg) void {
57 switch (self.data) {
58 Data.PathAndTree => |path_and_tree| {
59 path_and_tree.allocator.free(self.text);
60 path_and_tree.allocator.destroy(self);
61 },
62 Data.ScopeAndComp => |scope_and_comp| {
63 scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
64 scope_and_comp.compilation.gpa().free(self.text);
65 scope_and_comp.compilation.gpa().destroy(self);
66 },
67 }
68 }
69
70 fn getAllocator(self: *const Msg) *mem.Allocator {
71 switch (self.data) {
72 Data.PathAndTree => |path_and_tree| {
73 return path_and_tree.allocator;
74 },
75 Data.ScopeAndComp => |scope_and_comp| {
76 return scope_and_comp.compilation.gpa();
77 },
78 }
79 }
80
81 pub fn getRealPath(self: *const Msg) []const u8 {
82 switch (self.data) {
83 Data.PathAndTree => |path_and_tree| {
84 return path_and_tree.realpath;
85 },
86 Data.ScopeAndComp => |scope_and_comp| {
87 return scope_and_comp.root_scope.realpath;
88 },
89 }
90 }
91
92 pub fn getTree(self: *const Msg) *ast.Tree {
93 switch (self.data) {
94 Data.PathAndTree => |path_and_tree| {
95 return path_and_tree.tree;
96 },
97 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;
99 },
100 }
101 }
102
103 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
106 const msg = try comp.gpa().create(Msg{
107 .text = text,
108 .span = span,
109 .data = Data{
110 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,
112 .compilation = comp,
113 },
114 },
115 });
116 root_scope.base.ref();
117 return msg;
118 }
119
120 pub fn createFromParseErrorAndScope(
121 comp: *Compilation,
122 root_scope: *Scope.Root,
123 parse_error: *const ast.Error,
124 ) !*Msg {
125 const loc_token = parse_error.loc();
126 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127 defer text_buf.deinit();
128
129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130 try parse_error.render(&root_scope.tree.tokens, out_stream);
131
132 const msg = try comp.gpa().create(Msg{
133 .text = undefined,
134 .span = Span{
135 .first = loc_token,
136 .last = loc_token,
137 },
138 .data = Data{
139 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,
141 .compilation = comp,
142 },
143 },
144 });
145 root_scope.base.ref();
146 msg.text = text_buf.toOwnedSlice();
147 return msg;
148 }
149
150 /// `realpath` must outlive the returned Msg
151 /// `tree` must outlive the returned Msg
152 /// Caller owns returned Msg and must free with `allocator`
153 /// allocator will additionally be used for printing messages later.
154 pub fn createFromParseError(
155 allocator: *mem.Allocator,
156 parse_error: *const ast.Error,
157 tree: *ast.Tree,
158 realpath: []const u8,
159 ) !*Msg {
160 const loc_token = parse_error.loc();
161 var text_buf = try std.Buffer.initSize(allocator, 0);
162 defer text_buf.deinit();
163
164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165 try parse_error.render(&tree.tokens, out_stream);
166
167 const msg = try allocator.create(Msg{
168 .text = undefined,
169 .data = Data{
170 .PathAndTree = PathAndTree{
171 .allocator = allocator,
172 .realpath = realpath,
173 .tree = tree,
174 },
175 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180 });
181 msg.text = text_buf.toOwnedSlice();
182 errdefer allocator.destroy(msg);
183
184 return msg;
185 }
186
187 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
188 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190 const tree = msg.getTree();
191
192 const cwd = try os.getCwd(allocator);
193 defer allocator.free(cwd);
194
195 const relpath = try os.path.relative(allocator, cwd, realpath);
196 defer allocator.free(relpath);
197
198 const path = if (relpath.len < realpath.len) relpath else realpath;
199
200 const first_token = tree.tokens.at(msg.span.first);
201 const last_token = tree.tokens.at(msg.span.last);
202 const start_loc = tree.tokenLocationPtr(0, first_token);
203 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204 if (!color_on) {
205 try stream.print(
206 "{}:{}:{}: error: {}\n",
207 path,
208 start_loc.line + 1,
209 start_loc.column + 1,
210 msg.text,
211 );
212 return;
213 }
32214
33/// `path` must outlive the returned Msg
34/// `tree` must outlive the returned Msg
35/// Caller owns returned Msg and must free with `allocator`
36pub fn createFromParseError(
37 allocator: *mem.Allocator,
38 parse_error: *const ast.Error,
39 tree: *ast.Tree,
40 path: []const u8,
41) !*Msg {
42 const loc_token = parse_error.loc();
43 var text_buf = try std.Buffer.initSize(allocator, 0);
44 defer text_buf.deinit();
45
46 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
47 try parse_error.render(&tree.tokens, out_stream);
48
49 const msg = try allocator.create(Msg{
50 .tree = tree,
51 .path = path,
52 .text = text_buf.toOwnedSlice(),
53 .span = Span{
54 .first = loc_token,
55 .last = loc_token,
56 },
57 });
58 errdefer allocator.destroy(msg);
59
60 return msg;
61}
62
63pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
64 const first_token = msg.tree.tokens.at(msg.span.first);
65 const last_token = msg.tree.tokens.at(msg.span.last);
66 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
67 const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
68 if (!color_on) {
69 try stream.print(215 try stream.print(
70 "{}:{}:{}: error: {}\n",216 "{}:{}:{}: error: {}\n{}\n",
71 msg.path,217 path,
72 start_loc.line + 1,218 start_loc.line + 1,
73 start_loc.column + 1,219 start_loc.column + 1,
74 msg.text,220 msg.text,
221 tree.source[start_loc.line_start..start_loc.line_end],
75 );222 );
76 return;223 try stream.writeByteNTimes(' ', start_loc.column);
224 try stream.writeByteNTimes('~', last_token.end - first_token.start);
225 try stream.write("\n");
77 }226 }
78227
79 try stream.print(228 pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
80 "{}:{}:{}: error: {}\n{}\n",229 const color_on = switch (color) {
81 msg.path,230 Color.Auto => file.isTty(),
82 start_loc.line + 1,231 Color.On => true,
83 start_loc.column + 1,232 Color.Off => false,
84 msg.text,233 };
85 msg.tree.source[start_loc.line_start..start_loc.line_end],234 var stream = &std.io.FileOutStream.init(file).stream;
86 );235 return msg.printToStream(stream, color_on);
87 try stream.writeByteNTimes(' ', start_loc.column);236 }
88 try stream.writeByteNTimes('~', last_token.end - first_token.start);237};
89 try stream.write("\n");
90}
91
92pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void {
93 const color_on = switch (color) {
94 Color.Auto => file.isTty(),
95 Color.On => true,
96 Color.Off => false,
97 };
98 var stream = &std.io.FileOutStream.init(file).stream;
99 return printToStream(stream, msg, color_on);
100}
src-self-hosted/ir.zig+1566-199
...@@ -8,10 +8,10 @@ const Value = @import("value.zig").Value;...@@ -8,10 +8,10 @@ const Value = @import("value.zig").Value;
8const Type = Value.Type;8const Type = Value.Type;
9const assert = std.debug.assert;9const assert = std.debug.assert;
10const Token = std.zig.Token;10const Token = std.zig.Token;
11const ParsedFile = @import("parsed_file.zig").ParsedFile;
12const Span = @import("errmsg.zig").Span;11const Span = @import("errmsg.zig").Span;
13const llvm = @import("llvm.zig");12const llvm = @import("llvm.zig");
14const ObjectFile = @import("codegen.zig").ObjectFile;13const ObjectFile = @import("codegen.zig").ObjectFile;
14const Decl = @import("decl.zig").Decl;
1515
16pub const LVal = enum {16pub const LVal = enum {
17 None,17 None,
...@@ -31,10 +31,10 @@ pub const IrVal = union(enum) {...@@ -31,10 +31,10 @@ pub const IrVal = union(enum) {
3131
32 pub fn dump(self: IrVal) void {32 pub fn dump(self: IrVal) void {
33 switch (self) {33 switch (self) {
34 IrVal.Unknown => typeof.dump(),34 IrVal.Unknown => std.debug.warn("Unknown"),
35 IrVal.KnownType => |typeof| {35 IrVal.KnownType => |typ| {
36 std.debug.warn("KnownType(");36 std.debug.warn("KnownType(");
37 typeof.dump();37 typ.dump();
38 std.debug.warn(")");38 std.debug.warn(")");
39 },39 },
40 IrVal.KnownValue => |value| {40 IrVal.KnownValue => |value| {
...@@ -46,27 +46,28 @@ pub const IrVal = union(enum) {...@@ -46,27 +46,28 @@ pub const IrVal = union(enum) {
46 }46 }
47};47};
4848
49pub const Instruction = struct {49pub const Inst = struct {
50 id: Id,50 id: Id,
51 scope: *Scope,51 scope: *Scope,
52 debug_id: usize,52 debug_id: usize,
53 val: IrVal,53 val: IrVal,
54 ref_count: usize,54 ref_count: usize,
55 span: Span,55 span: Span,
56 owner_bb: *BasicBlock,
5657
57 /// true if this instruction was generated by zig and not from user code58 /// true if this instruction was generated by zig and not from user code
58 is_generated: bool,59 is_generated: bool,
5960
60 /// the instruction that is derived from this one in analysis61 /// the instruction that is derived from this one in analysis
61 child: ?*Instruction,62 child: ?*Inst,
6263
63 /// the instruction that this one derives from in analysis64 /// the instruction that this one derives from in analysis
64 parent: ?*Instruction,65 parent: ?*Inst,
6566
66 /// populated durign codegen67 /// populated durign codegen
67 llvm_value: ?llvm.ValueRef,68 llvm_value: ?llvm.ValueRef,
6869
69 pub fn cast(base: *Instruction, comptime T: type) ?*T {70 pub fn cast(base: *Inst, comptime T: type) ?*T {
70 if (base.id == comptime typeToId(T)) {71 if (base.id == comptime typeToId(T)) {
71 return @fieldParentPtr(T, "base", base);72 return @fieldParentPtr(T, "base", base);
72 }73 }
...@@ -76,18 +77,18 @@ pub const Instruction = struct {...@@ -76,18 +77,18 @@ pub const Instruction = struct {
76 pub fn typeToId(comptime T: type) Id {77 pub fn typeToId(comptime T: type) Id {
77 comptime var i = 0;78 comptime var i = 0;
78 inline while (i < @memberCount(Id)) : (i += 1) {79 inline while (i < @memberCount(Id)) : (i += 1) {
79 if (T == @field(Instruction, @memberName(Id, i))) {80 if (T == @field(Inst, @memberName(Id, i))) {
80 return @field(Id, @memberName(Id, i));81 return @field(Id, @memberName(Id, i));
81 }82 }
82 }83 }
83 unreachable;84 unreachable;
84 }85 }
8586
86 pub fn dump(base: *const Instruction) void {87 pub fn dump(base: *const Inst) void {
87 comptime var i = 0;88 comptime var i = 0;
88 inline while (i < @memberCount(Id)) : (i += 1) {89 inline while (i < @memberCount(Id)) : (i += 1) {
89 if (base.id == @field(Id, @memberName(Id, i))) {90 if (base.id == @field(Id, @memberName(Id, i))) {
90 const T = @field(Instruction, @memberName(Id, i));91 const T = @field(Inst, @memberName(Id, i));
91 std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id));92 std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id));
92 @fieldParentPtr(T, "base", base).dump();93 @fieldParentPtr(T, "base", base).dump();
93 std.debug.warn(")");94 std.debug.warn(")");
...@@ -97,32 +98,40 @@ pub const Instruction = struct {...@@ -97,32 +98,40 @@ pub const Instruction = struct {
97 unreachable;98 unreachable;
98 }99 }
99100
100 pub fn hasSideEffects(base: *const Instruction) bool {101 pub fn hasSideEffects(base: *const Inst) bool {
101 comptime var i = 0;102 comptime var i = 0;
102 inline while (i < @memberCount(Id)) : (i += 1) {103 inline while (i < @memberCount(Id)) : (i += 1) {
103 if (base.id == @field(Id, @memberName(Id, i))) {104 if (base.id == @field(Id, @memberName(Id, i))) {
104 const T = @field(Instruction, @memberName(Id, i));105 const T = @field(Inst, @memberName(Id, i));
105 return @fieldParentPtr(T, "base", base).hasSideEffects();106 return @fieldParentPtr(T, "base", base).hasSideEffects();
106 }107 }
107 }108 }
108 unreachable;109 unreachable;
109 }110 }
110111
111 pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction {112 pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
112 comptime var i = 0;113 switch (base.id) {
113 inline while (i < @memberCount(Id)) : (i += 1) {114 Id.Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
114 if (base.id == @field(Id, @memberName(Id, i))) {115 Id.Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
115 const T = @field(Instruction, @memberName(Id, i));116 Id.Call => return @fieldParentPtr(Call, "base", base).analyze(ira),
116 return @fieldParentPtr(T, "base", base).analyze(ira);117 Id.DeclRef => return await (async @fieldParentPtr(DeclRef, "base", base).analyze(ira) catch unreachable),
117 }118 Id.Ref => return await (async @fieldParentPtr(Ref, "base", base).analyze(ira) catch unreachable),
119 Id.DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira),
120 Id.CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira),
121 Id.Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira),
122 Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
123 Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
124 Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable),
118 }125 }
119 unreachable;
120 }126 }
121127
122 pub fn render(base: *Instruction, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) {128 pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) {
123 switch (base.id) {129 switch (base.id) {
124 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),130 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
125 Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),131 Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
132 Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
133 Id.DeclRef => unreachable,
134 Id.PtrType => unreachable,
126 Id.Ref => @panic("TODO"),135 Id.Ref => @panic("TODO"),
127 Id.DeclVar => @panic("TODO"),136 Id.DeclVar => @panic("TODO"),
128 Id.CheckVoidStmt => @panic("TODO"),137 Id.CheckVoidStmt => @panic("TODO"),
...@@ -132,7 +141,22 @@ pub const Instruction = struct {...@@ -132,7 +141,22 @@ pub const Instruction = struct {
132 }141 }
133 }142 }
134143
135 fn getAsParam(param: *Instruction) !*Instruction {144 fn ref(base: *Inst, builder: *Builder) void {
145 base.ref_count += 1;
146 if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) {
147 base.owner_bb.ref(builder);
148 }
149 }
150
151 fn copyVal(base: *Inst, comp: *Compilation) !*Value {
152 if (base.parent.?.ref_count == 0) {
153 return base.val.KnownValue.derefAndCopy(comp);
154 }
155 return base.val.KnownValue.copy(comp);
156 }
157
158 fn getAsParam(param: *Inst) !*Inst {
159 param.ref_count -= 1;
136 const child = param.child orelse return error.SemanticAnalysisFailed;160 const child = param.child orelse return error.SemanticAnalysisFailed;
137 switch (child.val) {161 switch (child.val) {
138 IrVal.Unknown => return error.SemanticAnalysisFailed,162 IrVal.Unknown => return error.SemanticAnalysisFailed,
...@@ -140,28 +164,72 @@ pub const Instruction = struct {...@@ -140,28 +164,72 @@ pub const Instruction = struct {
140 }164 }
141 }165 }
142166
167 fn getConstVal(self: *Inst, ira: *Analyze) !*Value {
168 if (self.isCompTime()) {
169 return self.val.KnownValue;
170 } else {
171 try ira.addCompileError(self.span, "unable to evaluate constant expression");
172 return error.SemanticAnalysisFailed;
173 }
174 }
175
176 fn getAsConstType(param: *Inst, ira: *Analyze) !*Type {
177 const meta_type = Type.MetaType.get(ira.irb.comp);
178 meta_type.base.base.deref(ira.irb.comp);
179
180 const inst = try param.getAsParam();
181 const casted = try ira.implicitCast(inst, &meta_type.base);
182 const val = try casted.getConstVal(ira);
183 return val.cast(Value.Type).?;
184 }
185
186 fn getAsConstAlign(param: *Inst, ira: *Analyze) !u32 {
187 return error.Unimplemented;
188 //const align_type = Type.Int.get_align(ira.irb.comp);
189 //align_type.base.base.deref(ira.irb.comp);
190
191 //const inst = try param.getAsParam();
192 //const casted = try ira.implicitCast(inst, align_type);
193 //const val = try casted.getConstVal(ira);
194
195 //uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint);
196 //if (align_bytes == 0) {
197 // ir_add_error(ira, value, buf_sprintf("alignment must be >= 1"));
198 // return false;
199 //}
200
201 //if (!is_power_of_2(align_bytes)) {
202 // ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
203 // return false;
204 //}
205 }
206
143 /// asserts that the type is known207 /// asserts that the type is known
144 fn getKnownType(self: *Instruction) *Type {208 fn getKnownType(self: *Inst) *Type {
145 switch (self.val) {209 switch (self.val) {
146 IrVal.KnownType => |typeof| return typeof,210 IrVal.KnownType => |typ| return typ,
147 IrVal.KnownValue => |value| return value.typeof,211 IrVal.KnownValue => |value| return value.typ,
148 IrVal.Unknown => unreachable,212 IrVal.Unknown => unreachable,
149 }213 }
150 }214 }
151215
152 pub fn setGenerated(base: *Instruction) void {216 pub fn setGenerated(base: *Inst) void {
153 base.is_generated = true;217 base.is_generated = true;
154 }218 }
155219
156 pub fn isNoReturn(base: *const Instruction) bool {220 pub fn isNoReturn(base: *const Inst) bool {
157 switch (base.val) {221 switch (base.val) {
158 IrVal.Unknown => return false,222 IrVal.Unknown => return false,
159 IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn,223 IrVal.KnownValue => |x| return x.typ.id == Type.Id.NoReturn,
160 IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn,224 IrVal.KnownType => |typ| return typ.id == Type.Id.NoReturn,
161 }225 }
162 }226 }
163227
164 pub fn linkToParent(self: *Instruction, parent: *Instruction) void {228 pub fn isCompTime(base: *const Inst) bool {
229 return base.val == IrVal.KnownValue;
230 }
231
232 pub fn linkToParent(self: *Inst, parent: *Inst) void {
165 assert(self.parent == null);233 assert(self.parent == null);
166 assert(parent.child == null);234 assert(parent.child == null);
167 self.parent = parent;235 self.parent = parent;
...@@ -177,10 +245,89 @@ pub const Instruction = struct {...@@ -177,10 +245,89 @@ pub const Instruction = struct {
177 Phi,245 Phi,
178 Br,246 Br,
179 AddImplicitReturnType,247 AddImplicitReturnType,
248 Call,
249 DeclRef,
250 PtrType,
251 };
252
253 pub const Call = struct {
254 base: Inst,
255 params: Params,
256
257 const Params = struct {
258 fn_ref: *Inst,
259 args: []*Inst,
260 };
261
262 const ir_val_init = IrVal.Init.Unknown;
263
264 pub fn dump(self: *const Call) void {
265 std.debug.warn("#{}(", self.params.fn_ref.debug_id);
266 for (self.params.args) |arg| {
267 std.debug.warn("#{},", arg.debug_id);
268 }
269 std.debug.warn(")");
270 }
271
272 pub fn hasSideEffects(self: *const Call) bool {
273 return true;
274 }
275
276 pub fn analyze(self: *const Call, ira: *Analyze) !*Inst {
277 const fn_ref = try self.params.fn_ref.getAsParam();
278 const fn_ref_type = fn_ref.getKnownType();
279 const fn_type = fn_ref_type.cast(Type.Fn) orelse {
280 try ira.addCompileError(fn_ref.span, "type '{}' not a function", fn_ref_type.name);
281 return error.SemanticAnalysisFailed;
282 };
283
284 if (fn_type.params.len != self.params.args.len) {
285 try ira.addCompileError(
286 self.base.span,
287 "expected {} arguments, found {}",
288 fn_type.params.len,
289 self.params.args.len,
290 );
291 return error.SemanticAnalysisFailed;
292 }
293
294 const args = try ira.irb.arena().alloc(*Inst, self.params.args.len);
295 for (self.params.args) |arg, i| {
296 args[i] = try arg.getAsParam();
297 }
298 const new_inst = try ira.irb.build(Call, self.base.scope, self.base.span, Params{
299 .fn_ref = fn_ref,
300 .args = args,
301 });
302 new_inst.val = IrVal{ .KnownType = fn_type.return_type };
303 return new_inst;
304 }
305
306 pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
307 const fn_ref = self.params.fn_ref.llvm_value.?;
308
309 const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len);
310 for (self.params.args) |arg, i| {
311 args[i] = arg.llvm_value.?;
312 }
313
314 const llvm_cc = llvm.CCallConv;
315 const fn_inline = llvm.FnInline.Auto;
316
317 return llvm.BuildCall(
318 ofile.builder,
319 fn_ref,
320 args.ptr,
321 @intCast(c_uint, args.len),
322 llvm_cc,
323 fn_inline,
324 c"",
325 ) orelse error.OutOfMemory;
326 }
180 };327 };
181328
182 pub const Const = struct {329 pub const Const = struct {
183 base: Instruction,330 base: Inst,
184 params: Params,331 params: Params,
185332
186 const Params = struct {};333 const Params = struct {};
...@@ -197,7 +344,7 @@ pub const Instruction = struct {...@@ -197,7 +344,7 @@ pub const Instruction = struct {
197 return false;344 return false;
198 }345 }
199346
200 pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction {347 pub fn analyze(self: *const Const, ira: *Analyze) !*Inst {
201 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});348 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
202 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };349 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
203 return new_inst;350 return new_inst;
...@@ -209,11 +356,11 @@ pub const Instruction = struct {...@@ -209,11 +356,11 @@ pub const Instruction = struct {
209 };356 };
210357
211 pub const Return = struct {358 pub const Return = struct {
212 base: Instruction,359 base: Inst,
213 params: Params,360 params: Params,
214361
215 const Params = struct {362 const Params = struct {
216 return_value: *Instruction,363 return_value: *Inst,
217 };364 };
218365
219 const ir_val_init = IrVal.Init.NoReturn;366 const ir_val_init = IrVal.Init.NoReturn;
...@@ -226,7 +373,7 @@ pub const Instruction = struct {...@@ -226,7 +373,7 @@ pub const Instruction = struct {
226 return true;373 return true;
227 }374 }
228375
229 pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction {376 pub fn analyze(self: *const Return, ira: *Analyze) !*Inst {
230 const value = try self.params.return_value.getAsParam();377 const value = try self.params.return_value.getAsParam();
231 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);378 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
232379
...@@ -235,25 +382,25 @@ pub const Instruction = struct {...@@ -235,25 +382,25 @@ pub const Instruction = struct {
235 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });382 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
236 }383 }
237384
238 pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef {385 pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
239 const value = self.params.return_value.llvm_value;386 const value = self.params.return_value.llvm_value;
240 const return_type = self.params.return_value.getKnownType();387 const return_type = self.params.return_value.getKnownType();
241388
242 if (return_type.handleIsPtr()) {389 if (return_type.handleIsPtr()) {
243 @panic("TODO");390 @panic("TODO");
244 } else {391 } else {
245 _ = llvm.BuildRet(ofile.builder, value);392 _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory;
246 }393 }
247 return null;394 return null;
248 }395 }
249 };396 };
250397
251 pub const Ref = struct {398 pub const Ref = struct {
252 base: Instruction,399 base: Inst,
253 params: Params,400 params: Params,
254401
255 const Params = struct {402 const Params = struct {
256 target: *Instruction,403 target: *Inst,
257 mut: Type.Pointer.Mut,404 mut: Type.Pointer.Mut,
258 volatility: Type.Pointer.Vol,405 volatility: Type.Pointer.Vol,
259 };406 };
...@@ -266,7 +413,7 @@ pub const Instruction = struct {...@@ -266,7 +413,7 @@ pub const Instruction = struct {
266 return false;413 return false;
267 }414 }
268415
269 pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction {416 pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
270 const target = try self.params.target.getAsParam();417 const target = try self.params.target.getAsParam();
271418
272 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {419 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
...@@ -275,7 +422,6 @@ pub const Instruction = struct {...@@ -275,7 +422,6 @@ pub const Instruction = struct {
275 Value.Ptr.Mut.CompTimeConst,422 Value.Ptr.Mut.CompTimeConst,
276 self.params.mut,423 self.params.mut,
277 self.params.volatility,424 self.params.volatility,
278 val.typeof.getAbiAlignment(ira.irb.comp),
279 );425 );
280 }426 }
281427
...@@ -285,14 +431,13 @@ pub const Instruction = struct {...@@ -285,14 +431,13 @@ pub const Instruction = struct {
285 .volatility = self.params.volatility,431 .volatility = self.params.volatility,
286 });432 });
287 const elem_type = target.getKnownType();433 const elem_type = target.getKnownType();
288 const ptr_type = Type.Pointer.get(434 const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
289 ira.irb.comp,435 .child_type = elem_type,
290 elem_type,436 .mut = self.params.mut,
291 self.params.mut,437 .vol = self.params.volatility,
292 self.params.volatility,438 .size = Type.Pointer.Size.One,
293 Type.Pointer.Size.One,439 .alignment = Type.Pointer.Align.Abi,
294 elem_type.getAbiAlignment(ira.irb.comp),440 }) catch unreachable);
295 );
296 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this441 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this
297 // could be a ref of a global, for example442 // could be a ref of a global, for example
298 new_inst.val = IrVal{ .KnownType = &ptr_type.base };443 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
...@@ -301,8 +446,99 @@ pub const Instruction = struct {...@@ -301,8 +446,99 @@ pub const Instruction = struct {
301 }446 }
302 };447 };
303448
449 pub const DeclRef = struct {
450 base: Inst,
451 params: Params,
452
453 const Params = struct {
454 decl: *Decl,
455 lval: LVal,
456 };
457
458 const ir_val_init = IrVal.Init.Unknown;
459
460 pub fn dump(inst: *const DeclRef) void {}
461
462 pub fn hasSideEffects(inst: *const DeclRef) bool {
463 return false;
464 }
465
466 pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
467 (await (async ira.irb.comp.resolveDecl(self.params.decl) catch unreachable)) catch |err| switch (err) {
468 error.OutOfMemory => return error.OutOfMemory,
469 else => return error.SemanticAnalysisFailed,
470 };
471 switch (self.params.decl.id) {
472 Decl.Id.CompTime => unreachable,
473 Decl.Id.Var => return error.Unimplemented,
474 Decl.Id.Fn => {
475 const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl);
476 const decl_val = switch (fn_decl.value) {
477 Decl.Fn.Val.Unresolved => unreachable,
478 Decl.Fn.Val.Fn => |fn_val| &fn_val.base,
479 Decl.Fn.Val.FnProto => |fn_proto| &fn_proto.base,
480 };
481 switch (self.params.lval) {
482 LVal.None => {
483 return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val);
484 },
485 LVal.Ptr => return error.Unimplemented,
486 }
487 },
488 }
489 }
490 };
491
492 pub const PtrType = struct {
493 base: Inst,
494 params: Params,
495
496 const Params = struct {
497 child_type: *Inst,
498 mut: Type.Pointer.Mut,
499 vol: Type.Pointer.Vol,
500 size: Type.Pointer.Size,
501 alignment: ?*Inst,
502 };
503
504 const ir_val_init = IrVal.Init.Unknown;
505
506 pub fn dump(inst: *const PtrType) void {}
507
508 pub fn hasSideEffects(inst: *const PtrType) bool {
509 return false;
510 }
511
512 pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
513 const child_type = try self.params.child_type.getAsConstType(ira);
514 // if (child_type->id == TypeTableEntryIdUnreachable) {
515 // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
516 // return ira->codegen->builtin_types.entry_invalid;
517 // } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) {
518 // ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
519 // return ira->codegen->builtin_types.entry_invalid;
520 // }
521 const alignment = if (self.params.alignment) |align_inst| blk: {
522 const amt = try align_inst.getAsConstAlign(ira);
523 break :blk Type.Pointer.Align{ .Override = amt };
524 } else blk: {
525 break :blk Type.Pointer.Align{ .Abi = {} };
526 };
527 const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
528 .child_type = child_type,
529 .mut = self.params.mut,
530 .vol = self.params.vol,
531 .size = self.params.size,
532 .alignment = alignment,
533 }) catch unreachable);
534 ptr_type.base.base.deref(ira.irb.comp);
535
536 return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base);
537 }
538 };
539
304 pub const DeclVar = struct {540 pub const DeclVar = struct {
305 base: Instruction,541 base: Inst,
306 params: Params,542 params: Params,
307543
308 const Params = struct {544 const Params = struct {
...@@ -317,39 +553,46 @@ pub const Instruction = struct {...@@ -317,39 +553,46 @@ pub const Instruction = struct {
317 return true;553 return true;
318 }554 }
319555
320 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction {556 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Inst {
321 return error.Unimplemented; // TODO557 return error.Unimplemented; // TODO
322 }558 }
323 };559 };
324560
325 pub const CheckVoidStmt = struct {561 pub const CheckVoidStmt = struct {
326 base: Instruction,562 base: Inst,
327 params: Params,563 params: Params,
328564
329 const Params = struct {565 const Params = struct {
330 target: *Instruction,566 target: *Inst,
331 };567 };
332568
333 const ir_val_init = IrVal.Init.Unknown;569 const ir_val_init = IrVal.Init.Unknown;
334570
335 pub fn dump(inst: *const CheckVoidStmt) void {}571 pub fn dump(self: *const CheckVoidStmt) void {
572 std.debug.warn("#{}", self.params.target.debug_id);
573 }
336574
337 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {575 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
338 return true;576 return true;
339 }577 }
340578
341 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction {579 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst {
342 return error.Unimplemented; // TODO580 const target = try self.params.target.getAsParam();
581 if (target.getKnownType().id != Type.Id.Void) {
582 try ira.addCompileError(self.base.span, "expression value is ignored");
583 return error.SemanticAnalysisFailed;
584 }
585 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
343 }586 }
344 };587 };
345588
346 pub const Phi = struct {589 pub const Phi = struct {
347 base: Instruction,590 base: Inst,
348 params: Params,591 params: Params,
349592
350 const Params = struct {593 const Params = struct {
351 incoming_blocks: []*BasicBlock,594 incoming_blocks: []*BasicBlock,
352 incoming_values: []*Instruction,595 incoming_values: []*Inst,
353 };596 };
354597
355 const ir_val_init = IrVal.Init.Unknown;598 const ir_val_init = IrVal.Init.Unknown;
...@@ -360,18 +603,18 @@ pub const Instruction = struct {...@@ -360,18 +603,18 @@ pub const Instruction = struct {
360 return false;603 return false;
361 }604 }
362605
363 pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction {606 pub fn analyze(self: *const Phi, ira: *Analyze) !*Inst {
364 return error.Unimplemented; // TODO607 return error.Unimplemented; // TODO
365 }608 }
366 };609 };
367610
368 pub const Br = struct {611 pub const Br = struct {
369 base: Instruction,612 base: Inst,
370 params: Params,613 params: Params,
371614
372 const Params = struct {615 const Params = struct {
373 dest_block: *BasicBlock,616 dest_block: *BasicBlock,
374 is_comptime: *Instruction,617 is_comptime: *Inst,
375 };618 };
376619
377 const ir_val_init = IrVal.Init.NoReturn;620 const ir_val_init = IrVal.Init.NoReturn;
...@@ -382,17 +625,41 @@ pub const Instruction = struct {...@@ -382,17 +625,41 @@ pub const Instruction = struct {
382 return true;625 return true;
383 }626 }
384627
385 pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction {628 pub fn analyze(self: *const Br, ira: *Analyze) !*Inst {
629 return error.Unimplemented; // TODO
630 }
631 };
632
633 pub const CondBr = struct {
634 base: Inst,
635 params: Params,
636
637 const Params = struct {
638 condition: *Inst,
639 then_block: *BasicBlock,
640 else_block: *BasicBlock,
641 is_comptime: *Inst,
642 };
643
644 const ir_val_init = IrVal.Init.NoReturn;
645
646 pub fn dump(inst: *const CondBr) void {}
647
648 pub fn hasSideEffects(inst: *const CondBr) bool {
649 return true;
650 }
651
652 pub fn analyze(self: *const CondBr, ira: *Analyze) !*Inst {
386 return error.Unimplemented; // TODO653 return error.Unimplemented; // TODO
387 }654 }
388 };655 };
389656
390 pub const AddImplicitReturnType = struct {657 pub const AddImplicitReturnType = struct {
391 base: Instruction,658 base: Inst,
392 params: Params,659 params: Params,
393660
394 pub const Params = struct {661 pub const Params = struct {
395 target: *Instruction,662 target: *Inst,
396 };663 };
397664
398 const ir_val_init = IrVal.Init.Unknown;665 const ir_val_init = IrVal.Init.Unknown;
...@@ -405,12 +672,117 @@ pub const Instruction = struct {...@@ -405,12 +672,117 @@ pub const Instruction = struct {
405 return true;672 return true;
406 }673 }
407674
408 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction {675 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Inst {
409 const target = try self.params.target.getAsParam();676 const target = try self.params.target.getAsParam();
410 try ira.src_implicit_return_type_list.append(target);677 try ira.src_implicit_return_type_list.append(target);
411 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);678 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
412 }679 }
413 };680 };
681
682 pub const TestErr = struct {
683 base: Inst,
684 params: Params,
685
686 pub const Params = struct {
687 target: *Inst,
688 };
689
690 const ir_val_init = IrVal.Init.Unknown;
691
692 pub fn dump(inst: *const TestErr) void {
693 std.debug.warn("#{}", inst.params.target.debug_id);
694 }
695
696 pub fn hasSideEffects(inst: *const TestErr) bool {
697 return false;
698 }
699
700 pub fn analyze(self: *const TestErr, ira: *Analyze) !*Inst {
701 const target = try self.params.target.getAsParam();
702 const target_type = target.getKnownType();
703 switch (target_type.id) {
704 Type.Id.ErrorUnion => {
705 return error.Unimplemented;
706 // if (instr_is_comptime(value)) {
707 // ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
708 // if (!err_union_val)
709 // return ira->codegen->builtin_types.entry_invalid;
710
711 // if (err_union_val->special != ConstValSpecialRuntime) {
712 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
713 // out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
714 // return ira->codegen->builtin_types.entry_bool;
715 // }
716 // }
717
718 // TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
719 // if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) {
720 // return ira->codegen->builtin_types.entry_invalid;
721 // }
722 // if (!type_is_global_error_set(err_set_type) &&
723 // err_set_type->data.error_set.err_count == 0)
724 // {
725 // assert(err_set_type->data.error_set.infer_fn == nullptr);
726 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
727 // out_val->data.x_bool = false;
728 // return ira->codegen->builtin_types.entry_bool;
729 // }
730
731 // ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
732 // return ira->codegen->builtin_types.entry_bool;
733 },
734 Type.Id.ErrorSet => {
735 return ira.irb.buildConstBool(self.base.scope, self.base.span, true);
736 },
737 else => {
738 return ira.irb.buildConstBool(self.base.scope, self.base.span, false);
739 },
740 }
741 }
742 };
743
744 pub const TestCompTime = struct {
745 base: Inst,
746 params: Params,
747
748 pub const Params = struct {
749 target: *Inst,
750 };
751
752 const ir_val_init = IrVal.Init.Unknown;
753
754 pub fn dump(inst: *const TestCompTime) void {
755 std.debug.warn("#{}", inst.params.target.debug_id);
756 }
757
758 pub fn hasSideEffects(inst: *const TestCompTime) bool {
759 return false;
760 }
761
762 pub fn analyze(self: *const TestCompTime, ira: *Analyze) !*Inst {
763 const target = try self.params.target.getAsParam();
764 return ira.irb.buildConstBool(self.base.scope, self.base.span, target.isCompTime());
765 }
766 };
767
768 pub const SaveErrRetAddr = struct {
769 base: Inst,
770 params: Params,
771
772 const Params = struct {};
773
774 const ir_val_init = IrVal.Init.Unknown;
775
776 pub fn dump(inst: *const SaveErrRetAddr) void {}
777
778 pub fn hasSideEffects(inst: *const SaveErrRetAddr) bool {
779 return true;
780 }
781
782 pub fn analyze(self: *const SaveErrRetAddr, ira: *Analyze) !*Inst {
783 return ira.irb.build(Inst.SaveErrRetAddr, self.base.scope, self.base.span, Params{});
784 }
785 };
414};786};
415787
416pub const Variable = struct {788pub const Variable = struct {
...@@ -422,8 +794,8 @@ pub const BasicBlock = struct {...@@ -422,8 +794,8 @@ pub const BasicBlock = struct {
422 name_hint: [*]const u8, // must be a C string literal794 name_hint: [*]const u8, // must be a C string literal
423 debug_id: usize,795 debug_id: usize,
424 scope: *Scope,796 scope: *Scope,
425 instruction_list: std.ArrayList(*Instruction),797 instruction_list: std.ArrayList(*Inst),
426 ref_instruction: ?*Instruction,798 ref_instruction: ?*Inst,
427799
428 /// for codegen800 /// for codegen
429 llvm_block: llvm.BasicBlockRef,801 llvm_block: llvm.BasicBlockRef,
...@@ -435,7 +807,7 @@ pub const BasicBlock = struct {...@@ -435,7 +807,7 @@ pub const BasicBlock = struct {
435 /// the basic block that this one derives from in analysis807 /// the basic block that this one derives from in analysis
436 parent: ?*BasicBlock,808 parent: ?*BasicBlock,
437809
438 pub fn ref(self: *BasicBlock) void {810 pub fn ref(self: *BasicBlock, builder: *Builder) void {
439 self.ref_count += 1;811 self.ref_count += 1;
440 }812 }
441813
...@@ -453,7 +825,7 @@ pub const Code = struct {...@@ -453,7 +825,7 @@ pub const Code = struct {
453 arena: std.heap.ArenaAllocator,825 arena: std.heap.ArenaAllocator,
454 return_type: ?*Type,826 return_type: ?*Type,
455827
456 /// allocator is comp.a()828 /// allocator is comp.gpa()
457 pub fn destroy(self: *Code, allocator: *Allocator) void {829 pub fn destroy(self: *Code, allocator: *Allocator) void {
458 self.arena.deinit();830 self.arena.deinit();
459 allocator.destroy(self);831 allocator.destroy(self);
...@@ -470,6 +842,33 @@ pub const Code = struct {...@@ -470,6 +842,33 @@ pub const Code = struct {
470 }842 }
471 }843 }
472 }844 }
845
846 /// returns a ref-incremented value, or adds a compile error
847 pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value {
848 const bb = self.basic_block_list.at(0);
849 for (bb.instruction_list.toSliceConst()) |inst| {
850 if (inst.cast(Inst.Return)) |ret_inst| {
851 const ret_value = ret_inst.params.return_value;
852 if (ret_value.isCompTime()) {
853 return ret_value.val.KnownValue.getRef();
854 }
855 try comp.addCompileError(
856 ret_value.scope.findRoot(),
857 ret_value.span,
858 "unable to evaluate constant expression",
859 );
860 return error.SemanticAnalysisFailed;
861 } else if (inst.hasSideEffects()) {
862 try comp.addCompileError(
863 inst.scope.findRoot(),
864 inst.span,
865 "unable to evaluate constant expression",
866 );
867 return error.SemanticAnalysisFailed;
868 }
869 }
870 unreachable;
871 }
473};872};
474873
475pub const Builder = struct {874pub const Builder = struct {
...@@ -477,32 +876,36 @@ pub const Builder = struct {...@@ -477,32 +876,36 @@ pub const Builder = struct {
477 code: *Code,876 code: *Code,
478 current_basic_block: *BasicBlock,877 current_basic_block: *BasicBlock,
479 next_debug_id: usize,878 next_debug_id: usize,
480 parsed_file: *ParsedFile,879 root_scope: *Scope.Root,
481 is_comptime: bool,880 is_comptime: bool,
881 is_async: bool,
882 begin_scope: ?*Scope,
482883
483 pub const Error = Analyze.Error;884 pub const Error = Analyze.Error;
484885
485 pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder {886 pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder {
486 const code = try comp.a().create(Code{887 const code = try comp.gpa().create(Code{
487 .basic_block_list = undefined,888 .basic_block_list = undefined,
488 .arena = std.heap.ArenaAllocator.init(comp.a()),889 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
489 .return_type = null,890 .return_type = null,
490 });891 });
491 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);892 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
492 errdefer code.destroy(comp.a());893 errdefer code.destroy(comp.gpa());
493894
494 return Builder{895 return Builder{
495 .comp = comp,896 .comp = comp,
496 .parsed_file = parsed_file,897 .root_scope = root_scope,
497 .current_basic_block = undefined,898 .current_basic_block = undefined,
498 .code = code,899 .code = code,
499 .next_debug_id = 0,900 .next_debug_id = 0,
500 .is_comptime = false,901 .is_comptime = false,
902 .is_async = false,
903 .begin_scope = begin_scope,
501 };904 };
502 }905 }
503906
504 pub fn abort(self: *Builder) void {907 pub fn abort(self: *Builder) void {
505 self.code.destroy(self.comp.a());908 self.code.destroy(self.comp.gpa());
506 }909 }
507910
508 /// Call code.destroy() when done911 /// Call code.destroy() when done
...@@ -517,7 +920,7 @@ pub const Builder = struct {...@@ -517,7 +920,7 @@ pub const Builder = struct {
517 .name_hint = name_hint,920 .name_hint = name_hint,
518 .debug_id = self.next_debug_id,921 .debug_id = self.next_debug_id,
519 .scope = scope,922 .scope = scope,
520 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),923 .instruction_list = std.ArrayList(*Inst).init(self.arena()),
521 .child = null,924 .child = null,
522 .parent = null,925 .parent = null,
523 .ref_instruction = null,926 .ref_instruction = null,
...@@ -537,67 +940,208 @@ pub const Builder = struct {...@@ -537,67 +940,208 @@ pub const Builder = struct {
537 self.current_basic_block = basic_block;940 self.current_basic_block = basic_block;
538 }941 }
539942
540 pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction {943 pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
541 switch (node.id) {944 switch (node.id) {
542 ast.Node.Id.Root => unreachable,945 ast.Node.Id.Root => unreachable,
543 ast.Node.Id.Use => unreachable,946 ast.Node.Id.Use => unreachable,
544 ast.Node.Id.TestDecl => unreachable,947 ast.Node.Id.TestDecl => unreachable,
545 ast.Node.Id.VarDecl => @panic("TODO"),948 ast.Node.Id.VarDecl => return error.Unimplemented,
546 ast.Node.Id.Defer => @panic("TODO"),949 ast.Node.Id.Defer => return error.Unimplemented,
547 ast.Node.Id.InfixOp => @panic("TODO"),950 ast.Node.Id.InfixOp => return error.Unimplemented,
548 ast.Node.Id.PrefixOp => @panic("TODO"),951 ast.Node.Id.PrefixOp => {
549 ast.Node.Id.SuffixOp => @panic("TODO"),952 const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node);
550 ast.Node.Id.Switch => @panic("TODO"),953 switch (prefix_op.op) {
551 ast.Node.Id.While => @panic("TODO"),954 ast.Node.PrefixOp.Op.AddressOf => return error.Unimplemented,
552 ast.Node.Id.For => @panic("TODO"),955 ast.Node.PrefixOp.Op.ArrayType => |n| return error.Unimplemented,
553 ast.Node.Id.If => @panic("TODO"),956 ast.Node.PrefixOp.Op.Await => return error.Unimplemented,
554 ast.Node.Id.ControlFlowExpression => return error.Unimplemented,957 ast.Node.PrefixOp.Op.BitNot => return error.Unimplemented,
555 ast.Node.Id.Suspend => @panic("TODO"),958 ast.Node.PrefixOp.Op.BoolNot => return error.Unimplemented,
556 ast.Node.Id.VarType => @panic("TODO"),959 ast.Node.PrefixOp.Op.Cancel => return error.Unimplemented,
557 ast.Node.Id.ErrorType => @panic("TODO"),960 ast.Node.PrefixOp.Op.OptionalType => return error.Unimplemented,
558 ast.Node.Id.FnProto => @panic("TODO"),961 ast.Node.PrefixOp.Op.Negation => return error.Unimplemented,
559 ast.Node.Id.PromiseType => @panic("TODO"),962 ast.Node.PrefixOp.Op.NegationWrap => return error.Unimplemented,
560 ast.Node.Id.IntegerLiteral => @panic("TODO"),963 ast.Node.PrefixOp.Op.Resume => return error.Unimplemented,
561 ast.Node.Id.FloatLiteral => @panic("TODO"),964 ast.Node.PrefixOp.Op.PtrType => |ptr_info| {
562 ast.Node.Id.StringLiteral => @panic("TODO"),965 const inst = try await (async irb.genPtrType(prefix_op, ptr_info, scope) catch unreachable);
563 ast.Node.Id.MultilineStringLiteral => @panic("TODO"),966 return irb.lvalWrap(scope, inst, lval);
564 ast.Node.Id.CharLiteral => @panic("TODO"),967 },
565 ast.Node.Id.BoolLiteral => @panic("TODO"),968 ast.Node.PrefixOp.Op.SliceType => |ptr_info| return error.Unimplemented,
566 ast.Node.Id.NullLiteral => @panic("TODO"),969 ast.Node.PrefixOp.Op.Try => return error.Unimplemented,
567 ast.Node.Id.UndefinedLiteral => @panic("TODO"),970 }
568 ast.Node.Id.ThisLiteral => @panic("TODO"),971 },
569 ast.Node.Id.Unreachable => @panic("TODO"),972 ast.Node.Id.SuffixOp => {
570 ast.Node.Id.Identifier => @panic("TODO"),973 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
974 switch (suffix_op.op) {
975 @TagType(ast.Node.SuffixOp.Op).Call => |*call| {
976 const inst = try await (async irb.genCall(suffix_op, call, scope) catch unreachable);
977 return irb.lvalWrap(scope, inst, lval);
978 },
979 @TagType(ast.Node.SuffixOp.Op).ArrayAccess => |n| return error.Unimplemented,
980 @TagType(ast.Node.SuffixOp.Op).Slice => |slice| return error.Unimplemented,
981 @TagType(ast.Node.SuffixOp.Op).ArrayInitializer => |init_list| return error.Unimplemented,
982 @TagType(ast.Node.SuffixOp.Op).StructInitializer => |init_list| return error.Unimplemented,
983 @TagType(ast.Node.SuffixOp.Op).Deref => return error.Unimplemented,
984 @TagType(ast.Node.SuffixOp.Op).UnwrapOptional => return error.Unimplemented,
985 }
986 },
987 ast.Node.Id.Switch => return error.Unimplemented,
988 ast.Node.Id.While => return error.Unimplemented,
989 ast.Node.Id.For => return error.Unimplemented,
990 ast.Node.Id.If => return error.Unimplemented,
991 ast.Node.Id.ControlFlowExpression => {
992 const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node);
993 return await (async irb.genControlFlowExpr(control_flow_expr, scope, lval) catch unreachable);
994 },
995 ast.Node.Id.Suspend => return error.Unimplemented,
996 ast.Node.Id.VarType => return error.Unimplemented,
997 ast.Node.Id.ErrorType => return error.Unimplemented,
998 ast.Node.Id.FnProto => return error.Unimplemented,
999 ast.Node.Id.PromiseType => return error.Unimplemented,
1000 ast.Node.Id.IntegerLiteral => {
1001 const int_lit = @fieldParentPtr(ast.Node.IntegerLiteral, "base", node);
1002 return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval);
1003 },
1004 ast.Node.Id.FloatLiteral => return error.Unimplemented,
1005 ast.Node.Id.StringLiteral => {
1006 const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node);
1007 const inst = try await (async irb.genStrLit(str_lit, scope) catch unreachable);
1008 return irb.lvalWrap(scope, inst, lval);
1009 },
1010 ast.Node.Id.MultilineStringLiteral => return error.Unimplemented,
1011 ast.Node.Id.CharLiteral => return error.Unimplemented,
1012 ast.Node.Id.BoolLiteral => return error.Unimplemented,
1013 ast.Node.Id.NullLiteral => return error.Unimplemented,
1014 ast.Node.Id.UndefinedLiteral => return error.Unimplemented,
1015 ast.Node.Id.ThisLiteral => return error.Unimplemented,
1016 ast.Node.Id.Unreachable => return error.Unimplemented,
1017 ast.Node.Id.Identifier => {
1018 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node);
1019 return await (async irb.genIdentifier(identifier, scope, lval) catch unreachable);
1020 },
571 ast.Node.Id.GroupedExpression => {1021 ast.Node.Id.GroupedExpression => {
572 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);1022 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
573 return irb.genNode(grouped_expr.expr, scope, lval);1023 return await (async irb.genNode(grouped_expr.expr, scope, lval) catch unreachable);
574 },1024 },
575 ast.Node.Id.BuiltinCall => @panic("TODO"),1025 ast.Node.Id.BuiltinCall => return error.Unimplemented,
576 ast.Node.Id.ErrorSetDecl => @panic("TODO"),1026 ast.Node.Id.ErrorSetDecl => return error.Unimplemented,
577 ast.Node.Id.ContainerDecl => @panic("TODO"),1027 ast.Node.Id.ContainerDecl => return error.Unimplemented,
578 ast.Node.Id.Asm => @panic("TODO"),1028 ast.Node.Id.Asm => return error.Unimplemented,
579 ast.Node.Id.Comptime => @panic("TODO"),1029 ast.Node.Id.Comptime => return error.Unimplemented,
580 ast.Node.Id.Block => {1030 ast.Node.Id.Block => {
581 const block = @fieldParentPtr(ast.Node.Block, "base", node);1031 const block = @fieldParentPtr(ast.Node.Block, "base", node);
582 return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval);1032 const inst = try await (async irb.genBlock(block, scope) catch unreachable);
1033 return irb.lvalWrap(scope, inst, lval);
583 },1034 },
584 ast.Node.Id.DocComment => @panic("TODO"),1035 ast.Node.Id.DocComment => return error.Unimplemented,
585 ast.Node.Id.SwitchCase => @panic("TODO"),1036 ast.Node.Id.SwitchCase => return error.Unimplemented,
586 ast.Node.Id.SwitchElse => @panic("TODO"),1037 ast.Node.Id.SwitchElse => return error.Unimplemented,
587 ast.Node.Id.Else => @panic("TODO"),1038 ast.Node.Id.Else => return error.Unimplemented,
588 ast.Node.Id.Payload => @panic("TODO"),1039 ast.Node.Id.Payload => return error.Unimplemented,
589 ast.Node.Id.PointerPayload => @panic("TODO"),1040 ast.Node.Id.PointerPayload => return error.Unimplemented,
590 ast.Node.Id.PointerIndexPayload => @panic("TODO"),1041 ast.Node.Id.PointerIndexPayload => return error.Unimplemented,
591 ast.Node.Id.StructField => @panic("TODO"),1042 ast.Node.Id.StructField => return error.Unimplemented,
592 ast.Node.Id.UnionTag => @panic("TODO"),1043 ast.Node.Id.UnionTag => return error.Unimplemented,
593 ast.Node.Id.EnumTag => @panic("TODO"),1044 ast.Node.Id.EnumTag => return error.Unimplemented,
594 ast.Node.Id.ErrorTag => @panic("TODO"),1045 ast.Node.Id.ErrorTag => return error.Unimplemented,
595 ast.Node.Id.AsmInput => @panic("TODO"),1046 ast.Node.Id.AsmInput => return error.Unimplemented,
596 ast.Node.Id.AsmOutput => @panic("TODO"),1047 ast.Node.Id.AsmOutput => return error.Unimplemented,
597 ast.Node.Id.AsyncAttribute => @panic("TODO"),1048 ast.Node.Id.AsyncAttribute => return error.Unimplemented,
598 ast.Node.Id.ParamDecl => @panic("TODO"),1049 ast.Node.Id.ParamDecl => return error.Unimplemented,
599 ast.Node.Id.FieldInitializer => @panic("TODO"),1050 ast.Node.Id.FieldInitializer => return error.Unimplemented,
1051 }
1052 }
1053
1054 async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
1055 const fn_ref = try await (async irb.genNode(suffix_op.lhs, scope, LVal.None) catch unreachable);
1056
1057 const args = try irb.arena().alloc(*Inst, call.params.len);
1058 var it = call.params.iterator(0);
1059 var i: usize = 0;
1060 while (it.next()) |arg_node_ptr| : (i += 1) {
1061 args[i] = try await (async irb.genNode(arg_node_ptr.*, scope, LVal.None) catch unreachable);
600 }1062 }
1063
1064 //bool is_async = node->data.fn_call_expr.is_async;
1065 //IrInstruction *async_allocator = nullptr;
1066 //if (is_async) {
1067 // if (node->data.fn_call_expr.async_allocator) {
1068 // async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope);
1069 // if (async_allocator == irb->codegen->invalid_instruction)
1070 // return async_allocator;
1071 // }
1072 //}
1073
1074 return irb.build(Inst.Call, scope, Span.token(suffix_op.rtoken), Inst.Call.Params{
1075 .fn_ref = fn_ref,
1076 .args = args,
1077 });
1078 //IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr);
1079 //return ir_lval_wrap(irb, scope, fn_call, lval);
1080 }
1081
1082 async fn genPtrType(
1083 irb: *Builder,
1084 prefix_op: *ast.Node.PrefixOp,
1085 ptr_info: ast.Node.PrefixOp.PtrInfo,
1086 scope: *Scope,
1087 ) !*Inst {
1088 // TODO port more logic
1089
1090 //assert(node->type == NodeTypePointerType);
1091 //PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
1092 // node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
1093 //bool is_const = node->data.pointer_type.is_const;
1094 //bool is_volatile = node->data.pointer_type.is_volatile;
1095 //AstNode *expr_node = node->data.pointer_type.op_expr;
1096 //AstNode *align_expr = node->data.pointer_type.align_expr;
1097
1098 //IrInstruction *align_value;
1099 //if (align_expr != nullptr) {
1100 // align_value = ir_gen_node(irb, align_expr, scope);
1101 // if (align_value == irb->codegen->invalid_instruction)
1102 // return align_value;
1103 //} else {
1104 // align_value = nullptr;
1105 //}
1106 const child_type = try await (async irb.genNode(prefix_op.rhs, scope, LVal.None) catch unreachable);
1107
1108 //uint32_t bit_offset_start = 0;
1109 //if (node->data.pointer_type.bit_offset_start != nullptr) {
1110 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
1111 // Buf *val_buf = buf_alloc();
1112 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
1113 // exec_add_error_node(irb->codegen, irb->exec, node,
1114 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1115 // return irb->codegen->invalid_instruction;
1116 // }
1117 // bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start);
1118 //}
1119
1120 //uint32_t bit_offset_end = 0;
1121 //if (node->data.pointer_type.bit_offset_end != nullptr) {
1122 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) {
1123 // Buf *val_buf = buf_alloc();
1124 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10);
1125 // exec_add_error_node(irb->codegen, irb->exec, node,
1126 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1127 // return irb->codegen->invalid_instruction;
1128 // }
1129 // bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end);
1130 //}
1131
1132 //if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) {
1133 // exec_add_error_node(irb->codegen, irb->exec, node,
1134 // buf_sprintf("bit offset start must be less than bit offset end"));
1135 // return irb->codegen->invalid_instruction;
1136 //}
1137
1138 return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{
1139 .child_type = child_type,
1140 .mut = Type.Pointer.Mut.Mut,
1141 .vol = Type.Pointer.Vol.Non,
1142 .size = Type.Pointer.Size.Many,
1143 .alignment = null,
1144 });
601 }1145 }
6021146
603 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {1147 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
...@@ -610,15 +1154,105 @@ pub const Builder = struct {...@@ -610,15 +1154,105 @@ pub const Builder = struct {
610 Scope.Id.CompTime => return true,1154 Scope.Id.CompTime => return true,
611 Scope.Id.FnDef => return false,1155 Scope.Id.FnDef => return false,
612 Scope.Id.Decls => unreachable,1156 Scope.Id.Decls => unreachable,
1157 Scope.Id.Root => unreachable,
613 Scope.Id.Block,1158 Scope.Id.Block,
614 Scope.Id.Defer,1159 Scope.Id.Defer,
615 Scope.Id.DeferExpr,1160 Scope.Id.DeferExpr,
616 => scope = scope.parent orelse return false,1161 => scope = scope.parent.?,
617 }1162 }
618 }1163 }
619 }1164 }
6201165
621 pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction {1166 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
1167 const int_token = irb.root_scope.tree.tokenSlice(int_lit.token);
1168
1169 var base: u8 = undefined;
1170 var rest: []const u8 = undefined;
1171 if (int_token.len >= 3 and int_token[0] == '0') {
1172 base = switch (int_token[1]) {
1173 'b' => u8(2),
1174 'o' => u8(8),
1175 'x' => u8(16),
1176 else => unreachable,
1177 };
1178 rest = int_token[2..];
1179 } else {
1180 base = 10;
1181 rest = int_token;
1182 }
1183
1184 const comptime_int_type = Type.ComptimeInt.get(irb.comp);
1185 defer comptime_int_type.base.base.deref(irb.comp);
1186
1187 const int_val = Value.Int.createFromString(
1188 irb.comp,
1189 &comptime_int_type.base,
1190 base,
1191 rest,
1192 ) catch |err| switch (err) {
1193 error.OutOfMemory => return error.OutOfMemory,
1194 error.InvalidBase => unreachable,
1195 error.InvalidCharForDigit => unreachable,
1196 error.DigitTooLargeForBase => unreachable,
1197 };
1198 errdefer int_val.base.deref(irb.comp);
1199
1200 const inst = try irb.build(Inst.Const, scope, Span.token(int_lit.token), Inst.Const.Params{});
1201 inst.val = IrVal{ .KnownValue = &int_val.base };
1202 return inst;
1203 }
1204
1205 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
1206 const str_token = irb.root_scope.tree.tokenSlice(str_lit.token);
1207 const src_span = Span.token(str_lit.token);
1208
1209 var bad_index: usize = undefined;
1210 var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) {
1211 error.OutOfMemory => return error.OutOfMemory,
1212 error.InvalidCharacter => {
1213 try irb.comp.addCompileError(
1214 irb.root_scope,
1215 src_span,
1216 "invalid character in string literal: '{c}'",
1217 str_token[bad_index],
1218 );
1219 return error.SemanticAnalysisFailed;
1220 },
1221 };
1222 var buf_cleaned = false;
1223 errdefer if (!buf_cleaned) irb.comp.gpa().free(buf);
1224
1225 if (str_token[0] == 'c') {
1226 // first we add a null
1227 buf = try irb.comp.gpa().realloc(u8, buf, buf.len + 1);
1228 buf[buf.len - 1] = 0;
1229
1230 // next make an array value
1231 const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable);
1232 buf_cleaned = true;
1233 defer array_val.base.deref(irb.comp);
1234
1235 // then make a pointer value pointing at the first element
1236 const ptr_val = try await (async Value.Ptr.createArrayElemPtr(
1237 irb.comp,
1238 array_val,
1239 Type.Pointer.Mut.Const,
1240 Type.Pointer.Size.Many,
1241 0,
1242 ) catch unreachable);
1243 defer ptr_val.base.deref(irb.comp);
1244
1245 return irb.buildConstValue(scope, src_span, &ptr_val.base);
1246 } else {
1247 const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable);
1248 buf_cleaned = true;
1249 defer array_val.base.deref(irb.comp);
1250
1251 return irb.buildConstValue(scope, src_span, &array_val.base);
1252 }
1253 }
1254
1255 pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
622 const block_scope = try Scope.Block.create(irb.comp, parent_scope);1256 const block_scope = try Scope.Block.create(irb.comp, parent_scope);
6231257
624 const outer_block_scope = &block_scope.base;1258 const outer_block_scope = &block_scope.base;
...@@ -636,7 +1270,7 @@ pub const Builder = struct {...@@ -636,7 +1270,7 @@ pub const Builder = struct {
636 }1270 }
6371271
638 if (block.label) |label| {1272 if (block.label) |label| {
639 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());1273 block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena());
640 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());1274 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
641 block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd");1275 block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd");
642 block_scope.is_comptime = try irb.buildConstBool(1276 block_scope.is_comptime = try irb.buildConstBool(
...@@ -647,7 +1281,7 @@ pub const Builder = struct {...@@ -647,7 +1281,7 @@ pub const Builder = struct {
647 }1281 }
6481282
649 var is_continuation_unreachable = false;1283 var is_continuation_unreachable = false;
650 var noreturn_return_value: ?*Instruction = null;1284 var noreturn_return_value: ?*Inst = null;
6511285
652 var stmt_it = block.statements.iterator(0);1286 var stmt_it = block.statements.iterator(0);
653 while (stmt_it.next()) |statement_node_ptr| {1287 while (stmt_it.next()) |statement_node_ptr| {
...@@ -655,7 +1289,7 @@ pub const Builder = struct {...@@ -655,7 +1289,7 @@ pub const Builder = struct {
6551289
656 if (statement_node.cast(ast.Node.Defer)) |defer_node| {1290 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
657 // defer starts a new scope1291 // defer starts a new scope
658 const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token);1292 const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token);
659 const kind = switch (defer_token.id) {1293 const kind = switch (defer_token.id) {
660 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,1294 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
661 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,1295 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
...@@ -666,7 +1300,7 @@ pub const Builder = struct {...@@ -666,7 +1300,7 @@ pub const Builder = struct {
666 child_scope = &defer_child_scope.base;1300 child_scope = &defer_child_scope.base;
667 continue;1301 continue;
668 }1302 }
669 const statement_value = try irb.genNode(statement_node, child_scope, LVal.None);1303 const statement_value = try await (async irb.genNode(statement_node, child_scope, LVal.None) catch unreachable);
6701304
671 is_continuation_unreachable = statement_value.isNoReturn();1305 is_continuation_unreachable = statement_value.isNoReturn();
672 if (is_continuation_unreachable) {1306 if (is_continuation_unreachable) {
...@@ -674,16 +1308,19 @@ pub const Builder = struct {...@@ -674,16 +1308,19 @@ pub const Builder = struct {
674 noreturn_return_value = statement_value;1308 noreturn_return_value = statement_value;
675 }1309 }
6761310
677 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {1311 if (statement_value.cast(Inst.DeclVar)) |decl_var| {
678 // variable declarations start a new scope1312 // variable declarations start a new scope
679 child_scope = decl_var.params.variable.child_scope;1313 child_scope = decl_var.params.variable.child_scope;
680 } else if (!is_continuation_unreachable) {1314 } else if (!is_continuation_unreachable) {
681 // this statement's value must be void1315 // this statement's value must be void
682 _ = irb.build(1316 _ = irb.build(
683 Instruction.CheckVoidStmt,1317 Inst.CheckVoidStmt,
684 child_scope,1318 child_scope,
685 statement_value.span,1319 Span{
686 Instruction.CheckVoidStmt.Params{ .target = statement_value },1320 .first = statement_node.firstToken(),
1321 .last = statement_node.lastToken(),
1322 },
1323 Inst.CheckVoidStmt.Params{ .target = statement_value },
687 );1324 );
688 }1325 }
689 }1326 }
...@@ -695,7 +1332,7 @@ pub const Builder = struct {...@@ -695,7 +1332,7 @@ pub const Builder = struct {
695 }1332 }
6961333
697 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);1334 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
698 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{1335 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
699 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),1336 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
700 .incoming_values = block_scope.incoming_values.toOwnedSlice(),1337 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
701 });1338 });
...@@ -706,26 +1343,216 @@ pub const Builder = struct {...@@ -706,26 +1343,216 @@ pub const Builder = struct {
706 try block_scope.incoming_values.append(1343 try block_scope.incoming_values.append(
707 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),1344 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
708 );1345 );
709 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);1346 _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
7101347
711 _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{1348 _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{
712 .dest_block = block_scope.end_block,1349 .dest_block = block_scope.end_block,
713 .is_comptime = block_scope.is_comptime,1350 .is_comptime = block_scope.is_comptime,
714 });1351 });
7151352
716 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);1353 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
7171354
718 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{1355 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
719 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),1356 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
720 .incoming_values = block_scope.incoming_values.toOwnedSlice(),1357 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
721 });1358 });
722 }1359 }
7231360
724 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);1361 _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
725 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);1362 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
726 }1363 }
7271364
728 fn genDefersForBlock(1365 pub async fn genControlFlowExpr(
1366 irb: *Builder,
1367 control_flow_expr: *ast.Node.ControlFlowExpression,
1368 scope: *Scope,
1369 lval: LVal,
1370 ) !*Inst {
1371 switch (control_flow_expr.kind) {
1372 ast.Node.ControlFlowExpression.Kind.Break => |arg| return error.Unimplemented,
1373 ast.Node.ControlFlowExpression.Kind.Continue => |arg| return error.Unimplemented,
1374 ast.Node.ControlFlowExpression.Kind.Return => {
1375 const src_span = Span.token(control_flow_expr.ltoken);
1376 if (scope.findFnDef() == null) {
1377 try irb.comp.addCompileError(
1378 irb.root_scope,
1379 src_span,
1380 "return expression outside function definition",
1381 );
1382 return error.SemanticAnalysisFailed;
1383 }
1384
1385 if (scope.findDeferExpr()) |scope_defer_expr| {
1386 if (!scope_defer_expr.reported_err) {
1387 try irb.comp.addCompileError(
1388 irb.root_scope,
1389 src_span,
1390 "cannot return from defer expression",
1391 );
1392 scope_defer_expr.reported_err = true;
1393 }
1394 return error.SemanticAnalysisFailed;
1395 }
1396
1397 const outer_scope = irb.begin_scope.?;
1398 const return_value = if (control_flow_expr.rhs) |rhs| blk: {
1399 break :blk try await (async irb.genNode(rhs, scope, LVal.None) catch unreachable);
1400 } else blk: {
1401 break :blk try irb.buildConstVoid(scope, src_span, true);
1402 };
1403
1404 const defer_counts = irb.countDefers(scope, outer_scope);
1405 const have_err_defers = defer_counts.error_exit != 0;
1406 if (have_err_defers or irb.comp.have_err_ret_tracing) {
1407 const err_block = try irb.createBasicBlock(scope, c"ErrRetErr");
1408 const ok_block = try irb.createBasicBlock(scope, c"ErrRetOk");
1409 if (!have_err_defers) {
1410 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
1411 }
1412
1413 const is_err = try irb.build(
1414 Inst.TestErr,
1415 scope,
1416 src_span,
1417 Inst.TestErr.Params{ .target = return_value },
1418 );
1419
1420 const err_is_comptime = try irb.buildTestCompTime(scope, src_span, is_err);
1421
1422 _ = try irb.buildGen(Inst.CondBr, scope, src_span, Inst.CondBr.Params{
1423 .condition = is_err,
1424 .then_block = err_block,
1425 .else_block = ok_block,
1426 .is_comptime = err_is_comptime,
1427 });
1428
1429 const ret_stmt_block = try irb.createBasicBlock(scope, c"RetStmt");
1430
1431 try irb.setCursorAtEndAndAppendBlock(err_block);
1432 if (have_err_defers) {
1433 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ErrorExit) catch unreachable);
1434 }
1435 if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) {
1436 _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{});
1437 }
1438 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1439 .dest_block = ret_stmt_block,
1440 .is_comptime = err_is_comptime,
1441 });
1442
1443 try irb.setCursorAtEndAndAppendBlock(ok_block);
1444 if (have_err_defers) {
1445 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
1446 }
1447 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1448 .dest_block = ret_stmt_block,
1449 .is_comptime = err_is_comptime,
1450 });
1451
1452 try irb.setCursorAtEndAndAppendBlock(ret_stmt_block);
1453 return irb.genAsyncReturn(scope, src_span, return_value, false);
1454 } else {
1455 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
1456 return irb.genAsyncReturn(scope, src_span, return_value, false);
1457 }
1458 },
1459 }
1460 }
1461
1462 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
1463 const src_span = Span.token(identifier.token);
1464 const name = irb.root_scope.tree.tokenSlice(identifier.token);
1465
1466 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
1467 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
1468 // const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
1469 // irb->codegen->builtin_types.entry_void, false);
1470 // const_instruction->base.value.special = ConstValSpecialStatic;
1471 // const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard;
1472 // return &const_instruction->base;
1473 //}
1474
1475 if (await (async irb.comp.getPrimitiveType(name) catch unreachable)) |result| {
1476 if (result) |primitive_type| {
1477 defer primitive_type.base.deref(irb.comp);
1478 switch (lval) {
1479 // if (lval == LValPtr) {
1480 // return ir_build_ref(irb, scope, node, value, false, false);
1481 LVal.Ptr => return error.Unimplemented,
1482 LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base),
1483 }
1484 }
1485 } else |err| switch (err) {
1486 error.Overflow => {
1487 try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large");
1488 return error.SemanticAnalysisFailed;
1489 },
1490 error.OutOfMemory => return error.OutOfMemory,
1491 }
1492
1493 //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
1494 //if (var) {
1495 // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);
1496 // if (lval == LValPtr)
1497 // return var_ptr;
1498 // else
1499 // return ir_build_load_ptr(irb, scope, node, var_ptr);
1500 //}
1501
1502 if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| {
1503 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1504 .decl = decl,
1505 .lval = lval,
1506 });
1507 }
1508
1509 //if (node->owner->any_imports_failed) {
1510 // // skip the error message since we had a failing import in this file
1511 // // if an import breaks we don't need redundant undeclared identifier errors
1512 // return irb->codegen->invalid_instruction;
1513 //}
1514
1515 // TODO put a variable of same name with invalid type in global scope
1516 // so that future references to this same name will find a variable with an invalid type
1517
1518 try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name);
1519 return error.SemanticAnalysisFailed;
1520 }
1521
1522 const DeferCounts = struct {
1523 scope_exit: usize,
1524 error_exit: usize,
1525 };
1526
1527 fn countDefers(irb: *Builder, inner_scope: *Scope, outer_scope: *Scope) DeferCounts {
1528 var result = DeferCounts{ .scope_exit = 0, .error_exit = 0 };
1529
1530 var scope = inner_scope;
1531 while (scope != outer_scope) {
1532 switch (scope.id) {
1533 Scope.Id.Defer => {
1534 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
1535 switch (defer_scope.kind) {
1536 Scope.Defer.Kind.ScopeExit => result.scope_exit += 1,
1537 Scope.Defer.Kind.ErrorExit => result.error_exit += 1,
1538 }
1539 scope = scope.parent orelse break;
1540 },
1541 Scope.Id.FnDef => break,
1542
1543 Scope.Id.CompTime,
1544 Scope.Id.Block,
1545 Scope.Id.Decls,
1546 Scope.Id.Root,
1547 => scope = scope.parent orelse break,
1548
1549 Scope.Id.DeferExpr => unreachable,
1550 }
1551 }
1552 return result;
1553 }
1554
1555 async fn genDefersForBlock(
729 irb: *Builder,1556 irb: *Builder,
730 inner_scope: *Scope,1557 inner_scope: *Scope,
731 outer_scope: *Scope,1558 outer_scope: *Scope,
...@@ -743,25 +1570,26 @@ pub const Builder = struct {...@@ -743,25 +1570,26 @@ pub const Builder = struct {
743 };1570 };
744 if (generate) {1571 if (generate) {
745 const defer_expr_scope = defer_scope.defer_expr_scope;1572 const defer_expr_scope = defer_scope.defer_expr_scope;
746 const instruction = try irb.genNode(1573 const instruction = try await (async irb.genNode(
747 defer_expr_scope.expr_node,1574 defer_expr_scope.expr_node,
748 &defer_expr_scope.base,1575 &defer_expr_scope.base,
749 LVal.None,1576 LVal.None,
750 );1577 ) catch unreachable);
751 if (instruction.isNoReturn()) {1578 if (instruction.isNoReturn()) {
752 is_noreturn = true;1579 is_noreturn = true;
753 } else {1580 } else {
754 _ = try irb.build(1581 _ = try irb.build(
755 Instruction.CheckVoidStmt,1582 Inst.CheckVoidStmt,
756 &defer_expr_scope.base,1583 &defer_expr_scope.base,
757 Span.token(defer_expr_scope.expr_node.lastToken()),1584 Span.token(defer_expr_scope.expr_node.lastToken()),
758 Instruction.CheckVoidStmt.Params{ .target = instruction },1585 Inst.CheckVoidStmt.Params{ .target = instruction },
759 );1586 );
760 }1587 }
761 }1588 }
762 },1589 },
763 Scope.Id.FnDef,1590 Scope.Id.FnDef,
764 Scope.Id.Decls,1591 Scope.Id.Decls,
1592 Scope.Id.Root,
765 => return is_noreturn,1593 => return is_noreturn,
7661594
767 Scope.Id.CompTime,1595 Scope.Id.CompTime,
...@@ -773,13 +1601,13 @@ pub const Builder = struct {...@@ -773,13 +1601,13 @@ pub const Builder = struct {
773 }1601 }
774 }1602 }
7751603
776 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction {1604 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst {
777 switch (lval) {1605 switch (lval) {
778 LVal.None => return instruction,1606 LVal.None => return instruction,
779 LVal.Ptr => {1607 LVal.Ptr => {
780 // We needed a pointer to a value, but we got a value. So we create1608 // We needed a pointer to a value, but we got a value. So we create
781 // an instruction which just makes a const pointer of it.1609 // an instruction which just makes a const pointer of it.
782 return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{1610 return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{
783 .target = instruction,1611 .target = instruction,
784 .mut = Type.Pointer.Mut.Const,1612 .mut = Type.Pointer.Mut.Const,
785 .volatility = Type.Pointer.Vol.Non,1613 .volatility = Type.Pointer.Vol.Non,
...@@ -799,10 +1627,10 @@ pub const Builder = struct {...@@ -799,10 +1627,10 @@ pub const Builder = struct {
799 span: Span,1627 span: Span,
800 params: I.Params,1628 params: I.Params,
801 is_generated: bool,1629 is_generated: bool,
802 ) !*Instruction {1630 ) !*Inst {
803 const inst = try self.arena().create(I{1631 const inst = try self.arena().create(I{
804 .base = Instruction{1632 .base = Inst{
805 .id = Instruction.typeToId(I),1633 .id = Inst.typeToId(I),
806 .is_generated = is_generated,1634 .is_generated = is_generated,
807 .scope = scope,1635 .scope = scope,
808 .debug_id = self.next_debug_id,1636 .debug_id = self.next_debug_id,
...@@ -816,6 +1644,7 @@ pub const Builder = struct {...@@ -816,6 +1644,7 @@ pub const Builder = struct {
816 .child = null,1644 .child = null,
817 .parent = null,1645 .parent = null,
818 .llvm_value = undefined,1646 .llvm_value = undefined,
1647 .owner_bb = self.current_basic_block,
819 },1648 },
820 .params = params,1649 .params = params,
821 });1650 });
...@@ -825,9 +1654,27 @@ pub const Builder = struct {...@@ -825,9 +1654,27 @@ pub const Builder = struct {
825 inline while (i < @memberCount(I.Params)) : (i += 1) {1654 inline while (i < @memberCount(I.Params)) : (i += 1) {
826 const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));1655 const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
827 switch (FieldType) {1656 switch (FieldType) {
828 *Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1,1657 *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self),
829 ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1,1658 *BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self),
830 else => {},1659 ?*Inst => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self),
1660 []*Inst => {
1661 // TODO https://github.com/ziglang/zig/issues/1269
1662 for (@field(inst.params, @memberName(I.Params, i))) |other|
1663 other.ref(self);
1664 },
1665 []*BasicBlock => {
1666 // TODO https://github.com/ziglang/zig/issues/1269
1667 for (@field(inst.params, @memberName(I.Params, i))) |other|
1668 other.ref(self);
1669 },
1670 Type.Pointer.Mut,
1671 Type.Pointer.Vol,
1672 Type.Pointer.Size,
1673 LVal,
1674 *Decl,
1675 => {},
1676 // it's ok to add more types here, just make sure any instructions are ref'd appropriately
1677 else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),
831 }1678 }
832 }1679 }
8331680
...@@ -842,7 +1689,7 @@ pub const Builder = struct {...@@ -842,7 +1689,7 @@ pub const Builder = struct {
842 scope: *Scope,1689 scope: *Scope,
843 span: Span,1690 span: Span,
844 params: I.Params,1691 params: I.Params,
845 ) !*Instruction {1692 ) !*Inst {
846 return self.buildExtra(I, scope, span, params, false);1693 return self.buildExtra(I, scope, span, params, false);
847 }1694 }
8481695
...@@ -852,21 +1699,95 @@ pub const Builder = struct {...@@ -852,21 +1699,95 @@ pub const Builder = struct {
852 scope: *Scope,1699 scope: *Scope,
853 span: Span,1700 span: Span,
854 params: I.Params,1701 params: I.Params,
855 ) !*Instruction {1702 ) !*Inst {
856 return self.buildExtra(I, scope, span, params, true);1703 return self.buildExtra(I, scope, span, params, true);
857 }1704 }
8581705
859 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction {1706 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Inst {
860 const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{});1707 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
861 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base };1708 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base };
862 return inst;1709 return inst;
863 }1710 }
8641711
865 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction {1712 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst {
866 const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated);1713 const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated);
867 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };1714 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };
868 return inst;1715 return inst;
869 }1716 }
1717
1718 fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst {
1719 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
1720 inst.val = IrVal{ .KnownValue = v.getRef() };
1721 return inst;
1722 }
1723
1724 /// If the code is explicitly set to be comptime, then builds a const bool,
1725 /// otherwise builds a TestCompTime instruction.
1726 fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst {
1727 if (self.isCompTime(scope)) {
1728 return self.buildConstBool(scope, span, true);
1729 } else {
1730 return self.build(
1731 Inst.TestCompTime,
1732 scope,
1733 span,
1734 Inst.TestCompTime.Params{ .target = target },
1735 );
1736 }
1737 }
1738
1739 fn genAsyncReturn(irb: *Builder, scope: *Scope, span: Span, result: *Inst, is_gen: bool) !*Inst {
1740 _ = irb.buildGen(
1741 Inst.AddImplicitReturnType,
1742 scope,
1743 span,
1744 Inst.AddImplicitReturnType.Params{ .target = result },
1745 );
1746
1747 if (!irb.is_async) {
1748 return irb.buildExtra(
1749 Inst.Return,
1750 scope,
1751 span,
1752 Inst.Return.Params{ .return_value = result },
1753 is_gen,
1754 );
1755 }
1756 return error.Unimplemented;
1757
1758 //ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
1759 //IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,
1760 // get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
1761 //// TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig
1762 //IrInstruction *replacement_value = irb->exec->coro_handle;
1763 //IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node,
1764 // promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr,
1765 // AtomicRmwOp_xchg, AtomicOrderSeqCst);
1766 //ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle);
1767 //IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle);
1768 //IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
1769 //return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final,
1770 // is_comptime);
1771 //// the above blocks are rendered by ir_gen after the rest of codegen
1772 }
1773
1774 async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl {
1775 var s = scope;
1776 while (true) {
1777 switch (s.id) {
1778 Scope.Id.Decls => {
1779 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1780 const table = await (async decls.getTableReadOnly() catch unreachable);
1781 if (table.get(name)) |entry| {
1782 return entry.value;
1783 }
1784 },
1785 Scope.Id.Root => return null,
1786 else => {},
1787 }
1788 s = s.parent.?;
1789 }
1790 }
870};1791};
8711792
872const Analyze = struct {1793const Analyze = struct {
...@@ -875,7 +1796,7 @@ const Analyze = struct {...@@ -875,7 +1796,7 @@ const Analyze = struct {
875 const_predecessor_bb: ?*BasicBlock,1796 const_predecessor_bb: ?*BasicBlock,
876 parent_basic_block: *BasicBlock,1797 parent_basic_block: *BasicBlock,
877 instruction_index: usize,1798 instruction_index: usize,
878 src_implicit_return_type_list: std.ArrayList(*Instruction),1799 src_implicit_return_type_list: std.ArrayList(*Inst),
879 explicit_return_type: ?*Type,1800 explicit_return_type: ?*Type,
8801801
881 pub const Error = error{1802 pub const Error = error{
...@@ -889,8 +1810,8 @@ const Analyze = struct {...@@ -889,8 +1810,8 @@ const Analyze = struct {
889 OutOfMemory,1810 OutOfMemory,
890 };1811 };
8911812
892 pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze {1813 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {
893 var irb = try Builder.init(comp, parsed_file);1814 var irb = try Builder.init(comp, root_scope, null);
894 errdefer irb.abort();1815 errdefer irb.abort();
8951816
896 return Analyze{1817 return Analyze{
...@@ -899,7 +1820,7 @@ const Analyze = struct {...@@ -899,7 +1820,7 @@ const Analyze = struct {
899 .const_predecessor_bb = null,1820 .const_predecessor_bb = null,
900 .parent_basic_block = undefined, // initialized with startBasicBlock1821 .parent_basic_block = undefined, // initialized with startBasicBlock
901 .instruction_index = undefined, // initialized with startBasicBlock1822 .instruction_index = undefined, // initialized with startBasicBlock
902 .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()),1823 .src_implicit_return_type_list = std.ArrayList(*Inst).init(irb.arena()),
903 .explicit_return_type = explicit_return_type,1824 .explicit_return_type = explicit_return_type,
904 };1825 };
905 }1826 }
...@@ -908,7 +1829,7 @@ const Analyze = struct {...@@ -908,7 +1829,7 @@ const Analyze = struct {
908 self.irb.abort();1829 self.irb.abort();
909 }1830 }
9101831
911 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock {1832 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Inst) !*BasicBlock {
912 if (old_bb.child) |child| {1833 if (old_bb.child) |child| {
913 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)1834 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
914 return child;1835 return child;
...@@ -968,21 +1889,478 @@ const Analyze = struct {...@@ -968,21 +1889,478 @@ const Analyze = struct {
968 }1889 }
9691890
970 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {1891 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
971 return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args);1892 return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args);
972 }1893 }
9731894
974 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type {1895 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
975 // TODO actual implementation1896 // TODO actual implementation
976 return &Type.Void.get(self.irb.comp).base;1897 return &Type.Void.get(self.irb.comp).base;
977 }1898 }
9781899
979 fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction {1900 fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst {
980 const dest_type = optional_dest_type orelse return target;1901 const dest_type = optional_dest_type orelse return target;
981 @panic("TODO implicitCast");1902 const from_type = target.getKnownType();
1903 if (from_type == dest_type or from_type.id == Type.Id.NoReturn) return target;
1904 return self.analyzeCast(target, target, dest_type);
982 }1905 }
9831906
984 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value {1907 fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst {
985 @panic("TODO getCompTimeValOrNullUndefOk");1908 const from_type = target.getKnownType();
1909
1910 //if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
1911 // return ira->codegen->invalid_instruction;
1912 //}
1913
1914 //// perfect match or non-const to const
1915 //ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
1916 // source_node, false);
1917 //if (const_cast_result.id == ConstCastResultIdOk) {
1918 // return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
1919 //}
1920
1921 //// widening conversion
1922 //if (wanted_type->id == TypeTableEntryIdInt &&
1923 // actual_type->id == TypeTableEntryIdInt &&
1924 // wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
1925 // wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
1926 //{
1927 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
1928 //}
1929
1930 //// small enough unsigned ints can get casted to large enough signed ints
1931 //if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed &&
1932 // actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed &&
1933 // wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count)
1934 //{
1935 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
1936 //}
1937
1938 //// float widening conversion
1939 //if (wanted_type->id == TypeTableEntryIdFloat &&
1940 // actual_type->id == TypeTableEntryIdFloat &&
1941 // wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
1942 //{
1943 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
1944 //}
1945
1946 //// cast from [N]T to []const T
1947 //if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) {
1948 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
1949 // assert(ptr_type->id == TypeTableEntryIdPointer);
1950 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
1951 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
1952 // source_node, false).id == ConstCastResultIdOk)
1953 // {
1954 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
1955 // }
1956 //}
1957
1958 //// cast from *const [N]T to []const T
1959 //if (is_slice(wanted_type) &&
1960 // actual_type->id == TypeTableEntryIdPointer &&
1961 // actual_type->data.pointer.is_const &&
1962 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
1963 //{
1964 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
1965 // assert(ptr_type->id == TypeTableEntryIdPointer);
1966
1967 // TypeTableEntry *array_type = actual_type->data.pointer.child_type;
1968
1969 // if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
1970 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type,
1971 // source_node, false).id == ConstCastResultIdOk)
1972 // {
1973 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
1974 // }
1975 //}
1976
1977 //// cast from [N]T to *const []const T
1978 //if (wanted_type->id == TypeTableEntryIdPointer &&
1979 // wanted_type->data.pointer.is_const &&
1980 // is_slice(wanted_type->data.pointer.child_type) &&
1981 // actual_type->id == TypeTableEntryIdArray)
1982 //{
1983 // TypeTableEntry *ptr_type =
1984 // wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
1985 // assert(ptr_type->id == TypeTableEntryIdPointer);
1986 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
1987 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
1988 // source_node, false).id == ConstCastResultIdOk)
1989 // {
1990 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
1991 // if (type_is_invalid(cast1->value.type))
1992 // return ira->codegen->invalid_instruction;
1993
1994 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
1995 // if (type_is_invalid(cast2->value.type))
1996 // return ira->codegen->invalid_instruction;
1997
1998 // return cast2;
1999 // }
2000 //}
2001
2002 //// cast from [N]T to ?[]const T
2003 //if (wanted_type->id == TypeTableEntryIdOptional &&
2004 // is_slice(wanted_type->data.maybe.child_type) &&
2005 // actual_type->id == TypeTableEntryIdArray)
2006 //{
2007 // TypeTableEntry *ptr_type =
2008 // wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
2009 // assert(ptr_type->id == TypeTableEntryIdPointer);
2010 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2011 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2012 // source_node, false).id == ConstCastResultIdOk)
2013 // {
2014 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
2015 // if (type_is_invalid(cast1->value.type))
2016 // return ira->codegen->invalid_instruction;
2017
2018 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2019 // if (type_is_invalid(cast2->value.type))
2020 // return ira->codegen->invalid_instruction;
2021
2022 // return cast2;
2023 // }
2024 //}
2025
2026 //// *[N]T to [*]T
2027 //if (wanted_type->id == TypeTableEntryIdPointer &&
2028 // wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
2029 // actual_type->id == TypeTableEntryIdPointer &&
2030 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2031 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
2032 // actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment &&
2033 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2034 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2035 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2036 //{
2037 // return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
2038 //}
2039
2040 //// *[N]T to []T
2041 //if (is_slice(wanted_type) &&
2042 // actual_type->id == TypeTableEntryIdPointer &&
2043 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2044 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
2045 //{
2046 // TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2047 // assert(slice_ptr_type->id == TypeTableEntryIdPointer);
2048 // if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
2049 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2050 // !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
2051 // {
2052 // return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type);
2053 // }
2054 //}
2055
2056 //// cast from T to ?T
2057 //// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
2058 //if (wanted_type->id == TypeTableEntryIdOptional) {
2059 // TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
2060 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node,
2061 // false).id == ConstCastResultIdOk)
2062 // {
2063 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2064 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2065 // actual_type->id == TypeTableEntryIdComptimeFloat)
2066 // {
2067 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
2068 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2069 // } else {
2070 // return ira->codegen->invalid_instruction;
2071 // }
2072 // } else if (wanted_child_type->id == TypeTableEntryIdPointer &&
2073 // wanted_child_type->data.pointer.is_const &&
2074 // (actual_type->id == TypeTableEntryIdPointer || is_container(actual_type)))
2075 // {
2076 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value);
2077 // if (type_is_invalid(cast1->value.type))
2078 // return ira->codegen->invalid_instruction;
2079
2080 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2081 // if (type_is_invalid(cast2->value.type))
2082 // return ira->codegen->invalid_instruction;
2083
2084 // return cast2;
2085 // }
2086 //}
2087
2088 //// cast from null literal to maybe type
2089 //if (wanted_type->id == TypeTableEntryIdOptional &&
2090 // actual_type->id == TypeTableEntryIdNull)
2091 //{
2092 // return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
2093 //}
2094
2095 //// cast from child type of error type to error type
2096 //if (wanted_type->id == TypeTableEntryIdErrorUnion) {
2097 // if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type,
2098 // source_node, false).id == ConstCastResultIdOk)
2099 // {
2100 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2101 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2102 // actual_type->id == TypeTableEntryIdComptimeFloat)
2103 // {
2104 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
2105 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2106 // } else {
2107 // return ira->codegen->invalid_instruction;
2108 // }
2109 // }
2110 //}
2111
2112 //// cast from [N]T to E![]const T
2113 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2114 // is_slice(wanted_type->data.error_union.payload_type) &&
2115 // actual_type->id == TypeTableEntryIdArray)
2116 //{
2117 // TypeTableEntry *ptr_type =
2118 // wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
2119 // assert(ptr_type->id == TypeTableEntryIdPointer);
2120 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2121 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2122 // source_node, false).id == ConstCastResultIdOk)
2123 // {
2124 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2125 // if (type_is_invalid(cast1->value.type))
2126 // return ira->codegen->invalid_instruction;
2127
2128 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2129 // if (type_is_invalid(cast2->value.type))
2130 // return ira->codegen->invalid_instruction;
2131
2132 // return cast2;
2133 // }
2134 //}
2135
2136 //// cast from error set to error union type
2137 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2138 // actual_type->id == TypeTableEntryIdErrorSet)
2139 //{
2140 // return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
2141 //}
2142
2143 //// cast from T to E!?T
2144 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2145 // wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional &&
2146 // actual_type->id != TypeTableEntryIdOptional)
2147 //{
2148 // TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
2149 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk ||
2150 // actual_type->id == TypeTableEntryIdNull ||
2151 // actual_type->id == TypeTableEntryIdComptimeInt ||
2152 // actual_type->id == TypeTableEntryIdComptimeFloat)
2153 // {
2154 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2155 // if (type_is_invalid(cast1->value.type))
2156 // return ira->codegen->invalid_instruction;
2157
2158 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2159 // if (type_is_invalid(cast2->value.type))
2160 // return ira->codegen->invalid_instruction;
2161
2162 // return cast2;
2163 // }
2164 //}
2165
2166 // cast from comptime-known integer to another integer where the value fits
2167 if (target.isCompTime() and (from_type.id == Type.Id.Int or from_type.id == Type.Id.ComptimeInt)) cast: {
2168 const target_val = target.val.KnownValue;
2169 const from_int = &target_val.cast(Value.Int).?.big_int;
2170 const fits = fits: {
2171 if (dest_type.cast(Type.ComptimeInt)) |ctint| {
2172 break :fits true;
2173 }
2174 if (dest_type.cast(Type.Int)) |int| {
2175 break :fits from_int.fitsInTwosComp(int.key.is_signed, int.key.bit_count);
2176 }
2177 break :cast;
2178 };
2179 if (!fits) {
2180 try ira.addCompileError(
2181 source_instr.span,
2182 "integer value '{}' cannot be stored in type '{}'",
2183 from_int,
2184 dest_type.name,
2185 );
2186 return error.SemanticAnalysisFailed;
2187 }
2188
2189 const new_val = try target.copyVal(ira.irb.comp);
2190 new_val.setType(dest_type, ira.irb.comp);
2191 return ira.irb.buildConstValue(source_instr.scope, source_instr.span, new_val);
2192 }
2193
2194 // cast from number literal to another type
2195 // cast from number literal to *const integer
2196 //if (actual_type->id == TypeTableEntryIdComptimeFloat ||
2197 // actual_type->id == TypeTableEntryIdComptimeInt)
2198 //{
2199 // ensure_complete_type(ira->codegen, wanted_type);
2200 // if (type_is_invalid(wanted_type))
2201 // return ira->codegen->invalid_instruction;
2202 // if (wanted_type->id == TypeTableEntryIdEnum) {
2203 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);
2204 // if (type_is_invalid(cast1->value.type))
2205 // return ira->codegen->invalid_instruction;
2206
2207 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2208 // if (type_is_invalid(cast2->value.type))
2209 // return ira->codegen->invalid_instruction;
2210
2211 // return cast2;
2212 // } else if (wanted_type->id == TypeTableEntryIdPointer &&
2213 // wanted_type->data.pointer.is_const)
2214 // {
2215 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
2216 // if (type_is_invalid(cast1->value.type))
2217 // return ira->codegen->invalid_instruction;
2218
2219 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2220 // if (type_is_invalid(cast2->value.type))
2221 // return ira->codegen->invalid_instruction;
2222
2223 // return cast2;
2224 // } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) {
2225 // CastOp op;
2226 // if ((actual_type->id == TypeTableEntryIdComptimeFloat &&
2227 // wanted_type->id == TypeTableEntryIdFloat) ||
2228 // (actual_type->id == TypeTableEntryIdComptimeInt &&
2229 // wanted_type->id == TypeTableEntryIdInt))
2230 // {
2231 // op = CastOpNumLitToConcrete;
2232 // } else if (wanted_type->id == TypeTableEntryIdInt) {
2233 // op = CastOpFloatToInt;
2234 // } else if (wanted_type->id == TypeTableEntryIdFloat) {
2235 // op = CastOpIntToFloat;
2236 // } else {
2237 // zig_unreachable();
2238 // }
2239 // return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false);
2240 // } else {
2241 // return ira->codegen->invalid_instruction;
2242 // }
2243 //}
2244
2245 //// cast from typed number to integer or float literal.
2246 //// works when the number is known at compile time
2247 //if (instr_is_comptime(value) &&
2248 // ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) ||
2249 // (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat)))
2250 //{
2251 // return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
2252 //}
2253
2254 //// cast from union to the enum type of the union
2255 //if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) {
2256 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2257 // if (type_is_invalid(actual_type))
2258 // return ira->codegen->invalid_instruction;
2259
2260 // if (actual_type->data.unionation.tag_type == wanted_type) {
2261 // return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type);
2262 // }
2263 //}
2264
2265 //// enum to union which has the enum as the tag type
2266 //if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
2267 // (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2268 // wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
2269 //{
2270 // type_ensure_zero_bits_known(ira->codegen, wanted_type);
2271 // if (wanted_type->data.unionation.tag_type == actual_type) {
2272 // return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
2273 // }
2274 //}
2275
2276 //// enum to &const union which has the enum as the tag type
2277 //if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) {
2278 // TypeTableEntry *union_type = wanted_type->data.pointer.child_type;
2279 // if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2280 // union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
2281 // {
2282 // type_ensure_zero_bits_known(ira->codegen, union_type);
2283 // if (union_type->data.unionation.tag_type == actual_type) {
2284 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);
2285 // if (type_is_invalid(cast1->value.type))
2286 // return ira->codegen->invalid_instruction;
2287
2288 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2289 // if (type_is_invalid(cast2->value.type))
2290 // return ira->codegen->invalid_instruction;
2291
2292 // return cast2;
2293 // }
2294 // }
2295 //}
2296
2297 //// cast from *T to *[1]T
2298 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2299 // actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle)
2300 //{
2301 // TypeTableEntry *array_type = wanted_type->data.pointer.child_type;
2302 // if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 &&
2303 // types_match_const_cast_only(ira, array_type->data.array.child_type,
2304 // actual_type->data.pointer.child_type, source_node,
2305 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2306 // {
2307 // if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) {
2308 // ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
2309 // add_error_note(ira->codegen, msg, value->source_node,
2310 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name),
2311 // actual_type->data.pointer.alignment));
2312 // add_error_note(ira->codegen, msg, source_instr->source_node,
2313 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name),
2314 // wanted_type->data.pointer.alignment));
2315 // return ira->codegen->invalid_instruction;
2316 // }
2317 // return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
2318 // }
2319 //}
2320
2321 //// cast from T to *T where T is zero bits
2322 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2323 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2324 // actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2325 //{
2326 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2327 // if (type_is_invalid(actual_type)) {
2328 // return ira->codegen->invalid_instruction;
2329 // }
2330 // if (!type_has_bits(actual_type)) {
2331 // return ir_get_ref(ira, source_instr, value, false, false);
2332 // }
2333 //}
2334
2335 //// cast from undefined to anything
2336 //if (actual_type->id == TypeTableEntryIdUndefined) {
2337 // return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
2338 //}
2339
2340 //// cast from something to const pointer of it
2341 //if (!type_requires_comptime(actual_type)) {
2342 // TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
2343 // if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) {
2344 // return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
2345 // }
2346 //}
2347
2348 try ira.addCompileError(
2349 source_instr.span,
2350 "expected type '{}', found '{}'",
2351 dest_type.name,
2352 from_type.name,
2353 );
2354 //ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node,
2355 // buf_sprintf("expected type '%s', found '%s'",
2356 // buf_ptr(&wanted_type->name),
2357 // buf_ptr(&actual_type->name)));
2358 //report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg);
2359 return error.SemanticAnalysisFailed;
2360 }
2361
2362 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value {
2363 @panic("TODO");
986 }2364 }
9872365
988 fn getCompTimeRef(2366 fn getCompTimeRef(
...@@ -991,9 +2369,8 @@ const Analyze = struct {...@@ -991,9 +2369,8 @@ const Analyze = struct {
991 ptr_mut: Value.Ptr.Mut,2369 ptr_mut: Value.Ptr.Mut,
992 mut: Type.Pointer.Mut,2370 mut: Type.Pointer.Mut,
993 volatility: Type.Pointer.Vol,2371 volatility: Type.Pointer.Vol,
994 ptr_align: u32,2372 ) Analyze.Error!*Inst {
995 ) Analyze.Error!*Instruction {2373 return error.Unimplemented;
996 @panic("TODO getCompTimeRef");
997 }2374 }
998};2375};
9992376
...@@ -1001,43 +2378,32 @@ pub async fn gen(...@@ -1001,43 +2378,32 @@ pub async fn gen(
1001 comp: *Compilation,2378 comp: *Compilation,
1002 body_node: *ast.Node,2379 body_node: *ast.Node,
1003 scope: *Scope,2380 scope: *Scope,
1004 end_span: Span,
1005 parsed_file: *ParsedFile,
1006) !*Code {2381) !*Code {
1007 var irb = try Builder.init(comp, parsed_file);2382 var irb = try Builder.init(comp, scope.findRoot(), scope);
1008 errdefer irb.abort();2383 errdefer irb.abort();
10092384
1010 const entry_block = try irb.createBasicBlock(scope, c"Entry");2385 const entry_block = try irb.createBasicBlock(scope, c"Entry");
1011 entry_block.ref(); // Entry block gets a reference because we enter it to begin.2386 entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin.
1012 try irb.setCursorAtEndAndAppendBlock(entry_block);2387 try irb.setCursorAtEndAndAppendBlock(entry_block);
10132388
1014 const result = try irb.genNode(body_node, scope, LVal.None);2389 const result = try await (async irb.genNode(body_node, scope, LVal.None) catch unreachable);
1015 if (!result.isNoReturn()) {2390 if (!result.isNoReturn()) {
1016 _ = irb.buildGen(2391 // no need for save_err_ret_addr because this cannot return error
1017 Instruction.AddImplicitReturnType,2392 _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true);
1018 scope,
1019 end_span,
1020 Instruction.AddImplicitReturnType.Params{ .target = result },
1021 );
1022 _ = irb.buildGen(
1023 Instruction.Return,
1024 scope,
1025 end_span,
1026 Instruction.Return.Params{ .return_value = result },
1027 );
1028 }2393 }
10292394
1030 return irb.finish();2395 return irb.finish();
1031}2396}
10322397
1033pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code {2398pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
1034 var ira = try Analyze.init(comp, parsed_file, expected_type);
1035 errdefer ira.abort();
1036
1037 const old_entry_bb = old_code.basic_block_list.at(0);2399 const old_entry_bb = old_code.basic_block_list.at(0);
2400 const root_scope = old_entry_bb.scope.findRoot();
2401
2402 var ira = try Analyze.init(comp, root_scope, expected_type);
2403 errdefer ira.abort();
10382404
1039 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);2405 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
1040 new_entry_bb.ref();2406 new_entry_bb.ref(&ira.irb);
10412407
1042 ira.irb.current_basic_block = new_entry_bb;2408 ira.irb.current_basic_block = new_entry_bb;
10432409
...@@ -1051,7 +2417,8 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co...@@ -1051,7 +2417,8 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co
1051 continue;2417 continue;
1052 }2418 }
10532419
1054 const return_inst = try old_instruction.analyze(&ira);2420 const return_inst = try await (async old_instruction.analyze(&ira) catch unreachable);
2421 assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point
1055 return_inst.linkToParent(old_instruction);2422 return_inst.linkToParent(old_instruction);
1056 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,2423 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,
1057 // then here we want to check if ira.isCompTime() and return early if true2424 // then here we want to check if ira.isCompTime() and return early if true
src-self-hosted/libc_installation.zig created+462
...@@ -0,0 +1,462 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const event = std.event;
4const Target = @import("target.zig").Target;
5const c = @import("c.zig");
6
7/// See the render function implementation for documentation of the fields.
8pub const LibCInstallation = struct {
9 include_dir: []const u8,
10 lib_dir: ?[]const u8,
11 static_lib_dir: ?[]const u8,
12 msvc_lib_dir: ?[]const u8,
13 kernel32_lib_dir: ?[]const u8,
14 dynamic_linker_path: ?[]const u8,
15
16 pub const FindError = error{
17 OutOfMemory,
18 FileSystem,
19 UnableToSpawnCCompiler,
20 CCompilerExitCode,
21 CCompilerCrashed,
22 CCompilerCannotFindHeaders,
23 LibCRuntimeNotFound,
24 LibCStdLibHeaderNotFound,
25 LibCKernel32LibNotFound,
26 UnsupportedArchitecture,
27 };
28
29 pub fn parse(
30 self: *LibCInstallation,
31 allocator: *std.mem.Allocator,
32 libc_file: []const u8,
33 stderr: *std.io.OutStream(std.io.FileOutStream.Error),
34 ) !void {
35 self.initEmpty();
36
37 const keys = []const []const u8{
38 "include_dir",
39 "lib_dir",
40 "static_lib_dir",
41 "msvc_lib_dir",
42 "kernel32_lib_dir",
43 "dynamic_linker_path",
44 };
45 const FoundKey = struct {
46 found: bool,
47 allocated: ?[]u8,
48 };
49 var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
50 errdefer {
51 self.initEmpty();
52 for (found_keys) |found_key| {
53 if (found_key.allocated) |s| allocator.free(s);
54 }
55 }
56
57 const contents = try std.io.readFileAlloc(allocator, libc_file);
58 defer allocator.free(contents);
59
60 var it = std.mem.split(contents, "\n");
61 while (it.next()) |line| {
62 if (line.len == 0 or line[0] == '#') continue;
63 var line_it = std.mem.split(line, "=");
64 const name = line_it.next() orelse {
65 try stderr.print("missing equal sign after field name\n");
66 return error.ParseError;
67 };
68 const value = line_it.rest();
69 inline for (keys) |key, i| {
70 if (std.mem.eql(u8, name, key)) {
71 found_keys[i].found = true;
72 switch (@typeInfo(@typeOf(@field(self, key)))) {
73 builtin.TypeId.Optional => {
74 if (value.len == 0) {
75 @field(self, key) = null;
76 } else {
77 found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
78 @field(self, key) = found_keys[i].allocated;
79 }
80 },
81 else => {
82 if (value.len == 0) {
83 try stderr.print("field cannot be empty: {}\n", key);
84 return error.ParseError;
85 }
86 const dupe = try std.mem.dupe(allocator, u8, value);
87 found_keys[i].allocated = dupe;
88 @field(self, key) = dupe;
89 },
90 }
91 break;
92 }
93 }
94 }
95 for (found_keys) |found_key, i| {
96 if (!found_key.found) {
97 try stderr.print("missing field: {}\n", keys[i]);
98 return error.ParseError;
99 }
100 }
101 }
102
103 pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void {
104 @setEvalBranchQuota(4000);
105 try out.print(
106 \\# The directory that contains `stdlib.h`.
107 \\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
108 \\include_dir={}
109 \\
110 \\# The directory that contains `crt1.o`.
111 \\# On Linux, can be found with `cc -print-file-name=crt1.o`.
112 \\# Not needed when targeting MacOS.
113 \\lib_dir={}
114 \\
115 \\# The directory that contains `crtbegin.o`.
116 \\# On Linux, can be found with `cc -print-file-name=crtbegin.o`.
117 \\# Not needed when targeting MacOS or Windows.
118 \\static_lib_dir={}
119 \\
120 \\# The directory that contains `vcruntime.lib`.
121 \\# Only needed when targeting Windows.
122 \\msvc_lib_dir={}
123 \\
124 \\# The directory that contains `kernel32.lib`.
125 \\# Only needed when targeting Windows.
126 \\kernel32_lib_dir={}
127 \\
128 \\# The full path to the dynamic linker, on the target system.
129 \\# Only needed when targeting Linux.
130 \\dynamic_linker_path={}
131 \\
132 ,
133 self.include_dir,
134 self.lib_dir orelse "",
135 self.static_lib_dir orelse "",
136 self.msvc_lib_dir orelse "",
137 self.kernel32_lib_dir orelse "",
138 self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(),
139 );
140 }
141
142 /// Finds the default, native libc.
143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
144 self.initEmpty();
145 var group = event.Group(FindError!void).init(loop);
146 errdefer group.cancelAll();
147 var windows_sdk: ?*c.ZigWindowsSDK = null;
148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
149
150 switch (builtin.os) {
151 builtin.Os.windows => {
152 var sdk: *c.ZigWindowsSDK = undefined;
153 switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) {
154 c.ZigFindWindowsSdkError.None => {
155 windows_sdk = sdk;
156
157 if (sdk.msvc_lib_dir_ptr) |ptr| {
158 self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]);
159 }
160 try group.call(findNativeKernel32LibDir, self, loop, sdk);
161 try group.call(findNativeIncludeDirWindows, self, loop, sdk);
162 try group.call(findNativeLibDirWindows, self, loop, sdk);
163 },
164 c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
165 c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
166 c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound,
167 }
168 },
169 builtin.Os.linux => {
170 try group.call(findNativeIncludeDirLinux, self, loop);
171 try group.call(findNativeLibDirLinux, self, loop);
172 try group.call(findNativeStaticLibDir, self, loop);
173 try group.call(findNativeDynamicLinker, self, loop);
174 },
175 builtin.Os.macosx => {
176 self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
177 },
178 else => @compileError("unimplemented: find libc for this OS"),
179 }
180 return await (async group.wait() catch unreachable);
181 }
182
183 async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void {
184 const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
185 const argv = []const []const u8{
186 cc_exe,
187 "-E",
188 "-Wp,-v",
189 "-xc",
190 "/dev/null",
191 };
192 // TODO make this use event loop
193 const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
194 const exec_result = if (std.debug.runtime_safety) blk: {
195 break :blk errorable_result catch unreachable;
196 } else blk: {
197 break :blk errorable_result catch |err| switch (err) {
198 error.OutOfMemory => return error.OutOfMemory,
199 else => return error.UnableToSpawnCCompiler,
200 };
201 };
202 defer {
203 loop.allocator.free(exec_result.stdout);
204 loop.allocator.free(exec_result.stderr);
205 }
206
207 switch (exec_result.term) {
208 std.os.ChildProcess.Term.Exited => |code| {
209 if (code != 0) return error.CCompilerExitCode;
210 },
211 else => {
212 return error.CCompilerCrashed;
213 },
214 }
215
216 var it = std.mem.split(exec_result.stderr, "\n\r");
217 var search_paths = std.ArrayList([]const u8).init(loop.allocator);
218 defer search_paths.deinit();
219 while (it.next()) |line| {
220 if (line.len != 0 and line[0] == ' ') {
221 try search_paths.append(line);
222 }
223 }
224 if (search_paths.len == 0) {
225 return error.CCompilerCannotFindHeaders;
226 }
227
228 // search in reverse order
229 var path_i: usize = 0;
230 while (path_i < search_paths.len) : (path_i += 1) {
231 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
232 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
234 defer loop.allocator.free(stdlib_path);
235
236 if (try fileExists(loop.allocator, stdlib_path)) {
237 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
238 return;
239 }
240 }
241
242 return error.LibCStdLibHeaderNotFound;
243 }
244
245 async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void {
246 var search_buf: [2]Search = undefined;
247 const searches = fillSearch(&search_buf, sdk);
248
249 var result_buf = try std.Buffer.initSize(loop.allocator, 0);
250 defer result_buf.deinit();
251
252 for (searches) |search| {
253 result_buf.shrink(0);
254 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
255 try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
256
257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
258 defer loop.allocator.free(stdlib_path);
259
260 if (try fileExists(loop.allocator, stdlib_path)) {
261 self.include_dir = result_buf.toOwnedSlice();
262 return;
263 }
264 }
265
266 return error.LibCStdLibHeaderNotFound;
267 }
268
269 async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
270 var search_buf: [2]Search = undefined;
271 const searches = fillSearch(&search_buf, sdk);
272
273 var result_buf = try std.Buffer.initSize(loop.allocator, 0);
274 defer result_buf.deinit();
275
276 for (searches) |search| {
277 result_buf.shrink(0);
278 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
279 try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version);
280 switch (builtin.arch) {
281 builtin.Arch.i386 => try stream.write("x86"),
282 builtin.Arch.x86_64 => try stream.write("x64"),
283 builtin.Arch.aarch64 => try stream.write("arm"),
284 else => return error.UnsupportedArchitecture,
285 }
286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
287 defer loop.allocator.free(ucrt_lib_path);
288 if (try fileExists(loop.allocator, ucrt_lib_path)) {
289 self.lib_dir = result_buf.toOwnedSlice();
290 return;
291 }
292 }
293 return error.LibCRuntimeNotFound;
294 }
295
296 async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void {
297 self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable);
298 }
299
300 async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
301 self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
302 }
303
304 async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void {
305 var dyn_tests = []DynTest{
306 DynTest{
307 .name = "ld-linux-x86-64.so.2",
308 .result = null,
309 },
310 DynTest{
311 .name = "ld-musl-x86_64.so.1",
312 .result = null,
313 },
314 };
315 var group = event.Group(FindError!void).init(loop);
316 errdefer group.cancelAll();
317 for (dyn_tests) |*dyn_test| {
318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);
319 }
320 try await (async group.wait() catch unreachable);
321 for (dyn_tests) |*dyn_test| {
322 if (dyn_test.result) |result| {
323 self.dynamic_linker_path = result;
324 return;
325 }
326 }
327 }
328
329 const DynTest = struct {
330 name: []const u8,
331 result: ?[]const u8,
332 };
333
334 async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void {
335 if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| {
336 dyn_test.result = result;
337 return;
338 } else |err| switch (err) {
339 error.LibCRuntimeNotFound => return,
340 else => return err,
341 }
342 }
343
344
345 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
346 var search_buf: [2]Search = undefined;
347 const searches = fillSearch(&search_buf, sdk);
348
349 var result_buf = try std.Buffer.initSize(loop.allocator, 0);
350 defer result_buf.deinit();
351
352 for (searches) |search| {
353 result_buf.shrink(0);
354 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
355 try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version);
356 switch (builtin.arch) {
357 builtin.Arch.i386 => try stream.write("x86\\"),
358 builtin.Arch.x86_64 => try stream.write("x64\\"),
359 builtin.Arch.aarch64 => try stream.write("arm\\"),
360 else => return error.UnsupportedArchitecture,
361 }
362 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
363 defer loop.allocator.free(kernel32_path);
364 if (try fileExists(loop.allocator, kernel32_path)) {
365 self.kernel32_lib_dir = result_buf.toOwnedSlice();
366 return;
367 }
368 }
369 return error.LibCKernel32LibNotFound;
370 }
371
372 fn initEmpty(self: *LibCInstallation) void {
373 self.* = LibCInstallation{
374 .include_dir = ([*]const u8)(undefined)[0..0],
375 .lib_dir = null,
376 .static_lib_dir = null,
377 .msvc_lib_dir = null,
378 .kernel32_lib_dir = null,
379 .dynamic_linker_path = null,
380 };
381 }
382};
383
384/// caller owns returned memory
385async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 {
386 const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
387 const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
388 defer loop.allocator.free(arg1);
389 const argv = []const []const u8{ cc_exe, arg1 };
390
391 // TODO This simulates evented I/O for the child process exec
392 await (async loop.yield() catch unreachable);
393 const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
394 const exec_result = if (std.debug.runtime_safety) blk: {
395 break :blk errorable_result catch unreachable;
396 } else blk: {
397 break :blk errorable_result catch |err| switch (err) {
398 error.OutOfMemory => return error.OutOfMemory,
399 else => return error.UnableToSpawnCCompiler,
400 };
401 };
402 defer {
403 loop.allocator.free(exec_result.stdout);
404 loop.allocator.free(exec_result.stderr);
405 }
406 switch (exec_result.term) {
407 std.os.ChildProcess.Term.Exited => |code| {
408 if (code != 0) return error.CCompilerExitCode;
409 },
410 else => {
411 return error.CCompilerCrashed;
412 },
413 }
414 var it = std.mem.split(exec_result.stdout, "\n\r");
415 const line = it.next() orelse return error.LibCRuntimeNotFound;
416 const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound;
417
418 if (want_dirname) {
419 return std.mem.dupe(loop.allocator, u8, dirname);
420 } else {
421 return std.mem.dupe(loop.allocator, u8, line);
422 }
423}
424
425const Search = struct {
426 path: []const u8,
427 version: []const u8,
428};
429
430fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
431 var search_end: usize = 0;
432 if (sdk.path10_ptr) |path10_ptr| {
433 if (sdk.version10_ptr) |ver10_ptr| {
434 search_buf[search_end] = Search{
435 .path = path10_ptr[0..sdk.path10_len],
436 .version = ver10_ptr[0..sdk.version10_len],
437 };
438 search_end += 1;
439 }
440 }
441 if (sdk.path81_ptr) |path81_ptr| {
442 if (sdk.version81_ptr) |ver81_ptr| {
443 search_buf[search_end] = Search{
444 .path = path81_ptr[0..sdk.path81_len],
445 .version = ver81_ptr[0..sdk.version81_len],
446 };
447 search_end += 1;
448 }
449 }
450 return search_buf[0..search_end];
451}
452
453
454fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
455 if (std.os.File.access(allocator, path)) |_| {
456 return true;
457 } else |err| switch (err) {
458 error.NotFound, error.PermissionDenied => return false,
459 error.OutOfMemory => return error.OutOfMemory,
460 else => return error.FileSystem,
461 }
462}
src-self-hosted/link.zig created+724
...@@ -0,0 +1,724 @@
1const std = @import("std");
2const mem = std.mem;
3const c = @import("c.zig");
4const builtin = @import("builtin");
5const ObjectFormat = builtin.ObjectFormat;
6const Compilation = @import("compilation.zig").Compilation;
7const Target = @import("target.zig").Target;
8const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
9const assert = std.debug.assert;
10
11const Context = struct {
12 comp: *Compilation,
13 arena: std.heap.ArenaAllocator,
14 args: std.ArrayList([*]const u8),
15 link_in_crt: bool,
16
17 link_err: error{OutOfMemory}!void,
18 link_msg: std.Buffer,
19
20 libc: *LibCInstallation,
21 out_file_path: std.Buffer,
22};
23
24pub async fn link(comp: *Compilation) !void {
25 var ctx = Context{
26 .comp = comp,
27 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
28 .args = undefined,
29 .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
30 .link_err = {},
31 .link_msg = undefined,
32 .libc = undefined,
33 .out_file_path = undefined,
34 };
35 defer ctx.arena.deinit();
36 ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
37 ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
38
39 if (comp.link_out_file) |out_file| {
40 ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file);
41 } else {
42 ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst());
43 switch (comp.kind) {
44 Compilation.Kind.Exe => {
45 try ctx.out_file_path.append(comp.target.exeFileExt());
46 },
47 Compilation.Kind.Lib => {
48 try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static));
49 },
50 Compilation.Kind.Obj => {
51 try ctx.out_file_path.append(comp.target.objFileExt());
52 },
53 }
54 }
55
56 // even though we're calling LLD as a library it thinks the first
57 // argument is its own exe name
58 try ctx.args.append(c"lld");
59
60 if (comp.haveLibC()) {
61 ctx.libc = ctx.comp.override_libc orelse blk: {
62 switch (comp.target) {
63 Target.Native => {
64 break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
65 },
66 else => return error.LibCRequiredButNotProvidedOrFound,
67 }
68 };
69 }
70
71 try constructLinkerArgs(&ctx);
72
73 if (comp.verbose_link) {
74 for (ctx.args.toSliceConst()) |arg, i| {
75 const space = if (i == 0) "" else " ";
76 std.debug.warn("{}{s}", space, arg);
77 }
78 std.debug.warn("\n");
79 }
80
81 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
82 const args_slice = ctx.args.toSlice();
83 // Not evented I/O. LLD does its own multithreading internally.
84 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
85 if (!ctx.link_msg.isNull()) {
86 // TODO capture these messages and pass them through the system, reporting them through the
87 // event system instead of printing them directly here.
88 // perhaps try to parse and understand them.
89 std.debug.warn("{}\n", ctx.link_msg.toSliceConst());
90 }
91 return error.LinkFailed;
92 }
93}
94
95extern fn ZigLLDLink(
96 oformat: c.ZigLLVM_ObjectFormatType,
97 args: [*]const [*]const u8,
98 arg_count: usize,
99 append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,
100 context: *c_void,
101) bool;
102
103extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void {
104 const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
105 ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
106}
107
108fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {
109 if (ctx.link_msg.isNull()) {
110 try ctx.link_msg.resize(0);
111 }
112 try ctx.link_msg.append(msg);
113}
114
115fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {
116 return switch (ofmt) {
117 ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat,
118 ObjectFormat.coff => c.ZigLLVM_COFF,
119 ObjectFormat.elf => c.ZigLLVM_ELF,
120 ObjectFormat.macho => c.ZigLLVM_MachO,
121 ObjectFormat.wasm => c.ZigLLVM_Wasm,
122 };
123}
124
125fn constructLinkerArgs(ctx: *Context) !void {
126 switch (ctx.comp.target.getObjectFormat()) {
127 ObjectFormat.unknown => unreachable,
128 ObjectFormat.coff => return constructLinkerArgsCoff(ctx),
129 ObjectFormat.elf => return constructLinkerArgsElf(ctx),
130 ObjectFormat.macho => return constructLinkerArgsMachO(ctx),
131 ObjectFormat.wasm => return constructLinkerArgsWasm(ctx),
132 }
133}
134
135fn constructLinkerArgsElf(ctx: *Context) !void {
136 // TODO commented out code in this function
137 //if (g->linker_script) {
138 // lj->args.append("-T");
139 // lj->args.append(g->linker_script);
140 //}
141
142 //if (g->no_rosegment_workaround) {
143 // lj->args.append("--no-rosegment");
144 //}
145 try ctx.args.append(c"--gc-sections");
146
147 //lj->args.append("-m");
148 //lj->args.append(getLDMOption(&g->zig_target));
149
150 //bool is_lib = g->out_type == OutTypeLib;
151 //bool shared = !g->is_static && is_lib;
152 //Buf *soname = nullptr;
153 if (ctx.comp.is_static) {
154 if (ctx.comp.target.isArmOrThumb()) {
155 try ctx.args.append(c"-Bstatic");
156 } else {
157 try ctx.args.append(c"-static");
158 }
159 }
160 //} else if (shared) {
161 // lj->args.append("-shared");
162
163 // if (buf_len(&lj->out_file) == 0) {
164 // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",
165 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
166 // }
167 // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
168 //}
169
170 try ctx.args.append(c"-o");
171 try ctx.args.append(ctx.out_file_path.ptr());
172
173 if (ctx.link_in_crt) {
174 const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
175 const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
176 try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
177 try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
178 try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
179 }
180
181 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
182 // Buf *rpath = g->rpath_list.at(i);
183 // add_rpath(lj, rpath);
184 //}
185 //if (g->each_lib_rpath) {
186 // for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
187 // const char *lib_dir = g->lib_dirs.at(i);
188 // for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
189 // LinkLib *link_lib = g->link_libs_list.at(i);
190 // if (buf_eql_str(link_lib->name, "c")) {
191 // continue;
192 // }
193 // bool does_exist;
194 // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
195 // if (os_file_exists(test_path, &does_exist) != ErrorNone) {
196 // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
197 // }
198 // if (does_exist) {
199 // add_rpath(lj, buf_create_from_str(lib_dir));
200 // break;
201 // }
202 // }
203 // }
204 //}
205
206 //for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
207 // const char *lib_dir = g->lib_dirs.at(i);
208 // lj->args.append("-L");
209 // lj->args.append(lib_dir);
210 //}
211
212 if (ctx.comp.haveLibC()) {
213 try ctx.args.append(c"-L");
214 try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
215
216 try ctx.args.append(c"-L");
217 try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
218
219 if (!ctx.comp.is_static) {
220 const dl = blk: {
221 if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
222 if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl;
223 return error.LibCMissingDynamicLinker;
224 };
225 try ctx.args.append(c"-dynamic-linker");
226 try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
227 }
228 }
229
230 //if (shared) {
231 // lj->args.append("-soname");
232 // lj->args.append(buf_ptr(soname));
233 //}
234
235 // .o files
236 for (ctx.comp.link_objects) |link_object| {
237 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
238 try ctx.args.append(link_obj_with_null.ptr);
239 }
240 try addFnObjects(ctx);
241
242 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
243 // if (g->libc_link_lib == nullptr) {
244 // Buf *builtin_o_path = build_o(g, "builtin");
245 // lj->args.append(buf_ptr(builtin_o_path));
246 // }
247
248 // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
249 // Buf *compiler_rt_o_path = build_compiler_rt(g);
250 // lj->args.append(buf_ptr(compiler_rt_o_path));
251 //}
252
253 //for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
254 // LinkLib *link_lib = g->link_libs_list.at(i);
255 // if (buf_eql_str(link_lib->name, "c")) {
256 // continue;
257 // }
258 // Buf *arg;
259 // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
260 // buf_ends_with_str(link_lib->name, ".so"))
261 // {
262 // arg = link_lib->name;
263 // } else {
264 // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
265 // }
266 // lj->args.append(buf_ptr(arg));
267 //}
268
269 // libc dep
270 if (ctx.comp.haveLibC()) {
271 if (ctx.comp.is_static) {
272 try ctx.args.append(c"--start-group");
273 try ctx.args.append(c"-lgcc");
274 try ctx.args.append(c"-lgcc_eh");
275 try ctx.args.append(c"-lc");
276 try ctx.args.append(c"-lm");
277 try ctx.args.append(c"--end-group");
278 } else {
279 try ctx.args.append(c"-lgcc");
280 try ctx.args.append(c"--as-needed");
281 try ctx.args.append(c"-lgcc_s");
282 try ctx.args.append(c"--no-as-needed");
283 try ctx.args.append(c"-lc");
284 try ctx.args.append(c"-lm");
285 try ctx.args.append(c"-lgcc");
286 try ctx.args.append(c"--as-needed");
287 try ctx.args.append(c"-lgcc_s");
288 try ctx.args.append(c"--no-as-needed");
289 }
290 }
291
292 // crt end
293 if (ctx.link_in_crt) {
294 try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
295 try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
296 }
297
298 if (ctx.comp.target != Target.Native) {
299 try ctx.args.append(c"--allow-shlib-undefined");
300 }
301
302 if (ctx.comp.target.getOs() == builtin.Os.zen) {
303 try ctx.args.append(c"-e");
304 try ctx.args.append(c"_start");
305
306 try ctx.args.append(c"--image-base=0x10000000");
307 }
308}
309
310fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
311 const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
312 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
313 try ctx.args.append(full_path_with_null.ptr);
314}
315
316fn constructLinkerArgsCoff(ctx: *Context) !void {
317 try ctx.args.append(c"-NOLOGO");
318
319 if (!ctx.comp.strip) {
320 try ctx.args.append(c"-DEBUG");
321 }
322
323 switch (ctx.comp.target.getArch()) {
324 builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"),
325 builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"),
326 builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"),
327 else => return error.UnsupportedLinkArchitecture,
328 }
329
330 if (ctx.comp.windows_subsystem_windows) {
331 try ctx.args.append(c"/SUBSYSTEM:windows");
332 } else if (ctx.comp.windows_subsystem_console) {
333 try ctx.args.append(c"/SUBSYSTEM:console");
334 }
335
336 const is_library = ctx.comp.kind == Compilation.Kind.Lib;
337
338 const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
339 try ctx.args.append(out_arg.ptr);
340
341 if (ctx.comp.haveLibC()) {
342 try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
343 try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
344 try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
345 }
346
347 if (ctx.link_in_crt) {
348 const lib_str = if (ctx.comp.is_static) "lib" else "";
349 const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else "";
350
351 if (ctx.comp.is_static) {
352 const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
353 try ctx.args.append(cmt_lib_name.ptr);
354 } else {
355 const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
356 try ctx.args.append(msvcrt_lib_name.ptr);
357 }
358
359 const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
360 try ctx.args.append(vcruntime_lib_name.ptr);
361
362 const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
363 try ctx.args.append(crt_lib_name.ptr);
364
365 // Visual C++ 2015 Conformance Changes
366 // https://msdn.microsoft.com/en-us/library/bb531344.aspx
367 try ctx.args.append(c"legacy_stdio_definitions.lib");
368
369 // msvcrt depends on kernel32
370 try ctx.args.append(c"kernel32.lib");
371 } else {
372 try ctx.args.append(c"-NODEFAULTLIB");
373 if (!is_library) {
374 try ctx.args.append(c"-ENTRY:WinMainCRTStartup");
375 // TODO
376 //if (g->have_winmain) {
377 // lj->args.append("-ENTRY:WinMain");
378 //} else {
379 // lj->args.append("-ENTRY:WinMainCRTStartup");
380 //}
381 }
382 }
383
384 if (is_library and !ctx.comp.is_static) {
385 try ctx.args.append(c"-DLL");
386 }
387
388 //for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
389 // const char *lib_dir = g->lib_dirs.at(i);
390 // lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir)));
391 //}
392
393 for (ctx.comp.link_objects) |link_object| {
394 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
395 try ctx.args.append(link_obj_with_null.ptr);
396 }
397 try addFnObjects(ctx);
398
399 switch (ctx.comp.kind) {
400 Compilation.Kind.Exe, Compilation.Kind.Lib => {
401 if (!ctx.comp.haveLibC()) {
402 @panic("TODO");
403 //Buf *builtin_o_path = build_o(g, "builtin");
404 //lj->args.append(buf_ptr(builtin_o_path));
405 }
406
407 // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
408 // TODO
409 //Buf *compiler_rt_o_path = build_compiler_rt(g);
410 //lj->args.append(buf_ptr(compiler_rt_o_path));
411 },
412 Compilation.Kind.Obj => {},
413 }
414
415 //Buf *def_contents = buf_alloc();
416 //ZigList<const char *> gen_lib_args = {0};
417 //for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
418 // LinkLib *link_lib = g->link_libs_list.at(lib_i);
419 // if (buf_eql_str(link_lib->name, "c")) {
420 // continue;
421 // }
422 // if (link_lib->provided_explicitly) {
423 // if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) {
424 // Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
425 // lj->args.append(buf_ptr(arg));
426 // }
427 // else {
428 // lj->args.append(buf_ptr(link_lib->name));
429 // }
430 // } else {
431 // buf_resize(def_contents, 0);
432 // buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name));
433 // for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) {
434 // Buf *symbol_name = link_lib->symbols.at(exp_i);
435 // buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name));
436 // }
437 // buf_appendf(def_contents, "\n");
438
439 // Buf *def_path = buf_alloc();
440 // os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path);
441 // os_write_file(def_path, def_contents);
442
443 // Buf *generated_lib_path = buf_alloc();
444 // os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path);
445
446 // gen_lib_args.resize(0);
447 // gen_lib_args.append("link");
448
449 // coff_append_machine_arg(g, &gen_lib_args);
450 // gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path))));
451 // gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path))));
452 // Buf diag = BUF_INIT;
453 // if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) {
454 // fprintf(stderr, "%s\n", buf_ptr(&diag));
455 // exit(1);
456 // }
457 // lj->args.append(buf_ptr(generated_lib_path));
458 // }
459 //}
460}
461
462fn constructLinkerArgsMachO(ctx: *Context) !void {
463 try ctx.args.append(c"-demangle");
464
465 if (ctx.comp.linker_rdynamic) {
466 try ctx.args.append(c"-export_dynamic");
467 }
468
469 const is_lib = ctx.comp.kind == Compilation.Kind.Lib;
470 const shared = !ctx.comp.is_static and is_lib;
471 if (ctx.comp.is_static) {
472 try ctx.args.append(c"-static");
473 } else {
474 try ctx.args.append(c"-dynamic");
475 }
476
477 //if (is_lib) {
478 // if (!g->is_static) {
479 // lj->args.append("-dylib");
480
481 // Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major);
482 // lj->args.append("-compatibility_version");
483 // lj->args.append(buf_ptr(compat_vers));
484
485 // Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
486 // g->version_major, g->version_minor, g->version_patch);
487 // lj->args.append("-current_version");
488 // lj->args.append(buf_ptr(cur_vers));
489
490 // // TODO getting an error when running an executable when doing this rpath thing
491 // //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
492 // // buf_ptr(g->root_out_name), g->version_major);
493 // //lj->args.append("-install_name");
494 // //lj->args.append(buf_ptr(dylib_install_name));
495
496 // if (buf_len(&lj->out_file) == 0) {
497 // buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
498 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
499 // }
500 // }
501 //}
502
503 try ctx.args.append(c"-arch");
504 const darwin_arch_str = try std.cstr.addNullByte(
505 &ctx.arena.allocator,
506 ctx.comp.target.getDarwinArchString(),
507 );
508 try ctx.args.append(darwin_arch_str.ptr);
509
510 const platform = try DarwinPlatform.get(ctx.comp);
511 switch (platform.kind) {
512 DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"),
513 DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"),
514 DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"),
515 }
516 const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
517 try ctx.args.append(ver_str.ptr);
518
519 if (ctx.comp.kind == Compilation.Kind.Exe) {
520 if (ctx.comp.is_static) {
521 try ctx.args.append(c"-no_pie");
522 } else {
523 try ctx.args.append(c"-pie");
524 }
525 }
526
527 try ctx.args.append(c"-o");
528 try ctx.args.append(ctx.out_file_path.ptr());
529
530 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
531 // Buf *rpath = g->rpath_list.at(i);
532 // add_rpath(lj, rpath);
533 //}
534 //add_rpath(lj, &lj->out_file);
535
536 if (shared) {
537 try ctx.args.append(c"-headerpad_max_install_names");
538 } else if (ctx.comp.is_static) {
539 try ctx.args.append(c"-lcrt0.o");
540 } else {
541 switch (platform.kind) {
542 DarwinPlatform.Kind.MacOS => {
543 if (platform.versionLessThan(10, 5)) {
544 try ctx.args.append(c"-lcrt1.o");
545 } else if (platform.versionLessThan(10, 6)) {
546 try ctx.args.append(c"-lcrt1.10.5.o");
547 } else if (platform.versionLessThan(10, 8)) {
548 try ctx.args.append(c"-lcrt1.10.6.o");
549 }
550 },
551 DarwinPlatform.Kind.IPhoneOS => {
552 if (ctx.comp.target.getArch() == builtin.Arch.aarch64) {
553 // iOS does not need any crt1 files for arm64
554 } else if (platform.versionLessThan(3, 1)) {
555 try ctx.args.append(c"-lcrt1.o");
556 } else if (platform.versionLessThan(6, 0)) {
557 try ctx.args.append(c"-lcrt1.3.1.o");
558 }
559 },
560 DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed
561 }
562 }
563
564 //for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
565 // const char *lib_dir = g->lib_dirs.at(i);
566 // lj->args.append("-L");
567 // lj->args.append(lib_dir);
568 //}
569
570 for (ctx.comp.link_objects) |link_object| {
571 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
572 try ctx.args.append(link_obj_with_null.ptr);
573 }
574 try addFnObjects(ctx);
575
576 //// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
577 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
578 // Buf *compiler_rt_o_path = build_compiler_rt(g);
579 // lj->args.append(buf_ptr(compiler_rt_o_path));
580 //}
581
582 if (ctx.comp.target == Target.Native) {
583 for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
584 if (mem.eql(u8, lib.name, "c")) {
585 // on Darwin, libSystem has libc in it, but also you have to use it
586 // to make syscalls because the syscall numbers are not documented
587 // and change between versions.
588 // so we always link against libSystem
589 try ctx.args.append(c"-lSystem");
590 } else {
591 if (mem.indexOfScalar(u8, lib.name, '/') == null) {
592 const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
593 try ctx.args.append(arg.ptr);
594 } else {
595 const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
596 try ctx.args.append(arg.ptr);
597 }
598 }
599 }
600 } else {
601 try ctx.args.append(c"-undefined");
602 try ctx.args.append(c"dynamic_lookup");
603 }
604
605 if (platform.kind == DarwinPlatform.Kind.MacOS) {
606 if (platform.versionLessThan(10, 5)) {
607 try ctx.args.append(c"-lgcc_s.10.4");
608 } else if (platform.versionLessThan(10, 6)) {
609 try ctx.args.append(c"-lgcc_s.10.5");
610 }
611 } else {
612 @panic("TODO");
613 }
614
615 //for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
616 // lj->args.append("-framework");
617 // lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
618 //}
619}
620
621fn constructLinkerArgsWasm(ctx: *Context) void {
622 @panic("TODO");
623}
624
625fn addFnObjects(ctx: *Context) !void {
626 // at this point it's guaranteed nobody else has this lock, so we circumvent it
627 // and avoid having to be a coroutine
628 const fn_link_set = &ctx.comp.fn_link_set.private_data;
629
630 var it = fn_link_set.first;
631 while (it) |node| {
632 const fn_val = node.data orelse {
633 // handle the tombstone. See Value.Fn.destroy.
634 it = node.next;
635 fn_link_set.remove(node);
636 ctx.comp.gpa().destroy(node);
637 continue;
638 };
639 try ctx.args.append(fn_val.containing_object.ptr());
640 it = node.next;
641 }
642}
643
644const DarwinPlatform = struct {
645 kind: Kind,
646 major: u32,
647 minor: u32,
648 micro: u32,
649
650 const Kind = enum {
651 MacOS,
652 IPhoneOS,
653 IPhoneOSSimulator,
654 };
655
656 fn get(comp: *Compilation) !DarwinPlatform {
657 var result: DarwinPlatform = undefined;
658 const ver_str = switch (comp.darwin_version_min) {
659 Compilation.DarwinVersionMin.MacOS => |ver| blk: {
660 result.kind = Kind.MacOS;
661 break :blk ver;
662 },
663 Compilation.DarwinVersionMin.Ios => |ver| blk: {
664 result.kind = Kind.IPhoneOS;
665 break :blk ver;
666 },
667 Compilation.DarwinVersionMin.None => blk: {
668 assert(comp.target.getOs() == builtin.Os.macosx);
669 result.kind = Kind.MacOS;
670 break :blk "10.10";
671 },
672 };
673
674 var had_extra: bool = undefined;
675 try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,);
676 if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
677 return error.InvalidDarwinVersionString;
678 }
679
680 if (result.kind == Kind.IPhoneOS) {
681 switch (comp.target.getArch()) {
682 builtin.Arch.i386,
683 builtin.Arch.x86_64,
684 => result.kind = Kind.IPhoneOSSimulator,
685 else => {},
686 }
687 }
688 return result;
689 }
690
691 fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
692 if (self.major < major)
693 return true;
694 if (self.major > major)
695 return false;
696 if (self.minor < minor)
697 return true;
698 return false;
699 }
700};
701
702/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
703/// grouped values as integers. Numbers which are not provided are set to 0.
704/// return true if the entire string was parsed (9.2), or all groups were
705/// parsed (10.3.5extrastuff).
706fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
707 major.* = 0;
708 minor.* = 0;
709 micro.* = 0;
710 had_extra.* = false;
711
712 if (str.len == 0)
713 return error.InvalidDarwinVersionString;
714
715 var start_pos: usize = 0;
716 for ([]*u32{major, minor, micro}) |v| {
717 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
718 const end_pos = dot_pos orelse str.len;
719 v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
720 start_pos = (dot_pos orelse return) + 1;
721 if (start_pos == str.len) return;
722 }
723 had_extra.* = true;
724}
src-self-hosted/llvm.zig+112-3
...@@ -2,6 +2,12 @@ const builtin = @import("builtin");...@@ -2,6 +2,12 @@ const builtin = @import("builtin");
2const c = @import("c.zig");2const c = @import("c.zig");
3const assert = @import("std").debug.assert;3const assert = @import("std").debug.assert;
44
5// we wrap the c module for 3 reasons:
6// 1. to avoid accidentally calling the non-thread-safe functions
7// 2. patch up some of the types to remove nullability
8// 3. some functions have been augmented by zig_llvm.cpp to be more powerful,
9// such as ZigLLVMTargetMachineEmitToFile
10
5pub const AttributeIndex = c_uint;11pub const AttributeIndex = c_uint;
6pub const Bool = c_int;12pub const Bool = c_int;
713
...@@ -12,25 +18,59 @@ pub const ValueRef = removeNullability(c.LLVMValueRef);...@@ -12,25 +18,59 @@ pub const ValueRef = removeNullability(c.LLVMValueRef);
12pub const TypeRef = removeNullability(c.LLVMTypeRef);18pub const TypeRef = removeNullability(c.LLVMTypeRef);
13pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);19pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);
14pub const AttributeRef = removeNullability(c.LLVMAttributeRef);20pub const AttributeRef = removeNullability(c.LLVMAttributeRef);
21pub const TargetRef = removeNullability(c.LLVMTargetRef);
22pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
23pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
24pub const DIBuilder = c.ZigLLVMDIBuilder;
1525
26pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
16pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;27pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
17pub const AddFunction = c.LLVMAddFunction;28pub const AddFunction = c.LLVMAddFunction;
29pub const AddGlobal = c.LLVMAddGlobal;
30pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
31pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
32pub const ArrayType = c.LLVMArrayType;
18pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;33pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
34pub const ConstAllOnes = c.LLVMConstAllOnes;
35pub const ConstArray = c.LLVMConstArray;
36pub const ConstBitCast = c.LLVMConstBitCast;
19pub const ConstInt = c.LLVMConstInt;37pub const ConstInt = c.LLVMConstInt;
38pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
39pub const ConstNeg = c.LLVMConstNeg;
40pub const ConstNull = c.LLVMConstNull;
20pub const ConstStringInContext = c.LLVMConstStringInContext;41pub const ConstStringInContext = c.LLVMConstStringInContext;
21pub const ConstStructInContext = c.LLVMConstStructInContext;42pub const ConstStructInContext = c.LLVMConstStructInContext;
43pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData;
22pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;44pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
45pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit;
46pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder;
23pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;47pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;
48pub const CreateFile = c.ZigLLVMCreateFile;
24pub const CreateStringAttribute = c.LLVMCreateStringAttribute;49pub const CreateStringAttribute = c.LLVMCreateStringAttribute;
50pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout;
51pub const CreateTargetMachine = c.LLVMCreateTargetMachine;
52pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
25pub const DisposeBuilder = c.LLVMDisposeBuilder;53pub const DisposeBuilder = c.LLVMDisposeBuilder;
54pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
55pub const DisposeMessage = c.LLVMDisposeMessage;
26pub const DisposeModule = c.LLVMDisposeModule;56pub const DisposeModule = c.LLVMDisposeModule;
57pub const DisposeTargetData = c.LLVMDisposeTargetData;
58pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine;
27pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext;59pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext;
28pub const DumpModule = c.LLVMDumpModule;60pub const DumpModule = c.LLVMDumpModule;
29pub const FP128TypeInContext = c.LLVMFP128TypeInContext;61pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
30pub const FloatTypeInContext = c.LLVMFloatTypeInContext;62pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
31pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;63pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
64pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
32pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;65pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
66pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
67pub const GetUndef = c.LLVMGetUndef;
33pub const HalfTypeInContext = c.LLVMHalfTypeInContext;68pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
69pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
70pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
71pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos;
72pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs;
73pub const InitializeAllTargets = c.LLVMInitializeAllTargets;
34pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext;74pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext;
35pub const Int128TypeInContext = c.LLVMInt128TypeInContext;75pub const Int128TypeInContext = c.LLVMInt128TypeInContext;
36pub const Int16TypeInContext = c.LLVMInt16TypeInContext;76pub const Int16TypeInContext = c.LLVMInt16TypeInContext;
...@@ -47,13 +87,26 @@ pub const MDStringInContext = c.LLVMMDStringInContext;...@@ -47,13 +87,26 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
47pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;87pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
48pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;88pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
49pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;89pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
90pub const PointerType = c.LLVMPointerType;
91pub const SetAlignment = c.LLVMSetAlignment;
92pub const SetDataLayout = c.LLVMSetDataLayout;
93pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
94pub const SetInitializer = c.LLVMSetInitializer;
95pub const SetLinkage = c.LLVMSetLinkage;
96pub const SetTarget = c.LLVMSetTarget;
97pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
50pub const StructTypeInContext = c.LLVMStructTypeInContext;98pub const StructTypeInContext = c.LLVMStructTypeInContext;
51pub const TokenTypeInContext = c.LLVMTokenTypeInContext;99pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
100pub const TypeOf = c.LLVMTypeOf;
52pub const VoidTypeInContext = c.LLVMVoidTypeInContext;101pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
53pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;102pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
54pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;103pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
55pub const ConstAllOnes = c.LLVMConstAllOnes;104
56pub const ConstNull = c.LLVMConstNull;105pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
106pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
107
108pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
109extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
57110
58pub const VerifyModule = LLVMVerifyModule;111pub const VerifyModule = LLVMVerifyModule;
59extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;112extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
...@@ -83,10 +136,66 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;...@@ -83,10 +136,66 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
83pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction;136pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction;
84pub const VerifierFailureAction = c.LLVMVerifierFailureAction;137pub const VerifierFailureAction = c.LLVMVerifierFailureAction;
85138
139pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone;
140pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess;
141pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault;
142pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive;
143pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel;
144
145pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault;
146pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic;
147pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC;
148pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic;
149pub const RelocMode = c.LLVMRelocMode;
150
151pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault;
152pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault;
153pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall;
154pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel;
155pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium;
156pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge;
157pub const CodeModel = c.LLVMCodeModel;
158
159pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly;
160pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
161pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
162pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
163
164pub const CCallConv = c.LLVMCCallConv;
165pub const FastCallConv = c.LLVMFastCallConv;
166pub const ColdCallConv = c.LLVMColdCallConv;
167pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv;
168pub const AnyRegCallConv = c.LLVMAnyRegCallConv;
169pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv;
170pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv;
171pub const CallConv = c.LLVMCallConv;
172
173pub const FnInline = extern enum {
174 Auto,
175 Always,
176 Never,
177};
178
86fn removeNullability(comptime T: type) type {179fn removeNullability(comptime T: type) type {
87 comptime assert(@typeId(T) == builtin.TypeId.Optional);180 comptime assert(@typeId(T) == builtin.TypeId.Optional);
88 return T.Child;181 return T.Child;
89}182}
90183
91pub const BuildRet = LLVMBuildRet;184pub const BuildRet = LLVMBuildRet;
92extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef;185extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
186
187pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
188extern fn ZigLLVMTargetMachineEmitToFile(
189 targ_machine_ref: TargetMachineRef,
190 module_ref: ModuleRef,
191 filename: [*]const u8,
192 output_type: EmitOutputType,
193 error_message: *[*]u8,
194 is_debug: bool,
195 is_small: bool,
196) bool;
197
198pub const BuildCall = ZigLLVMBuildCall;
199extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
200
201pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;
src-self-hosted/main.zig+87-60
...@@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;...@@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
18const Compilation = @import("compilation.zig").Compilation;18const Compilation = @import("compilation.zig").Compilation;
19const Target = @import("target.zig").Target;19const Target = @import("target.zig").Target;
20const errmsg = @import("errmsg.zig");20const errmsg = @import("errmsg.zig");
21const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2122
22var stderr_file: os.File = undefined;23var stderr_file: os.File = undefined;
23var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;24var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
...@@ -28,13 +29,14 @@ const usage =...@@ -28,13 +29,14 @@ const usage =
28 \\29 \\
29 \\Commands:30 \\Commands:
30 \\31 \\
31 \\ build-exe [source] Create executable from source or object files32 \\ build-exe [source] Create executable from source or object files
32 \\ build-lib [source] Create library from source or object files33 \\ build-lib [source] Create library from source or object files
33 \\ build-obj [source] Create object from source or assembly34 \\ build-obj [source] Create object from source or assembly
34 \\ fmt [source] Parse file and render in canonical zig format35 \\ fmt [source] Parse file and render in canonical zig format
35 \\ targets List available compilation targets36 \\ libc [paths_file] Display native libc paths file or validate one
36 \\ version Print version number and exit37 \\ targets List available compilation targets
37 \\ zen Print zen of zig and exit38 \\ version Print version number and exit
39 \\ zen Print zen of zig and exit
38 \\40 \\
39 \\41 \\
40;42;
...@@ -85,6 +87,10 @@ pub fn main() !void {...@@ -85,6 +87,10 @@ pub fn main() !void {
85 .name = "fmt",87 .name = "fmt",
86 .exec = cmdFmt,88 .exec = cmdFmt,
87 },89 },
90 Command{
91 .name = "libc",
92 .exec = cmdLibC,
93 },
88 Command{94 Command{
89 .name = "targets",95 .name = "targets",
90 .exec = cmdTargets,96 .exec = cmdTargets,
...@@ -130,11 +136,10 @@ const usage_build_generic =...@@ -130,11 +136,10 @@ const usage_build_generic =
130 \\ --color [auto|off|on] Enable or disable colored error messages136 \\ --color [auto|off|on] Enable or disable colored error messages
131 \\137 \\
132 \\Compile Options:138 \\Compile Options:
139 \\ --libc [file] Provide a file which specifies libc paths
133 \\ --assembly [source] Add assembly file to build140 \\ --assembly [source] Add assembly file to build
134 \\ --cache-dir [path] Override the cache directory
135 \\ --emit [filetype] Emit a specific file format as compilation output141 \\ --emit [filetype] Emit a specific file format as compilation output
136 \\ --enable-timing-info Print timing diagnostics142 \\ --enable-timing-info Print timing diagnostics
137 \\ --libc-include-dir [path] Directory where libc stdlib.h resides
138 \\ --name [name] Override output name143 \\ --name [name] Override output name
139 \\ --output [file] Override destination path144 \\ --output [file] Override destination path
140 \\ --output-h [file] Override generated header file path145 \\ --output-h [file] Override generated header file path
...@@ -163,12 +168,7 @@ const usage_build_generic =...@@ -163,12 +168,7 @@ const usage_build_generic =
163 \\168 \\
164 \\Link Options:169 \\Link Options:
165 \\ --ar-path [path] Set the path to ar170 \\ --ar-path [path] Set the path to ar
166 \\ --dynamic-linker [path] Set the path to ld.so
167 \\ --each-lib-rpath Add rpath for each used dynamic library171 \\ --each-lib-rpath Add rpath for each used dynamic library
168 \\ --libc-lib-dir [path] Directory where libc crt1.o resides
169 \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
170 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
171 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
172 \\ --library [lib] Link against lib172 \\ --library [lib] Link against lib
173 \\ --forbid-library [lib] Make it an error to link against lib173 \\ --forbid-library [lib] Make it an error to link against lib
174 \\ --library-path [dir] Add a directory to the library search path174 \\ --library-path [dir] Add a directory to the library search path
...@@ -203,14 +203,13 @@ const args_build_generic = []Flag{...@@ -203,14 +203,13 @@ const args_build_generic = []Flag{
203 }),203 }),
204204
205 Flag.ArgMergeN("--assembly", 1),205 Flag.ArgMergeN("--assembly", 1),
206 Flag.Arg1("--cache-dir"),
207 Flag.Option("--emit", []const []const u8{206 Flag.Option("--emit", []const []const u8{
208 "asm",207 "asm",
209 "bin",208 "bin",
210 "llvm-ir",209 "llvm-ir",
211 }),210 }),
212 Flag.Bool("--enable-timing-info"),211 Flag.Bool("--enable-timing-info"),
213 Flag.Arg1("--libc-include-dir"),212 Flag.Arg1("--libc"),
214 Flag.Arg1("--name"),213 Flag.Arg1("--name"),
215 Flag.Arg1("--output"),214 Flag.Arg1("--output"),
216 Flag.Arg1("--output-h"),215 Flag.Arg1("--output-h"),
...@@ -234,12 +233,7 @@ const args_build_generic = []Flag{...@@ -234,12 +233,7 @@ const args_build_generic = []Flag{
234 Flag.Arg1("-mllvm"),233 Flag.Arg1("-mllvm"),
235234
236 Flag.Arg1("--ar-path"),235 Flag.Arg1("--ar-path"),
237 Flag.Arg1("--dynamic-linker"),
238 Flag.Bool("--each-lib-rpath"),236 Flag.Bool("--each-lib-rpath"),
239 Flag.Arg1("--libc-lib-dir"),
240 Flag.Arg1("--libc-static-lib-dir"),
241 Flag.Arg1("--msvc-lib-dir"),
242 Flag.Arg1("--kernel32-lib-dir"),
243 Flag.ArgMergeN("--library", 1),237 Flag.ArgMergeN("--library", 1),
244 Flag.ArgMergeN("--forbid-library", 1),238 Flag.ArgMergeN("--forbid-library", 1),
245 Flag.ArgMergeN("--library-path", 1),239 Flag.ArgMergeN("--library-path", 1),
...@@ -363,6 +357,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -363,6 +357,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
363 }357 }
364 };358 };
365359
360 const is_static = flags.present("static");
361
366 const assembly_files = flags.many("assembly");362 const assembly_files = flags.many("assembly");
367 const link_objects = flags.many("object");363 const link_objects = flags.many("object");
368 if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) {364 if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) {
...@@ -375,21 +371,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -375,21 +371,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
375 os.exit(1);371 os.exit(1);
376 }372 }
377373
378 const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
379 const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
380 try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
381 os.exit(1);
382 };
383 defer allocator.free(full_cache_dir);
384
385 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);374 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
386 defer allocator.free(zig_lib_dir);375 defer allocator.free(zig_lib_dir);
387376
377 var override_libc: LibCInstallation = undefined;
378
388 var loop: event.Loop = undefined;379 var loop: event.Loop = undefined;
389 try loop.initMultiThreaded(allocator);380 try loop.initMultiThreaded(allocator);
390 defer loop.deinit();381 defer loop.deinit();
391382
392 var event_loop_local = EventLoopLocal.init(&loop);383 var event_loop_local = try EventLoopLocal.init(&loop);
393 defer event_loop_local.deinit();384 defer event_loop_local.deinit();
394385
395 var comp = try Compilation.create(386 var comp = try Compilation.create(
...@@ -399,11 +390,20 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -399,11 +390,20 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
399 Target.Native,390 Target.Native,
400 out_type,391 out_type,
401 build_mode,392 build_mode,
393 is_static,
402 zig_lib_dir,394 zig_lib_dir,
403 full_cache_dir,
404 );395 );
405 defer comp.destroy();396 defer comp.destroy();
406397
398 if (flags.single("libc")) |libc_path| {
399 parseLibcPaths(loop.allocator, &override_libc, libc_path);
400 comp.override_libc = &override_libc;
401 }
402
403 for (flags.many("library")) |lib| {
404 _ = try comp.addLinkLib(lib, true);
405 }
406
407 comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);407 comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
408 comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);408 comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
409 comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);409 comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
...@@ -426,26 +426,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -426,26 +426,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
426 comp.clang_argv = clang_argv_buf.toSliceConst();426 comp.clang_argv = clang_argv_buf.toSliceConst();
427427
428 comp.strip = flags.present("strip");428 comp.strip = flags.present("strip");
429 comp.is_static = flags.present("static");
430
431 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
432 comp.libc_lib_dir = libc_lib_dir;
433 }
434 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
435 comp.libc_static_lib_dir = libc_static_lib_dir;
436 }
437 if (flags.single("libc-include-dir")) |libc_include_dir| {
438 comp.libc_include_dir = libc_include_dir;
439 }
440 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
441 comp.msvc_lib_dir = msvc_lib_dir;
442 }
443 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
444 comp.kernel32_lib_dir = kernel32_lib_dir;
445 }
446 if (flags.single("dynamic-linker")) |dynamic_linker| {
447 comp.dynamic_linker = dynamic_linker;
448 }
449429
450 comp.verbose_tokenize = flags.present("verbose-tokenize");430 comp.verbose_tokenize = flags.present("verbose-tokenize");
451 comp.verbose_ast_tree = flags.present("verbose-ast-tree");431 comp.verbose_ast_tree = flags.present("verbose-ast-tree");
...@@ -481,9 +461,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -481,9 +461,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
481 }461 }
482462
483 comp.emit_file_type = emit_type;463 comp.emit_file_type = emit_type;
484 comp.link_objects = link_objects;
485 comp.assembly_files = assembly_files;464 comp.assembly_files = assembly_files;
486 comp.link_out_file = flags.single("out-file");465 comp.link_out_file = flags.single("output");
466 comp.link_objects = link_objects;
487467
488 try comp.build();468 try comp.build();
489 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);469 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
...@@ -497,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {...@@ -497,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
497477
498 switch (build_event) {478 switch (build_event) {
499 Compilation.Event.Ok => {479 Compilation.Event.Ok => {
500 std.debug.warn("Build succeeded\n");
501 return;480 return;
502 },481 },
503 Compilation.Event.Error => |err| {482 Compilation.Event.Error => |err| {
...@@ -506,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {...@@ -506,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
506 },485 },
507 Compilation.Event.Fail => |msgs| {486 Compilation.Event.Fail => |msgs| {
508 for (msgs) |msg| {487 for (msgs) |msg| {
509 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);488 defer msg.destroy();
489 msg.printToFile(&stderr_file, color) catch os.exit(1);
510 }490 }
511 },491 },
512 }492 }
...@@ -577,6 +557,53 @@ const Fmt = struct {...@@ -577,6 +557,53 @@ const Fmt = struct {
577 }557 }
578};558};
579559
560fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
561 libc.parse(allocator, libc_paths_file, stderr) catch |err| {
562 stderr.print(
563 "Unable to parse libc path file '{}': {}.\n" ++
564 "Try running `zig libc` to see an example for the native target.\n",
565 libc_paths_file,
566 @errorName(err),
567 ) catch os.exit(1);
568 os.exit(1);
569 };
570}
571
572fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
573 switch (args.len) {
574 0 => {},
575 1 => {
576 var libc_installation: LibCInstallation = undefined;
577 parseLibcPaths(allocator, &libc_installation, args[0]);
578 return;
579 },
580 else => {
581 try stderr.print("unexpected extra parameter: {}\n", args[1]);
582 os.exit(1);
583 },
584 }
585
586 var loop: event.Loop = undefined;
587 try loop.initMultiThreaded(allocator);
588 defer loop.deinit();
589
590 var event_loop_local = try EventLoopLocal.init(&loop);
591 defer event_loop_local.deinit();
592
593 const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
594 defer cancel handle;
595
596 loop.run();
597}
598
599async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
600 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
601 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
602 os.exit(1);
603 };
604 libc.render(stdout) catch os.exit(1);
605}
606
580fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {607fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
581 var flags = try Args.parse(allocator, args_fmt_spec, args);608 var flags = try Args.parse(allocator, args_fmt_spec, args);
582 defer flags.deinit();609 defer flags.deinit();
...@@ -620,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -620,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
620647
621 var error_it = tree.errors.iterator(0);648 var error_it = tree.errors.iterator(0);
622 while (error_it.next()) |parse_error| {649 while (error_it.next()) |parse_error| {
623 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");650 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
624 defer allocator.destroy(msg);651 defer msg.destroy();
625652
626 try errmsg.printToFile(&stderr_file, msg, color);653 try msg.printToFile(&stderr_file, color);
627 }654 }
628 if (tree.errors.len != 0) {655 if (tree.errors.len != 0) {
629 os.exit(1);656 os.exit(1);
...@@ -676,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -676,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
676703
677 var error_it = tree.errors.iterator(0);704 var error_it = tree.errors.iterator(0);
678 while (error_it.next()) |parse_error| {705 while (error_it.next()) |parse_error| {
679 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);706 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
680 defer allocator.destroy(msg);707 defer msg.destroy();
681708
682 try errmsg.printToFile(&stderr_file, msg, color);709 try msg.printToFile(&stderr_file, color);
683 }710 }
684 if (tree.errors.len != 0) {711 if (tree.errors.len != 0) {
685 fmt.any_error = true;712 fmt.any_error = true;
src-self-hosted/package.zig created+29
...@@ -0,0 +1,29 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const Buffer = std.Buffer;
5
6pub const Package = struct {
7 root_src_dir: Buffer,
8 root_src_path: Buffer,
9
10 /// relative to root_src_dir
11 table: Table,
12
13 pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8);
14
15 /// makes internal copies of root_src_dir and root_src_path
16 /// allocator should be an arena allocator because Package never frees anything
17 pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
18 return allocator.create(Package{
19 .root_src_dir = try Buffer.init(allocator, root_src_dir),
20 .root_src_path = try Buffer.init(allocator, root_src_path),
21 .table = Table.init(allocator),
22 });
23 }
24
25 pub fn add(self: *Package, name: []const u8, package: *Package) !void {
26 const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package);
27 assert(entry == null);
28 }
29};
src-self-hosted/parsed_file.zig deleted-6
...@@ -1,6 +0,0 @@
1const ast = @import("std").zig.ast;
2
3pub const ParsedFile = struct {
4 tree: ast.Tree,
5 realpath: []const u8,
6};
src-self-hosted/scope.zig+114-39
...@@ -8,6 +8,8 @@ const ast = std.zig.ast;...@@ -8,6 +8,8 @@ const ast = std.zig.ast;
8const Value = @import("value.zig").Value;8const Value = @import("value.zig").Value;
9const ir = @import("ir.zig");9const ir = @import("ir.zig");
10const Span = @import("errmsg.zig").Span;10const Span = @import("errmsg.zig").Span;
11const assert = std.debug.assert;
12const event = std.event;
1113
12pub const Scope = struct {14pub const Scope = struct {
13 id: Id,15 id: Id,
...@@ -23,7 +25,8 @@ pub const Scope = struct {...@@ -23,7 +25,8 @@ pub const Scope = struct {
23 if (base.ref_count == 0) {25 if (base.ref_count == 0) {
24 if (base.parent) |parent| parent.deref(comp);26 if (base.parent) |parent| parent.deref(comp);
25 switch (base.id) {27 switch (base.id) {
26 Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(),28 Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
29 Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp),
27 Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),30 Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
28 Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),31 Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
29 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),32 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
...@@ -33,6 +36,15 @@ pub const Scope = struct {...@@ -33,6 +36,15 @@ pub const Scope = struct {
33 }36 }
34 }37 }
3538
39 pub fn findRoot(base: *Scope) *Root {
40 var scope = base;
41 while (scope.parent) |parent| {
42 scope = parent;
43 }
44 assert(scope.id == Id.Root);
45 return @fieldParentPtr(Root, "base", scope);
46 }
47
36 pub fn findFnDef(base: *Scope) ?*FnDef {48 pub fn findFnDef(base: *Scope) ?*FnDef {
37 var scope = base;49 var scope = base;
38 while (true) {50 while (true) {
...@@ -44,12 +56,33 @@ pub const Scope = struct {...@@ -44,12 +56,33 @@ pub const Scope = struct {
44 Id.Defer,56 Id.Defer,
45 Id.DeferExpr,57 Id.DeferExpr,
46 Id.CompTime,58 Id.CompTime,
59 Id.Root,
60 => scope = scope.parent orelse return null,
61 }
62 }
63 }
64
65 pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
66 var scope = base;
67 while (true) {
68 switch (scope.id) {
69 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
70
71 Id.FnDef,
72 Id.Decls,
73 => return null,
74
75 Id.Block,
76 Id.Defer,
77 Id.CompTime,
78 Id.Root,
47 => scope = scope.parent orelse return null,79 => scope = scope.parent orelse return null,
48 }80 }
49 }81 }
50 }82 }
5183
52 pub const Id = enum {84 pub const Id = enum {
85 Root,
53 Decls,86 Decls,
54 Block,87 Block,
55 FnDef,88 FnDef,
...@@ -58,42 +91,82 @@ pub const Scope = struct {...@@ -58,42 +91,82 @@ pub const Scope = struct {
58 DeferExpr,91 DeferExpr,
59 };92 };
6093
94 pub const Root = struct {
95 base: Scope,
96 tree: *ast.Tree,
97 realpath: []const u8,
98
99 /// Creates a Root scope with 1 reference
100 /// Takes ownership of realpath
101 /// Takes ownership of tree, will deinit and destroy when done.
102 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
103 const self = try comp.gpa().create(Root{
104 .base = Scope{
105 .id = Id.Root,
106 .parent = null,
107 .ref_count = 1,
108 },
109 .tree = tree,
110 .realpath = realpath,
111 });
112 errdefer comp.gpa().destroy(self);
113
114 return self;
115 }
116
117 pub fn destroy(self: *Root, comp: *Compilation) void {
118 comp.gpa().free(self.tree.source);
119 self.tree.deinit();
120 comp.gpa().destroy(self.tree);
121 comp.gpa().free(self.realpath);
122 comp.gpa().destroy(self);
123 }
124 };
125
61 pub const Decls = struct {126 pub const Decls = struct {
62 base: Scope,127 base: Scope,
63 table: Decl.Table,128
129 /// The lock must be respected for writing. However once name_future resolves,
130 /// readers can freely access it.
131 table: event.Locked(Decl.Table),
132
133 /// Once this future is resolved, the table is complete and available for unlocked
134 /// read-only access. It does not mean all the decls are resolved; it means only that
135 /// the table has all the names. Each decl in the table has its own resolution state.
136 name_future: event.Future(void),
64137
65 /// Creates a Decls scope with 1 reference138 /// Creates a Decls scope with 1 reference
66 pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls {139 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
67 const self = try comp.a().create(Decls{140 const self = try comp.gpa().create(Decls{
68 .base = Scope{141 .base = Scope{
69 .id = Id.Decls,142 .id = Id.Decls,
70 .parent = parent,143 .parent = parent,
71 .ref_count = 1,144 .ref_count = 1,
72 },145 },
73 .table = undefined,146 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
147 .name_future = event.Future(void).init(comp.loop),
74 });148 });
75 errdefer comp.a().destroy(self);149 parent.ref();
76
77 self.table = Decl.Table.init(comp.a());
78 errdefer self.table.deinit();
79
80 if (parent) |p| p.ref();
81
82 return self;150 return self;
83 }151 }
84152
85 pub fn destroy(self: *Decls) void {153 pub fn destroy(self: *Decls, comp: *Compilation) void {
86 self.table.deinit();154 self.table.deinit();
87 self.table.allocator.destroy(self);155 comp.gpa().destroy(self);
156 }
157
158 pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
159 _ = await (async self.name_future.get() catch unreachable);
160 return &self.table.private_data;
88 }161 }
89 };162 };
90163
91 pub const Block = struct {164 pub const Block = struct {
92 base: Scope,165 base: Scope,
93 incoming_values: std.ArrayList(*ir.Instruction),166 incoming_values: std.ArrayList(*ir.Inst),
94 incoming_blocks: std.ArrayList(*ir.BasicBlock),167 incoming_blocks: std.ArrayList(*ir.BasicBlock),
95 end_block: *ir.BasicBlock,168 end_block: *ir.BasicBlock,
96 is_comptime: *ir.Instruction,169 is_comptime: *ir.Inst,
97170
98 safety: Safety,171 safety: Safety,
99172
...@@ -125,8 +198,8 @@ pub const Scope = struct {...@@ -125,8 +198,8 @@ pub const Scope = struct {
125 };198 };
126199
127 /// Creates a Block scope with 1 reference200 /// Creates a Block scope with 1 reference
128 pub fn create(comp: *Compilation, parent: ?*Scope) !*Block {201 pub fn create(comp: *Compilation, parent: *Scope) !*Block {
129 const self = try comp.a().create(Block{202 const self = try comp.gpa().create(Block{
130 .base = Scope{203 .base = Scope{
131 .id = Id.Block,204 .id = Id.Block,
132 .parent = parent,205 .parent = parent,
...@@ -138,14 +211,14 @@ pub const Scope = struct {...@@ -138,14 +211,14 @@ pub const Scope = struct {
138 .is_comptime = undefined,211 .is_comptime = undefined,
139 .safety = Safety.Auto,212 .safety = Safety.Auto,
140 });213 });
141 errdefer comp.a().destroy(self);214 errdefer comp.gpa().destroy(self);
142215
143 if (parent) |p| p.ref();216 parent.ref();
144 return self;217 return self;
145 }218 }
146219
147 pub fn destroy(self: *Block, comp: *Compilation) void {220 pub fn destroy(self: *Block, comp: *Compilation) void {
148 comp.a().destroy(self);221 comp.gpa().destroy(self);
149 }222 }
150 };223 };
151224
...@@ -157,8 +230,8 @@ pub const Scope = struct {...@@ -157,8 +230,8 @@ pub const Scope = struct {
157230
158 /// Creates a FnDef scope with 1 reference231 /// Creates a FnDef scope with 1 reference
159 /// Must set the fn_val later232 /// Must set the fn_val later
160 pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef {233 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
161 const self = try comp.a().create(FnDef{234 const self = try comp.gpa().create(FnDef{
162 .base = Scope{235 .base = Scope{
163 .id = Id.FnDef,236 .id = Id.FnDef,
164 .parent = parent,237 .parent = parent,
...@@ -167,13 +240,13 @@ pub const Scope = struct {...@@ -167,13 +240,13 @@ pub const Scope = struct {
167 .fn_val = undefined,240 .fn_val = undefined,
168 });241 });
169242
170 if (parent) |p| p.ref();243 parent.ref();
171244
172 return self;245 return self;
173 }246 }
174247
175 pub fn destroy(self: *FnDef, comp: *Compilation) void {248 pub fn destroy(self: *FnDef, comp: *Compilation) void {
176 comp.a().destroy(self);249 comp.gpa().destroy(self);
177 }250 }
178 };251 };
179252
...@@ -181,8 +254,8 @@ pub const Scope = struct {...@@ -181,8 +254,8 @@ pub const Scope = struct {
181 base: Scope,254 base: Scope,
182255
183 /// Creates a CompTime scope with 1 reference256 /// Creates a CompTime scope with 1 reference
184 pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime {257 pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
185 const self = try comp.a().create(CompTime{258 const self = try comp.gpa().create(CompTime{
186 .base = Scope{259 .base = Scope{
187 .id = Id.CompTime,260 .id = Id.CompTime,
188 .parent = parent,261 .parent = parent,
...@@ -190,12 +263,12 @@ pub const Scope = struct {...@@ -190,12 +263,12 @@ pub const Scope = struct {
190 },263 },
191 });264 });
192265
193 if (parent) |p| p.ref();266 parent.ref();
194 return self;267 return self;
195 }268 }
196269
197 pub fn destroy(self: *CompTime, comp: *Compilation) void {270 pub fn destroy(self: *CompTime, comp: *Compilation) void {
198 comp.a().destroy(self);271 comp.gpa().destroy(self);
199 }272 }
200 };273 };
201274
...@@ -212,11 +285,11 @@ pub const Scope = struct {...@@ -212,11 +285,11 @@ pub const Scope = struct {
212 /// Creates a Defer scope with 1 reference285 /// Creates a Defer scope with 1 reference
213 pub fn create(286 pub fn create(
214 comp: *Compilation,287 comp: *Compilation,
215 parent: ?*Scope,288 parent: *Scope,
216 kind: Kind,289 kind: Kind,
217 defer_expr_scope: *DeferExpr,290 defer_expr_scope: *DeferExpr,
218 ) !*Defer {291 ) !*Defer {
219 const self = try comp.a().create(Defer{292 const self = try comp.gpa().create(Defer{
220 .base = Scope{293 .base = Scope{
221 .id = Id.Defer,294 .id = Id.Defer,
222 .parent = parent,295 .parent = parent,
...@@ -225,42 +298,44 @@ pub const Scope = struct {...@@ -225,42 +298,44 @@ pub const Scope = struct {
225 .defer_expr_scope = defer_expr_scope,298 .defer_expr_scope = defer_expr_scope,
226 .kind = kind,299 .kind = kind,
227 });300 });
228 errdefer comp.a().destroy(self);301 errdefer comp.gpa().destroy(self);
229302
230 defer_expr_scope.base.ref();303 defer_expr_scope.base.ref();
231304
232 if (parent) |p| p.ref();305 parent.ref();
233 return self;306 return self;
234 }307 }
235308
236 pub fn destroy(self: *Defer, comp: *Compilation) void {309 pub fn destroy(self: *Defer, comp: *Compilation) void {
237 self.defer_expr_scope.base.deref(comp);310 self.defer_expr_scope.base.deref(comp);
238 comp.a().destroy(self);311 comp.gpa().destroy(self);
239 }312 }
240 };313 };
241314
242 pub const DeferExpr = struct {315 pub const DeferExpr = struct {
243 base: Scope,316 base: Scope,
244 expr_node: *ast.Node,317 expr_node: *ast.Node,
318 reported_err: bool,
245319
246 /// Creates a DeferExpr scope with 1 reference320 /// Creates a DeferExpr scope with 1 reference
247 pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {321 pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
248 const self = try comp.a().create(DeferExpr{322 const self = try comp.gpa().create(DeferExpr{
249 .base = Scope{323 .base = Scope{
250 .id = Id.DeferExpr,324 .id = Id.DeferExpr,
251 .parent = parent,325 .parent = parent,
252 .ref_count = 1,326 .ref_count = 1,
253 },327 },
254 .expr_node = expr_node,328 .expr_node = expr_node,
329 .reported_err = false,
255 });330 });
256 errdefer comp.a().destroy(self);331 errdefer comp.gpa().destroy(self);
257332
258 if (parent) |p| p.ref();333 parent.ref();
259 return self;334 return self;
260 }335 }
261336
262 pub fn destroy(self: *DeferExpr, comp: *Compilation) void {337 pub fn destroy(self: *DeferExpr, comp: *Compilation) void {
263 comp.a().destroy(self);338 comp.gpa().destroy(self);
264 }339 }
265 };340 };
266};341};
src-self-hosted/target.zig+529-27
...@@ -1,60 +1,562 @@...@@ -1,60 +1,562 @@
1const std = @import("std");
1const builtin = @import("builtin");2const builtin = @import("builtin");
2const c = @import("c.zig");3const llvm = @import("llvm.zig");
4const CInt = @import("c_int.zig").CInt;
35
4pub const CrossTarget = struct {6pub const FloatAbi = enum {
5 arch: builtin.Arch,7 Hard,
6 os: builtin.Os,8 Soft,
7 environ: builtin.Environ,9 SoftFp,
8};10};
911
10pub const Target = union(enum) {12pub const Target = union(enum) {
11 Native,13 Native,
12 Cross: CrossTarget,14 Cross: Cross,
1315
14 pub fn oFileExt(self: *const Target) []const u8 {16 pub const Cross = struct {
15 const environ = switch (self.*) {17 arch: builtin.Arch,
16 Target.Native => builtin.environ,18 os: builtin.Os,
17 Target.Cross => |t| t.environ,19 environ: builtin.Environ,
18 };20 object_format: builtin.ObjectFormat,
19 return switch (environ) {21 };
20 builtin.Environ.msvc => ".obj",22
23 pub fn objFileExt(self: Target) []const u8 {
24 return switch (self.getObjectFormat()) {
25 builtin.ObjectFormat.coff => ".obj",
21 else => ".o",26 else => ".o",
22 };27 };
23 }28 }
2429
25 pub fn exeFileExt(self: *const Target) []const u8 {30 pub fn exeFileExt(self: Target) []const u8 {
26 return switch (self.getOs()) {31 return switch (self.getOs()) {
27 builtin.Os.windows => ".exe",32 builtin.Os.windows => ".exe",
28 else => "",33 else => "",
29 };34 };
30 }35 }
3136
32 pub fn getOs(self: *const Target) builtin.Os {37 pub fn libFileExt(self: Target, is_static: bool) []const u8 {
33 return switch (self.*) {38 return switch (self.getOs()) {
39 builtin.Os.windows => if (is_static) ".lib" else ".dll",
40 else => if (is_static) ".a" else ".so",
41 };
42 }
43
44 pub fn getOs(self: Target) builtin.Os {
45 return switch (self) {
34 Target.Native => builtin.os,46 Target.Native => builtin.os,
35 Target.Cross => |t| t.os,47 @TagType(Target).Cross => |t| t.os,
48 };
49 }
50
51 pub fn getArch(self: Target) builtin.Arch {
52 return switch (self) {
53 Target.Native => builtin.arch,
54 @TagType(Target).Cross => |t| t.arch,
55 };
56 }
57
58 pub fn getEnviron(self: Target) builtin.Environ {
59 return switch (self) {
60 Target.Native => builtin.environ,
61 @TagType(Target).Cross => |t| t.environ,
62 };
63 }
64
65 pub fn getObjectFormat(self: Target) builtin.ObjectFormat {
66 return switch (self) {
67 Target.Native => builtin.object_format,
68 @TagType(Target).Cross => |t| t.object_format,
69 };
70 }
71
72 pub fn isWasm(self: Target) bool {
73 return switch (self.getArch()) {
74 builtin.Arch.wasm32, builtin.Arch.wasm64 => true,
75 else => false,
36 };76 };
37 }77 }
3878
39 pub fn isDarwin(self: *const Target) bool {79 pub fn isDarwin(self: Target) bool {
40 return switch (self.getOs()) {80 return switch (self.getOs()) {
41 builtin.Os.ios, builtin.Os.macosx => true,81 builtin.Os.ios, builtin.Os.macosx => true,
42 else => false,82 else => false,
43 };83 };
44 }84 }
4585
46 pub fn isWindows(self: *const Target) bool {86 pub fn isWindows(self: Target) bool {
47 return switch (self.getOs()) {87 return switch (self.getOs()) {
48 builtin.Os.windows => true,88 builtin.Os.windows => true,
49 else => false,89 else => false,
50 };90 };
51 }91 }
52};
5392
54pub fn initializeAll() void {93 /// TODO expose the arch and subarch separately
55 c.LLVMInitializeAllTargets();94 pub fn isArmOrThumb(self: Target) bool {
56 c.LLVMInitializeAllTargetInfos();95 return switch (self.getArch()) {
57 c.LLVMInitializeAllTargetMCs();96 builtin.Arch.armv8_3a,
58 c.LLVMInitializeAllAsmPrinters();97 builtin.Arch.armv8_2a,
59 c.LLVMInitializeAllAsmParsers();98 builtin.Arch.armv8_1a,
60}99 builtin.Arch.armv8,
100 builtin.Arch.armv8r,
101 builtin.Arch.armv8m_baseline,
102 builtin.Arch.armv8m_mainline,
103 builtin.Arch.armv7,
104 builtin.Arch.armv7em,
105 builtin.Arch.armv7m,
106 builtin.Arch.armv7s,
107 builtin.Arch.armv7k,
108 builtin.Arch.armv7ve,
109 builtin.Arch.armv6,
110 builtin.Arch.armv6m,
111 builtin.Arch.armv6k,
112 builtin.Arch.armv6t2,
113 builtin.Arch.armv5,
114 builtin.Arch.armv5te,
115 builtin.Arch.armv4t,
116 builtin.Arch.armebv8_3a,
117 builtin.Arch.armebv8_2a,
118 builtin.Arch.armebv8_1a,
119 builtin.Arch.armebv8,
120 builtin.Arch.armebv8r,
121 builtin.Arch.armebv8m_baseline,
122 builtin.Arch.armebv8m_mainline,
123 builtin.Arch.armebv7,
124 builtin.Arch.armebv7em,
125 builtin.Arch.armebv7m,
126 builtin.Arch.armebv7s,
127 builtin.Arch.armebv7k,
128 builtin.Arch.armebv7ve,
129 builtin.Arch.armebv6,
130 builtin.Arch.armebv6m,
131 builtin.Arch.armebv6k,
132 builtin.Arch.armebv6t2,
133 builtin.Arch.armebv5,
134 builtin.Arch.armebv5te,
135 builtin.Arch.armebv4t,
136 builtin.Arch.thumb,
137 builtin.Arch.thumbeb,
138 => true,
139 else => false,
140 };
141 }
142
143 pub fn initializeAll() void {
144 llvm.InitializeAllTargets();
145 llvm.InitializeAllTargetInfos();
146 llvm.InitializeAllTargetMCs();
147 llvm.InitializeAllAsmPrinters();
148 llvm.InitializeAllAsmParsers();
149 }
150
151 pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer {
152 var result = try std.Buffer.initSize(allocator, 0);
153 errdefer result.deinit();
154
155 // LLVM WebAssembly output support requires the target to be activated at
156 // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
157 //
158 // LLVM determines the output format based on the environment suffix,
159 // defaulting to an object based on the architecture. The default format in
160 // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
161 // explicitly set this ourself in order for it to work.
162 //
163 // This is fixed in LLVM 7 and you will be able to get wasm output by
164 // using the target triple `wasm32-unknown-unknown-unknown`.
165 const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron());
166
167 var out = &std.io.BufferOutStream.init(&result).stream;
168 try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name);
169
170 return result;
171 }
172
173 pub fn is64bit(self: Target) bool {
174 return self.getArchPtrBitWidth() == 64;
175 }
176
177 pub fn getArchPtrBitWidth(self: Target) u32 {
178 switch (self.getArch()) {
179 builtin.Arch.avr,
180 builtin.Arch.msp430,
181 => return 16,
182
183 builtin.Arch.arc,
184 builtin.Arch.armv8_3a,
185 builtin.Arch.armv8_2a,
186 builtin.Arch.armv8_1a,
187 builtin.Arch.armv8,
188 builtin.Arch.armv8r,
189 builtin.Arch.armv8m_baseline,
190 builtin.Arch.armv8m_mainline,
191 builtin.Arch.armv7,
192 builtin.Arch.armv7em,
193 builtin.Arch.armv7m,
194 builtin.Arch.armv7s,
195 builtin.Arch.armv7k,
196 builtin.Arch.armv7ve,
197 builtin.Arch.armv6,
198 builtin.Arch.armv6m,
199 builtin.Arch.armv6k,
200 builtin.Arch.armv6t2,
201 builtin.Arch.armv5,
202 builtin.Arch.armv5te,
203 builtin.Arch.armv4t,
204 builtin.Arch.armebv8_3a,
205 builtin.Arch.armebv8_2a,
206 builtin.Arch.armebv8_1a,
207 builtin.Arch.armebv8,
208 builtin.Arch.armebv8r,
209 builtin.Arch.armebv8m_baseline,
210 builtin.Arch.armebv8m_mainline,
211 builtin.Arch.armebv7,
212 builtin.Arch.armebv7em,
213 builtin.Arch.armebv7m,
214 builtin.Arch.armebv7s,
215 builtin.Arch.armebv7k,
216 builtin.Arch.armebv7ve,
217 builtin.Arch.armebv6,
218 builtin.Arch.armebv6m,
219 builtin.Arch.armebv6k,
220 builtin.Arch.armebv6t2,
221 builtin.Arch.armebv5,
222 builtin.Arch.armebv5te,
223 builtin.Arch.armebv4t,
224 builtin.Arch.hexagon,
225 builtin.Arch.le32,
226 builtin.Arch.mips,
227 builtin.Arch.mipsel,
228 builtin.Arch.nios2,
229 builtin.Arch.powerpc,
230 builtin.Arch.r600,
231 builtin.Arch.riscv32,
232 builtin.Arch.sparc,
233 builtin.Arch.sparcel,
234 builtin.Arch.tce,
235 builtin.Arch.tcele,
236 builtin.Arch.thumb,
237 builtin.Arch.thumbeb,
238 builtin.Arch.i386,
239 builtin.Arch.xcore,
240 builtin.Arch.nvptx,
241 builtin.Arch.amdil,
242 builtin.Arch.hsail,
243 builtin.Arch.spir,
244 builtin.Arch.kalimbav3,
245 builtin.Arch.kalimbav4,
246 builtin.Arch.kalimbav5,
247 builtin.Arch.shave,
248 builtin.Arch.lanai,
249 builtin.Arch.wasm32,
250 builtin.Arch.renderscript32,
251 => return 32,
252
253 builtin.Arch.aarch64,
254 builtin.Arch.aarch64_be,
255 builtin.Arch.mips64,
256 builtin.Arch.mips64el,
257 builtin.Arch.powerpc64,
258 builtin.Arch.powerpc64le,
259 builtin.Arch.riscv64,
260 builtin.Arch.x86_64,
261 builtin.Arch.nvptx64,
262 builtin.Arch.le64,
263 builtin.Arch.amdil64,
264 builtin.Arch.hsail64,
265 builtin.Arch.spir64,
266 builtin.Arch.wasm64,
267 builtin.Arch.renderscript64,
268 builtin.Arch.amdgcn,
269 builtin.Arch.bpfel,
270 builtin.Arch.bpfeb,
271 builtin.Arch.sparcv9,
272 builtin.Arch.s390x,
273 => return 64,
274 }
275 }
276
277 pub fn getFloatAbi(self: Target) FloatAbi {
278 return switch (self.getEnviron()) {
279 builtin.Environ.gnueabihf,
280 builtin.Environ.eabihf,
281 builtin.Environ.musleabihf,
282 => FloatAbi.Hard,
283 else => FloatAbi.Soft,
284 };
285 }
286
287 pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
288 const env = self.getEnviron();
289 const arch = self.getArch();
290 switch (env) {
291 builtin.Environ.android => {
292 if (self.is64bit()) {
293 return "/system/bin/linker64";
294 } else {
295 return "/system/bin/linker";
296 }
297 },
298 builtin.Environ.gnux32 => {
299 if (arch == builtin.Arch.x86_64) {
300 return "/libx32/ld-linux-x32.so.2";
301 }
302 },
303 builtin.Environ.musl,
304 builtin.Environ.musleabi,
305 builtin.Environ.musleabihf,
306 => {
307 if (arch == builtin.Arch.x86_64) {
308 return "/lib/ld-musl-x86_64.so.1";
309 }
310 },
311 else => {},
312 }
313 switch (arch) {
314 builtin.Arch.i386,
315 builtin.Arch.sparc,
316 builtin.Arch.sparcel,
317 => return "/lib/ld-linux.so.2",
318
319 builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
320 builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
321
322 builtin.Arch.armv8_3a,
323 builtin.Arch.armv8_2a,
324 builtin.Arch.armv8_1a,
325 builtin.Arch.armv8,
326 builtin.Arch.armv8r,
327 builtin.Arch.armv8m_baseline,
328 builtin.Arch.armv8m_mainline,
329 builtin.Arch.armv7,
330 builtin.Arch.armv7em,
331 builtin.Arch.armv7m,
332 builtin.Arch.armv7s,
333 builtin.Arch.armv7k,
334 builtin.Arch.armv7ve,
335 builtin.Arch.armv6,
336 builtin.Arch.armv6m,
337 builtin.Arch.armv6k,
338 builtin.Arch.armv6t2,
339 builtin.Arch.armv5,
340 builtin.Arch.armv5te,
341 builtin.Arch.armv4t,
342 builtin.Arch.thumb,
343 => return switch (self.getFloatAbi()) {
344 FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
345 else => return "/lib/ld-linux.so.3",
346 },
347
348 builtin.Arch.armebv8_3a,
349 builtin.Arch.armebv8_2a,
350 builtin.Arch.armebv8_1a,
351 builtin.Arch.armebv8,
352 builtin.Arch.armebv8r,
353 builtin.Arch.armebv8m_baseline,
354 builtin.Arch.armebv8m_mainline,
355 builtin.Arch.armebv7,
356 builtin.Arch.armebv7em,
357 builtin.Arch.armebv7m,
358 builtin.Arch.armebv7s,
359 builtin.Arch.armebv7k,
360 builtin.Arch.armebv7ve,
361 builtin.Arch.armebv6,
362 builtin.Arch.armebv6m,
363 builtin.Arch.armebv6k,
364 builtin.Arch.armebv6t2,
365 builtin.Arch.armebv5,
366 builtin.Arch.armebv5te,
367 builtin.Arch.armebv4t,
368 builtin.Arch.thumbeb,
369 => return switch (self.getFloatAbi()) {
370 FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
371 else => return "/lib/ld-linux.so.3",
372 },
373
374 builtin.Arch.mips,
375 builtin.Arch.mipsel,
376 builtin.Arch.mips64,
377 builtin.Arch.mips64el,
378 => return null,
379
380 builtin.Arch.powerpc => return "/lib/ld.so.1",
381 builtin.Arch.powerpc64 => return "/lib64/ld64.so.2",
382 builtin.Arch.powerpc64le => return "/lib64/ld64.so.2",
383 builtin.Arch.s390x => return "/lib64/ld64.so.1",
384 builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2",
385 builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2",
386
387 builtin.Arch.arc,
388 builtin.Arch.avr,
389 builtin.Arch.bpfel,
390 builtin.Arch.bpfeb,
391 builtin.Arch.hexagon,
392 builtin.Arch.msp430,
393 builtin.Arch.nios2,
394 builtin.Arch.r600,
395 builtin.Arch.amdgcn,
396 builtin.Arch.riscv32,
397 builtin.Arch.riscv64,
398 builtin.Arch.tce,
399 builtin.Arch.tcele,
400 builtin.Arch.xcore,
401 builtin.Arch.nvptx,
402 builtin.Arch.nvptx64,
403 builtin.Arch.le32,
404 builtin.Arch.le64,
405 builtin.Arch.amdil,
406 builtin.Arch.amdil64,
407 builtin.Arch.hsail,
408 builtin.Arch.hsail64,
409 builtin.Arch.spir,
410 builtin.Arch.spir64,
411 builtin.Arch.kalimbav3,
412 builtin.Arch.kalimbav4,
413 builtin.Arch.kalimbav5,
414 builtin.Arch.shave,
415 builtin.Arch.lanai,
416 builtin.Arch.wasm32,
417 builtin.Arch.wasm64,
418 builtin.Arch.renderscript32,
419 builtin.Arch.renderscript64,
420 => return null,
421 }
422 }
423
424 pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
425 var result: llvm.TargetRef = undefined;
426 var err_msg: [*]u8 = undefined;
427 if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) {
428 std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg);
429 return error.UnsupportedTarget;
430 }
431 return result;
432 }
433
434 pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 {
435 const arch = self.getArch();
436 switch (self.getOs()) {
437 builtin.Os.freestanding => switch (self.getArch()) {
438 builtin.Arch.msp430 => switch (id) {
439 CInt.Id.Short,
440 CInt.Id.UShort,
441 CInt.Id.Int,
442 CInt.Id.UInt,
443 => return 16,
444 CInt.Id.Long,
445 CInt.Id.ULong,
446 => return 32,
447 CInt.Id.LongLong,
448 CInt.Id.ULongLong,
449 => return 64,
450 },
451 else => switch (id) {
452 CInt.Id.Short,
453 CInt.Id.UShort,
454 => return 16,
455 CInt.Id.Int,
456 CInt.Id.UInt,
457 => return 32,
458 CInt.Id.Long,
459 CInt.Id.ULong,
460 => return self.getArchPtrBitWidth(),
461 CInt.Id.LongLong,
462 CInt.Id.ULongLong,
463 => return 64,
464 },
465 },
466
467 builtin.Os.linux,
468 builtin.Os.macosx,
469 builtin.Os.openbsd,
470 builtin.Os.zen,
471 => switch (id) {
472 CInt.Id.Short,
473 CInt.Id.UShort,
474 => return 16,
475 CInt.Id.Int,
476 CInt.Id.UInt,
477 => return 32,
478 CInt.Id.Long,
479 CInt.Id.ULong,
480 => return self.getArchPtrBitWidth(),
481 CInt.Id.LongLong,
482 CInt.Id.ULongLong,
483 => return 64,
484 },
485
486 builtin.Os.windows => switch (id) {
487 CInt.Id.Short,
488 CInt.Id.UShort,
489 => return 16,
490 CInt.Id.Int,
491 CInt.Id.UInt,
492 => return 32,
493 CInt.Id.Long,
494 CInt.Id.ULong,
495 CInt.Id.LongLong,
496 CInt.Id.ULongLong,
497 => return 64,
498 },
499
500 builtin.Os.ananas,
501 builtin.Os.cloudabi,
502 builtin.Os.dragonfly,
503 builtin.Os.freebsd,
504 builtin.Os.fuchsia,
505 builtin.Os.ios,
506 builtin.Os.kfreebsd,
507 builtin.Os.lv2,
508 builtin.Os.netbsd,
509 builtin.Os.solaris,
510 builtin.Os.haiku,
511 builtin.Os.minix,
512 builtin.Os.rtems,
513 builtin.Os.nacl,
514 builtin.Os.cnk,
515 builtin.Os.aix,
516 builtin.Os.cuda,
517 builtin.Os.nvcl,
518 builtin.Os.amdhsa,
519 builtin.Os.ps4,
520 builtin.Os.elfiamcu,
521 builtin.Os.tvos,
522 builtin.Os.watchos,
523 builtin.Os.mesa3d,
524 builtin.Os.contiki,
525 builtin.Os.amdpal,
526 => @panic("TODO specify the C integer type sizes for this OS"),
527 }
528 }
529
530 pub fn getDarwinArchString(self: Target) []const u8 {
531 const arch = self.getArch();
532 switch (arch) {
533 builtin.Arch.aarch64 => return "arm64",
534 builtin.Arch.thumb,
535 builtin.Arch.armv8_3a,
536 builtin.Arch.armv8_2a,
537 builtin.Arch.armv8_1a,
538 builtin.Arch.armv8,
539 builtin.Arch.armv8r,
540 builtin.Arch.armv8m_baseline,
541 builtin.Arch.armv8m_mainline,
542 builtin.Arch.armv7,
543 builtin.Arch.armv7em,
544 builtin.Arch.armv7m,
545 builtin.Arch.armv7s,
546 builtin.Arch.armv7k,
547 builtin.Arch.armv7ve,
548 builtin.Arch.armv6,
549 builtin.Arch.armv6m,
550 builtin.Arch.armv6k,
551 builtin.Arch.armv6t2,
552 builtin.Arch.armv5,
553 builtin.Arch.armv5te,
554 builtin.Arch.armv4t,
555 => return "arm",
556 builtin.Arch.powerpc => return "ppc",
557 builtin.Arch.powerpc64 => return "ppc64",
558 builtin.Arch.powerpc64le => return "ppc64le",
559 else => return @tagName(arch),
560 }
561 }
562};
src-self-hosted/test.zig+90-15
...@@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic;...@@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic;
8const errmsg = @import("errmsg.zig");8const errmsg = @import("errmsg.zig");
9const EventLoopLocal = @import("compilation.zig").EventLoopLocal;9const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
1010
11test "compile errors" {11var ctx: TestContext = undefined;
12 var ctx: TestContext = undefined;12
13test "stage2" {
13 try ctx.init();14 try ctx.init();
14 defer ctx.deinit();15 defer ctx.deinit();
1516
16 try @import("../test/stage2/compile_errors.zig").addCases(&ctx);17 try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
18 try @import("../test/stage2/compare_output.zig").addCases(&ctx);
1719
18 try ctx.run();20 try ctx.run();
19}21}
...@@ -25,7 +27,6 @@ pub const TestContext = struct {...@@ -25,7 +27,6 @@ pub const TestContext = struct {
25 loop: std.event.Loop,27 loop: std.event.Loop,
26 event_loop_local: EventLoopLocal,28 event_loop_local: EventLoopLocal,
27 zig_lib_dir: []u8,29 zig_lib_dir: []u8,
28 zig_cache_dir: []u8,
29 file_index: std.atomic.Int(usize),30 file_index: std.atomic.Int(usize),
30 group: std.event.Group(error!void),31 group: std.event.Group(error!void),
31 any_err: error!void,32 any_err: error!void,
...@@ -38,7 +39,6 @@ pub const TestContext = struct {...@@ -38,7 +39,6 @@ pub const TestContext = struct {
38 .loop = undefined,39 .loop = undefined,
39 .event_loop_local = undefined,40 .event_loop_local = undefined,
40 .zig_lib_dir = undefined,41 .zig_lib_dir = undefined,
41 .zig_cache_dir = undefined,
42 .group = undefined,42 .group = undefined,
43 .file_index = std.atomic.Int(usize).init(0),43 .file_index = std.atomic.Int(usize).init(0),
44 };44 };
...@@ -46,7 +46,7 @@ pub const TestContext = struct {...@@ -46,7 +46,7 @@ pub const TestContext = struct {
46 try self.loop.initMultiThreaded(allocator);46 try self.loop.initMultiThreaded(allocator);
47 errdefer self.loop.deinit();47 errdefer self.loop.deinit();
4848
49 self.event_loop_local = EventLoopLocal.init(&self.loop);49 self.event_loop_local = try EventLoopLocal.init(&self.loop);
50 errdefer self.event_loop_local.deinit();50 errdefer self.event_loop_local.deinit();
5151
52 self.group = std.event.Group(error!void).init(&self.loop);52 self.group = std.event.Group(error!void).init(&self.loop);
...@@ -55,16 +55,12 @@ pub const TestContext = struct {...@@ -55,16 +55,12 @@ pub const TestContext = struct {
55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
56 errdefer allocator.free(self.zig_lib_dir);56 errdefer allocator.free(self.zig_lib_dir);
5757
58 self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator);
59 errdefer allocator.free(self.zig_cache_dir);
60
61 try std.os.makePath(allocator, tmp_dir_name);58 try std.os.makePath(allocator, tmp_dir_name);
62 errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};59 errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
63 }60 }
6461
65 fn deinit(self: *TestContext) void {62 fn deinit(self: *TestContext) void {
66 std.os.deleteTree(allocator, tmp_dir_name) catch {};63 std.os.deleteTree(allocator, tmp_dir_name) catch {};
67 allocator.free(self.zig_cache_dir);
68 allocator.free(self.zig_lib_dir);64 allocator.free(self.zig_lib_dir);
69 self.event_loop_local.deinit();65 self.event_loop_local.deinit();
70 self.loop.deinit();66 self.loop.deinit();
...@@ -107,8 +103,8 @@ pub const TestContext = struct {...@@ -107,8 +103,8 @@ pub const TestContext = struct {
107 Target.Native,103 Target.Native,
108 Compilation.Kind.Obj,104 Compilation.Kind.Obj,
109 builtin.Mode.Debug,105 builtin.Mode.Debug,
106 true, // is_static
110 self.zig_lib_dir,107 self.zig_lib_dir,
111 self.zig_cache_dir,
112 );108 );
113 errdefer comp.destroy();109 errdefer comp.destroy();
114110
...@@ -117,6 +113,84 @@ pub const TestContext = struct {...@@ -117,6 +113,84 @@ pub const TestContext = struct {
117 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
118 }114 }
119115
116 fn testCompareOutputLibC(
117 self: *TestContext,
118 source: []const u8,
119 expected_output: []const u8,
120 ) !void {
121 var file_index_buf: [20]u8 = undefined;
122 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
123 const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
124
125 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
126 if (std.os.path.dirname(file1_path)) |dirname| {
127 try std.os.makePath(allocator, dirname);
128 }
129
130 // TODO async I/O
131 try std.io.writeFile(allocator, file1_path, source);
132
133 var comp = try Compilation.create(
134 &self.event_loop_local,
135 "test",
136 file1_path,
137 Target.Native,
138 Compilation.Kind.Exe,
139 builtin.Mode.Debug,
140 false,
141 self.zig_lib_dir,
142 );
143 errdefer comp.destroy();
144
145 _ = try comp.addLinkLib("c", true);
146 comp.link_out_file = output_file;
147 try comp.build();
148
149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
150 }
151
152 async fn getModuleEventSuccess(
153 comp: *Compilation,
154 exe_file: []const u8,
155 expected_output: []const u8,
156 ) !void {
157 // TODO this should not be necessary
158 const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
159
160 defer comp.destroy();
161 const build_event = await (async comp.events.get() catch unreachable);
162
163 switch (build_event) {
164 Compilation.Event.Ok => {
165 const argv = []const []const u8{exe_file_2};
166 // TODO use event loop
167 const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
168 switch (child.term) {
169 std.os.ChildProcess.Term.Exited => |code| {
170 if (code != 0) {
171 return error.BadReturnCode;
172 }
173 },
174 else => {
175 return error.Crashed;
176 },
177 }
178 if (!mem.eql(u8, child.stdout, expected_output)) {
179 return error.OutputMismatch;
180 }
181 },
182 Compilation.Event.Error => |err| return err,
183 Compilation.Event.Fail => |msgs| {
184 var stderr = try std.io.getStdErr();
185 try stderr.write("build incorrectly failed:\n");
186 for (msgs) |msg| {
187 defer msg.destroy();
188 try msg.printToFile(&stderr, errmsg.Color.Auto);
189 }
190 },
191 }
192 }
193
120 async fn getModuleEvent(194 async fn getModuleEvent(
121 comp: *Compilation,195 comp: *Compilation,
122 source: []const u8,196 source: []const u8,
...@@ -138,10 +212,10 @@ pub const TestContext = struct {...@@ -138,10 +212,10 @@ pub const TestContext = struct {
138 Compilation.Event.Fail => |msgs| {212 Compilation.Event.Fail => |msgs| {
139 assertOrPanic(msgs.len != 0);213 assertOrPanic(msgs.len != 0);
140 for (msgs) |msg| {214 for (msgs) |msg| {
141 if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
142 const first_token = msg.tree.tokens.at(msg.span.first);216 const first_token = msg.getTree().tokens.at(msg.span.first);
143 const last_token = msg.tree.tokens.at(msg.span.first);217 const last_token = msg.getTree().tokens.at(msg.span.first);
144 const start_loc = msg.tree.tokenLocationPtr(0, first_token);218 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
145 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {219 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
146 return;220 return;
147 }221 }
...@@ -158,7 +232,8 @@ pub const TestContext = struct {...@@ -158,7 +232,8 @@ pub const TestContext = struct {
158 std.debug.warn("\n====found:========\n");232 std.debug.warn("\n====found:========\n");
159 var stderr = try std.io.getStdErr();233 var stderr = try std.io.getStdErr();
160 for (msgs) |msg| {234 for (msgs) |msg| {
161 try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);235 defer msg.destroy();
236 try msg.printToFile(&stderr, errmsg.Color.Auto);
162 }237 }
163 std.debug.warn("============\n");238 std.debug.warn("============\n");
164 return error.TestFailed;239 return error.TestFailed;
src-self-hosted/type.zig+419-92
...@@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope;...@@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope;
4const Compilation = @import("compilation.zig").Compilation;4const Compilation = @import("compilation.zig").Compilation;
5const Value = @import("value.zig").Value;5const Value = @import("value.zig").Value;
6const llvm = @import("llvm.zig");6const llvm = @import("llvm.zig");
7const ObjectFile = @import("codegen.zig").ObjectFile;7const event = std.event;
8const Allocator = std.mem.Allocator;
9const assert = std.debug.assert;
810
9pub const Type = struct {11pub const Type = struct {
10 base: Value,12 base: Value,
11 id: Id,13 id: Id,
14 name: []const u8,
15 abi_alignment: AbiAlignment,
16
17 pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
1218
13 pub const Id = builtin.TypeId;19 pub const Id = builtin.TypeId;
1420
...@@ -42,33 +48,37 @@ pub const Type = struct {...@@ -42,33 +48,37 @@ pub const Type = struct {
42 }48 }
43 }49 }
4450
45 pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) {51 pub fn getLlvmType(
52 base: *Type,
53 allocator: *Allocator,
54 llvm_context: llvm.ContextRef,
55 ) (error{OutOfMemory}!llvm.TypeRef) {
46 switch (base.id) {56 switch (base.id) {
47 Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile),57 Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
48 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile),58 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
49 Id.Type => unreachable,59 Id.Type => unreachable,
50 Id.Void => unreachable,60 Id.Void => unreachable,
51 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile),61 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
52 Id.NoReturn => unreachable,62 Id.NoReturn => unreachable,
53 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile),63 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
54 Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile),64 Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
55 Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile),65 Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
56 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile),66 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
57 Id.ComptimeFloat => unreachable,67 Id.ComptimeFloat => unreachable,
58 Id.ComptimeInt => unreachable,68 Id.ComptimeInt => unreachable,
59 Id.Undefined => unreachable,69 Id.Undefined => unreachable,
60 Id.Null => unreachable,70 Id.Null => unreachable,
61 Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile),71 Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
62 Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile),72 Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
63 Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile),73 Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
64 Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile),74 Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
65 Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile),75 Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
66 Id.Namespace => unreachable,76 Id.Namespace => unreachable,
67 Id.Block => unreachable,77 Id.Block => unreachable,
68 Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile),78 Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
69 Id.ArgTuple => unreachable,79 Id.ArgTuple => unreachable,
70 Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile),80 Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
71 Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile),81 Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
72 }82 }
73 }83 }
7484
...@@ -151,8 +161,49 @@ pub const Type = struct {...@@ -151,8 +161,49 @@ pub const Type = struct {
151 std.debug.warn("{}", @tagName(base.id));161 std.debug.warn("{}", @tagName(base.id));
152 }162 }
153163
154 pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 {164 fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
155 @panic("TODO getAbiAlignment");165 base.* = Type{
166 .base = Value{
167 .id = Value.Id.Type,
168 .typ = &MetaType.get(comp).base,
169 .ref_count = std.atomic.Int(usize).init(1),
170 },
171 .id = id,
172 .name = name,
173 .abi_alignment = AbiAlignment.init(comp.loop),
174 };
175 }
176
177 /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
178 /// Otherwise, this one will grab one from the pool and then release it.
179 pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
180 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
181
182 {
183 const held = try comp.event_loop_local.getAnyLlvmContext();
184 defer held.release(comp.event_loop_local);
185
186 const llvm_context = held.node.data;
187
188 base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
189 }
190 base.abi_alignment.resolve();
191 return base.abi_alignment.data;
192 }
193
194 /// If you have an llvm conext handy, you can use it here.
195 pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
196 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
197
198 base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
199 base.abi_alignment.resolve();
200 return base.abi_alignment.data;
201 }
202
203 /// Lower level function that does the work. See getAbiAlignment.
204 async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
205 const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
206 return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
156 }207 }
157208
158 pub const Struct = struct {209 pub const Struct = struct {
...@@ -160,10 +211,10 @@ pub const Type = struct {...@@ -160,10 +211,10 @@ pub const Type = struct {
160 decls: *Scope.Decls,211 decls: *Scope.Decls,
161212
162 pub fn destroy(self: *Struct, comp: *Compilation) void {213 pub fn destroy(self: *Struct, comp: *Compilation) void {
163 comp.a().destroy(self);214 comp.gpa().destroy(self);
164 }215 }
165216
166 pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef {217 pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
167 @panic("TODO");218 @panic("TODO");
168 }219 }
169 };220 };
...@@ -176,28 +227,23 @@ pub const Type = struct {...@@ -176,28 +227,23 @@ pub const Type = struct {
176227
177 pub const Param = struct {228 pub const Param = struct {
178 is_noalias: bool,229 is_noalias: bool,
179 typeof: *Type,230 typ: *Type,
180 };231 };
181232
182 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {233 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
183 const result = try comp.a().create(Fn{234 const result = try comp.gpa().create(Fn{
184 .base = Type{235 .base = undefined,
185 .base = Value{
186 .id = Value.Id.Type,
187 .typeof = &MetaType.get(comp).base,
188 .ref_count = std.atomic.Int(usize).init(1),
189 },
190 .id = builtin.TypeId.Fn,
191 },
192 .return_type = return_type,236 .return_type = return_type,
193 .params = params,237 .params = params,
194 .is_var_args = is_var_args,238 .is_var_args = is_var_args,
195 });239 });
196 errdefer comp.a().destroy(result);240 errdefer comp.gpa().destroy(result);
241
242 result.base.init(comp, Id.Fn, "TODO fn type name");
197243
198 result.return_type.base.ref();244 result.return_type.base.ref();
199 for (result.params) |param| {245 for (result.params) |param| {
200 param.typeof.base.ref();246 param.typ.base.ref();
201 }247 }
202 return result;248 return result;
203 }249 }
...@@ -205,20 +251,20 @@ pub const Type = struct {...@@ -205,20 +251,20 @@ pub const Type = struct {
205 pub fn destroy(self: *Fn, comp: *Compilation) void {251 pub fn destroy(self: *Fn, comp: *Compilation) void {
206 self.return_type.base.deref(comp);252 self.return_type.base.deref(comp);
207 for (self.params) |param| {253 for (self.params) |param| {
208 param.typeof.base.deref(comp);254 param.typ.base.deref(comp);
209 }255 }
210 comp.a().destroy(self);256 comp.gpa().destroy(self);
211 }257 }
212258
213 pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef {259 pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
214 const llvm_return_type = switch (self.return_type.id) {260 const llvm_return_type = switch (self.return_type.id) {
215 Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,261 Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
216 else => try self.return_type.getLlvmType(ofile),262 else => try self.return_type.getLlvmType(allocator, llvm_context),
217 };263 };
218 const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len);264 const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
219 defer ofile.a().free(llvm_param_types);265 defer allocator.free(llvm_param_types);
220 for (llvm_param_types) |*llvm_param_type, i| {266 for (llvm_param_types) |*llvm_param_type, i| {
221 llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile);267 llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
222 }268 }
223269
224 return llvm.FunctionType(270 return llvm.FunctionType(
...@@ -241,7 +287,7 @@ pub const Type = struct {...@@ -241,7 +287,7 @@ pub const Type = struct {
241 }287 }
242288
243 pub fn destroy(self: *MetaType, comp: *Compilation) void {289 pub fn destroy(self: *MetaType, comp: *Compilation) void {
244 comp.a().destroy(self);290 comp.gpa().destroy(self);
245 }291 }
246 };292 };
247293
...@@ -255,7 +301,7 @@ pub const Type = struct {...@@ -255,7 +301,7 @@ pub const Type = struct {
255 }301 }
256302
257 pub fn destroy(self: *Void, comp: *Compilation) void {303 pub fn destroy(self: *Void, comp: *Compilation) void {
258 comp.a().destroy(self);304 comp.gpa().destroy(self);
259 }305 }
260 };306 };
261307
...@@ -269,10 +315,10 @@ pub const Type = struct {...@@ -269,10 +315,10 @@ pub const Type = struct {
269 }315 }
270316
271 pub fn destroy(self: *Bool, comp: *Compilation) void {317 pub fn destroy(self: *Bool, comp: *Compilation) void {
272 comp.a().destroy(self);318 comp.gpa().destroy(self);
273 }319 }
274320
275 pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef {321 pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
276 @panic("TODO");322 @panic("TODO");
277 }323 }
278 };324 };
...@@ -287,19 +333,89 @@ pub const Type = struct {...@@ -287,19 +333,89 @@ pub const Type = struct {
287 }333 }
288334
289 pub fn destroy(self: *NoReturn, comp: *Compilation) void {335 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
290 comp.a().destroy(self);336 comp.gpa().destroy(self);
291 }337 }
292 };338 };
293339
294 pub const Int = struct {340 pub const Int = struct {
295 base: Type,341 base: Type,
342 key: Key,
343 garbage_node: std.atomic.Stack(*Int).Node,
344
345 pub const Key = struct {
346 bit_count: u32,
347 is_signed: bool,
348
349 pub fn hash(self: *const Key) u32 {
350 const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
351 return rands[@boolToInt(self.is_signed)] *% self.bit_count;
352 }
353
354 pub fn eql(self: *const Key, other: *const Key) bool {
355 return self.bit_count == other.bit_count and self.is_signed == other.is_signed;
356 }
357 };
358
359 pub fn get_u8(comp: *Compilation) *Int {
360 comp.u8_type.base.base.ref();
361 return comp.u8_type;
362 }
363
364 pub async fn get(comp: *Compilation, key: Key) !*Int {
365 {
366 const held = await (async comp.int_type_table.acquire() catch unreachable);
367 defer held.release();
368
369 if (held.value.get(&key)) |entry| {
370 entry.value.base.base.ref();
371 return entry.value;
372 }
373 }
374
375 const self = try comp.gpa().create(Int{
376 .base = undefined,
377 .key = key,
378 .garbage_node = undefined,
379 });
380 errdefer comp.gpa().destroy(self);
381
382 const u_or_i = "ui"[@boolToInt(key.is_signed)];
383 const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count);
384 errdefer comp.gpa().free(name);
385
386 self.base.init(comp, Id.Int, name);
387
388 {
389 const held = await (async comp.int_type_table.acquire() catch unreachable);
390 defer held.release();
391
392 _ = try held.value.put(&self.key, self);
393 }
394 return self;
395 }
296396
297 pub fn destroy(self: *Int, comp: *Compilation) void {397 pub fn destroy(self: *Int, comp: *Compilation) void {
298 comp.a().destroy(self);398 self.garbage_node = std.atomic.Stack(*Int).Node{
399 .data = self,
400 .next = undefined,
401 };
402 comp.registerGarbage(Int, &self.garbage_node);
299 }403 }
300404
301 pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef {405 pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
302 @panic("TODO");406 {
407 const held = await (async comp.int_type_table.acquire() catch unreachable);
408 defer held.release();
409
410 _ = held.value.remove(&self.key).?;
411 }
412 // we allocated the name
413 comp.gpa().free(self.base.name);
414 comp.gpa().destroy(self);
415 }
416
417 pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
418 return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
303 }419 }
304 };420 };
305421
...@@ -307,59 +423,239 @@ pub const Type = struct {...@@ -307,59 +423,239 @@ pub const Type = struct {
307 base: Type,423 base: Type,
308424
309 pub fn destroy(self: *Float, comp: *Compilation) void {425 pub fn destroy(self: *Float, comp: *Compilation) void {
310 comp.a().destroy(self);426 comp.gpa().destroy(self);
311 }427 }
312428
313 pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef {429 pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
314 @panic("TODO");430 @panic("TODO");
315 }431 }
316 };432 };
317 pub const Pointer = struct {433 pub const Pointer = struct {
318 base: Type,434 base: Type,
319 mut: Mut,435 key: Key,
320 vol: Vol,436 garbage_node: std.atomic.Stack(*Pointer).Node,
321 size: Size,437
322 alignment: u32,438 pub const Key = struct {
439 child_type: *Type,
440 mut: Mut,
441 vol: Vol,
442 size: Size,
443 alignment: Align,
444
445 pub fn hash(self: *const Key) u32 {
446 const align_hash = switch (self.alignment) {
447 Align.Abi => 0xf201c090,
448 Align.Override => |x| x,
449 };
450 return hash_usize(@ptrToInt(self.child_type)) *%
451 hash_enum(self.mut) *%
452 hash_enum(self.vol) *%
453 hash_enum(self.size) *%
454 align_hash;
455 }
456
457 pub fn eql(self: *const Key, other: *const Key) bool {
458 if (self.child_type != other.child_type or
459 self.mut != other.mut or
460 self.vol != other.vol or
461 self.size != other.size or
462 @TagType(Align)(self.alignment) != @TagType(Align)(other.alignment))
463 {
464 return false;
465 }
466 switch (self.alignment) {
467 Align.Abi => return true,
468 Align.Override => |x| return x == other.alignment.Override,
469 }
470 }
471 };
323472
324 pub const Mut = enum {473 pub const Mut = enum {
325 Mut,474 Mut,
326 Const,475 Const,
327 };476 };
477
328 pub const Vol = enum {478 pub const Vol = enum {
329 Non,479 Non,
330 Volatile,480 Volatile,
331 };481 };
482
483 pub const Align = union(enum) {
484 Abi,
485 Override: u32,
486 };
487
332 pub const Size = builtin.TypeInfo.Pointer.Size;488 pub const Size = builtin.TypeInfo.Pointer.Size;
333489
334 pub fn destroy(self: *Pointer, comp: *Compilation) void {490 pub fn destroy(self: *Pointer, comp: *Compilation) void {
335 comp.a().destroy(self);491 self.garbage_node = std.atomic.Stack(*Pointer).Node{
492 .data = self,
493 .next = undefined,
494 };
495 comp.registerGarbage(Pointer, &self.garbage_node);
336 }496 }
337497
338 pub fn get(498 pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
499 {
500 const held = await (async comp.ptr_type_table.acquire() catch unreachable);
501 defer held.release();
502
503 _ = held.value.remove(&self.key).?;
504 }
505 self.key.child_type.base.deref(comp);
506 comp.gpa().destroy(self);
507 }
508
509 pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
510 switch (self.key.alignment) {
511 Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable),
512 Align.Override => |alignment| return alignment,
513 }
514 }
515
516 pub async fn get(
339 comp: *Compilation,517 comp: *Compilation,
340 elem_type: *Type,518 key: Key,
341 mut: Mut,519 ) !*Pointer {
342 vol: Vol,520 var normal_key = key;
343 size: Size,521 switch (key.alignment) {
344 alignment: u32,522 Align.Abi => {},
345 ) *Pointer {523 Align.Override => |alignment| {
346 @panic("TODO get pointer");524 const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable);
525 if (abi_align == alignment) {
526 normal_key.alignment = Align.Abi;
527 }
528 },
529 }
530 {
531 const held = await (async comp.ptr_type_table.acquire() catch unreachable);
532 defer held.release();
533
534 if (held.value.get(&normal_key)) |entry| {
535 entry.value.base.base.ref();
536 return entry.value;
537 }
538 }
539
540 const self = try comp.gpa().create(Pointer{
541 .base = undefined,
542 .key = normal_key,
543 .garbage_node = undefined,
544 });
545 errdefer comp.gpa().destroy(self);
546
547 const size_str = switch (self.key.size) {
548 Size.One => "*",
549 Size.Many => "[*]",
550 Size.Slice => "[]",
551 };
552 const mut_str = switch (self.key.mut) {
553 Mut.Const => "const ",
554 Mut.Mut => "",
555 };
556 const vol_str = switch (self.key.vol) {
557 Vol.Volatile => "volatile ",
558 Vol.Non => "",
559 };
560 const name = switch (self.key.alignment) {
561 Align.Abi => try std.fmt.allocPrint(
562 comp.gpa(),
563 "{}{}{}{}",
564 size_str,
565 mut_str,
566 vol_str,
567 self.key.child_type.name,
568 ),
569 Align.Override => |alignment| try std.fmt.allocPrint(
570 comp.gpa(),
571 "{}align<{}> {}{}{}",
572 size_str,
573 alignment,
574 mut_str,
575 vol_str,
576 self.key.child_type.name,
577 ),
578 };
579 errdefer comp.gpa().free(name);
580
581 self.base.init(comp, Id.Pointer, name);
582
583 {
584 const held = await (async comp.ptr_type_table.acquire() catch unreachable);
585 defer held.release();
586
587 _ = try held.value.put(&self.key, self);
588 }
589 return self;
347 }590 }
348591
349 pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef {592 pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
350 @panic("TODO");593 const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
594 return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
351 }595 }
352 };596 };
353597
354 pub const Array = struct {598 pub const Array = struct {
355 base: Type,599 base: Type,
600 key: Key,
601 garbage_node: std.atomic.Stack(*Array).Node,
602
603 pub const Key = struct {
604 elem_type: *Type,
605 len: usize,
606
607 pub fn hash(self: *const Key) u32 {
608 return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
609 }
610
611 pub fn eql(self: *const Key, other: *const Key) bool {
612 return self.elem_type == other.elem_type and self.len == other.len;
613 }
614 };
356615
357 pub fn destroy(self: *Array, comp: *Compilation) void {616 pub fn destroy(self: *Array, comp: *Compilation) void {
358 comp.a().destroy(self);617 self.key.elem_type.base.deref(comp);
618 comp.gpa().destroy(self);
359 }619 }
360620
361 pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {621 pub async fn get(comp: *Compilation, key: Key) !*Array {
362 @panic("TODO");622 key.elem_type.base.ref();
623 errdefer key.elem_type.base.deref(comp);
624
625 {
626 const held = await (async comp.array_type_table.acquire() catch unreachable);
627 defer held.release();
628
629 if (held.value.get(&key)) |entry| {
630 entry.value.base.base.ref();
631 return entry.value;
632 }
633 }
634
635 const self = try comp.gpa().create(Array{
636 .base = undefined,
637 .key = key,
638 .garbage_node = undefined,
639 });
640 errdefer comp.gpa().destroy(self);
641
642 const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name);
643 errdefer comp.gpa().free(name);
644
645 self.base.init(comp, Id.Array, name);
646
647 {
648 const held = await (async comp.array_type_table.acquire() catch unreachable);
649 defer held.release();
650
651 _ = try held.value.put(&self.key, self);
652 }
653 return self;
654 }
655
656 pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
657 const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
658 return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
363 }659 }
364 };660 };
365661
...@@ -367,15 +663,21 @@ pub const Type = struct {...@@ -367,15 +663,21 @@ pub const Type = struct {
367 base: Type,663 base: Type,
368664
369 pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {665 pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {
370 comp.a().destroy(self);666 comp.gpa().destroy(self);
371 }667 }
372 };668 };
373669
374 pub const ComptimeInt = struct {670 pub const ComptimeInt = struct {
375 base: Type,671 base: Type,
376672
673 /// Adds 1 reference to the resulting type
674 pub fn get(comp: *Compilation) *ComptimeInt {
675 comp.comptime_int_type.base.base.ref();
676 return comp.comptime_int_type;
677 }
678
377 pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {679 pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
378 comp.a().destroy(self);680 comp.gpa().destroy(self);
379 }681 }
380 };682 };
381683
...@@ -383,7 +685,7 @@ pub const Type = struct {...@@ -383,7 +685,7 @@ pub const Type = struct {
383 base: Type,685 base: Type,
384686
385 pub fn destroy(self: *Undefined, comp: *Compilation) void {687 pub fn destroy(self: *Undefined, comp: *Compilation) void {
386 comp.a().destroy(self);688 comp.gpa().destroy(self);
387 }689 }
388 };690 };
389691
...@@ -391,7 +693,7 @@ pub const Type = struct {...@@ -391,7 +693,7 @@ pub const Type = struct {
391 base: Type,693 base: Type,
392694
393 pub fn destroy(self: *Null, comp: *Compilation) void {695 pub fn destroy(self: *Null, comp: *Compilation) void {
394 comp.a().destroy(self);696 comp.gpa().destroy(self);
395 }697 }
396 };698 };
397699
...@@ -399,10 +701,10 @@ pub const Type = struct {...@@ -399,10 +701,10 @@ pub const Type = struct {
399 base: Type,701 base: Type,
400702
401 pub fn destroy(self: *Optional, comp: *Compilation) void {703 pub fn destroy(self: *Optional, comp: *Compilation) void {
402 comp.a().destroy(self);704 comp.gpa().destroy(self);
403 }705 }
404706
405 pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef {707 pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
406 @panic("TODO");708 @panic("TODO");
407 }709 }
408 };710 };
...@@ -411,10 +713,10 @@ pub const Type = struct {...@@ -411,10 +713,10 @@ pub const Type = struct {
411 base: Type,713 base: Type,
412714
413 pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {715 pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {
414 comp.a().destroy(self);716 comp.gpa().destroy(self);
415 }717 }
416718
417 pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef {719 pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
418 @panic("TODO");720 @panic("TODO");
419 }721 }
420 };722 };
...@@ -423,10 +725,10 @@ pub const Type = struct {...@@ -423,10 +725,10 @@ pub const Type = struct {
423 base: Type,725 base: Type,
424726
425 pub fn destroy(self: *ErrorSet, comp: *Compilation) void {727 pub fn destroy(self: *ErrorSet, comp: *Compilation) void {
426 comp.a().destroy(self);728 comp.gpa().destroy(self);
427 }729 }
428730
429 pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef {731 pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
430 @panic("TODO");732 @panic("TODO");
431 }733 }
432 };734 };
...@@ -435,10 +737,10 @@ pub const Type = struct {...@@ -435,10 +737,10 @@ pub const Type = struct {
435 base: Type,737 base: Type,
436738
437 pub fn destroy(self: *Enum, comp: *Compilation) void {739 pub fn destroy(self: *Enum, comp: *Compilation) void {
438 comp.a().destroy(self);740 comp.gpa().destroy(self);
439 }741 }
440742
441 pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef {743 pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
442 @panic("TODO");744 @panic("TODO");
443 }745 }
444 };746 };
...@@ -447,10 +749,10 @@ pub const Type = struct {...@@ -447,10 +749,10 @@ pub const Type = struct {
447 base: Type,749 base: Type,
448750
449 pub fn destroy(self: *Union, comp: *Compilation) void {751 pub fn destroy(self: *Union, comp: *Compilation) void {
450 comp.a().destroy(self);752 comp.gpa().destroy(self);
451 }753 }
452754
453 pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef {755 pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
454 @panic("TODO");756 @panic("TODO");
455 }757 }
456 };758 };
...@@ -459,7 +761,7 @@ pub const Type = struct {...@@ -459,7 +761,7 @@ pub const Type = struct {
459 base: Type,761 base: Type,
460762
461 pub fn destroy(self: *Namespace, comp: *Compilation) void {763 pub fn destroy(self: *Namespace, comp: *Compilation) void {
462 comp.a().destroy(self);764 comp.gpa().destroy(self);
463 }765 }
464 };766 };
465767
...@@ -467,7 +769,7 @@ pub const Type = struct {...@@ -467,7 +769,7 @@ pub const Type = struct {
467 base: Type,769 base: Type,
468770
469 pub fn destroy(self: *Block, comp: *Compilation) void {771 pub fn destroy(self: *Block, comp: *Compilation) void {
470 comp.a().destroy(self);772 comp.gpa().destroy(self);
471 }773 }
472 };774 };
473775
...@@ -475,10 +777,10 @@ pub const Type = struct {...@@ -475,10 +777,10 @@ pub const Type = struct {
475 base: Type,777 base: Type,
476778
477 pub fn destroy(self: *BoundFn, comp: *Compilation) void {779 pub fn destroy(self: *BoundFn, comp: *Compilation) void {
478 comp.a().destroy(self);780 comp.gpa().destroy(self);
479 }781 }
480782
481 pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef {783 pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
482 @panic("TODO");784 @panic("TODO");
483 }785 }
484 };786 };
...@@ -487,7 +789,7 @@ pub const Type = struct {...@@ -487,7 +789,7 @@ pub const Type = struct {
487 base: Type,789 base: Type,
488790
489 pub fn destroy(self: *ArgTuple, comp: *Compilation) void {791 pub fn destroy(self: *ArgTuple, comp: *Compilation) void {
490 comp.a().destroy(self);792 comp.gpa().destroy(self);
491 }793 }
492 };794 };
493795
...@@ -495,10 +797,10 @@ pub const Type = struct {...@@ -495,10 +797,10 @@ pub const Type = struct {
495 base: Type,797 base: Type,
496798
497 pub fn destroy(self: *Opaque, comp: *Compilation) void {799 pub fn destroy(self: *Opaque, comp: *Compilation) void {
498 comp.a().destroy(self);800 comp.gpa().destroy(self);
499 }801 }
500802
501 pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef {803 pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
502 @panic("TODO");804 @panic("TODO");
503 }805 }
504 };806 };
...@@ -507,11 +809,36 @@ pub const Type = struct {...@@ -507,11 +809,36 @@ pub const Type = struct {
507 base: Type,809 base: Type,
508810
509 pub fn destroy(self: *Promise, comp: *Compilation) void {811 pub fn destroy(self: *Promise, comp: *Compilation) void {
510 comp.a().destroy(self);812 comp.gpa().destroy(self);
511 }813 }
512814
513 pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef {815 pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
514 @panic("TODO");816 @panic("TODO");
515 }817 }
516 };818 };
517};819};
820
821fn hash_usize(x: usize) u32 {
822 return switch (@sizeOf(usize)) {
823 4 => x,
824 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
825 else => @compileError("implement this hash function"),
826 };
827}
828
829fn hash_enum(x: var) u32 {
830 const rands = []u32{
831 0x85ebf64f,
832 0x3fcb3211,
833 0x240a4e8e,
834 0x40bb0e3c,
835 0x78be45af,
836 0x1ca98e37,
837 0xec56053a,
838 0x906adc48,
839 0xd4fe9763,
840 0x54c80dac,
841 };
842 comptime assert(@memberCount(@typeOf(x)) < rands.len);
843 return rands[@enumToInt(x)];
844}
src-self-hosted/value.zig+401-12
...@@ -4,12 +4,14 @@ const Scope = @import("scope.zig").Scope;...@@ -4,12 +4,14 @@ const Scope = @import("scope.zig").Scope;
4const Compilation = @import("compilation.zig").Compilation;4const Compilation = @import("compilation.zig").Compilation;
5const ObjectFile = @import("codegen.zig").ObjectFile;5const ObjectFile = @import("codegen.zig").ObjectFile;
6const llvm = @import("llvm.zig");6const llvm = @import("llvm.zig");
7const Buffer = std.Buffer;
8const assert = std.debug.assert;
79
8/// Values are ref-counted, heap-allocated, and copy-on-write10/// Values are ref-counted, heap-allocated, and copy-on-write
9/// If there is only 1 ref then write need not copy11/// If there is only 1 ref then write need not copy
10pub const Value = struct {12pub const Value = struct {
11 id: Id,13 id: Id,
12 typeof: *Type,14 typ: *Type,
13 ref_count: std.atomic.Int(usize),15 ref_count: std.atomic.Int(usize),
1416
15 /// Thread-safe17 /// Thread-safe
...@@ -20,23 +22,37 @@ pub const Value = struct {...@@ -20,23 +22,37 @@ pub const Value = struct {
20 /// Thread-safe22 /// Thread-safe
21 pub fn deref(base: *Value, comp: *Compilation) void {23 pub fn deref(base: *Value, comp: *Compilation) void {
22 if (base.ref_count.decr() == 1) {24 if (base.ref_count.decr() == 1) {
23 base.typeof.base.deref(comp);25 base.typ.base.deref(comp);
24 switch (base.id) {26 switch (base.id) {
25 Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),27 Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
26 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),28 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
29 Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
27 Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),30 Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
28 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),31 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
29 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),32 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
30 Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),33 Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
34 Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
35 Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
31 }36 }
32 }37 }
33 }38 }
3439
40 pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
41 base.typ.base.deref(comp);
42 new_type.base.ref();
43 base.typ = new_type;
44 }
45
35 pub fn getRef(base: *Value) *Value {46 pub fn getRef(base: *Value) *Value {
36 base.ref();47 base.ref();
37 return base;48 return base;
38 }49 }
3950
51 pub fn cast(base: *Value, comptime T: type) ?*T {
52 if (base.id != @field(Id, @typeName(T))) return null;
53 return @fieldParentPtr(T, "base", base);
54 }
55
40 pub fn dump(base: *const Value) void {56 pub fn dump(base: *const Value) void {
41 std.debug.warn("{}", @tagName(base.id));57 std.debug.warn("{}", @tagName(base.id));
42 }58 }
...@@ -45,30 +61,117 @@ pub const Value = struct {...@@ -45,30 +61,117 @@ pub const Value = struct {
45 switch (base.id) {61 switch (base.id) {
46 Id.Type => unreachable,62 Id.Type => unreachable,
47 Id.Fn => @panic("TODO"),63 Id.Fn => @panic("TODO"),
64 Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
48 Id.Void => return null,65 Id.Void => return null,
49 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),66 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
50 Id.NoReturn => unreachable,67 Id.NoReturn => unreachable,
51 Id.Ptr => @panic("TODO"),68 Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
69 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
70 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
71 }
72 }
73
74 pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
75 if (self.ref_count.get() == 1) {
76 // ( Í¡° ͜ʖ Í¡°)
77 return self;
78 }
79
80 assert(self.ref_count.decr() != 1);
81 return self.copy(comp);
82 }
83
84 pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
85 switch (base.id) {
86 Id.Type => unreachable,
87 Id.Fn => unreachable,
88 Id.FnProto => unreachable,
89 Id.Void => unreachable,
90 Id.Bool => unreachable,
91 Id.NoReturn => unreachable,
92 Id.Ptr => unreachable,
93 Id.Array => unreachable,
94 Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
52 }95 }
53 }96 }
5497
98 pub const Parent = union(enum) {
99 None,
100 BaseStruct: BaseStruct,
101 BaseArray: BaseArray,
102 BaseUnion: *Value,
103 BaseScalar: *Value,
104
105 pub const BaseStruct = struct {
106 val: *Value,
107 field_index: usize,
108 };
109
110 pub const BaseArray = struct {
111 val: *Value,
112 elem_index: usize,
113 };
114 };
115
55 pub const Id = enum {116 pub const Id = enum {
56 Type,117 Type,
57 Fn,118 Fn,
58 Void,119 Void,
59 Bool,120 Bool,
60 NoReturn,121 NoReturn,
122 Array,
61 Ptr,123 Ptr,
124 Int,
125 FnProto,
62 };126 };
63127
64 pub const Type = @import("type.zig").Type;128 pub const Type = @import("type.zig").Type;
65129
130 pub const FnProto = struct {
131 base: Value,
132
133 /// The main external name that is used in the .o file.
134 /// TODO https://github.com/ziglang/zig/issues/265
135 symbol_name: Buffer,
136
137 pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
138 const self = try comp.gpa().create(FnProto{
139 .base = Value{
140 .id = Value.Id.FnProto,
141 .typ = &fn_type.base,
142 .ref_count = std.atomic.Int(usize).init(1),
143 },
144 .symbol_name = symbol_name,
145 });
146 fn_type.base.base.ref();
147 return self;
148 }
149
150 pub fn destroy(self: *FnProto, comp: *Compilation) void {
151 self.symbol_name.deinit();
152 comp.gpa().destroy(self);
153 }
154
155 pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
156 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
157 const llvm_fn = llvm.AddFunction(
158 ofile.module,
159 self.symbol_name.ptr(),
160 llvm_fn_type,
161 ) orelse return error.OutOfMemory;
162
163 // TODO port more logic from codegen.cpp:fn_llvm_value
164
165 return llvm_fn;
166 }
167 };
168
66 pub const Fn = struct {169 pub const Fn = struct {
67 base: Value,170 base: Value,
68171
69 /// The main external name that is used in the .o file.172 /// The main external name that is used in the .o file.
70 /// TODO https://github.com/ziglang/zig/issues/265173 /// TODO https://github.com/ziglang/zig/issues/265
71 symbol_name: std.Buffer,174 symbol_name: Buffer,
72175
73 /// parent should be the top level decls or container decls176 /// parent should be the top level decls or container decls
74 fndef_scope: *Scope.FnDef,177 fndef_scope: *Scope.FnDef,
...@@ -79,19 +182,33 @@ pub const Value = struct {...@@ -79,19 +182,33 @@ pub const Value = struct {
79 /// parent is child_scope182 /// parent is child_scope
80 block_scope: *Scope.Block,183 block_scope: *Scope.Block,
81184
185 /// Path to the object file that contains this function
186 containing_object: Buffer,
187
188 link_set_node: *std.LinkedList(?*Value.Fn).Node,
189
82 /// Creates a Fn value with 1 ref190 /// Creates a Fn value with 1 ref
83 /// Takes ownership of symbol_name191 /// Takes ownership of symbol_name
84 pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn {192 pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
85 const self = try comp.a().create(Fn{193 const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{
194 .data = null,
195 .next = undefined,
196 .prev = undefined,
197 });
198 errdefer comp.gpa().destroy(link_set_node);
199
200 const self = try comp.gpa().create(Fn{
86 .base = Value{201 .base = Value{
87 .id = Value.Id.Fn,202 .id = Value.Id.Fn,
88 .typeof = &fn_type.base,203 .typ = &fn_type.base,
89 .ref_count = std.atomic.Int(usize).init(1),204 .ref_count = std.atomic.Int(usize).init(1),
90 },205 },
91 .fndef_scope = fndef_scope,206 .fndef_scope = fndef_scope,
92 .child_scope = &fndef_scope.base,207 .child_scope = &fndef_scope.base,
93 .block_scope = undefined,208 .block_scope = undefined,
94 .symbol_name = symbol_name,209 .symbol_name = symbol_name,
210 .containing_object = Buffer.initNull(comp.gpa()),
211 .link_set_node = link_set_node,
95 });212 });
96 fn_type.base.base.ref();213 fn_type.base.base.ref();
97 fndef_scope.fn_val = self;214 fndef_scope.fn_val = self;
...@@ -100,9 +217,19 @@ pub const Value = struct {...@@ -100,9 +217,19 @@ pub const Value = struct {
100 }217 }
101218
102 pub fn destroy(self: *Fn, comp: *Compilation) void {219 pub fn destroy(self: *Fn, comp: *Compilation) void {
220 // remove with a tombstone so that we do not have to grab a lock
221 if (self.link_set_node.data != null) {
222 // it's now the job of the link step to find this tombstone and
223 // deallocate it.
224 self.link_set_node.data = null;
225 } else {
226 comp.gpa().destroy(self.link_set_node);
227 }
228
229 self.containing_object.deinit();
103 self.fndef_scope.base.deref(comp);230 self.fndef_scope.base.deref(comp);
104 self.symbol_name.deinit();231 self.symbol_name.deinit();
105 comp.a().destroy(self);232 comp.gpa().destroy(self);
106 }233 }
107 };234 };
108235
...@@ -115,7 +242,7 @@ pub const Value = struct {...@@ -115,7 +242,7 @@ pub const Value = struct {
115 }242 }
116243
117 pub fn destroy(self: *Void, comp: *Compilation) void {244 pub fn destroy(self: *Void, comp: *Compilation) void {
118 comp.a().destroy(self);245 comp.gpa().destroy(self);
119 }246 }
120 };247 };
121248
...@@ -134,7 +261,7 @@ pub const Value = struct {...@@ -134,7 +261,7 @@ pub const Value = struct {
134 }261 }
135262
136 pub fn destroy(self: *Bool, comp: *Compilation) void {263 pub fn destroy(self: *Bool, comp: *Compilation) void {
137 comp.a().destroy(self);264 comp.gpa().destroy(self);
138 }265 }
139266
140 pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {267 pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
...@@ -156,12 +283,14 @@ pub const Value = struct {...@@ -156,12 +283,14 @@ pub const Value = struct {
156 }283 }
157284
158 pub fn destroy(self: *NoReturn, comp: *Compilation) void {285 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
159 comp.a().destroy(self);286 comp.gpa().destroy(self);
160 }287 }
161 };288 };
162289
163 pub const Ptr = struct {290 pub const Ptr = struct {
164 base: Value,291 base: Value,
292 special: Special,
293 mut: Mut,
165294
166 pub const Mut = enum {295 pub const Mut = enum {
167 CompTimeConst,296 CompTimeConst,
...@@ -169,8 +298,268 @@ pub const Value = struct {...@@ -169,8 +298,268 @@ pub const Value = struct {
169 RunTime,298 RunTime,
170 };299 };
171300
301 pub const Special = union(enum) {
302 Scalar: *Value,
303 BaseArray: BaseArray,
304 BaseStruct: BaseStruct,
305 HardCodedAddr: u64,
306 Discard,
307 };
308
309 pub const BaseArray = struct {
310 val: *Value,
311 elem_index: usize,
312 };
313
314 pub const BaseStruct = struct {
315 val: *Value,
316 field_index: usize,
317 };
318
319 pub async fn createArrayElemPtr(
320 comp: *Compilation,
321 array_val: *Array,
322 mut: Type.Pointer.Mut,
323 size: Type.Pointer.Size,
324 elem_index: usize,
325 ) !*Ptr {
326 array_val.base.ref();
327 errdefer array_val.base.deref(comp);
328
329 const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
330 const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{
331 .child_type = elem_type,
332 .mut = mut,
333 .vol = Type.Pointer.Vol.Non,
334 .size = size,
335 .alignment = Type.Pointer.Align.Abi,
336 }) catch unreachable);
337 var ptr_type_consumed = false;
338 errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
339
340 const self = try comp.gpa().create(Value.Ptr{
341 .base = Value{
342 .id = Value.Id.Ptr,
343 .typ = &ptr_type.base,
344 .ref_count = std.atomic.Int(usize).init(1),
345 },
346 .special = Special{
347 .BaseArray = BaseArray{
348 .val = &array_val.base,
349 .elem_index = 0,
350 },
351 },
352 .mut = Mut.CompTimeConst,
353 });
354 ptr_type_consumed = true;
355 errdefer comp.gpa().destroy(self);
356
357 return self;
358 }
359
172 pub fn destroy(self: *Ptr, comp: *Compilation) void {360 pub fn destroy(self: *Ptr, comp: *Compilation) void {
173 comp.a().destroy(self);361 comp.gpa().destroy(self);
362 }
363
364 pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
365 const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
366 // TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
367 switch (self.special) {
368 Special.Scalar => |scalar| @panic("TODO"),
369 Special.BaseArray => |base_array| {
370 // TODO put this in one .o file only, and after that, generate extern references to it
371 const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
372 const ptr_bit_count = ofile.comp.target_ptr_bits;
373 const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
374 const indices = []llvm.ValueRef{
375 llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
376 llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
377 };
378 return llvm.ConstInBoundsGEP(
379 array_llvm_value,
380 &indices,
381 @intCast(c_uint, indices.len),
382 ) orelse return error.OutOfMemory;
383 },
384 Special.BaseStruct => |base_struct| @panic("TODO"),
385 Special.HardCodedAddr => |addr| @panic("TODO"),
386 Special.Discard => unreachable,
387 }
388 }
389 };
390
391 pub const Array = struct {
392 base: Value,
393 special: Special,
394
395 pub const Special = union(enum) {
396 Undefined,
397 OwnedBuffer: []u8,
398 Explicit: Data,
399 };
400
401 pub const Data = struct {
402 parent: Parent,
403 elements: []*Value,
404 };
405
406 /// Takes ownership of buffer
407 pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
408 const u8_type = Type.Int.get_u8(comp);
409 defer u8_type.base.base.deref(comp);
410
411 const array_type = try await (async Type.Array.get(comp, Type.Array.Key{
412 .elem_type = &u8_type.base,
413 .len = buffer.len,
414 }) catch unreachable);
415 errdefer array_type.base.base.deref(comp);
416
417 const self = try comp.gpa().create(Value.Array{
418 .base = Value{
419 .id = Value.Id.Array,
420 .typ = &array_type.base,
421 .ref_count = std.atomic.Int(usize).init(1),
422 },
423 .special = Special{ .OwnedBuffer = buffer },
424 });
425 errdefer comp.gpa().destroy(self);
426
427 return self;
428 }
429
430 pub fn destroy(self: *Array, comp: *Compilation) void {
431 switch (self.special) {
432 Special.Undefined => {},
433 Special.OwnedBuffer => |buf| {
434 comp.gpa().free(buf);
435 },
436 Special.Explicit => {},
437 }
438 comp.gpa().destroy(self);
439 }
440
441 pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
442 switch (self.special) {
443 Special.Undefined => {
444 const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
445 return llvm.GetUndef(llvm_type);
446 },
447 Special.OwnedBuffer => |buf| {
448 const dont_null_terminate = 1;
449 const llvm_str_init = llvm.ConstStringInContext(
450 ofile.context,
451 buf.ptr,
452 @intCast(c_uint, buf.len),
453 dont_null_terminate,
454 ) orelse return error.OutOfMemory;
455 const str_init_type = llvm.TypeOf(llvm_str_init);
456 const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory;
457 llvm.SetInitializer(global, llvm_str_init);
458 llvm.SetLinkage(global, llvm.PrivateLinkage);
459 llvm.SetGlobalConstant(global, 1);
460 llvm.SetUnnamedAddr(global, 1);
461 llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
462 return global;
463 },
464 Special.Explicit => @panic("TODO"),
465 }
466
467 //{
468 // uint64_t len = type_entry->data.array.len;
469 // if (const_val->data.x_array.special == ConstArraySpecialUndef) {
470 // return LLVMGetUndef(type_entry->type_ref);
471 // }
472
473 // LLVMValueRef *values = allocate<LLVMValueRef>(len);
474 // LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
475 // bool make_unnamed_struct = false;
476 // for (uint64_t i = 0; i < len; i += 1) {
477 // ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
478 // LLVMValueRef val = gen_const_val(g, elem_value, "");
479 // values[i] = val;
480 // make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
481 // }
482 // if (make_unnamed_struct) {
483 // return LLVMConstStruct(values, len, true);
484 // } else {
485 // return LLVMConstArray(element_type_ref, values, (unsigned)len);
486 // }
487 //}
488 }
489 };
490
491 pub const Int = struct {
492 base: Value,
493 big_int: std.math.big.Int,
494
495 pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
496 const self = try comp.gpa().create(Value.Int{
497 .base = Value{
498 .id = Value.Id.Int,
499 .typ = typ,
500 .ref_count = std.atomic.Int(usize).init(1),
501 },
502 .big_int = undefined,
503 });
504 typ.base.ref();
505 errdefer comp.gpa().destroy(self);
506
507 self.big_int = try std.math.big.Int.init(comp.gpa());
508 errdefer self.big_int.deinit();
509
510 try self.big_int.setString(base, value);
511
512 return self;
513 }
514
515 pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
516 switch (self.base.typ.id) {
517 Type.Id.Int => {
518 const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
519 if (self.big_int.len == 0) {
520 return llvm.ConstNull(type_ref);
521 }
522 const unsigned_val = if (self.big_int.len == 1) blk: {
523 break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
524 } else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
525 break :blk llvm.ConstIntOfArbitraryPrecision(
526 type_ref,
527 @intCast(c_uint, self.big_int.len),
528 @ptrCast([*]u64, self.big_int.limbs.ptr),
529 );
530 } else {
531 @compileError("std.math.Big.Int.Limb size does not match LLVM");
532 };
533 return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
534 },
535 Type.Id.ComptimeInt => unreachable,
536 else => unreachable,
537 }
538 }
539
540 pub fn copy(old: *Int, comp: *Compilation) !*Int {
541 old.base.typ.base.ref();
542 errdefer old.base.typ.base.deref(comp);
543
544 const new = try comp.gpa().create(Value.Int{
545 .base = Value{
546 .id = Value.Id.Int,
547 .typ = old.base.typ,
548 .ref_count = std.atomic.Int(usize).init(1),
549 },
550 .big_int = undefined,
551 });
552 errdefer comp.gpa().destroy(new);
553
554 new.big_int = try old.big_int.clone();
555 errdefer new.big_int.deinit();
556
557 return new;
558 }
559
560 pub fn destroy(self: *Int, comp: *Compilation) void {
561 self.big_int.deinit();
562 comp.gpa().destroy(self);
174 }563 }
175 };564 };
176};565};
src/analyze.cpp+6-5
...@@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {...@@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
1454 case TypeTableEntryIdFn:1454 case TypeTableEntryIdFn:
1455 return type_entry->data.fn.fn_type_id.cc == CallingConventionC;1455 return type_entry->data.fn.fn_type_id.cc == CallingConventionC;
1456 case TypeTableEntryIdPointer:1456 case TypeTableEntryIdPointer:
1457 return type_allowed_in_extern(g, type_entry->data.pointer.child_type);1457 if (type_size(g, type_entry) == 0)
1458 return false;
1459 return true;
1458 case TypeTableEntryIdStruct:1460 case TypeTableEntryIdStruct:
1459 return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;1461 return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;
1460 case TypeTableEntryIdOptional:1462 case TypeTableEntryIdOptional:
...@@ -4377,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {...@@ -4377,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
43774379
4378static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {4380static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
4379 if (g->win_sdk == nullptr) {4381 if (g->win_sdk == nullptr) {
4380 if (os_find_windows_sdk(&g->win_sdk)) {4382 if (zig_find_windows_sdk(&g->win_sdk)) {
4381 fprintf(stderr, "unable to determine windows sdk path\n");4383 fprintf(stderr, "unable to determine windows sdk path\n");
4382 exit(1);4384 exit(1);
4383 }4385 }
...@@ -4497,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) {...@@ -4497,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) {
4497 ZigWindowsSDK *sdk = get_windows_sdk(g);4499 ZigWindowsSDK *sdk = get_windows_sdk(g);
44984500
4499 if (g->msvc_lib_dir == nullptr) {4501 if (g->msvc_lib_dir == nullptr) {
4500 Buf* vc_lib_dir = buf_alloc();4502 if (sdk->msvc_lib_dir_ptr == nullptr) {
4501 if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
4502 fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");4503 fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
4503 exit(1);4504 exit(1);
4504 }4505 }
4505 g->msvc_lib_dir = vc_lib_dir;4506 g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
4506 }4507 }
45074508
4508 if (g->libc_lib_dir == nullptr) {4509 if (g->libc_lib_dir == nullptr) {
src/codegen.cpp+76-36
...@@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) {...@@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) {
60 return new_package("", "");60 return new_package("", "");
61}61}
6262
63static const char *symbols_that_llvm_depends_on[] = {
64 "memcpy",
65 "memset",
66 "sqrt",
67 "powi",
68 "sin",
69 "cos",
70 "pow",
71 "exp",
72 "exp2",
73 "log",
74 "log10",
75 "log2",
76 "fma",
77 "fabs",
78 "minnum",
79 "maxnum",
80 "copysign",
81 "floor",
82 "ceil",
83 "trunc",
84 "rint",
85 "nearbyint",
86 "round",
87 // TODO probably all of compiler-rt needs to go here
88};
89
63CodeGen *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,
64 Buf *zig_lib_dir)91 Buf *zig_lib_dir)
65{92{
...@@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out...@@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
94 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);121 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
95 buf_resize(&g->global_asm, 0);122 buf_resize(&g->global_asm, 0);
96123
124 for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) {
125 g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr);
126 }
127
97 if (root_src_path) {128 if (root_src_path) {
98 Buf *src_basename = buf_alloc();129 Buf *src_basename = buf_alloc();
99 Buf *src_dir = buf_alloc();130 Buf *src_dir = buf_alloc();
...@@ -7419,51 +7450,60 @@ static void gen_h_file(CodeGen *g) {...@@ -7419,51 +7450,60 @@ static void gen_h_file(CodeGen *g) {
7419 case TypeTableEntryIdPromise:7450 case TypeTableEntryIdPromise:
7420 zig_unreachable();7451 zig_unreachable();
7421 case TypeTableEntryIdEnum:7452 case TypeTableEntryIdEnum:
7422 assert(type_entry->data.enumeration.layout == ContainerLayoutExtern);7453 if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
7423 fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name));7454 fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name));
7424 for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {7455 for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
7425 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];7456 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
7426 Buf *value_buf = buf_alloc();7457 Buf *value_buf = buf_alloc();
7427 bigint_append_buf(value_buf, &enum_field->value, 10);7458 bigint_append_buf(value_buf, &enum_field->value, 10);
7428 fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));7459 fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
7429 if (field_i != type_entry->data.enumeration.src_field_count - 1) {7460 if (field_i != type_entry->data.enumeration.src_field_count - 1) {
7430 fprintf(out_h, ",");7461 fprintf(out_h, ",");
7462 }
7463 fprintf(out_h, "\n");
7431 }7464 }
7432 fprintf(out_h, "\n");7465 fprintf(out_h, "};\n\n");
7466 } else {
7467 fprintf(out_h, "enum %s;\n", buf_ptr(&type_entry->name));
7433 }7468 }
7434 fprintf(out_h, "};\n\n");
7435 break;7469 break;
7436 case TypeTableEntryIdStruct:7470 case TypeTableEntryIdStruct:
7437 assert(type_entry->data.structure.layout == ContainerLayoutExtern);7471 if (type_entry->data.structure.layout == ContainerLayoutExtern) {
7438 fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name));7472 fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name));
7439 for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {7473 for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
7440 TypeStructField *struct_field = &type_entry->data.structure.fields[field_i];7474 TypeStructField *struct_field = &type_entry->data.structure.fields[field_i];
74417475
7442 Buf *type_name_buf = buf_alloc();7476 Buf *type_name_buf = buf_alloc();
7443 get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);7477 get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
74447478
7445 if (struct_field->type_entry->id == TypeTableEntryIdArray) {7479 if (struct_field->type_entry->id == TypeTableEntryIdArray) {
7446 fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),7480 fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
7447 buf_ptr(struct_field->name),7481 buf_ptr(struct_field->name),
7448 struct_field->type_entry->data.array.len);7482 struct_field->type_entry->data.array.len);
7449 } else {7483 } else {
7450 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));7484 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
7451 }7485 }
74527486
7487 }
7488 fprintf(out_h, "};\n\n");
7489 } else {
7490 fprintf(out_h, "struct %s;\n", buf_ptr(&type_entry->name));
7453 }7491 }
7454 fprintf(out_h, "};\n\n");
7455 break;7492 break;
7456 case TypeTableEntryIdUnion:7493 case TypeTableEntryIdUnion:
7457 assert(type_entry->data.unionation.layout == ContainerLayoutExtern);7494 if (type_entry->data.unionation.layout == ContainerLayoutExtern) {
7458 fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name));7495 fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name));
7459 for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {7496 for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
7460 TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];7497 TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
74617498
7462 Buf *type_name_buf = buf_alloc();7499 Buf *type_name_buf = buf_alloc();
7463 get_c_type(g, gen_h, union_field->type_entry, type_name_buf);7500 get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
7464 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));7501 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
7502 }
7503 fprintf(out_h, "};\n\n");
7504 } else {
7505 fprintf(out_h, "union %s;\n", buf_ptr(&type_entry->name));
7465 }7506 }
7466 fprintf(out_h, "};\n\n");
7467 break;7507 break;
7468 case TypeTableEntryIdOpaque:7508 case TypeTableEntryIdOpaque:
7469 fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name));7509 fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name));
src/ir.cpp+63-26
...@@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) {...@@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) {
246static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {246static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {
247 assert(instruction->id != IrInstructionIdInvalid);247 assert(instruction->id != IrInstructionIdInvalid);
248 instruction->ref_count += 1;248 instruction->ref_count += 1;
249 if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction))
250 ir_ref_bb(instruction->owner_bb);
249}251}
250252
251static void ir_ref_var(VariableTableEntry *var) {253static void ir_ref_var(VariableTableEntry *var) {
...@@ -2959,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco...@@ -2959,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco
2959 results[ReturnKindUnconditional] = 0;2961 results[ReturnKindUnconditional] = 0;
2960 results[ReturnKindError] = 0;2962 results[ReturnKindError] = 0;
29612963
2962 while (inner_scope != outer_scope) {2964 Scope *scope = inner_scope;
2963 assert(inner_scope);
2964 if (inner_scope->id == ScopeIdDefer) {
2965 AstNode *defer_node = inner_scope->source_node;
2966 assert(defer_node->type == NodeTypeDefer);
2967 ReturnKind defer_kind = defer_node->data.defer.kind;
2968 results[defer_kind] += 1;
29692965
2966 while (scope != outer_scope) {
2967 assert(scope);
2968 switch (scope->id) {
2969 case ScopeIdDefer: {
2970 AstNode *defer_node = scope->source_node;
2971 assert(defer_node->type == NodeTypeDefer);
2972 ReturnKind defer_kind = defer_node->data.defer.kind;
2973 results[defer_kind] += 1;
2974 scope = scope->parent;
2975 continue;
2976 }
2977 case ScopeIdDecls:
2978 case ScopeIdFnDef:
2979 return;
2980 case ScopeIdBlock:
2981 case ScopeIdVarDecl:
2982 case ScopeIdLoop:
2983 case ScopeIdSuspend:
2984 case ScopeIdCompTime:
2985 scope = scope->parent;
2986 continue;
2987 case ScopeIdDeferExpr:
2988 case ScopeIdCImport:
2989 case ScopeIdCoroPrelude:
2990 zig_unreachable();
2970 }2991 }
2971 inner_scope = inner_scope->parent;
2972 }2992 }
2973}2993}
29742994
...@@ -2984,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o...@@ -2984,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
2984 if (!scope)3004 if (!scope)
2985 return is_noreturn;3005 return is_noreturn;
29863006
2987 if (scope->id == ScopeIdDefer) {3007 switch (scope->id) {
2988 AstNode *defer_node = scope->source_node;3008 case ScopeIdDefer: {
2989 assert(defer_node->type == NodeTypeDefer);3009 AstNode *defer_node = scope->source_node;
2990 ReturnKind defer_kind = defer_node->data.defer.kind;3010 assert(defer_node->type == NodeTypeDefer);
2991 if (defer_kind == ReturnKindUnconditional ||3011 ReturnKind defer_kind = defer_node->data.defer.kind;
2992 (gen_error_defers && defer_kind == ReturnKindError))3012 if (defer_kind == ReturnKindUnconditional ||
2993 {3013 (gen_error_defers && defer_kind == ReturnKindError))
2994 AstNode *defer_expr_node = defer_node->data.defer.expr;3014 {
2995 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;3015 AstNode *defer_expr_node = defer_node->data.defer.expr;
2996 IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);3016 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
2997 if (defer_expr_value != irb->codegen->invalid_instruction) {3017 IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
2998 if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {3018 if (defer_expr_value != irb->codegen->invalid_instruction) {
2999 is_noreturn = true;3019 if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
3000 } else {3020 is_noreturn = true;
3001 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));3021 } else {
3022 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
3023 }
3002 }3024 }
3003 }3025 }
3026 scope = scope->parent;
3027 continue;
3004 }3028 }
30053029 case ScopeIdDecls:
3030 case ScopeIdFnDef:
3031 return is_noreturn;
3032 case ScopeIdBlock:
3033 case ScopeIdVarDecl:
3034 case ScopeIdLoop:
3035 case ScopeIdSuspend:
3036 case ScopeIdCompTime:
3037 scope = scope->parent;
3038 continue;
3039 case ScopeIdDeferExpr:
3040 case ScopeIdCImport:
3041 case ScopeIdCoroPrelude:
3042 zig_unreachable();
3006 }3043 }
3007 scope = scope->parent;
3008 }3044 }
3009 return is_noreturn;3045 return is_noreturn;
3010}3046}
...@@ -9408,7 +9444,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc...@@ -9408,7 +9444,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc
9408 if (type_is_invalid(casted_payload->value.type))9444 if (type_is_invalid(casted_payload->value.type))
9409 return ira->codegen->invalid_instruction;9445 return ira->codegen->invalid_instruction;
94109446
9411 ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad);9447 ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk);
9412 if (!val)9448 if (!val)
9413 return ira->codegen->invalid_instruction;9449 return ira->codegen->invalid_instruction;
94149450
...@@ -13090,6 +13126,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -13090,6 +13126,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
13090 impl_fn->ir_executable.parent_exec = ira->new_irb.exec;13126 impl_fn->ir_executable.parent_exec = ira->new_irb.exec;
13091 impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;13127 impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;
13092 impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;13128 impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;
13129 impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota;
13093 impl_fn->analyzed_executable.is_generic_instantiation = true;13130 impl_fn->analyzed_executable.is_generic_instantiation = true;
1309413131
13095 ira->codegen->fn_defs.append(impl_fn);13132 ira->codegen->fn_defs.append(impl_fn);
src/link.cpp+1-1
...@@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {...@@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
901 if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {901 if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {
902 Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));902 Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
903 lj->args.append(buf_ptr(arg));903 lj->args.append(buf_ptr(arg));
904 } else {904 } else {
905 lj->args.append(buf_ptr(link_lib->name));905 lj->args.append(buf_ptr(link_lib->name));
906 }906 }
907 }907 }
src/os.cpp+4-244
...@@ -26,7 +26,6 @@...@@ -26,7 +26,6 @@
26#include <windows.h>26#include <windows.h>
27#include <io.h>27#include <io.h>
28#include <fcntl.h>28#include <fcntl.h>
29#include "windows_com.hpp"
3029
31typedef SSIZE_T ssize_t;30typedef SSIZE_T ssize_t;
32#else31#else
...@@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {...@@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {
1115#endif1114#endif
1116}1115}
11171116
1118int os_find_windows_sdk(ZigWindowsSDK **out_sdk) {
1119#if defined(ZIG_OS_WINDOWS)
1120 ZigWindowsSDK *result_sdk = allocate<ZigWindowsSDK>(1);
1121 buf_resize(&result_sdk->path10, 0);
1122 buf_resize(&result_sdk->path81, 0);
1123
1124 HKEY key;
1125 HRESULT rc;
1126 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
1127 if (rc != ERROR_SUCCESS) {
1128 return ErrorFileNotFound;
1129 }
1130
1131 {
1132 DWORD tmp_buf_len = MAX_PATH;
1133 buf_resize(&result_sdk->path10, tmp_buf_len);
1134 rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len);
1135 if (rc == ERROR_FILE_NOT_FOUND) {
1136 buf_resize(&result_sdk->path10, 0);
1137 } else {
1138 buf_resize(&result_sdk->path10, tmp_buf_len);
1139 }
1140 }
1141 {
1142 DWORD tmp_buf_len = MAX_PATH;
1143 buf_resize(&result_sdk->path81, tmp_buf_len);
1144 rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len);
1145 if (rc == ERROR_FILE_NOT_FOUND) {
1146 buf_resize(&result_sdk->path81, 0);
1147 } else {
1148 buf_resize(&result_sdk->path81, tmp_buf_len);
1149 }
1150 }
1151
1152 if (buf_len(&result_sdk->path10) != 0) {
1153 Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10));
1154
1155 // enumerate files in sdk path looking for latest version
1156 WIN32_FIND_DATA ffd;
1157 HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
1158 if (hFind == INVALID_HANDLE_VALUE) {
1159 return ErrorFileNotFound;
1160 }
1161 int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
1162 bool found_version_dir = false;
1163 for (;;) {
1164 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1165 int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
1166 sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
1167 if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
1168 // Microsoft released 26624 as 10240 accidentally.
1169 // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
1170 c2 = 26624;
1171 }
1172 if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
1173 v0 = c0, v1 = c1, v2 = c2, v3 = c3;
1174 buf_init_from_str(&result_sdk->version10, ffd.cFileName);
1175 found_version_dir = true;
1176 }
1177 }
1178 if (FindNextFile(hFind, &ffd) == 0) {
1179 FindClose(hFind);
1180 break;
1181 }
1182 }
1183 if (!found_version_dir) {
1184 buf_resize(&result_sdk->path10, 0);
1185 }
1186 }
1187
1188 if (buf_len(&result_sdk->path81) != 0) {
1189 Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81));
1190
1191 // enumerate files in sdk path looking for latest version
1192 WIN32_FIND_DATA ffd;
1193 HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
1194 if (hFind == INVALID_HANDLE_VALUE) {
1195 return ErrorFileNotFound;
1196 }
1197 int v0 = 0, v1 = 0;
1198 bool found_version_dir = false;
1199 for (;;) {
1200 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1201 int c0 = 0, c1 = 0;
1202 sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
1203 if ((c0 > v0) || (c1 > v1)) {
1204 v0 = c0, v1 = c1;
1205 buf_init_from_str(&result_sdk->version81, ffd.cFileName);
1206 found_version_dir = true;
1207 }
1208 }
1209 if (FindNextFile(hFind, &ffd) == 0) {
1210 FindClose(hFind);
1211 break;
1212 }
1213 }
1214 if (!found_version_dir) {
1215 buf_resize(&result_sdk->path81, 0);
1216 }
1217 }
1218
1219 *out_sdk = result_sdk;
1220 return 0;
1221#else
1222 return ErrorFileNotFound;
1223#endif
1224}
1225
1226int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) {
1227#if defined(ZIG_OS_WINDOWS)
1228 buf_resize(output_buf, 0);
1229 //COM Smart Pointerse requires explicit scope
1230 {
1231 HRESULT rc;
1232 rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
1233 if (rc != S_OK) {
1234 goto com_done;
1235 }
1236
1237 //This COM class is installed when a VS2017
1238 ISetupConfigurationPtr setup_config;
1239 rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
1240 if (rc != S_OK) {
1241 goto com_done;
1242 }
1243
1244 IEnumSetupInstancesPtr all_instances;
1245 rc = setup_config->EnumInstances(&all_instances);
1246 if (rc != S_OK) {
1247 goto com_done;
1248 }
1249
1250 ISetupInstance* curr_instance;
1251 ULONG found_inst;
1252 while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
1253 BSTR bstr_inst_path;
1254 rc = curr_instance->GetInstallationPath(&bstr_inst_path);
1255 if (rc != S_OK) {
1256 goto com_done;
1257 }
1258 //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
1259 UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
1260 ULONG tmp_path_len = bstr_path_len / 2 + 1;
1261 char* conv_path = (char*)bstr_inst_path;
1262 char *tmp_path = (char*)alloca(tmp_path_len);
1263 memset(tmp_path, 0, tmp_path_len);
1264 uint32_t c = 0;
1265 for (uint32_t i = 0; i < bstr_path_len; i += 2) {
1266 tmp_path[c] = conv_path[i];
1267 ++c;
1268 assert(c != tmp_path_len);
1269 }
1270
1271 buf_append_str(output_buf, tmp_path);
1272 buf_append_char(output_buf, '\\');
1273
1274 Buf* tmp_buf = buf_alloc();
1275 buf_append_buf(tmp_buf, output_buf);
1276 buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
1277 FILE* tools_file = fopen(buf_ptr(tmp_buf), "r");
1278 if (!tools_file) {
1279 goto com_done;
1280 }
1281 memset(tmp_path, 0, tmp_path_len);
1282 fgets(tmp_path, tmp_path_len, tools_file);
1283 strtok(tmp_path, " \r\n");
1284 fclose(tools_file);
1285 buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
1286 switch (platform_type) {
1287 case ZigLLVM_x86:
1288 buf_append_str(output_buf, "x86\\");
1289 break;
1290 case ZigLLVM_x86_64:
1291 buf_append_str(output_buf, "x64\\");
1292 break;
1293 case ZigLLVM_arm:
1294 buf_append_str(output_buf, "arm\\");
1295 break;
1296 default:
1297 zig_panic("Attemped to use vcruntime for non-supported platform.");
1298 }
1299 buf_resize(tmp_buf, 0);
1300 buf_append_buf(tmp_buf, output_buf);
1301 buf_append_str(tmp_buf, "vcruntime.lib");
1302
1303 if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1304 return 0;
1305 }
1306 }
1307 }
1308
1309com_done:;
1310 HKEY key;
1311 HRESULT rc;
1312 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
1313 if (rc != ERROR_SUCCESS) {
1314 return ErrorFileNotFound;
1315 }
1316
1317 DWORD dw_type = 0;
1318 DWORD cb_data = 0;
1319 rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
1320 if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
1321 return ErrorFileNotFound;
1322 }
1323
1324 Buf* tmp_buf = buf_alloc_fixed(cb_data);
1325 RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data);
1326 //RegQueryValueExA returns the length of the string INCLUDING the null terminator
1327 buf_resize(tmp_buf, cb_data-1);
1328 buf_append_str(tmp_buf, "VC\\Lib\\");
1329 switch (platform_type) {
1330 case ZigLLVM_x86:
1331 //x86 is in the root of the Lib folder
1332 break;
1333 case ZigLLVM_x86_64:
1334 buf_append_str(tmp_buf, "amd64\\");
1335 break;
1336 case ZigLLVM_arm:
1337 buf_append_str(tmp_buf, "arm\\");
1338 break;
1339 default:
1340 zig_panic("Attemped to use vcruntime for non-supported platform.");
1341 }
1342
1343 buf_append_buf(output_buf, tmp_buf);
1344 buf_append_str(tmp_buf, "vcruntime.lib");
1345
1346 if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1347 return 0;
1348 } else {
1349 buf_resize(output_buf, 0);
1350 return ErrorFileNotFound;
1351 }
1352#else
1353 return ErrorFileNotFound;
1354#endif
1355}
1356
1357int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {1117int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
1358#if defined(ZIG_OS_WINDOWS)1118#if defined(ZIG_OS_WINDOWS)
1359 buf_resize(output_buf, 0);1119 buf_resize(output_buf, 0);
1360 buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));1120 buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
1361 switch (platform_type) {1121 switch (platform_type) {
1362 case ZigLLVM_x86:1122 case ZigLLVM_x86:
1363 buf_append_str(output_buf, "x86\\");1123 buf_append_str(output_buf, "x86\\");
...@@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch...@@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
1389int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {1149int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
1390#if defined(ZIG_OS_WINDOWS)1150#if defined(ZIG_OS_WINDOWS)
1391 buf_resize(output_buf, 0);1151 buf_resize(output_buf, 0);
1392 buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));1152 buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
1393 if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {1153 if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
1394 return 0;1154 return 0;
1395 }1155 }
...@@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy...@@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
1406#if defined(ZIG_OS_WINDOWS)1166#if defined(ZIG_OS_WINDOWS)
1407 {1167 {
1408 buf_resize(output_buf, 0);1168 buf_resize(output_buf, 0);
1409 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));1169 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
1410 switch (platform_type) {1170 switch (platform_type) {
1411 case ZigLLVM_x86:1171 case ZigLLVM_x86:
1412 buf_append_str(output_buf, "x86\\");1172 buf_append_str(output_buf, "x86\\");
...@@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy...@@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
1429 }1189 }
1430 {1190 {
1431 buf_resize(output_buf, 0);1191 buf_resize(output_buf, 0);
1432 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81));1192 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
1433 switch (platform_type) {1193 switch (platform_type) {
1434 case ZigLLVM_x86:1194 case ZigLLVM_x86:
1435 buf_append_str(output_buf, "x86\\");1195 buf_append_str(output_buf, "x86\\");
src/os.hpp+1-9
...@@ -12,6 +12,7 @@...@@ -12,6 +12,7 @@
12#include "buffer.hpp"12#include "buffer.hpp"
13#include "error.hpp"13#include "error.hpp"
14#include "zig_llvm.h"14#include "zig_llvm.h"
15#include "windows_sdk.h"
1516
16#include <stdio.h>17#include <stdio.h>
17#include <inttypes.h>18#include <inttypes.h>
...@@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);...@@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);
7980
80int os_self_exe_path(Buf *out_path);81int os_self_exe_path(Buf *out_path);
8182
82struct ZigWindowsSDK {
83 Buf path10;
84 Buf version10;
85 Buf path81;
86 Buf version81;
87};
88
89int os_find_windows_sdk(ZigWindowsSDK **out_sdk);
90int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type);
91int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);83int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
92int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);84int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
93int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);85int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
src/tokenizer.cpp+13-8
...@@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) {...@@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) {
460static void invalid_char_error(Tokenize *t, uint8_t c) {460static void invalid_char_error(Tokenize *t, uint8_t c) {
461 if (c == '\r') {461 if (c == '\r') {
462 tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported");462 tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported");
463 } else if (isprint(c)) {463 return;
464 }
465
466 const char *sh = get_escape_shorthand(c);
467 if (sh) {
468 tokenize_error(t, "invalid character: '%s'", sh);
469 return;
470 }
471
472 if (isprint(c)) {
464 tokenize_error(t, "invalid character: '%c'", c);473 tokenize_error(t, "invalid character: '%c'", c);
465 } else {474 return;
466 const char *sh = get_escape_shorthand(c);
467 if (sh) {
468 tokenize_error(t, "invalid character: '%s'", sh);
469 } else {
470 tokenize_error(t, "invalid character: '\\x%x'", c);
471 }
472 }475 }
476
477 tokenize_error(t, "invalid character: '\\x%02x'", c);
473}478}
474479
475void tokenize(Buf *buf, Tokenization *out) {480void tokenize(Buf *buf, Tokenization *out) {
src/windows_sdk.cpp created+352
...@@ -0,0 +1,352 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#include "windows_sdk.h"
9
10#if defined(_WIN32)
11
12#include "windows_com.hpp"
13#include <inttypes.h>
14#include <assert.h>
15
16struct ZigWindowsSDKPrivate {
17 ZigWindowsSDK base;
18};
19
20enum NativeArch {
21 NativeArchArm,
22 NativeArchi386,
23 NativeArchx86_64,
24};
25
26#if defined(_M_ARM) || defined(__arm_)
27static const NativeArch native_arch = NativeArchArm;
28#endif
29#if defined(_M_IX86) || defined(__i386__)
30static const NativeArch native_arch = NativeArchi386;
31#endif
32#if defined(_M_X64) || defined(__x86_64__)
33static const NativeArch native_arch = NativeArchx86_64;
34#endif
35
36void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {
37 if (sdk == nullptr) {
38 return;
39 }
40 free((void*)sdk->path10_ptr);
41 free((void*)sdk->version10_ptr);
42 free((void*)sdk->path81_ptr);
43 free((void*)sdk->version81_ptr);
44 free((void*)sdk->msvc_lib_dir_ptr);
45}
46
47static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) {
48 //COM Smart Pointers requires explicit scope
49 {
50 HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
51 if (rc != S_OK && rc != S_FALSE) {
52 goto com_done;
53 }
54
55 //This COM class is installed when a VS2017
56 ISetupConfigurationPtr setup_config;
57 rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
58 if (rc != S_OK) {
59 goto com_done;
60 }
61
62 IEnumSetupInstancesPtr all_instances;
63 rc = setup_config->EnumInstances(&all_instances);
64 if (rc != S_OK) {
65 goto com_done;
66 }
67
68 ISetupInstance* curr_instance;
69 ULONG found_inst;
70 while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
71 BSTR bstr_inst_path;
72 rc = curr_instance->GetInstallationPath(&bstr_inst_path);
73 if (rc != S_OK) {
74 goto com_done;
75 }
76 //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
77 //TODO call an actual function to do this
78 UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
79 ULONG tmp_path_len = bstr_path_len / 2 + 1;
80 char* conv_path = (char*)bstr_inst_path;
81 // TODO don't use alloca
82 char *tmp_path = (char*)alloca(tmp_path_len);
83 memset(tmp_path, 0, tmp_path_len);
84 uint32_t c = 0;
85 for (uint32_t i = 0; i < bstr_path_len; i += 2) {
86 tmp_path[c] = conv_path[i];
87 ++c;
88 assert(c != tmp_path_len);
89 }
90 char output_path[4096];
91 output_path[0] = 0;
92 char *out_append_ptr = output_path;
93
94 out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path);
95
96 char tmp_buf[4096];
97 sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
98 FILE* tools_file = fopen(tmp_buf, "rb");
99 if (!tools_file) {
100 goto com_done;
101 }
102 memset(tmp_path, 0, tmp_path_len);
103 fgets(tmp_path, tmp_path_len, tools_file);
104 strtok(tmp_path, " \r\n");
105 fclose(tools_file);
106 out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
107 switch (native_arch) {
108 case NativeArchi386:
109 out_append_ptr += sprintf(out_append_ptr, "x86\\");
110 break;
111 case NativeArchx86_64:
112 out_append_ptr += sprintf(out_append_ptr, "x64\\");
113 break;
114 case NativeArchArm:
115 out_append_ptr += sprintf(out_append_ptr, "arm\\");
116 break;
117 }
118 sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib");
119
120 if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
121 priv->base.msvc_lib_dir_ptr = strdup(output_path);
122 if (priv->base.msvc_lib_dir_ptr == nullptr) {
123 return ZigFindWindowsSdkErrorOutOfMemory;
124 }
125 priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr);
126 return ZigFindWindowsSdkErrorNone;
127 }
128 }
129 }
130
131com_done:;
132 HKEY key;
133 HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0,
134 KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
135 if (rc != ERROR_SUCCESS) {
136 return ZigFindWindowsSdkErrorNotFound;
137 }
138
139 DWORD dw_type = 0;
140 DWORD cb_data = 0;
141 rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
142 if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
143 return ZigFindWindowsSdkErrorNotFound;
144 }
145
146 char tmp_buf[4096];
147
148 RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data);
149 // RegQueryValueExA returns the length of the string INCLUDING the null terminator
150 char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1);
151 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\");
152 switch (native_arch) {
153 case NativeArchi386:
154 //x86 is in the root of the Lib folder
155 break;
156 case NativeArchx86_64:
157 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\");
158 break;
159 case NativeArchArm:
160 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\");
161 break;
162 }
163
164 char *output_path = strdup(tmp_buf);
165 if (output_path == nullptr) {
166 return ZigFindWindowsSdkErrorOutOfMemory;
167 }
168
169 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib");
170
171 if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
172 priv->base.msvc_lib_dir_ptr = output_path;
173 priv->base.msvc_lib_dir_len = strlen(output_path);
174 return ZigFindWindowsSdkErrorNone;
175 } else {
176 free(output_path);
177 return ZigFindWindowsSdkErrorNotFound;
178 }
179}
180
181static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
182 if (priv->base.path10_ptr == nullptr)
183 return ZigFindWindowsSdkErrorNone;
184
185 char sdk_lib_dir[4096];
186 int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr);
187 if (n < 0 || n >= 4096) {
188 return ZigFindWindowsSdkErrorPathTooLong;
189 }
190
191 // enumerate files in sdk path looking for latest version
192 WIN32_FIND_DATA ffd;
193 HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
194 if (hFind == INVALID_HANDLE_VALUE) {
195 return ZigFindWindowsSdkErrorNotFound;
196 }
197 int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
198 for (;;) {
199 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
200 int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
201 sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
202 if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
203 // Microsoft released 26624 as 10240 accidentally.
204 // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
205 c2 = 26624;
206 }
207 if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
208 v0 = c0, v1 = c1, v2 = c2, v3 = c3;
209 free((void*)priv->base.version10_ptr);
210 priv->base.version10_ptr = strdup(ffd.cFileName);
211 if (priv->base.version10_ptr == nullptr) {
212 FindClose(hFind);
213 return ZigFindWindowsSdkErrorOutOfMemory;
214 }
215 }
216 }
217 if (FindNextFile(hFind, &ffd) == 0) {
218 FindClose(hFind);
219 break;
220 }
221 }
222 priv->base.version10_len = strlen(priv->base.version10_ptr);
223 return ZigFindWindowsSdkErrorNone;
224}
225
226static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
227 if (priv->base.path81_ptr == nullptr)
228 return ZigFindWindowsSdkErrorNone;
229
230 char sdk_lib_dir[4096];
231 int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr);
232 if (n < 0 || n >= 4096) {
233 return ZigFindWindowsSdkErrorPathTooLong;
234 }
235
236 // enumerate files in sdk path looking for latest version
237 WIN32_FIND_DATA ffd;
238 HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
239 if (hFind == INVALID_HANDLE_VALUE) {
240 return ZigFindWindowsSdkErrorNotFound;
241 }
242 int v0 = 0, v1 = 0;
243 for (;;) {
244 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
245 int c0 = 0, c1 = 0;
246 sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
247 if ((c0 > v0) || (c1 > v1)) {
248 v0 = c0, v1 = c1;
249 free((void*)priv->base.version81_ptr);
250 priv->base.version81_ptr = strdup(ffd.cFileName);
251 if (priv->base.version81_ptr == nullptr) {
252 FindClose(hFind);
253 return ZigFindWindowsSdkErrorOutOfMemory;
254 }
255 }
256 }
257 if (FindNextFile(hFind, &ffd) == 0) {
258 FindClose(hFind);
259 break;
260 }
261 }
262 priv->base.version81_len = strlen(priv->base.version81_ptr);
263 return ZigFindWindowsSdkErrorNone;
264}
265
266ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
267 ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate));
268 if (priv == nullptr) {
269 return ZigFindWindowsSdkErrorOutOfMemory;
270 }
271
272 HKEY key;
273 HRESULT rc;
274 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0,
275 KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
276 if (rc != ERROR_SUCCESS) {
277 zig_free_windows_sdk(&priv->base);
278 return ZigFindWindowsSdkErrorNotFound;
279 }
280
281 {
282 DWORD tmp_buf_len = MAX_PATH;
283 priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1);
284 if (priv->base.path10_ptr == nullptr) {
285 zig_free_windows_sdk(&priv->base);
286 return ZigFindWindowsSdkErrorOutOfMemory;
287 }
288 rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len);
289 if (rc == ERROR_SUCCESS) {
290 priv->base.path10_len = tmp_buf_len - 1;
291 if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') {
292 priv->base.path10_len -= 1;
293 }
294 } else {
295 free((void*)priv->base.path10_ptr);
296 priv->base.path10_ptr = nullptr;
297 }
298 }
299 {
300 DWORD tmp_buf_len = MAX_PATH;
301 priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1);
302 if (priv->base.path81_ptr == nullptr) {
303 zig_free_windows_sdk(&priv->base);
304 return ZigFindWindowsSdkErrorOutOfMemory;
305 }
306 rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len);
307 if (rc == ERROR_SUCCESS) {
308 priv->base.path81_len = tmp_buf_len - 1;
309 if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') {
310 priv->base.path81_len -= 1;
311 }
312 } else {
313 free((void*)priv->base.path81_ptr);
314 priv->base.path81_ptr = nullptr;
315 }
316 }
317
318 {
319 ZigFindWindowsSdkError err = find_10_version(priv);
320 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
321 zig_free_windows_sdk(&priv->base);
322 return err;
323 }
324 }
325 {
326 ZigFindWindowsSdkError err = find_81_version(priv);
327 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
328 zig_free_windows_sdk(&priv->base);
329 return err;
330 }
331 }
332
333 {
334 ZigFindWindowsSdkError err = find_msvc_lib_dir(priv);
335 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
336 zig_free_windows_sdk(&priv->base);
337 return err;
338 }
339 }
340
341 *out_sdk = &priv->base;
342 return ZigFindWindowsSdkErrorNone;
343}
344
345#else
346
347void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {}
348ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
349 return ZigFindWindowsSdkErrorNotFound;
350}
351
352#endif
src/windows_sdk.h created+47
...@@ -0,0 +1,47 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_WINDOWS_SDK_H
9#define ZIG_WINDOWS_SDK_H
10
11#ifdef __cplusplus
12#define ZIG_EXTERN_C extern "C"
13#else
14#define ZIG_EXTERN_C
15#endif
16
17#include <stddef.h>
18
19struct ZigWindowsSDK {
20 const char *path10_ptr;
21 size_t path10_len;
22
23 const char *version10_ptr;
24 size_t version10_len;
25
26 const char *path81_ptr;
27 size_t path81_len;
28
29 const char *version81_ptr;
30 size_t version81_len;
31
32 const char *msvc_lib_dir_ptr;
33 size_t msvc_lib_dir_len;
34};
35
36enum ZigFindWindowsSdkError {
37 ZigFindWindowsSdkErrorNone,
38 ZigFindWindowsSdkErrorOutOfMemory,
39 ZigFindWindowsSdkErrorNotFound,
40 ZigFindWindowsSdkErrorPathTooLong,
41};
42
43ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
44
45ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
46
47#endif
src/zig_llvm.cpp+5
...@@ -455,6 +455,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso...@@ -455,6 +455,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso
455 return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);455 return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
456}456}
457457
458void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) {
459 DIBuilder *di_builder = reinterpret_cast<DIBuilder *>(dbuilder);
460 delete di_builder;
461}
462
458void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) {463void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) {
459 unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get(464 unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get(
460 line, column, reinterpret_cast<DIScope*>(scope)));465 line, column, reinterpret_cast<DIScope*>(scope)));
src/zig_llvm.h+5-1
...@@ -22,6 +22,9 @@...@@ -22,6 +22,9 @@
22#define ZIG_EXTERN_C22#define ZIG_EXTERN_C
23#endif23#endif
2424
25// ATTENTION: If you modify this file, be sure to update the corresponding
26// extern function declarations in the self-hosted compiler.
27
25struct ZigLLVMDIType;28struct ZigLLVMDIType;
26struct ZigLLVMDIBuilder;29struct ZigLLVMDIBuilder;
27struct ZigLLVMDICompileUnit;30struct ZigLLVMDICompileUnit;
...@@ -39,7 +42,7 @@ struct ZigLLVMInsertionPoint;...@@ -39,7 +42,7 @@ struct ZigLLVMInsertionPoint;
39ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);42ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
40ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);43ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
4144
42/// Caller must free memory.45/// Caller must free memory with LLVMDisposeMessage
43ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void);46ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void);
44ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);47ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
4548
...@@ -145,6 +148,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);...@@ -145,6 +148,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
145ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);148ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
146149
147ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);150ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
151ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder);
148ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);152ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
149ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module);153ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module);
150154
std/array_list.zig+38-11
...@@ -113,13 +113,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -113,13 +113,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
113 return old_item;113 return old_item;
114 }114 }
115115
116 pub fn removeOrError(self: *Self, n: usize) !T {116 /// Removes the element at the specified index and returns it
117 if (n >= self.len) return error.OutOfBounds;117 /// or an error.OutOfBounds is returned. If no error then
118 if (self.len - 1 == n) return self.pop();118 /// the empty slot is filled from the end of the list.
119119 pub fn swapRemoveOrError(self: *Self, i: usize) !T {
120 var old_item = self.at(n);120 if (i >= self.len) return error.OutOfBounds;
121 try self.setOrError(n, self.pop());121 return self.swapRemove(i);
122 return old_item;
123 }122 }
124123
125 pub fn appendSlice(self: *Self, items: []align(A) const T) !void {124 pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
...@@ -192,7 +191,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -192,7 +191,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
192 };191 };
193}192}
194193
195test "basic ArrayList test" {194test "std.ArrayList.basic" {
196 var bytes: [1024]u8 = undefined;195 var bytes: [1024]u8 = undefined;
197 const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;196 const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
198197
...@@ -279,7 +278,35 @@ test "std.ArrayList.swapRemove" {...@@ -279,7 +278,35 @@ test "std.ArrayList.swapRemove" {
279 assert(list.len == 4);278 assert(list.len == 4);
280}279}
281280
282test "iterator ArrayList test" {281test "std.ArrayList.swapRemoveOrError" {
282 var list = ArrayList(i32).init(debug.global_allocator);
283 defer list.deinit();
284
285 // Test just after initialization
286 assertError(list.swapRemoveOrError(0), error.OutOfBounds);
287
288 // Test after adding one item and remote it
289 try list.append(1);
290 assert((try list.swapRemoveOrError(0)) == 1);
291 assertError(list.swapRemoveOrError(0), error.OutOfBounds);
292
293 // Test after adding two items and remote both
294 try list.append(1);
295 try list.append(2);
296 assert((try list.swapRemoveOrError(1)) == 2);
297 assert((try list.swapRemoveOrError(0)) == 1);
298 assertError(list.swapRemoveOrError(0), error.OutOfBounds);
299
300 // Test out of bounds with one item
301 try list.append(1);
302 assertError(list.swapRemoveOrError(1), error.OutOfBounds);
303
304 // Test out of bounds with two items
305 try list.append(2);
306 assertError(list.swapRemoveOrError(2), error.OutOfBounds);
307}
308
309test "std.ArrayList.iterator" {
283 var list = ArrayList(i32).init(debug.global_allocator);310 var list = ArrayList(i32).init(debug.global_allocator);
284 defer list.deinit();311 defer list.deinit();
285312
...@@ -308,7 +335,7 @@ test "iterator ArrayList test" {...@@ -308,7 +335,7 @@ test "iterator ArrayList test" {
308 assert(it.next().? == 1);335 assert(it.next().? == 1);
309}336}
310337
311test "insert ArrayList test" {338test "std.ArrayList.insert" {
312 var list = ArrayList(i32).init(debug.global_allocator);339 var list = ArrayList(i32).init(debug.global_allocator);
313 defer list.deinit();340 defer list.deinit();
314341
...@@ -322,7 +349,7 @@ test "insert ArrayList test" {...@@ -322,7 +349,7 @@ test "insert ArrayList test" {
322 assert(list.items[3] == 3);349 assert(list.items[3] == 3);
323}350}
324351
325test "insertSlice ArrayList test" {352test "std.ArrayList.insertSlice" {
326 var list = ArrayList(i32).init(debug.global_allocator);353 var list = ArrayList(i32).init(debug.global_allocator);
327 defer list.deinit();354 defer list.deinit();
328355
std/atomic/int.zig+4
...@@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type {...@@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type {
25 pub fn get(self: *Self) T {25 pub fn get(self: *Self) T {
26 return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);26 return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);
27 }27 }
28
29 pub fn xchg(self: *Self, new_value: T) T {
30 return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst);
31 }
28 };32 };
29}33}
std/atomic/queue.zig+14
...@@ -51,6 +51,20 @@ pub fn Queue(comptime T: type) type {...@@ -51,6 +51,20 @@ pub fn Queue(comptime T: type) type {
51 return head;51 return head;
52 }52 }
5353
54 pub fn unget(self: *Self, node: *Node) void {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
57
58 const opt_head = self.head;
59 self.head = node;
60 if (opt_head) |head| {
61 head.next = node;
62 } else {
63 assert(self.tail == null);
64 self.tail = node;
65 }
66 }
67
54 pub fn isEmpty(self: *Self) bool {68 pub fn isEmpty(self: *Self) bool {
55 return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;69 return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;
56 }70 }
std/buffer.zig+13
...@@ -54,6 +54,19 @@ pub const Buffer = struct {...@@ -54,6 +54,19 @@ pub const Buffer = struct {
54 return result;54 return result;
55 }55 }
5656
57 pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer {
58 const countSize = struct {
59 fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
60 size.* += bytes.len;
61 }
62 }.countSize;
63 var size: usize = 0;
64 std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {};
65 var self = try Buffer.initSize(allocator, size);
66 assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
67 return self;
68 }
69
57 pub fn deinit(self: *Buffer) void {70 pub fn deinit(self: *Buffer) void {
58 self.list.deinit();71 self.list.deinit();
59 }72 }
std/c/darwin.zig+1-1
...@@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen...@@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen
30pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;30pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
31pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;31pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
3232
33pub use @import("../os/darwin_errno.zig");33pub use @import("../os/darwin/errno.zig");
3434
35pub const _errno = __error;35pub const _errno = __error;
3636
std/dwarf.zig+37
...@@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03;...@@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03;
639pub const LNE_set_discriminator = 0x04;639pub const LNE_set_discriminator = 0x04;
640pub const LNE_lo_user = 0x80;640pub const LNE_lo_user = 0x80;
641pub const LNE_hi_user = 0xff;641pub const LNE_hi_user = 0xff;
642
643pub const LANG_C89 = 0x0001;
644pub const LANG_C = 0x0002;
645pub const LANG_Ada83 = 0x0003;
646pub const LANG_C_plus_plus = 0x0004;
647pub const LANG_Cobol74 = 0x0005;
648pub const LANG_Cobol85 = 0x0006;
649pub const LANG_Fortran77 = 0x0007;
650pub const LANG_Fortran90 = 0x0008;
651pub const LANG_Pascal83 = 0x0009;
652pub const LANG_Modula2 = 0x000a;
653pub const LANG_Java = 0x000b;
654pub const LANG_C99 = 0x000c;
655pub const LANG_Ada95 = 0x000d;
656pub const LANG_Fortran95 = 0x000e;
657pub const LANG_PLI = 0x000f;
658pub const LANG_ObjC = 0x0010;
659pub const LANG_ObjC_plus_plus = 0x0011;
660pub const LANG_UPC = 0x0012;
661pub const LANG_D = 0x0013;
662pub const LANG_Python = 0x0014;
663pub const LANG_Go = 0x0016;
664pub const LANG_C_plus_plus_11 = 0x001a;
665pub const LANG_Rust = 0x001c;
666pub const LANG_C11 = 0x001d;
667pub const LANG_C_plus_plus_14 = 0x0021;
668pub const LANG_Fortran03 = 0x0022;
669pub const LANG_Fortran08 = 0x0023;
670pub const LANG_lo_user = 0x8000;
671pub const LANG_hi_user = 0xffff;
672pub const LANG_Mips_Assembler = 0x8001;
673pub const LANG_Upc = 0x8765;
674pub const LANG_HP_Bliss = 0x8003;
675pub const LANG_HP_Basic91 = 0x8004;
676pub const LANG_HP_Pascal91 = 0x8005;
677pub const LANG_HP_IMacro = 0x8006;
678pub const LANG_HP_Assembler = 0x8007;
std/event/future.zig+31-8
...@@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder;...@@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder;
6const Lock = std.event.Lock;6const Lock = std.event.Lock;
7const Loop = std.event.Loop;7const Loop = std.event.Loop;
88
9/// This is a value that starts out unavailable, until a value is put().9/// This is a value that starts out unavailable, until resolve() is called
10/// While it is unavailable, coroutines suspend when they try to get() it,10/// While it is unavailable, coroutines suspend when they try to get() it,
11/// and then are resumed when the value is put().11/// and then are resumed when resolve() is called.
12/// At this point the value remains forever available, and another put() is not allowed.12/// At this point the value remains forever available, and another resolve() is not allowed.
13pub fn Future(comptime T: type) type {13pub fn Future(comptime T: type) type {
14 return struct {14 return struct {
15 lock: Lock,15 lock: Lock,
16 data: T,16 data: T,
17 available: u8, // TODO make this a bool17
18 /// TODO make this an enum
19 /// 0 - not started
20 /// 1 - started
21 /// 2 - finished
22 available: u8,
1823
19 const Self = this;24 const Self = this;
20 const Queue = std.atomic.Queue(promise);25 const Queue = std.atomic.Queue(promise);
...@@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type {...@@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type {
31 /// available.36 /// available.
32 /// Thread-safe.37 /// Thread-safe.
33 pub async fn get(self: *Self) *T {38 pub async fn get(self: *Self) *T {
34 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) {39 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
35 return &self.data;40 return &self.data;
36 }41 }
37 const held = await (async self.lock.acquire() catch unreachable);42 const held = await (async self.lock.acquire() catch unreachable);
...@@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type {...@@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type {
43 /// Gets the data without waiting for it. If it's available, a pointer is48 /// Gets the data without waiting for it. If it's available, a pointer is
44 /// returned. Otherwise, null is returned.49 /// returned. Otherwise, null is returned.
45 pub fn getOrNull(self: *Self) ?*T {50 pub fn getOrNull(self: *Self) ?*T {
46 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) {51 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
47 return &self.data;52 return &self.data;
48 } else {53 } else {
49 return null;54 return null;
50 }55 }
51 }56 }
5257
58 /// If someone else has started working on the data, wait for them to complete
59 /// and return a pointer to the data. Otherwise, return null, and the caller
60 /// should start working on the data.
61 /// It's not required to call start() before resolve() but it can be useful since
62 /// this method is thread-safe.
63 pub async fn start(self: *Self) ?*T {
64 const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
65 switch (state) {
66 1 => {
67 const held = await (async self.lock.acquire() catch unreachable);
68 held.release();
69 return &self.data;
70 },
71 2 => return &self.data,
72 else => unreachable,
73 }
74 }
75
53 /// Make the data become available. May be called only once.76 /// Make the data become available. May be called only once.
54 /// Before calling this, modify the `data` property.77 /// Before calling this, modify the `data` property.
55 pub fn resolve(self: *Self) void {78 pub fn resolve(self: *Self) void {
56 const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);79 const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
57 assert(prev == 0); // put() called twice80 assert(prev == 0 or prev == 1); // resolve() called twice
58 Lock.Held.release(Lock.Held{ .lock = &self.lock });81 Lock.Held.release(Lock.Held{ .lock = &self.lock });
59 }82 }
60 };83 };
std/event/group.zig+14-2
...@@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;6const AtomicOrder = builtin.AtomicOrder;
7const assert = std.debug.assert;7const assert = std.debug.assert;
88
9/// ReturnType should be `void` or `E!void`9/// ReturnType must be `void` or `E!void`
10pub fn Group(comptime ReturnType: type) type {10pub fn Group(comptime ReturnType: type) type {
11 return struct {11 return struct {
12 coro_stack: Stack,12 coro_stack: Stack,
...@@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type {...@@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type {
38 self.alloc_stack.push(node);38 self.alloc_stack.push(node);
39 }39 }
4040
41 /// Add a node to the group. Thread-safe. Cannot fail.
42 /// `node.data` should be the promise handle to add to the group.
43 /// The node's memory should be in the coroutine frame of
44 /// the handle that is in the node, or somewhere guaranteed to live
45 /// at least as long.
46 pub fn addNode(self: *Self, node: *Stack.Node) void {
47 self.coro_stack.push(node);
48 }
49
41 /// This is equivalent to an async call, but the async function is added to the group, instead50 /// This is equivalent to an async call, but the async function is added to the group, instead
42 /// of returning a promise. func must be async and have return type void.51 /// of returning a promise. func must be async and have return type ReturnType.
43 /// Thread-safe.52 /// Thread-safe.
44 pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {53 pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
45 const S = struct {54 const S = struct {
...@@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {...@@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {
6776
68 /// Wait for all the calls and promises of the group to complete.77 /// Wait for all the calls and promises of the group to complete.
69 /// Thread-safe.78 /// Thread-safe.
79 /// Safe to call any number of times.
70 pub async fn wait(self: *Self) ReturnType {80 pub async fn wait(self: *Self) ReturnType {
71 // TODO catch unreachable because the allocation can be grouped with81 // TODO catch unreachable because the allocation can be grouped with
72 // the coro frame allocation82 // the coro frame allocation
...@@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type {...@@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type {
98 }108 }
99109
100 /// Cancel all the outstanding promises. May only be called if wait was never called.110 /// Cancel all the outstanding promises. May only be called if wait was never called.
111 /// TODO These should be `cancelasync` not `cancel`.
112 /// See https://github.com/ziglang/zig/issues/1261
101 pub fn cancelAll(self: *Self) void {113 pub fn cancelAll(self: *Self) void {
102 while (self.coro_stack.pop()) |node| {114 while (self.coro_stack.pop()) |node| {
103 cancel node.data;115 cancel node.data;
std/event/loop.zig+100-109
...@@ -12,7 +12,6 @@ pub const Loop = struct {...@@ -12,7 +12,6 @@ pub const Loop = struct {
12 next_tick_queue: std.atomic.Queue(promise),12 next_tick_queue: std.atomic.Queue(promise),
13 os_data: OsData,13 os_data: OsData,
14 final_resume_node: ResumeNode,14 final_resume_node: ResumeNode,
15 dispatch_lock: u8, // TODO make this a bool
16 pending_event_count: usize,15 pending_event_count: usize,
17 extra_threads: []*std.os.Thread,16 extra_threads: []*std.os.Thread,
1817
...@@ -74,11 +73,10 @@ pub const Loop = struct {...@@ -74,11 +73,10 @@ pub const Loop = struct {
74 /// max(thread_count - 1, 0)73 /// max(thread_count - 1, 0)
75 fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void {74 fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void {
76 self.* = Loop{75 self.* = Loop{
77 .pending_event_count = 0,76 .pending_event_count = 1,
78 .allocator = allocator,77 .allocator = allocator,
79 .os_data = undefined,78 .os_data = undefined,
80 .next_tick_queue = std.atomic.Queue(promise).init(),79 .next_tick_queue = std.atomic.Queue(promise).init(),
81 .dispatch_lock = 1, // start locked so threads go directly into epoll wait
82 .extra_threads = undefined,80 .extra_threads = undefined,
83 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),81 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),
84 .eventfd_resume_nodes = undefined,82 .eventfd_resume_nodes = undefined,
...@@ -235,8 +233,6 @@ pub const Loop = struct {...@@ -235,8 +233,6 @@ pub const Loop = struct {
235 }233 }
236 },234 },
237 builtin.Os.windows => {235 builtin.Os.windows => {
238 self.os_data.extra_thread_count = extra_thread_count;
239
240 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(
241 windows.INVALID_HANDLE_VALUE,237 windows.INVALID_HANDLE_VALUE,
242 null,238 null,
...@@ -306,7 +302,7 @@ pub const Loop = struct {...@@ -306,7 +302,7 @@ pub const Loop = struct {
306 pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {302 pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
307 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);303 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
308 errdefer {304 errdefer {
309 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);305 self.finishOneEvent();
310 }306 }
311 try self.modFd(307 try self.modFd(
312 fd,308 fd,
...@@ -326,7 +322,7 @@ pub const Loop = struct {...@@ -326,7 +322,7 @@ pub const Loop = struct {
326322
327 pub fn removeFd(self: *Loop, fd: i32) void {323 pub fn removeFd(self: *Loop, fd: i32) void {
328 self.removeFdNoCounter(fd);324 self.removeFdNoCounter(fd);
329 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);325 self.finishOneEvent();
330 }326 }
331327
332 fn removeFdNoCounter(self: *Loop, fd: i32) void {328 fn removeFdNoCounter(self: *Loop, fd: i32) void {
...@@ -345,14 +341,70 @@ pub const Loop = struct {...@@ -345,14 +341,70 @@ pub const Loop = struct {
345 }341 }
346 }342 }
347343
344 fn dispatch(self: *Loop) void {
345 while (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {
346 const next_tick_node = self.next_tick_queue.get() orelse {
347 self.available_eventfd_resume_nodes.push(resume_stack_node);
348 return;
349 };
350 const eventfd_node = &resume_stack_node.data;
351 eventfd_node.base.handle = next_tick_node.data;
352 switch (builtin.os) {
353 builtin.Os.macosx => {
354 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
355 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
356 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
357 self.next_tick_queue.unget(next_tick_node);
358 self.available_eventfd_resume_nodes.push(resume_stack_node);
359 return;
360 };
361 },
362 builtin.Os.linux => {
363 // the pending count is already accounted for
364 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |
365 std.os.linux.EPOLLET;
366 self.modFd(
367 eventfd_node.eventfd,
368 eventfd_node.epoll_op,
369 epoll_events,
370 &eventfd_node.base,
371 ) catch {
372 self.next_tick_queue.unget(next_tick_node);
373 self.available_eventfd_resume_nodes.push(resume_stack_node);
374 return;
375 };
376 },
377 builtin.Os.windows => {
378 // this value is never dereferenced but we need it to be non-null so that
379 // the consumer code can decide whether to read the completion key.
380 // it has to do this for normal I/O, so we match that behavior here.
381 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
382 std.os.windowsPostQueuedCompletionStatus(
383 self.os_data.io_port,
384 undefined,
385 eventfd_node.completion_key,
386 overlapped,
387 ) catch {
388 self.next_tick_queue.unget(next_tick_node);
389 self.available_eventfd_resume_nodes.push(resume_stack_node);
390 return;
391 };
392 },
393 else => @compileError("unsupported OS"),
394 }
395 }
396 }
397
348 /// Bring your own linked list node. This means it can't fail.398 /// Bring your own linked list node. This means it can't fail.
349 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {399 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
350 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);400 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
351 self.next_tick_queue.put(node);401 self.next_tick_queue.put(node);
402 self.dispatch();
352 }403 }
353404
354 pub fn run(self: *Loop) void {405 pub fn run(self: *Loop) void {
355 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);406 self.finishOneEvent(); // the reference we start with
407
356 self.workerRun();408 self.workerRun();
357 for (self.extra_threads) |extra_thread| {409 for (self.extra_threads) |extra_thread| {
358 extra_thread.wait();410 extra_thread.wait();
...@@ -392,110 +444,49 @@ pub const Loop = struct {...@@ -392,110 +444,49 @@ pub const Loop = struct {
392 .next = undefined,444 .next = undefined,
393 .data = p,445 .data = p,
394 };446 };
395 loop.onNextTick(&my_tick_node);447 self.onNextTick(&my_tick_node);
396 }448 }
397 }449 }
398450
399 fn workerRun(self: *Loop) void {451 fn finishOneEvent(self: *Loop) void {
400 start_over: while (true) {452 if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {
401 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {453 // cause all the threads to stop
402 while (self.next_tick_queue.get()) |next_tick_node| {454 switch (builtin.os) {
403 const handle = next_tick_node.data;455 builtin.Os.linux => {
404 if (self.next_tick_queue.isEmpty()) {456 // writing 8 bytes to an eventfd cannot fail
405 // last node, just resume it457 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
406 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);458 return;
407 resume handle;459 },
408 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);460 builtin.Os.macosx => {
409 continue :start_over;461 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
410 }462 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
411463 // cannot fail because we already added it and this just enables it
412 // non-last node, stick it in the epoll/kqueue set so that464 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
413 // other threads can get to it465 return;
414 if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {466 },
415 const eventfd_node = &resume_stack_node.data;467 builtin.Os.windows => {
416 eventfd_node.base.handle = handle;468 var i: usize = 0;
417 switch (builtin.os) {469 while (i < self.extra_threads.len + 1) : (i += 1) {
418 builtin.Os.macosx => {470 while (true) {
419 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);471 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
420 const eventlist = ([*]posix.Kevent)(undefined)[0..0];472 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
421 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {473 break;
422 // fine, we didn't need it anyway
423 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
424 self.available_eventfd_resume_nodes.push(resume_stack_node);
425 resume handle;
426 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
427 continue :start_over;
428 };
429 },
430 builtin.Os.linux => {
431 // the pending count is already accounted for
432 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET;
433 self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch {
434 // fine, we didn't need it anyway
435 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
436 self.available_eventfd_resume_nodes.push(resume_stack_node);
437 resume handle;
438 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
439 continue :start_over;
440 };
441 },
442 builtin.Os.windows => {
443 // this value is never dereferenced but we need it to be non-null so that
444 // the consumer code can decide whether to read the completion key.
445 // it has to do this for normal I/O, so we match that behavior here.
446 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
447 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch {
448 // fine, we didn't need it anyway
449 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
450 self.available_eventfd_resume_nodes.push(resume_stack_node);
451 resume handle;
452 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
453 continue :start_over;
454 };
455 },
456 else => @compileError("unsupported OS"),
457 }474 }
458 } else {
459 // threads are too busy, can't add another eventfd to wake one up
460 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
461 resume handle;
462 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
463 continue :start_over;
464 }475 }
465 }476 return;
466477 },
467 const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst);478 else => @compileError("unsupported OS"),
468 if (pending_event_count == 0) {479 }
469 // cause all the threads to stop480 }
470 switch (builtin.os) {481 }
471 builtin.Os.linux => {
472 // writing 8 bytes to an eventfd cannot fail
473 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
474 return;
475 },
476 builtin.Os.macosx => {
477 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
478 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
479 // cannot fail because we already added it and this just enables it
480 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
481 return;
482 },
483 builtin.Os.windows => {
484 var i: usize = 0;
485 while (i < self.os_data.extra_thread_count) : (i += 1) {
486 while (true) {
487 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
488 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
489 break;
490 }
491 }
492 return;
493 },
494 else => @compileError("unsupported OS"),
495 }
496 }
497482
498 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);483 fn workerRun(self: *Loop) void {
484 while (true) {
485 while (true) {
486 const next_tick_node = self.next_tick_queue.get() orelse break;
487 self.dispatch();
488 resume next_tick_node.data;
489 self.finishOneEvent();
499 }490 }
500491
501 switch (builtin.os) {492 switch (builtin.os) {
...@@ -519,7 +510,7 @@ pub const Loop = struct {...@@ -519,7 +510,7 @@ pub const Loop = struct {
519 }510 }
520 resume handle;511 resume handle;
521 if (resume_node_id == ResumeNode.Id.EventFd) {512 if (resume_node_id == ResumeNode.Id.EventFd) {
522 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);513 self.finishOneEvent();
523 }514 }
524 }515 }
525 },516 },
...@@ -541,7 +532,7 @@ pub const Loop = struct {...@@ -541,7 +532,7 @@ pub const Loop = struct {
541 }532 }
542 resume handle;533 resume handle;
543 if (resume_node_id == ResumeNode.Id.EventFd) {534 if (resume_node_id == ResumeNode.Id.EventFd) {
544 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);535 self.finishOneEvent();
545 }536 }
546 }537 }
547 },538 },
...@@ -570,7 +561,7 @@ pub const Loop = struct {...@@ -570,7 +561,7 @@ pub const Loop = struct {
570 }561 }
571 resume handle;562 resume handle;
572 if (resume_node_id == ResumeNode.Id.EventFd) {563 if (resume_node_id == ResumeNode.Id.EventFd) {
573 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);564 self.finishOneEvent();
574 }565 }
575 },566 },
576 else => @compileError("unsupported OS"),567 else => @compileError("unsupported OS"),
std/event/tcp.zig+2-1
...@@ -125,8 +125,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File...@@ -125,8 +125,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
125test "listen on a port, send bytes, receive bytes" {125test "listen on a port, send bytes, receive bytes" {
126 if (builtin.os != builtin.Os.linux) {126 if (builtin.os != builtin.Os.linux) {
127 // TODO build abstractions for other operating systems127 // TODO build abstractions for other operating systems
128 return;128 return error.SkipZigTest;
129 }129 }
130
130 const MyServer = struct {131 const MyServer = struct {
131 tcp_server: Server,132 tcp_server: Server,
132133
std/fmt/index.zig+6-2
...@@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {...@@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
785 return buf[0 .. buf.len - context.remaining.len];785 return buf[0 .. buf.len - context.remaining.len];
786}786}
787787
788pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 {788pub const AllocPrintError = error{OutOfMemory};
789
790pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 {
789 var size: usize = 0;791 var size: usize = 0;
790 format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};792 format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
791 const buf = try allocator.alloc(u8, size);793 const buf = try allocator.alloc(u8, size);
792 return bufPrint(buf, fmt, args);794 return bufPrint(buf, fmt, args) catch |err| switch (err) {
795 error.BufferTooSmall => unreachable, // we just counted the size above
796 };
793}797}
794798
795fn countSize(size: *usize, bytes: []const u8) (error{}!void) {799fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
std/index.zig+3
...@@ -36,6 +36,8 @@ pub const sort = @import("sort.zig");...@@ -36,6 +36,8 @@ pub const sort = @import("sort.zig");
36pub const unicode = @import("unicode.zig");36pub const unicode = @import("unicode.zig");
37pub const zig = @import("zig/index.zig");37pub const zig = @import("zig/index.zig");
3838
39pub const lazyInit = @import("lazy_init.zig").lazyInit;
40
39test "std" {41test "std" {
40 // run tests from these42 // run tests from these
41 _ = @import("atomic/index.zig");43 _ = @import("atomic/index.zig");
...@@ -71,4 +73,5 @@ test "std" {...@@ -71,4 +73,5 @@ test "std" {
71 _ = @import("sort.zig");73 _ = @import("sort.zig");
72 _ = @import("unicode.zig");74 _ = @import("unicode.zig");
73 _ = @import("zig/index.zig");75 _ = @import("zig/index.zig");
76 _ = @import("lazy_init.zig");
74}77}
std/lazy_init.zig created+85
...@@ -0,0 +1,85 @@
1const std = @import("index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const AtomicRmwOp = builtin.AtomicRmwOp;
5const AtomicOrder = builtin.AtomicOrder;
6
7/// Thread-safe initialization of global data.
8/// TODO use a mutex instead of a spinlock
9pub fn lazyInit(comptime T: type) LazyInit(T) {
10 return LazyInit(T){
11 .data = undefined,
12 .state = 0,
13 };
14}
15
16fn LazyInit(comptime T: type) type {
17 return struct {
18 state: u8, // TODO make this an enum
19 data: Data,
20
21 const Self = this;
22
23 // TODO this isn't working for void, investigate and then remove this special case
24 const Data = if (@sizeOf(T) == 0) u8 else T;
25 const Ptr = if (T == void) void else *T;
26
27 /// Returns a usable pointer to the initialized data,
28 /// or returns null, indicating that the caller should
29 /// perform the initialization and then call resolve().
30 pub fn get(self: *Self) ?Ptr {
31 while (true) {
32 var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
33 switch (state) {
34 0 => continue,
35 1 => {
36 // TODO mutex instead of a spinlock
37 continue;
38 },
39 2 => {
40 if (@sizeOf(T) == 0) {
41 return T(undefined);
42 } else {
43 return &self.data;
44 }
45 },
46 else => unreachable,
47 }
48 }
49 }
50
51 pub fn resolve(self: *Self) void {
52 const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
53 assert(prev == 1); // resolve() called twice
54 }
55 };
56}
57
58var global_number = lazyInit(i32);
59
60test "std.lazyInit" {
61 if (global_number.get()) |_| @panic("bad") else {
62 global_number.data = 1234;
63 global_number.resolve();
64 }
65 if (global_number.get()) |x| {
66 assert(x.* == 1234);
67 } else {
68 @panic("bad");
69 }
70 if (global_number.get()) |x| {
71 assert(x.* == 1234);
72 } else {
73 @panic("bad");
74 }
75}
76
77var global_void = lazyInit(void);
78
79test "std.lazyInit(void)" {
80 if (global_void.get()) |_| @panic("bad") else {
81 global_void.resolve();
82 }
83 assert(global_void.get() != null);
84 assert(global_void.get() != null);
85}
std/macho.zig+1-1
...@@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable...@@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
141 }141 }
142142
143 // Effectively a no-op, lld emits symbols in ascending order.143 // Effectively a no-op, lld emits symbols in ascending order.
144 std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);144 std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
145145
146 // Insert the sentinel. Since we don't know where the last function ends,146 // Insert the sentinel. Since we don't know where the last function ends,
147 // we arbitrarily limit it to the start address + 4 KB.147 // we arbitrarily limit it to the start address + 4 KB.
std/math/big/int.zig+152-13
...@@ -60,8 +60,9 @@ pub const Int = struct {...@@ -60,8 +60,9 @@ pub const Int = struct {
60 self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);60 self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
61 }61 }
6262
63 pub fn deinit(self: Int) void {63 pub fn deinit(self: *Int) void {
64 self.allocator.free(self.limbs);64 self.allocator.free(self.limbs);
65 self.* = undefined;
65 }66 }
6667
67 pub fn clone(other: Int) !Int {68 pub fn clone(other: Int) !Int {
...@@ -115,13 +116,63 @@ pub const Int = struct {...@@ -115,13 +116,63 @@ pub const Int = struct {
115 return !r.isOdd();116 return !r.isOdd();
116 }117 }
117118
118 fn bitcount(self: Int) usize {119 // Returns the number of bits required to represent the absolute value of self.
119 const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));120 fn bitCountAbs(self: Int) usize {
120 return usize(@boolToInt(!self.positive)) + u_bit_count;121 return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
121 }122 }
122123
124 // Returns the number of bits required to represent the integer in twos-complement form.
125 //
126 // If the integer is negative the value returned is the number of bits needed by a signed
127 // integer to represent the value. If positive the value is the number of bits for an
128 // unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
129 // one greater than the returned value.
130 //
131 // e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
132 fn bitCountTwosComp(self: Int) usize {
133 var bits = self.bitCountAbs();
134
135 // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
136 // complement requires one less bit.
137 if (!self.positive) block: {
138 bits += 1;
139
140 if (@popCount(self.limbs[self.len - 1]) == 1) {
141 for (self.limbs[0 .. self.len - 1]) |limb| {
142 if (@popCount(limb) != 0) {
143 break :block;
144 }
145 }
146
147 bits -= 1;
148 }
149 }
150
151 return bits;
152 }
153
154 pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
155 if (self.eqZero()) {
156 return true;
157 }
158 if (!is_signed and !self.positive) {
159 return false;
160 }
161
162 const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed);
163 return bit_count >= req_bits;
164 }
165
166 pub fn fits(self: Int, comptime T: type) bool {
167 return self.fitsInTwosComp(T.is_signed, T.bit_count);
168 }
169
170 // Returns the approximate size of the integer in the given base. Negative values accomodate for
171 // the minus sign. This is used for determining the number of characters needed to print the
172 // value. It is inexact and will exceed the given value by 1-2 digits.
123 pub fn sizeInBase(self: Int, base: usize) usize {173 pub fn sizeInBase(self: Int, base: usize) usize {
124 return (self.bitcount() / math.log2(base)) + 1;174 const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs();
175 return (bit_count / math.log2(base)) + 1;
125 }176 }
126177
127 pub fn set(self: *Int, value: var) Allocator.Error!void {178 pub fn set(self: *Int, value: var) Allocator.Error!void {
...@@ -189,9 +240,9 @@ pub const Int = struct {...@@ -189,9 +240,9 @@ pub const Int = struct {
189 pub fn to(self: Int, comptime T: type) ConvertError!T {240 pub fn to(self: Int, comptime T: type) ConvertError!T {
190 switch (@typeId(T)) {241 switch (@typeId(T)) {
191 TypeId.Int => {242 TypeId.Int => {
192 const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;243 const UT = @IntType(false, T.bit_count);
193244
194 if (self.bitcount() > 8 * @sizeOf(UT)) {245 if (self.bitCountTwosComp() > T.bit_count) {
195 return error.TargetTooSmall;246 return error.TargetTooSmall;
196 }247 }
197248
...@@ -208,9 +259,17 @@ pub const Int = struct {...@@ -208,9 +259,17 @@ pub const Int = struct {
208 }259 }
209260
210 if (!T.is_signed) {261 if (!T.is_signed) {
211 return if (self.positive) r else error.NegativeIntoUnsigned;262 return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
212 } else {263 } else {
213 return if (self.positive) @intCast(T, r) else -@intCast(T, r);264 if (self.positive) {
265 return @intCast(T, r);
266 } else {
267 if (math.cast(T, r)) |ok| {
268 return -ok;
269 } else |_| {
270 return @minValue(T);
271 }
272 }
214 }273 }
215 },274 },
216 else => {275 else => {
...@@ -274,6 +333,7 @@ pub const Int = struct {...@@ -274,6 +333,7 @@ pub const Int = struct {
274 self.positive = positive;333 self.positive = positive;
275 }334 }
276335
336 /// TODO make this call format instead of the other way around
277 pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {337 pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
278 if (base < 2 or base > 16) {338 if (base < 2 or base > 16) {
279 return error.InvalidBase;339 return error.InvalidBase;
...@@ -356,6 +416,21 @@ pub const Int = struct {...@@ -356,6 +416,21 @@ pub const Int = struct {
356 return s;416 return s;
357 }417 }
358418
419 /// for the std lib format function
420 /// TODO make this non-allocating
421 pub fn format(
422 self: Int,
423 comptime fmt: []const u8,
424 context: var,
425 comptime FmtError: type,
426 output: fn (@typeOf(context), []const u8) FmtError!void,
427 ) FmtError!void {
428 // TODO look at fmt and support other bases
429 const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating");
430 defer self.allocator.free(str);
431 return output(context, str);
432 }
433
359 // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.434 // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
360 pub fn cmpAbs(a: Int, b: Int) i8 {435 pub fn cmpAbs(a: Int, b: Int) i8 {
361 if (a.len < b.len) {436 if (a.len < b.len) {
...@@ -1120,24 +1195,88 @@ test "big.int bitcount + sizeInBase" {...@@ -1120,24 +1195,88 @@ test "big.int bitcount + sizeInBase" {
1120 var a = try Int.init(al);1195 var a = try Int.init(al);
11211196
1122 try a.set(0b100);1197 try a.set(0b100);
1123 debug.assert(a.bitcount() == 3);1198 debug.assert(a.bitCountAbs() == 3);
1124 debug.assert(a.sizeInBase(2) >= 3);1199 debug.assert(a.sizeInBase(2) >= 3);
1125 debug.assert(a.sizeInBase(10) >= 1);1200 debug.assert(a.sizeInBase(10) >= 1);
11261201
1202 a.negate();
1203 debug.assert(a.bitCountAbs() == 3);
1204 debug.assert(a.sizeInBase(2) >= 4);
1205 debug.assert(a.sizeInBase(10) >= 2);
1206
1127 try a.set(0xffffffff);1207 try a.set(0xffffffff);
1128 debug.assert(a.bitcount() == 32);1208 debug.assert(a.bitCountAbs() == 32);
1129 debug.assert(a.sizeInBase(2) >= 32);1209 debug.assert(a.sizeInBase(2) >= 32);
1130 debug.assert(a.sizeInBase(10) >= 10);1210 debug.assert(a.sizeInBase(10) >= 10);
11311211
1132 try a.shiftLeft(a, 5000);1212 try a.shiftLeft(a, 5000);
1133 debug.assert(a.bitcount() == 5032);1213 debug.assert(a.bitCountAbs() == 5032);
1134 debug.assert(a.sizeInBase(2) >= 5032);1214 debug.assert(a.sizeInBase(2) >= 5032);
1135 a.positive = false;1215 a.positive = false;
11361216
1137 debug.assert(a.bitcount() == 5033);1217 debug.assert(a.bitCountAbs() == 5032);
1138 debug.assert(a.sizeInBase(2) >= 5033);1218 debug.assert(a.sizeInBase(2) >= 5033);
1139}1219}
11401220
1221test "big.int bitcount/to" {
1222 var a = try Int.init(al);
1223
1224 try a.set(0);
1225 debug.assert(a.bitCountTwosComp() == 0);
1226
1227 // TODO: stack smashing
1228 // debug.assert((try a.to(u0)) == 0);
1229 // TODO: sigsegv
1230 // debug.assert((try a.to(i0)) == 0);
1231
1232 try a.set(-1);
1233 debug.assert(a.bitCountTwosComp() == 1);
1234 debug.assert((try a.to(i1)) == -1);
1235
1236 try a.set(-8);
1237 debug.assert(a.bitCountTwosComp() == 4);
1238 debug.assert((try a.to(i4)) == -8);
1239
1240 try a.set(127);
1241 debug.assert(a.bitCountTwosComp() == 7);
1242 debug.assert((try a.to(u7)) == 127);
1243
1244 try a.set(-128);
1245 debug.assert(a.bitCountTwosComp() == 8);
1246 debug.assert((try a.to(i8)) == -128);
1247
1248 try a.set(-129);
1249 debug.assert(a.bitCountTwosComp() == 9);
1250 debug.assert((try a.to(i9)) == -129);
1251}
1252
1253test "big.int fits" {
1254 var a = try Int.init(al);
1255
1256 try a.set(0);
1257 debug.assert(a.fits(u0));
1258 debug.assert(a.fits(i0));
1259
1260 try a.set(255);
1261 debug.assert(!a.fits(u0));
1262 debug.assert(!a.fits(u1));
1263 debug.assert(!a.fits(i8));
1264 debug.assert(a.fits(u8));
1265 debug.assert(a.fits(u9));
1266 debug.assert(a.fits(i9));
1267
1268 try a.set(-128);
1269 debug.assert(!a.fits(i7));
1270 debug.assert(a.fits(i8));
1271 debug.assert(a.fits(i9));
1272 debug.assert(!a.fits(u9));
1273
1274 try a.set(0x1ffffffffeeeeeeee);
1275 debug.assert(!a.fits(u32));
1276 debug.assert(!a.fits(u64));
1277 debug.assert(a.fits(u65));
1278}
1279
1141test "big.int string set" {1280test "big.int string set" {
1142 var a = try Int.init(al);1281 var a = try Int.init(al);
1143 try a.setString(10, "120317241209124781241290847124");1282 try a.setString(10, "120317241209124781241290847124");
std/mem.zig+10-2
...@@ -35,6 +35,7 @@ pub const Allocator = struct {...@@ -35,6 +35,7 @@ pub const Allocator = struct {
35 freeFn: fn (self: *Allocator, old_mem: []u8) void,35 freeFn: fn (self: *Allocator, old_mem: []u8) void,
3636
37 /// Call `destroy` with the result37 /// Call `destroy` with the result
38 /// TODO this is deprecated. use createOne instead
38 pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {39 pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {
39 const T = @typeOf(init);40 const T = @typeOf(init);
40 if (@sizeOf(T) == 0) return &(T{});41 if (@sizeOf(T) == 0) return &(T{});
...@@ -44,6 +45,14 @@ pub const Allocator = struct {...@@ -44,6 +45,14 @@ pub const Allocator = struct {
44 return ptr;45 return ptr;
45 }46 }
4647
48 /// Call `destroy` with the result.
49 /// Returns undefined memory.
50 pub fn createOne(self: *Allocator, comptime T: type) Error!*T {
51 if (@sizeOf(T) == 0) return &(T{});
52 const slice = try self.alloc(T, 1);
53 return &slice[0];
54 }
55
47 /// `ptr` should be the return value of `create`56 /// `ptr` should be the return value of `create`
48 pub fn destroy(self: *Allocator, ptr: var) void {57 pub fn destroy(self: *Allocator, ptr: var) void {
49 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));58 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
...@@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {...@@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
149 @setRuntimeSafety(false);158 @setRuntimeSafety(false);
150 assert(dest.len >= source.len);159 assert(dest.len >= source.len);
151 var i = source.len;160 var i = source.len;
152 while(i > 0){161 while (i > 0) {
153 i -= 1;162 i -= 1;
154 dest[i] = source[i];163 dest[i] = source[i];
155 }164 }
156}165}
157166
158
159pub fn set(comptime T: type, dest: []T, value: T) void {167pub fn set(comptime T: type, dest: []T, value: T) void {
160 for (dest) |*d|168 for (dest) |*d|
161 d.* = value;169 d.* = value;
std/os/darwin.zig+87-1
...@@ -2,7 +2,7 @@ const std = @import("../index.zig");...@@ -2,7 +2,7 @@ const std = @import("../index.zig");
2const c = std.c;2const c = std.c;
3const assert = std.debug.assert;3const assert = std.debug.assert;
44
5pub use @import("darwin_errno.zig");5pub use @import("darwin/errno.zig");
66
7pub const PATH_MAX = 1024;7pub const PATH_MAX = 1024;
88
...@@ -482,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;...@@ -482,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
482/// data is mach absolute time units482/// data is mach absolute time units
483pub const NOTE_MACHTIME = 0x00000100;483pub const NOTE_MACHTIME = 0x00000100;
484484
485pub const AF_UNSPEC: c_int = 0;
486pub const AF_LOCAL: c_int = 1;
487pub const AF_UNIX: c_int = AF_LOCAL;
488pub const AF_INET: c_int = 2;
489pub const AF_SYS_CONTROL: c_int = 2;
490pub const AF_IMPLINK: c_int = 3;
491pub const AF_PUP: c_int = 4;
492pub const AF_CHAOS: c_int = 5;
493pub const AF_NS: c_int = 6;
494pub const AF_ISO: c_int = 7;
495pub const AF_OSI: c_int = AF_ISO;
496pub const AF_ECMA: c_int = 8;
497pub const AF_DATAKIT: c_int = 9;
498pub const AF_CCITT: c_int = 10;
499pub const AF_SNA: c_int = 11;
500pub const AF_DECnet: c_int = 12;
501pub const AF_DLI: c_int = 13;
502pub const AF_LAT: c_int = 14;
503pub const AF_HYLINK: c_int = 15;
504pub const AF_APPLETALK: c_int = 16;
505pub const AF_ROUTE: c_int = 17;
506pub const AF_LINK: c_int = 18;
507pub const AF_XTP: c_int = 19;
508pub const AF_COIP: c_int = 20;
509pub const AF_CNT: c_int = 21;
510pub const AF_RTIP: c_int = 22;
511pub const AF_IPX: c_int = 23;
512pub const AF_SIP: c_int = 24;
513pub const AF_PIP: c_int = 25;
514pub const AF_ISDN: c_int = 28;
515pub const AF_E164: c_int = AF_ISDN;
516pub const AF_KEY: c_int = 29;
517pub const AF_INET6: c_int = 30;
518pub const AF_NATM: c_int = 31;
519pub const AF_SYSTEM: c_int = 32;
520pub const AF_NETBIOS: c_int = 33;
521pub const AF_PPP: c_int = 34;
522pub const AF_MAX: c_int = 40;
523
524pub const PF_UNSPEC: c_int = AF_UNSPEC;
525pub const PF_LOCAL: c_int = AF_LOCAL;
526pub const PF_UNIX: c_int = PF_LOCAL;
527pub const PF_INET: c_int = AF_INET;
528pub const PF_IMPLINK: c_int = AF_IMPLINK;
529pub const PF_PUP: c_int = AF_PUP;
530pub const PF_CHAOS: c_int = AF_CHAOS;
531pub const PF_NS: c_int = AF_NS;
532pub const PF_ISO: c_int = AF_ISO;
533pub const PF_OSI: c_int = AF_ISO;
534pub const PF_ECMA: c_int = AF_ECMA;
535pub const PF_DATAKIT: c_int = AF_DATAKIT;
536pub const PF_CCITT: c_int = AF_CCITT;
537pub const PF_SNA: c_int = AF_SNA;
538pub const PF_DECnet: c_int = AF_DECnet;
539pub const PF_DLI: c_int = AF_DLI;
540pub const PF_LAT: c_int = AF_LAT;
541pub const PF_HYLINK: c_int = AF_HYLINK;
542pub const PF_APPLETALK: c_int = AF_APPLETALK;
543pub const PF_ROUTE: c_int = AF_ROUTE;
544pub const PF_LINK: c_int = AF_LINK;
545pub const PF_XTP: c_int = AF_XTP;
546pub const PF_COIP: c_int = AF_COIP;
547pub const PF_CNT: c_int = AF_CNT;
548pub const PF_SIP: c_int = AF_SIP;
549pub const PF_IPX: c_int = AF_IPX;
550pub const PF_RTIP: c_int = AF_RTIP;
551pub const PF_PIP: c_int = AF_PIP;
552pub const PF_ISDN: c_int = AF_ISDN;
553pub const PF_KEY: c_int = AF_KEY;
554pub const PF_INET6: c_int = AF_INET6;
555pub const PF_NATM: c_int = AF_NATM;
556pub const PF_SYSTEM: c_int = AF_SYSTEM;
557pub const PF_NETBIOS: c_int = AF_NETBIOS;
558pub const PF_PPP: c_int = AF_PPP;
559pub const PF_MAX: c_int = AF_MAX;
560
561pub const SYSPROTO_EVENT: c_int = 1;
562pub const SYSPROTO_CONTROL: c_int = 2;
563
564pub const SOCK_STREAM: c_int = 1;
565pub const SOCK_DGRAM: c_int = 2;
566pub const SOCK_RAW: c_int = 3;
567pub const SOCK_RDM: c_int = 4;
568pub const SOCK_SEQPACKET: c_int = 5;
569pub const SOCK_MAXADDRLEN: c_int = 255;
570
485fn wstatus(x: i32) i32 {571fn wstatus(x: i32) i32 {
486 return x & 0o177;572 return x & 0o177;
487}573}
std/os/darwin/errno.zig created+328
...@@ -0,0 +1,328 @@
1/// Operation not permitted
2pub const EPERM = 1;
3
4/// No such file or directory
5pub const ENOENT = 2;
6
7/// No such process
8pub const ESRCH = 3;
9
10/// Interrupted system call
11pub const EINTR = 4;
12
13/// Input/output error
14pub const EIO = 5;
15
16/// Device not configured
17pub const ENXIO = 6;
18
19/// Argument list too long
20pub const E2BIG = 7;
21
22/// Exec format error
23pub const ENOEXEC = 8;
24
25/// Bad file descriptor
26pub const EBADF = 9;
27
28/// No child processes
29pub const ECHILD = 10;
30
31/// Resource deadlock avoided
32pub const EDEADLK = 11;
33
34/// Cannot allocate memory
35pub const ENOMEM = 12;
36
37/// Permission denied
38pub const EACCES = 13;
39
40/// Bad address
41pub const EFAULT = 14;
42
43/// Block device required
44pub const ENOTBLK = 15;
45
46/// Device / Resource busy
47pub const EBUSY = 16;
48
49/// File exists
50pub const EEXIST = 17;
51
52/// Cross-device link
53pub const EXDEV = 18;
54
55/// Operation not supported by device
56pub const ENODEV = 19;
57
58/// Not a directory
59pub const ENOTDIR = 20;
60
61/// Is a directory
62pub const EISDIR = 21;
63
64/// Invalid argument
65pub const EINVAL = 22;
66
67/// Too many open files in system
68pub const ENFILE = 23;
69
70/// Too many open files
71pub const EMFILE = 24;
72
73/// Inappropriate ioctl for device
74pub const ENOTTY = 25;
75
76/// Text file busy
77pub const ETXTBSY = 26;
78
79/// File too large
80pub const EFBIG = 27;
81
82/// No space left on device
83pub const ENOSPC = 28;
84
85/// Illegal seek
86pub const ESPIPE = 29;
87
88/// Read-only file system
89pub const EROFS = 30;
90
91/// Too many links
92pub const EMLINK = 31;
93/// Broken pipe
94
95// math software
96pub const EPIPE = 32;
97
98/// Numerical argument out of domain
99pub const EDOM = 33;
100/// Result too large
101
102// non-blocking and interrupt i/o
103pub const ERANGE = 34;
104
105/// Resource temporarily unavailable
106pub const EAGAIN = 35;
107
108/// Operation would block
109pub const EWOULDBLOCK = EAGAIN;
110
111/// Operation now in progress
112pub const EINPROGRESS = 36;
113/// Operation already in progress
114
115// ipc/network software -- argument errors
116pub const EALREADY = 37;
117
118/// Socket operation on non-socket
119pub const ENOTSOCK = 38;
120
121/// Destination address required
122pub const EDESTADDRREQ = 39;
123
124/// Message too long
125pub const EMSGSIZE = 40;
126
127/// Protocol wrong type for socket
128pub const EPROTOTYPE = 41;
129
130/// Protocol not available
131pub const ENOPROTOOPT = 42;
132
133/// Protocol not supported
134pub const EPROTONOSUPPORT = 43;
135
136/// Socket type not supported
137pub const ESOCKTNOSUPPORT = 44;
138
139/// Operation not supported
140pub const ENOTSUP = 45;
141
142/// Protocol family not supported
143pub const EPFNOSUPPORT = 46;
144
145/// Address family not supported by protocol family
146pub const EAFNOSUPPORT = 47;
147
148/// Address already in use
149pub const EADDRINUSE = 48;
150/// Can't assign requested address
151
152// ipc/network software -- operational errors
153pub const EADDRNOTAVAIL = 49;
154
155/// Network is down
156pub const ENETDOWN = 50;
157
158/// Network is unreachable
159pub const ENETUNREACH = 51;
160
161/// Network dropped connection on reset
162pub const ENETRESET = 52;
163
164/// Software caused connection abort
165pub const ECONNABORTED = 53;
166
167/// Connection reset by peer
168pub const ECONNRESET = 54;
169
170/// No buffer space available
171pub const ENOBUFS = 55;
172
173/// Socket is already connected
174pub const EISCONN = 56;
175
176/// Socket is not connected
177pub const ENOTCONN = 57;
178
179/// Can't send after socket shutdown
180pub const ESHUTDOWN = 58;
181
182/// Too many references: can't splice
183pub const ETOOMANYREFS = 59;
184
185/// Operation timed out
186pub const ETIMEDOUT = 60;
187
188/// Connection refused
189pub const ECONNREFUSED = 61;
190
191/// Too many levels of symbolic links
192pub const ELOOP = 62;
193
194/// File name too long
195pub const ENAMETOOLONG = 63;
196
197/// Host is down
198pub const EHOSTDOWN = 64;
199
200/// No route to host
201pub const EHOSTUNREACH = 65;
202/// Directory not empty
203
204// quotas & mush
205pub const ENOTEMPTY = 66;
206
207/// Too many processes
208pub const EPROCLIM = 67;
209
210/// Too many users
211pub const EUSERS = 68;
212/// Disc quota exceeded
213
214// Network File System
215pub const EDQUOT = 69;
216
217/// Stale NFS file handle
218pub const ESTALE = 70;
219
220/// Too many levels of remote in path
221pub const EREMOTE = 71;
222
223/// RPC struct is bad
224pub const EBADRPC = 72;
225
226/// RPC version wrong
227pub const ERPCMISMATCH = 73;
228
229/// RPC prog. not avail
230pub const EPROGUNAVAIL = 74;
231
232/// Program version wrong
233pub const EPROGMISMATCH = 75;
234
235/// Bad procedure for program
236pub const EPROCUNAVAIL = 76;
237
238/// No locks available
239pub const ENOLCK = 77;
240
241/// Function not implemented
242pub const ENOSYS = 78;
243
244/// Inappropriate file type or format
245pub const EFTYPE = 79;
246
247/// Authentication error
248pub const EAUTH = 80;
249/// Need authenticator
250
251// Intelligent device errors
252pub const ENEEDAUTH = 81;
253
254/// Device power is off
255pub const EPWROFF = 82;
256
257/// Device error, e.g. paper out
258pub const EDEVERR = 83;
259/// Value too large to be stored in data type
260
261// Program loading errors
262pub const EOVERFLOW = 84;
263
264/// Bad executable
265pub const EBADEXEC = 85;
266
267/// Bad CPU type in executable
268pub const EBADARCH = 86;
269
270/// Shared library version mismatch
271pub const ESHLIBVERS = 87;
272
273/// Malformed Macho file
274pub const EBADMACHO = 88;
275
276/// Operation canceled
277pub const ECANCELED = 89;
278
279/// Identifier removed
280pub const EIDRM = 90;
281
282/// No message of desired type
283pub const ENOMSG = 91;
284
285/// Illegal byte sequence
286pub const EILSEQ = 92;
287
288/// Attribute not found
289pub const ENOATTR = 93;
290
291/// Bad message
292pub const EBADMSG = 94;
293
294/// Reserved
295pub const EMULTIHOP = 95;
296
297/// No message available on STREAM
298pub const ENODATA = 96;
299
300/// Reserved
301pub const ENOLINK = 97;
302
303/// No STREAM resources
304pub const ENOSR = 98;
305
306/// Not a STREAM
307pub const ENOSTR = 99;
308
309/// Protocol error
310pub const EPROTO = 100;
311
312/// STREAM ioctl timeout
313pub const ETIME = 101;
314
315/// No such policy registered
316pub const ENOPOLICY = 103;
317
318/// State not recoverable
319pub const ENOTRECOVERABLE = 104;
320
321/// Previous owner died
322pub const EOWNERDEAD = 105;
323
324/// Interface output queue is full
325pub const EQFULL = 106;
326
327/// Must be equal largest errno
328pub const ELAST = 106;
std/os/darwin_errno.zig deleted-328
...@@ -1,328 +0,0 @@
1/// Operation not permitted
2pub const EPERM = 1;
3
4/// No such file or directory
5pub const ENOENT = 2;
6
7/// No such process
8pub const ESRCH = 3;
9
10/// Interrupted system call
11pub const EINTR = 4;
12
13/// Input/output error
14pub const EIO = 5;
15
16/// Device not configured
17pub const ENXIO = 6;
18
19/// Argument list too long
20pub const E2BIG = 7;
21
22/// Exec format error
23pub const ENOEXEC = 8;
24
25/// Bad file descriptor
26pub const EBADF = 9;
27
28/// No child processes
29pub const ECHILD = 10;
30
31/// Resource deadlock avoided
32pub const EDEADLK = 11;
33
34/// Cannot allocate memory
35pub const ENOMEM = 12;
36
37/// Permission denied
38pub const EACCES = 13;
39
40/// Bad address
41pub const EFAULT = 14;
42
43/// Block device required
44pub const ENOTBLK = 15;
45
46/// Device / Resource busy
47pub const EBUSY = 16;
48
49/// File exists
50pub const EEXIST = 17;
51
52/// Cross-device link
53pub const EXDEV = 18;
54
55/// Operation not supported by device
56pub const ENODEV = 19;
57
58/// Not a directory
59pub const ENOTDIR = 20;
60
61/// Is a directory
62pub const EISDIR = 21;
63
64/// Invalid argument
65pub const EINVAL = 22;
66
67/// Too many open files in system
68pub const ENFILE = 23;
69
70/// Too many open files
71pub const EMFILE = 24;
72
73/// Inappropriate ioctl for device
74pub const ENOTTY = 25;
75
76/// Text file busy
77pub const ETXTBSY = 26;
78
79/// File too large
80pub const EFBIG = 27;
81
82/// No space left on device
83pub const ENOSPC = 28;
84
85/// Illegal seek
86pub const ESPIPE = 29;
87
88/// Read-only file system
89pub const EROFS = 30;
90
91/// Too many links
92pub const EMLINK = 31;
93/// Broken pipe
94
95// math software
96pub const EPIPE = 32;
97
98/// Numerical argument out of domain
99pub const EDOM = 33;
100/// Result too large
101
102// non-blocking and interrupt i/o
103pub const ERANGE = 34;
104
105/// Resource temporarily unavailable
106pub const EAGAIN = 35;
107
108/// Operation would block
109pub const EWOULDBLOCK = EAGAIN;
110
111/// Operation now in progress
112pub const EINPROGRESS = 36;
113/// Operation already in progress
114
115// ipc/network software -- argument errors
116pub const EALREADY = 37;
117
118/// Socket operation on non-socket
119pub const ENOTSOCK = 38;
120
121/// Destination address required
122pub const EDESTADDRREQ = 39;
123
124/// Message too long
125pub const EMSGSIZE = 40;
126
127/// Protocol wrong type for socket
128pub const EPROTOTYPE = 41;
129
130/// Protocol not available
131pub const ENOPROTOOPT = 42;
132
133/// Protocol not supported
134pub const EPROTONOSUPPORT = 43;
135
136/// Socket type not supported
137pub const ESOCKTNOSUPPORT = 44;
138
139/// Operation not supported
140pub const ENOTSUP = 45;
141
142/// Protocol family not supported
143pub const EPFNOSUPPORT = 46;
144
145/// Address family not supported by protocol family
146pub const EAFNOSUPPORT = 47;
147
148/// Address already in use
149pub const EADDRINUSE = 48;
150/// Can't assign requested address
151
152// ipc/network software -- operational errors
153pub const EADDRNOTAVAIL = 49;
154
155/// Network is down
156pub const ENETDOWN = 50;
157
158/// Network is unreachable
159pub const ENETUNREACH = 51;
160
161/// Network dropped connection on reset
162pub const ENETRESET = 52;
163
164/// Software caused connection abort
165pub const ECONNABORTED = 53;
166
167/// Connection reset by peer
168pub const ECONNRESET = 54;
169
170/// No buffer space available
171pub const ENOBUFS = 55;
172
173/// Socket is already connected
174pub const EISCONN = 56;
175
176/// Socket is not connected
177pub const ENOTCONN = 57;
178
179/// Can't send after socket shutdown
180pub const ESHUTDOWN = 58;
181
182/// Too many references: can't splice
183pub const ETOOMANYREFS = 59;
184
185/// Operation timed out
186pub const ETIMEDOUT = 60;
187
188/// Connection refused
189pub const ECONNREFUSED = 61;
190
191/// Too many levels of symbolic links
192pub const ELOOP = 62;
193
194/// File name too long
195pub const ENAMETOOLONG = 63;
196
197/// Host is down
198pub const EHOSTDOWN = 64;
199
200/// No route to host
201pub const EHOSTUNREACH = 65;
202/// Directory not empty
203
204// quotas & mush
205pub const ENOTEMPTY = 66;
206
207/// Too many processes
208pub const EPROCLIM = 67;
209
210/// Too many users
211pub const EUSERS = 68;
212/// Disc quota exceeded
213
214// Network File System
215pub const EDQUOT = 69;
216
217/// Stale NFS file handle
218pub const ESTALE = 70;
219
220/// Too many levels of remote in path
221pub const EREMOTE = 71;
222
223/// RPC struct is bad
224pub const EBADRPC = 72;
225
226/// RPC version wrong
227pub const ERPCMISMATCH = 73;
228
229/// RPC prog. not avail
230pub const EPROGUNAVAIL = 74;
231
232/// Program version wrong
233pub const EPROGMISMATCH = 75;
234
235/// Bad procedure for program
236pub const EPROCUNAVAIL = 76;
237
238/// No locks available
239pub const ENOLCK = 77;
240
241/// Function not implemented
242pub const ENOSYS = 78;
243
244/// Inappropriate file type or format
245pub const EFTYPE = 79;
246
247/// Authentication error
248pub const EAUTH = 80;
249/// Need authenticator
250
251// Intelligent device errors
252pub const ENEEDAUTH = 81;
253
254/// Device power is off
255pub const EPWROFF = 82;
256
257/// Device error, e.g. paper out
258pub const EDEVERR = 83;
259/// Value too large to be stored in data type
260
261// Program loading errors
262pub const EOVERFLOW = 84;
263
264/// Bad executable
265pub const EBADEXEC = 85;
266
267/// Bad CPU type in executable
268pub const EBADARCH = 86;
269
270/// Shared library version mismatch
271pub const ESHLIBVERS = 87;
272
273/// Malformed Macho file
274pub const EBADMACHO = 88;
275
276/// Operation canceled
277pub const ECANCELED = 89;
278
279/// Identifier removed
280pub const EIDRM = 90;
281
282/// No message of desired type
283pub const ENOMSG = 91;
284
285/// Illegal byte sequence
286pub const EILSEQ = 92;
287
288/// Attribute not found
289pub const ENOATTR = 93;
290
291/// Bad message
292pub const EBADMSG = 94;
293
294/// Reserved
295pub const EMULTIHOP = 95;
296
297/// No message available on STREAM
298pub const ENODATA = 96;
299
300/// Reserved
301pub const ENOLINK = 97;
302
303/// No STREAM resources
304pub const ENOSR = 98;
305
306/// Not a STREAM
307pub const ENOSTR = 99;
308
309/// Protocol error
310pub const EPROTO = 100;
311
312/// STREAM ioctl timeout
313pub const ETIME = 101;
314
315/// No such policy registered
316pub const ENOPOLICY = 103;
317
318/// State not recoverable
319pub const ENOTRECOVERABLE = 104;
320
321/// Previous owner died
322pub const EOWNERDEAD = 105;
323
324/// Interface output queue is full
325pub const EQFULL = 106;
326
327/// Must be equal largest errno
328pub const ELAST = 106;
std/os/file.zig+28-35
...@@ -15,7 +15,7 @@ pub const File = struct {...@@ -15,7 +15,7 @@ pub const File = struct {
15 /// The OS-specific file descriptor or file handle.15 /// The OS-specific file descriptor or file handle.
16 handle: os.FileHandle,16 handle: os.FileHandle,
1717
18 const OpenError = os.WindowsOpenError || os.PosixOpenError;18 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
1919
20 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.20 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
21 /// Call close to clean up.21 /// Call close to clean up.
...@@ -109,43 +109,42 @@ pub const File = struct {...@@ -109,43 +109,42 @@ pub const File = struct {
109 Unexpected,109 Unexpected,
110 };110 };
111111
112 pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {112 pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
113 const path_with_null = try std.cstr.addNullByte(allocator, path);113 const path_with_null = try std.cstr.addNullByte(allocator, path);
114 defer allocator.free(path_with_null);114 defer allocator.free(path_with_null);
115115
116 if (is_posix) {116 if (is_posix) {
117 // mode is ignored and is always F_OK for now
118 const result = posix.access(path_with_null.ptr, posix.F_OK);117 const result = posix.access(path_with_null.ptr, posix.F_OK);
119 const err = posix.getErrno(result);118 const err = posix.getErrno(result);
120 if (err > 0) {119 switch (err) {
121 return switch (err) {120 0 => return,
122 posix.EACCES => error.PermissionDenied,121 posix.EACCES => return error.PermissionDenied,
123 posix.EROFS => error.PermissionDenied,122 posix.EROFS => return error.PermissionDenied,
124 posix.ELOOP => error.PermissionDenied,123 posix.ELOOP => return error.PermissionDenied,
125 posix.ETXTBSY => error.PermissionDenied,124 posix.ETXTBSY => return error.PermissionDenied,
126 posix.ENOTDIR => error.NotFound,125 posix.ENOTDIR => return error.NotFound,
127 posix.ENOENT => error.NotFound,126 posix.ENOENT => return error.NotFound,
128127
129 posix.ENAMETOOLONG => error.NameTooLong,128 posix.ENAMETOOLONG => return error.NameTooLong,
130 posix.EINVAL => error.BadMode,129 posix.EINVAL => unreachable,
131 posix.EFAULT => error.BadPathName,130 posix.EFAULT => return error.BadPathName,
132 posix.EIO => error.Io,131 posix.EIO => return error.Io,
133 posix.ENOMEM => error.SystemResources,132 posix.ENOMEM => return error.SystemResources,
134 else => os.unexpectedErrorPosix(err),133 else => return os.unexpectedErrorPosix(err),
135 };
136 }134 }
137 return true;
138 } else if (is_windows) {135 } else if (is_windows) {
139 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {136 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
140 return true;137 return;
141 }138 }
142139
143 const err = windows.GetLastError();140 const err = windows.GetLastError();
144 return switch (err) {141 switch (err) {
145 windows.ERROR.FILE_NOT_FOUND => error.NotFound,142 windows.ERROR.FILE_NOT_FOUND,
146 windows.ERROR.ACCESS_DENIED => error.PermissionDenied,143 windows.ERROR.PATH_NOT_FOUND,
147 else => os.unexpectedErrorWindows(err),144 => return error.NotFound,
148 };145 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
146 else => return os.unexpectedErrorWindows(err),
147 }
149 } else {148 } else {
150 @compileError("TODO implement access for this OS");149 @compileError("TODO implement access for this OS");
151 }150 }
...@@ -242,7 +241,7 @@ pub const File = struct {...@@ -242,7 +241,7 @@ pub const File = struct {
242 },241 },
243 Os.windows => {242 Os.windows => {
244 var pos: windows.LARGE_INTEGER = undefined;243 var pos: windows.LARGE_INTEGER = undefined;
245 if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) {244 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
246 const err = windows.GetLastError();245 const err = windows.GetLastError();
247 return switch (err) {246 return switch (err) {
248 windows.ERROR.INVALID_PARAMETER => error.BadFd,247 windows.ERROR.INVALID_PARAMETER => error.BadFd,
...@@ -251,13 +250,7 @@ pub const File = struct {...@@ -251,13 +250,7 @@ pub const File = struct {
251 }250 }
252251
253 assert(pos >= 0);252 assert(pos >= 0);
254 if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) {253 return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange;
255 if (pos > @maxValue(usize)) {
256 return error.FilePosLargerThanPointerRange;
257 }
258 }
259
260 return usize(pos);
261 },254 },
262 else => @compileError("unsupported OS"),255 else => @compileError("unsupported OS"),
263 }256 }
...@@ -289,7 +282,7 @@ pub const File = struct {...@@ -289,7 +282,7 @@ pub const File = struct {
289 Unexpected,282 Unexpected,
290 };283 };
291284
292 fn mode(self: *File) ModeError!os.FileMode {285 pub fn mode(self: *File) ModeError!os.FileMode {
293 if (is_posix) {286 if (is_posix) {
294 var stat: posix.Stat = undefined;287 var stat: posix.Stat = undefined;
295 const err = posix.getErrno(posix.fstat(self.handle, &stat));288 const err = posix.getErrno(posix.fstat(self.handle, &stat));
...@@ -364,7 +357,7 @@ pub const File = struct {...@@ -364,7 +357,7 @@ pub const File = struct {
364357
365 pub const WriteError = os.WindowsWriteError || os.PosixWriteError;358 pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
366359
367 fn write(self: *File, bytes: []const u8) WriteError!void {360 pub fn write(self: *File, bytes: []const u8) WriteError!void {
368 if (is_posix) {361 if (is_posix) {
369 try os.posixWrite(self.handle, bytes);362 try os.posixWrite(self.handle, bytes);
370 } else if (is_windows) {363 } else if (is_windows) {
std/os/get_app_data_dir.zig created+69
...@@ -0,0 +1,69 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const unicode = std.unicode;
4const mem = std.mem;
5const os = std.os;
6
7pub const GetAppDataDirError = error{
8 OutOfMemory,
9 AppDataDirUnavailable,
10};
11
12/// Caller owns returned memory.
13pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
14 switch (builtin.os) {
15 builtin.Os.windows => {
16 var dir_path_ptr: [*]u16 = undefined;
17 switch (os.windows.SHGetKnownFolderPath(
18 &os.windows.FOLDERID_LocalAppData,
19 os.windows.KF_FLAG_CREATE,
20 null,
21 &dir_path_ptr,
22 )) {
23 os.windows.S_OK => {
24 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
25 const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) {
26 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
27 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
28 error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
29 error.OutOfMemory => return error.OutOfMemory,
30 };
31 defer allocator.free(global_dir);
32 return os.path.join(allocator, global_dir, appname);
33 },
34 os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
35 else => return error.AppDataDirUnavailable,
36 }
37 },
38 builtin.Os.macosx => {
39 const home_dir = os.getEnvPosix("HOME") orelse {
40 // TODO look in /etc/passwd
41 return error.AppDataDirUnavailable;
42 };
43 return os.path.join(allocator, home_dir, "Library", "Application Support", appname);
44 },
45 builtin.Os.linux => {
46 const home_dir = os.getEnvPosix("HOME") orelse {
47 // TODO look in /etc/passwd
48 return error.AppDataDirUnavailable;
49 };
50 return os.path.join(allocator, home_dir, ".local", "share", appname);
51 },
52 else => @compileError("Unsupported OS"),
53 }
54}
55
56fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
57 var index: usize = 0;
58 while (ptr[index] != 0) : (index += 1) {}
59 return ptr[0..index];
60}
61
62test "std.os.getAppDataDir" {
63 var buf: [512]u8 = undefined;
64 const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
65
66 // We can't actually validate the result
67 _ = getAppDataDir(allocator, "zig") catch return;
68}
69
std/os/index.zig+8-1
...@@ -11,13 +11,14 @@ const os = this;...@@ -11,13 +11,14 @@ const os = this;
11test "std.os" {11test "std.os" {
12 _ = @import("child_process.zig");12 _ = @import("child_process.zig");
13 _ = @import("darwin.zig");13 _ = @import("darwin.zig");
14 _ = @import("darwin_errno.zig");14 _ = @import("darwin/errno.zig");
15 _ = @import("get_user_id.zig");15 _ = @import("get_user_id.zig");
16 _ = @import("linux/index.zig");16 _ = @import("linux/index.zig");
17 _ = @import("path.zig");17 _ = @import("path.zig");
18 _ = @import("test.zig");18 _ = @import("test.zig");
19 _ = @import("time.zig");19 _ = @import("time.zig");
20 _ = @import("windows/index.zig");20 _ = @import("windows/index.zig");
21 _ = @import("get_app_data_dir.zig");
21}22}
2223
23pub const windows = @import("windows/index.zig");24pub const windows = @import("windows/index.zig");
...@@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError;...@@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError;
7677
77pub const FileHandle = if (is_windows) windows.HANDLE else i32;78pub const FileHandle = if (is_windows) windows.HANDLE else i32;
7879
80pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir;
81pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError;
82
79const debug = std.debug;83const debug = std.debug;
80const assert = debug.assert;84const assert = debug.assert;
8185
...@@ -494,6 +498,7 @@ pub var linux_aux_raw = []usize{0} ** 38;...@@ -494,6 +498,7 @@ pub var linux_aux_raw = []usize{0} ** 38;
494pub var posix_environ_raw: [][*]u8 = undefined;498pub var posix_environ_raw: [][*]u8 = undefined;
495499
496/// Caller must free result when done.500/// Caller must free result when done.
501/// TODO make this go through libc when we have it
497pub fn getEnvMap(allocator: *Allocator) !BufMap {502pub fn getEnvMap(allocator: *Allocator) !BufMap {
498 var result = BufMap.init(allocator);503 var result = BufMap.init(allocator);
499 errdefer result.deinit();504 errdefer result.deinit();
...@@ -537,6 +542,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {...@@ -537,6 +542,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
537 }542 }
538}543}
539544
545/// TODO make this go through libc when we have it
540pub fn getEnvPosix(key: []const u8) ?[]const u8 {546pub fn getEnvPosix(key: []const u8) ?[]const u8 {
541 for (posix_environ_raw) |ptr| {547 for (posix_environ_raw) |ptr| {
542 var line_i: usize = 0;548 var line_i: usize = 0;
...@@ -559,6 +565,7 @@ pub const GetEnvVarOwnedError = error{...@@ -559,6 +565,7 @@ pub const GetEnvVarOwnedError = error{
559};565};
560566
561/// Caller must free returned memory.567/// Caller must free returned memory.
568/// TODO make this go through libc when we have it
562pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {569pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
563 if (is_windows) {570 if (is_windows) {
564 const key_with_null = try cstr.addNullByte(allocator, key);571 const key_with_null = try cstr.addNullByte(allocator, key);
std/os/test.zig+3-3
...@@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {...@@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {
2323
24test "access file" {24test "access file" {
25 try os.makePath(a, "os_test_tmp");25 try os.makePath(a, "os_test_tmp");
26 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {26 if (os.File.access(a, "os_test_tmp/file.txt")) |ok| {
27 unreachable;27 @panic("expected error");
28 } else |err| {28 } else |err| {
29 assert(err == error.NotFound);29 assert(err == error.NotFound);
30 }30 }
3131
32 try io.writeFile(a, "os_test_tmp/file.txt", "");32 try io.writeFile(a, "os_test_tmp/file.txt", "");
33 assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);33 try os.File.access(a, "os_test_tmp/file.txt");
34 try os.deleteTree(a, "os_test_tmp");34 try os.deleteTree(a, "os_test_tmp");
35}35}
3636
std/os/windows/advapi32.zig created+30
...@@ -0,0 +1,30 @@
1use @import("index.zig");
2
3pub const PROV_RSA_FULL = 1;
4
5pub const REGSAM = ACCESS_MASK;
6pub const ACCESS_MASK = DWORD;
7pub const PHKEY = &HKEY;
8pub const HKEY = &HKEY__;
9pub const HKEY__ = extern struct {
10 unused: c_int,
11};
12pub const LSTATUS = LONG;
13
14pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
15 phProv: *HCRYPTPROV,
16 pszContainer: ?LPCSTR,
17 pszProvider: ?LPCSTR,
18 dwProvType: DWORD,
19 dwFlags: DWORD,
20) BOOL;
21
22pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
23
24pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
25
26pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM,
27 phkResult: &HKEY,) LSTATUS;
28
29pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
30 lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;
std/os/windows/index.zig+90-179
...@@ -1,188 +1,19 @@...@@ -1,188 +1,19 @@
1const std = @import("../../index.zig");
2const assert = std.debug.assert;
3
4pub use @import("advapi32.zig");
5pub use @import("kernel32.zig");
6pub use @import("ole32.zig");
7pub use @import("shell32.zig");
8pub use @import("shlwapi.zig");
9pub use @import("user32.zig");
10
1test "import" {11test "import" {
2 _ = @import("util.zig");12 _ = @import("util.zig");
3}13}
414
5pub const ERROR = @import("error.zig");15pub const ERROR = @import("error.zig");
616
7pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
8 phProv: *HCRYPTPROV,
9 pszContainer: ?LPCSTR,
10 pszProvider: ?LPCSTR,
11 dwProvType: DWORD,
12 dwFlags: DWORD,
13) BOOL;
14
15pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
16
17pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
18
19pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
20
21pub extern "kernel32" stdcallcc fn CreateDirectoryA(
22 lpPathName: LPCSTR,
23 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
24) BOOL;
25
26pub extern "kernel32" stdcallcc fn CreateFileA(
27 lpFileName: LPCSTR,
28 dwDesiredAccess: DWORD,
29 dwShareMode: DWORD,
30 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
31 dwCreationDisposition: DWORD,
32 dwFlagsAndAttributes: DWORD,
33 hTemplateFile: ?HANDLE,
34) HANDLE;
35
36pub extern "kernel32" stdcallcc fn CreatePipe(
37 hReadPipe: *HANDLE,
38 hWritePipe: *HANDLE,
39 lpPipeAttributes: *const SECURITY_ATTRIBUTES,
40 nSize: DWORD,
41) BOOL;
42
43pub extern "kernel32" stdcallcc fn CreateProcessA(
44 lpApplicationName: ?LPCSTR,
45 lpCommandLine: LPSTR,
46 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
47 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
48 bInheritHandles: BOOL,
49 dwCreationFlags: DWORD,
50 lpEnvironment: ?*c_void,
51 lpCurrentDirectory: ?LPCSTR,
52 lpStartupInfo: *STARTUPINFOA,
53 lpProcessInformation: *PROCESS_INFORMATION,
54) BOOL;
55
56pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
57 lpSymlinkFileName: LPCSTR,
58 lpTargetFileName: LPCSTR,
59 dwFlags: DWORD,
60) BOOLEAN;
61
62pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
63
64pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
65
66pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
67
68pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
69
70pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
71pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
72pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
73
74pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
75
76pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
77
78pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
79
80pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
81
82pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
83
84pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
85
86pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
87
88pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
89
90pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
91
92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
93
94pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
95
96pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
97 in_hFile: HANDLE,
98 in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
99 out_lpFileInformation: *c_void,
100 in_dwBufferSize: DWORD,
101) BOOL;
102
103pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
104 hFile: HANDLE,
105 lpszFilePath: LPSTR,
106 cchFilePath: DWORD,
107 dwFlags: DWORD,
108) DWORD;
109
110pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
111pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
112
113pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
114pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
115
116pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
117pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
118pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
119pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
120pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
121pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
122pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
123
124pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
125
126pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
127
128pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
129
130pub extern "kernel32" stdcallcc fn MoveFileExA(
131 lpExistingFileName: LPCSTR,
132 lpNewFileName: LPCSTR,
133 dwFlags: DWORD,
134) BOOL;
135
136pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
137
138pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
139
140pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
141
142pub extern "kernel32" stdcallcc fn ReadFile(
143 in_hFile: HANDLE,
144 out_lpBuffer: *c_void,
145 in_nNumberOfBytesToRead: DWORD,
146 out_lpNumberOfBytesRead: *DWORD,
147 in_out_lpOverlapped: ?*OVERLAPPED,
148) BOOL;
149
150pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
151
152pub extern "kernel32" stdcallcc fn SetFilePointerEx(
153 in_fFile: HANDLE,
154 in_liDistanceToMove: LARGE_INTEGER,
155 out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
156 in_dwMoveMethod: DWORD,
157) BOOL;
158
159pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
160
161pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
162
163pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
164
165pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
166
167pub extern "kernel32" stdcallcc fn WriteFile(
168 in_hFile: HANDLE,
169 in_lpBuffer: *const c_void,
170 in_nNumberOfBytesToWrite: DWORD,
171 out_lpNumberOfBytesWritten: ?*DWORD,
172 in_out_lpOverlapped: ?*OVERLAPPED,
173) BOOL;
174
175//TODO: call unicode versions instead of relying on ANSI code page
176pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
177
178pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
179
180pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
181
182pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
183
184pub const PROV_RSA_FULL = 1;
185
186pub const BOOL = c_int;17pub const BOOL = c_int;
187pub const BOOLEAN = BYTE;18pub const BOOLEAN = BYTE;
188pub const BYTE = u8;19pub const BYTE = u8;
...@@ -204,6 +35,7 @@ pub const LPSTR = [*]CHAR;...@@ -204,6 +35,7 @@ pub const LPSTR = [*]CHAR;
204pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR;35pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR;
205pub const LPVOID = *c_void;36pub const LPVOID = *c_void;
206pub const LPWSTR = [*]WCHAR;37pub const LPWSTR = [*]WCHAR;
38pub const LPCWSTR = [*]const WCHAR;
207pub const PVOID = *c_void;39pub const PVOID = *c_void;
208pub const PWSTR = [*]WCHAR;40pub const PWSTR = [*]WCHAR;
209pub const SIZE_T = usize;41pub const SIZE_T = usize;
...@@ -439,3 +271,82 @@ pub const SYSTEM_INFO = extern struct {...@@ -439,3 +271,82 @@ pub const SYSTEM_INFO = extern struct {
439 wProcessorLevel: WORD,271 wProcessorLevel: WORD,
440 wProcessorRevision: WORD,272 wProcessorRevision: WORD,
441};273};
274
275pub const HRESULT = c_long;
276
277pub const KNOWNFOLDERID = GUID;
278pub const GUID = extern struct {
279 Data1: c_ulong,
280 Data2: c_ushort,
281 Data3: c_ushort,
282 Data4: [8]u8,
283
284 pub fn parse(str: []const u8) GUID {
285 var guid: GUID = undefined;
286 var index: usize = 0;
287 assert(str[index] == '{');
288 index += 1;
289
290 guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable;
291 index += 8;
292
293 assert(str[index] == '-');
294 index += 1;
295
296 guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
297 index += 4;
298
299 assert(str[index] == '-');
300 index += 1;
301
302 guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
303 index += 4;
304
305 assert(str[index] == '-');
306 index += 1;
307
308 guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
309 index += 2;
310 guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
311 index += 2;
312
313 assert(str[index] == '-');
314 index += 1;
315
316 var i: usize = 2;
317 while (i < guid.Data4.len) : (i += 1) {
318 guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
319 index += 2;
320 }
321
322 assert(str[index] == '}');
323 index += 1;
324 return guid;
325 }
326};
327
328pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
329
330pub const KF_FLAG_DEFAULT = 0;
331pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
332pub const KF_FLAG_CREATE = 32768;
333pub const KF_FLAG_DONT_VERIFY = 16384;
334pub const KF_FLAG_DONT_UNEXPAND = 8192;
335pub const KF_FLAG_NO_ALIAS = 4096;
336pub const KF_FLAG_INIT = 2048;
337pub const KF_FLAG_DEFAULT_PATH = 1024;
338pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
339pub const KF_FLAG_SIMPLE_IDLIST = 256;
340pub const KF_FLAG_ALIAS_ONLY = -2147483648;
341
342pub const S_OK = 0;
343pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001));
344pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002));
345pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003));
346pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004));
347pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005));
348pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF));
349pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
350pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
351pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
352pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));
std/os/windows/kernel32.zig created+162
...@@ -0,0 +1,162 @@
1use @import("index.zig");
2
3pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
4
5pub extern "kernel32" stdcallcc fn CreateDirectoryA(
6 lpPathName: LPCSTR,
7 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
8) BOOL;
9
10pub extern "kernel32" stdcallcc fn CreateFileA(
11 lpFileName: LPCSTR,
12 dwDesiredAccess: DWORD,
13 dwShareMode: DWORD,
14 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
15 dwCreationDisposition: DWORD,
16 dwFlagsAndAttributes: DWORD,
17 hTemplateFile: ?HANDLE,
18) HANDLE;
19
20pub extern "kernel32" stdcallcc fn CreatePipe(
21 hReadPipe: *HANDLE,
22 hWritePipe: *HANDLE,
23 lpPipeAttributes: *const SECURITY_ATTRIBUTES,
24 nSize: DWORD,
25) BOOL;
26
27pub extern "kernel32" stdcallcc fn CreateProcessA(
28 lpApplicationName: ?LPCSTR,
29 lpCommandLine: LPSTR,
30 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
31 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
32 bInheritHandles: BOOL,
33 dwCreationFlags: DWORD,
34 lpEnvironment: ?*c_void,
35 lpCurrentDirectory: ?LPCSTR,
36 lpStartupInfo: *STARTUPINFOA,
37 lpProcessInformation: *PROCESS_INFORMATION,
38) BOOL;
39
40pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
41 lpSymlinkFileName: LPCSTR,
42 lpTargetFileName: LPCSTR,
43 dwFlags: DWORD,
44) BOOLEAN;
45
46pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
47
48pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
49
50pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
51
52pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
53
54pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
55pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
56pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
57
58pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
59
60pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
61
62pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
63
64pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
65
66pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
67
68pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
69
70pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
71
72pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
73
74pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
75
76pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
77
78pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
79
80pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
81 in_hFile: HANDLE,
82 in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
83 out_lpFileInformation: *c_void,
84 in_dwBufferSize: DWORD,
85) BOOL;
86
87pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
88 hFile: HANDLE,
89 lpszFilePath: LPSTR,
90 cchFilePath: DWORD,
91 dwFlags: DWORD,
92) DWORD;
93
94pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
95pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
96
97pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
98pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
99
100pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
101pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
102pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
103pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
104pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
105pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
106pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
107
108pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
109
110pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
111
112pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
113
114pub extern "kernel32" stdcallcc fn MoveFileExA(
115 lpExistingFileName: LPCSTR,
116 lpNewFileName: LPCSTR,
117 dwFlags: DWORD,
118) BOOL;
119
120pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
121
122pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
123
124pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
125
126pub extern "kernel32" stdcallcc fn ReadFile(
127 in_hFile: HANDLE,
128 out_lpBuffer: *c_void,
129 in_nNumberOfBytesToRead: DWORD,
130 out_lpNumberOfBytesRead: *DWORD,
131 in_out_lpOverlapped: ?*OVERLAPPED,
132) BOOL;
133
134pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
135
136pub extern "kernel32" stdcallcc fn SetFilePointerEx(
137 in_fFile: HANDLE,
138 in_liDistanceToMove: LARGE_INTEGER,
139 out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
140 in_dwMoveMethod: DWORD,
141) BOOL;
142
143pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
144
145pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
146
147pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
148
149pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
150
151pub extern "kernel32" stdcallcc fn WriteFile(
152 in_hFile: HANDLE,
153 in_lpBuffer: *const c_void,
154 in_nNumberOfBytesToWrite: DWORD,
155 out_lpNumberOfBytesWritten: ?*DWORD,
156 in_out_lpOverlapped: ?*OVERLAPPED,
157) BOOL;
158
159//TODO: call unicode versions instead of relying on ANSI code page
160pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
161
162pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
std/os/windows/ole32.zig created+18
...@@ -0,0 +1,18 @@
1use @import("index.zig");
2
3pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
4pub extern "ole32.dll" stdcallcc fn CoUninitialize() void;
5pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD;
6pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
7
8
9pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
10pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
11pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
12pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
13pub const COINIT = extern enum {
14 COINIT_APARTMENTTHREADED = 2,
15 COINIT_MULTITHREADED = 0,
16 COINIT_DISABLE_OLE1DDE = 4,
17 COINIT_SPEED_OVER_MEMORY = 8,
18};
std/os/windows/shell32.zig created+4
...@@ -0,0 +1,4 @@
1use @import("index.zig");
2
3pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
4
std/os/windows/shlwapi.zig created+4
...@@ -0,0 +1,4 @@
1use @import("index.zig");
2
3pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
4
std/os/windows/user32.zig created+4
...@@ -0,0 +1,4 @@
1use @import("index.zig");
2
3pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
4
std/special/test_runner.zig+17-3
...@@ -5,11 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice;...@@ -5,11 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice;
5const warn = std.debug.warn;5const warn = std.debug.warn;
66
7pub fn main() !void {7pub fn main() !void {
8 var ok_count: usize = 0;
9 var skip_count: usize = 0;
8 for (test_fn_list) |test_fn, i| {10 for (test_fn_list) |test_fn, i| {
9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);11 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1012
11 try test_fn.func();13 if (test_fn.func()) |_| {
1214 ok_count += 1;
13 warn("OK\n");15 warn("OK\n");
16 } else |err| switch (err) {
17 error.SkipZigTest => {
18 skip_count += 1;
19 warn("SKIP\n");
20 },
21 else => return err,
22 }
23 }
24 if (ok_count == test_fn_list.len) {
25 warn("All tests passed.\n");
26 } else {
27 warn("{} passed; {} skipped.\n", ok_count, skip_count);
14 }28 }
15}29}
std/unicode.zig+89
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1const std = @import("./index.zig");1const std = @import("./index.zig");
2const builtin = @import("builtin");
2const debug = std.debug;3const debug = std.debug;
4const assert = std.debug.assert;
5const mem = std.mem;
36
4/// Returns how many bytes the UTF-8 representation would require7/// Returns how many bytes the UTF-8 representation would require
5/// for the given codepoint.8/// for the given codepoint.
...@@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 {...@@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 {
441 debug.assert(bytes.len == length);444 debug.assert(bytes.len == length);
442 return utf8Decode(bytes);445 return utf8Decode(bytes);
443}446}
447
448// TODO: make this API on top of a non-allocating Utf16LeView
449pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
450 var result = std.ArrayList(u8).init(allocator);
451 // optimistically guess that it will all be ascii.
452 try result.ensureCapacity(utf16le.len);
453
454 const utf16le_as_bytes = @sliceToBytes(utf16le);
455 var i: usize = 0;
456 var out_index: usize = 0;
457 while (i < utf16le_as_bytes.len) : (i += 2) {
458 // decode
459 const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
460 var codepoint: u32 = undefined;
461 if (c0 & ~u32(0x03ff) == 0xd800) {
462 // surrogate pair
463 i += 2;
464 if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
465 const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
466 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
467 codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
468 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
469 return error.UnexpectedSecondSurrogateHalf;
470 } else {
471 codepoint = c0;
472 }
473
474 // encode
475 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
476 try result.resize(result.len + utf8_len);
477 _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
478 out_index += utf8_len;
479 }
480
481 return result.toOwnedSlice();
482}
483
484test "utf16leToUtf8" {
485 var utf16le: [2]u16 = undefined;
486 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
487
488 {
489 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
490 mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);
491 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
492 assert(mem.eql(u8, utf8, "Aa"));
493 }
494
495 {
496 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
497 mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);
498 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
499 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
500 }
501
502 {
503 // the values just outside the surrogate half range
504 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
505 mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);
506 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
507 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
508 }
509
510 {
511 // smallest surrogate pair
512 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
513 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
514 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
515 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
516 }
517
518 {
519 // largest surrogate pair
520 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
521 mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);
522 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
523 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
524 }
525
526 {
527 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
528 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
529 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
530 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
531 }
532}
std/zig/index.zig+3
...@@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");...@@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");
2pub const Token = tokenizer.Token;2pub const Token = tokenizer.Token;
3pub const Tokenizer = tokenizer.Tokenizer;3pub const Tokenizer = tokenizer.Tokenizer;
4pub const parse = @import("parse.zig").parse;4pub const parse = @import("parse.zig").parse;
5pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral;
5pub const render = @import("render.zig").render;6pub const render = @import("render.zig").render;
6pub const ast = @import("ast.zig");7pub const ast = @import("ast.zig");
78
...@@ -10,4 +11,6 @@ test "std.zig tests" {...@@ -10,4 +11,6 @@ test "std.zig tests" {
10 _ = @import("parse.zig");11 _ = @import("parse.zig");
11 _ = @import("render.zig");12 _ = @import("render.zig");
12 _ = @import("tokenizer.zig");13 _ = @import("tokenizer.zig");
14 _ = @import("parse_string_literal.zig");
13}15}
16
std/zig/parse.zig+1-1
...@@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2356 const token = nextToken(&tok_it, &tree);2356 const token = nextToken(&tok_it, &tree);
2357 switch (token.ptr.id) {2357 switch (token.ptr.id) {
2358 Token.Id.IntegerLiteral => {2358 Token.Id.IntegerLiteral => {
2359 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index);2359 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index);
2360 continue;2360 continue;
2361 },2361 },
2362 Token.Id.FloatLiteral => {2362 Token.Id.FloatLiteral => {
std/zig/parse_string_literal.zig created+76
...@@ -0,0 +1,76 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3
4const State = enum {
5 Start,
6 Backslash,
7};
8
9pub const ParseStringLiteralError = error{
10 OutOfMemory,
11
12 /// When this is returned, index will be the position of the character.
13 InvalidCharacter,
14};
15
16/// caller owns returned memory
17pub fn parseStringLiteral(
18 allocator: *std.mem.Allocator,
19 bytes: []const u8,
20 bad_index: *usize, // populated if error.InvalidCharacter is returned
21) ParseStringLiteralError![]u8 {
22 const first_index = if (bytes[0] == 'c') usize(2) else usize(1);
23 assert(bytes[bytes.len - 1] == '"');
24
25 var list = std.ArrayList(u8).init(allocator);
26 errdefer list.deinit();
27
28 const slice = bytes[first_index..];
29 try list.ensureCapacity(slice.len - 1);
30
31 var state = State.Start;
32 for (slice) |b, index| {
33 switch (state) {
34 State.Start => switch (b) {
35 '\\' => state = State.Backslash,
36 '\n' => {
37 bad_index.* = index;
38 return error.InvalidCharacter;
39 },
40 '"' => return list.toOwnedSlice(),
41 else => try list.append(b),
42 },
43 State.Backslash => switch (b) {
44 'x' => @panic("TODO"),
45 'u' => @panic("TODO"),
46 'U' => @panic("TODO"),
47 'n' => {
48 try list.append('\n');
49 state = State.Start;
50 },
51 'r' => {
52 try list.append('\r');
53 state = State.Start;
54 },
55 '\\' => {
56 try list.append('\\');
57 state = State.Start;
58 },
59 't' => {
60 try list.append('\t');
61 state = State.Start;
62 },
63 '"' => {
64 try list.append('"');
65 state = State.Start;
66 },
67 else => {
68 bad_index.* = index;
69 return error.InvalidCharacter;
70 },
71 },
72 else => unreachable,
73 }
74 }
75 unreachable;
76}
std/zig/tokenizer.zig+1
...@@ -73,6 +73,7 @@ pub const Token = struct {...@@ -73,6 +73,7 @@ pub const Token = struct {
73 return null;73 return null;
74 }74 }
7575
76 /// TODO remove this enum
76 const StrLitKind = enum {77 const StrLitKind = enum {
77 Normal,78 Normal,
78 C,79 C,
test/cases/cast.zig+17
...@@ -468,3 +468,20 @@ test "@intCast i32 to u7" {...@@ -468,3 +468,20 @@ test "@intCast i32 to u7" {
468 var z = x >> @intCast(u7, y);468 var z = x >> @intCast(u7, y);
469 assert(z == 0xff);469 assert(z == 0xff);
470}470}
471
472test "implicit cast undefined to optional" {
473 assert(MakeType(void).getNull() == null);
474 assert(MakeType(void).getNonNull() != null);
475}
476
477fn MakeType(comptime T: type) type {
478 return struct {
479 fn getNull() ?T {
480 return null;
481 }
482
483 fn getNonNull() ?T {
484 return T(undefined);
485 }
486 };
487}
test/cases/defer.zig+15
...@@ -61,3 +61,18 @@ test "defer and labeled break" {...@@ -61,3 +61,18 @@ test "defer and labeled break" {
6161
62 assert(i == 1);62 assert(i == 1);
63}63}
64
65test "errdefer does not apply to fn inside fn" {
66 if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad);
67}
68
69fn testNestedFnErrDefer() error!void {
70 var a: i32 = 0;
71 errdefer a += 1;
72 const S = struct {
73 fn baz() error {
74 return error.Bad;
75 }
76 };
77 return S.baz();
78}
test/cases/eval.zig+10
...@@ -642,3 +642,13 @@ test "@tagName of @typeId" {...@@ -642,3 +642,13 @@ test "@tagName of @typeId" {
642 const str = @tagName(@typeId(u8));642 const str = @tagName(@typeId(u8));
643 assert(std.mem.eql(u8, str, "Int"));643 assert(std.mem.eql(u8, str, "Int"));
644}644}
645
646test "setting backward branch quota just before a generic fn call" {
647 @setEvalBranchQuota(1001);
648 loopNTimes(1001);
649}
650
651fn loopNTimes(comptime n: usize) void {
652 comptime var i = 0;
653 inline while (i < n) : (i += 1) {}
654}
test/gen_h.zig+47
...@@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void {...@@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void {
76 \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]);76 \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]);
77 \\77 \\
78 );78 );
79
80 cases.add("ptr to zig struct",
81 \\const S = struct {
82 \\ a: u8,
83 \\};
84 \\
85 \\export fn a(s: *S) u8 {
86 \\ return s.a;
87 \\}
88
89 ,
90 \\struct S;
91 \\TEST_EXPORT uint8_t a(struct S * s);
92 \\
93 );
94
95 cases.add("ptr to zig union",
96 \\const U = union(enum) {
97 \\ A: u8,
98 \\ B: u16,
99 \\};
100 \\
101 \\export fn a(s: *U) u8 {
102 \\ return s.A;
103 \\}
104
105 ,
106 \\union U;
107 \\TEST_EXPORT uint8_t a(union U * s);
108 \\
109 );
110
111 cases.add("ptr to zig enum",
112 \\const E = enum(u8) {
113 \\ A,
114 \\ B,
115 \\};
116 \\
117 \\export fn a(s: *E) u8 {
118 \\ return @enumToInt(s.*);
119 \\}
120
121 ,
122 \\enum E;
123 \\TEST_EXPORT uint8_t a(enum E * s);
124 \\
125 );
79}126}
test/stage2/compare_output.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3
4pub fn addCases(ctx: *TestContext) !void {
5 try ctx.testCompareOutputLibC(
6 \\extern fn puts([*]const u8) void;
7 \\export fn main() c_int {
8 \\ puts(c"Hello, world!");
9 \\ return 0;
10 \\}
11 , "Hello, world!" ++ std.cstr.line_sep);
12}
test/stage2/compile_errors.zig+18
...@@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void {
9 try ctx.testCompileError(9 try ctx.testCompileError(
10 \\fn() void {}10 \\fn() void {}
11 , "1.zig", 1, 1, "missing function name");11 , "1.zig", 1, 1, "missing function name");
12
13 try ctx.testCompileError(
14 \\comptime {
15 \\ return;
16 \\}
17 , "1.zig", 2, 5, "return expression outside function definition");
18
19 try ctx.testCompileError(
20 \\export fn entry() void {
21 \\ defer return;
22 \\}
23 , "1.zig", 2, 11, "cannot return from defer expression");
24
25 try ctx.testCompileError(
26 \\export fn entry() c_int {
27 \\ return 36893488147419103232;
28 \\}
29 , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
12}30}
test/tests.zig+4-7
...@@ -89,12 +89,13 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes:...@@ -89,12 +89,13 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes:
89 return cases.step;89 return cases.step;
90}90}
9191
92pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {92pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
93 const cases = b.allocator.create(BuildExamplesContext{93 const cases = b.allocator.create(BuildExamplesContext{
94 .b = b,94 .b = b,
95 .step = b.step("test-build-examples", "Build the examples"),95 .step = b.step("test-build-examples", "Build the examples"),
96 .test_index = 0,96 .test_index = 0,
97 .test_filter = test_filter,97 .test_filter = test_filter,
98 .modes = modes,
98 }) catch unreachable;99 }) catch unreachable;
99100
100 build_examples.addCases(cases);101 build_examples.addCases(cases);
...@@ -697,6 +698,7 @@ pub const BuildExamplesContext = struct {...@@ -697,6 +698,7 @@ pub const BuildExamplesContext = struct {
697 step: *build.Step,698 step: *build.Step,
698 test_index: usize,699 test_index: usize,
699 test_filter: ?[]const u8,700 test_filter: ?[]const u8,
701 modes: []const Mode,
700702
701 pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void {703 pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void {
702 self.addAllArgs(root_src, true);704 self.addAllArgs(root_src, true);
...@@ -739,12 +741,7 @@ pub const BuildExamplesContext = struct {...@@ -739,12 +741,7 @@ pub const BuildExamplesContext = struct {
739 pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void {741 pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void {
740 const b = self.b;742 const b = self.b;
741743
742 for ([]Mode{744 for (self.modes) |mode| {
743 Mode.Debug,
744 Mode.ReleaseSafe,
745 Mode.ReleaseFast,
746 Mode.ReleaseSmall,
747 }) |mode| {
748 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable;745 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable;
749 if (self.test_filter) |filter| {746 if (self.test_filter) |filter| {
750 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;747 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;