authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 00:31:33-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-07-24 00:31:33-04:00
log10bdf73a02c90dc375985e49b08b5020cfc20b93
tree2485d62496dd7436bcbabe80e7b47ca369ccbd38
parent99153ac0aa390f01091308073b39947c45851ae6
parent72599d420b1bebb37efb2179a91d8256287f7c28
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1266 from ziglang/self-hosted-libc-hello-world

Self hosted libc hello world

46 files changed, 5624 insertions(+), 1204 deletions(-)

CMakeLists.txt+9
......@@ -426,6 +426,7 @@ set(ZIG_SOURCES
426426)
427427set(ZIG_CPP_SOURCES
428428 "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
429 "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
429430)
430431
431432set(ZIG_STD_FILES
......@@ -489,6 +490,7 @@ set(ZIG_STD_FILES
489490 "math/atan.zig"
490491 "math/atan2.zig"
491492 "math/atanh.zig"
493 "math/big/index.zig"
492494 "math/big/int.zig"
493495 "math/cbrt.zig"
494496 "math/ceil.zig"
......@@ -566,8 +568,14 @@ set(ZIG_STD_FILES
566568 "os/linux/x86_64.zig"
567569 "os/path.zig"
568570 "os/time.zig"
571 "os/windows/advapi32.zig"
569572 "os/windows/error.zig"
570573 "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"
571579 "os/windows/util.zig"
572580 "os/zen.zig"
573581 "rand/index.zig"
......@@ -616,6 +624,7 @@ set(ZIG_STD_FILES
616624 "zig/ast.zig"
617625 "zig/index.zig"
618626 "zig/parse.zig"
627 "zig/parse_string_literal.zig"
619628 "zig/render.zig"
620629 "zig/tokenizer.zig"
621630)
README.md+4-4
......@@ -21,19 +21,19 @@ clarity.
2121 * Compatible with C libraries with no wrapper necessary. Directly include
2222 C .h files and get access to the functions and symbols therein.
2323 * Provides standard library which competes with the C standard library and is
24 always compiled against statically in source form. Compile units do not
24 always compiled against statically in source form. Zig binaries do not
2525 depend on libc unless explicitly linked.
26 * Nullable type instead of null pointers.
26 * Optional type instead of null pointers.
2727 * Safe unions, tagged unions, and C ABI compatible unions.
2828 * Generics so that one can write efficient data structures that work for any
2929 data type.
3030 * No header files required. Top level declarations are entirely
3131 order-independent.
3232 * Compile-time code execution. Compile-time reflection.
33 * Partial compile-time function evaluation with eliminates the need for
33 * Partial compile-time function evaluation which eliminates the need for
3434 a preprocessor or macros.
3535 * 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.
3737 * Built-in unit tests with `zig test`.
3838 * Friendly toward package maintainers. Reproducible build, bootstrapping
3939 process carefully documented. Issues filed by package maintainers are
src-self-hosted/c.zig+1
......@@ -4,4 +4,5 @@ pub use @cImport({
44 @cInclude("inttypes.h");
55 @cInclude("config.h");
66 @cInclude("zig_llvm.h");
7 @cInclude("windows_sdk.h");
78});
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+5-3
......@@ -15,7 +15,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
1515 defer fn_val.base.deref(comp);
1616 defer code.destroy(comp.gpa());
1717
18 var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable);
18 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
1919 errdefer output_path.deinit();
2020
2121 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
......@@ -78,6 +78,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
7878 .dibuilder = dibuilder,
7979 .context = context,
8080 .lock = event.Lock.init(comp.loop),
81 .arena = &code.arena.allocator,
8182 };
8283
8384 try renderToLlvmModule(&ofile, fn_val, code);
......@@ -139,6 +140,7 @@ pub const ObjectFile = struct {
139140 dibuilder: *llvm.DIBuilder,
140141 context: llvm.ContextRef,
141142 lock: event.Lock,
143 arena: *std.mem.Allocator,
142144
143145 fn gpa(self: *ObjectFile) *std.mem.Allocator {
144146 return self.comp.gpa();
......@@ -147,7 +149,7 @@ pub const ObjectFile = struct {
147149
148150pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
149151 // TODO audit more of codegen.cpp:fn_llvm_value and port more logic
150 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);
151153 const llvm_fn = llvm.AddFunction(
152154 ofile.module,
153155 fn_val.symbol_name.ptr(),
......@@ -165,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
165167 // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
166168 //}
167169
168 const fn_type = fn_val.base.typeof.cast(Type.Fn).?;
170 const fn_type = fn_val.base.typ.cast(Type.Fn).?;
169171
170172 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
171173 //add_uwtable_attr(g, fn_table_entry->llvm_value);
src-self-hosted/compilation.zig+479-193
......@@ -21,13 +21,15 @@ const Scope = @import("scope.zig").Scope;
2121const Decl = @import("decl.zig").Decl;
2222const ir = @import("ir.zig");
2323const Visib = @import("visib.zig").Visib;
24const ParsedFile = @import("parsed_file.zig").ParsedFile;
2524const Value = @import("value.zig").Value;
2625const Type = Value.Type;
2726const Span = errmsg.Span;
27const Msg = errmsg.Msg;
2828const codegen = @import("codegen.zig");
2929const Package = @import("package.zig").Package;
3030const link = @import("link.zig").link;
31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
32const CInt = @import("c_int.zig").CInt;
3133
3234/// Data that is local to the event loop.
3335pub const EventLoopLocal = struct {
......@@ -37,6 +39,8 @@ pub const EventLoopLocal = struct {
3739 /// TODO pool these so that it doesn't have to lock
3840 prng: event.Locked(std.rand.DefaultPrng),
3941
42 native_libc: event.Future(LibCInstallation),
43
4044 var lazy_init_targets = std.lazyInit(void);
4145
4246 fn init(loop: *event.Loop) !EventLoopLocal {
......@@ -48,13 +52,16 @@ pub const EventLoopLocal = struct {
4852 var seed_bytes: [@sizeOf(u64)]u8 = undefined;
4953 try std.os.getRandomBytes(seed_bytes[0..]);
5054 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
55
5156 return EventLoopLocal{
5257 .loop = loop,
5358 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
5459 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
60 .native_libc = event.Future(LibCInstallation).init(loop),
5561 };
5662 }
5763
64 /// Must be called only after EventLoop.run completes.
5865 fn deinit(self: *EventLoopLocal) void {
5966 while (self.llvm_handle_pool.pop()) |node| {
6067 c.LLVMContextDispose(node.data);
......@@ -78,6 +85,13 @@ pub const EventLoopLocal = struct {
7885
7986 return LlvmHandle{ .node = node };
8087 }
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 }
8195};
8296
8397pub const LlvmHandle = struct {
......@@ -108,13 +122,6 @@ pub const Compilation = struct {
108122 version_patch: u32,
109123
110124 linker_script: ?[]const u8,
111 cache_dir: []const u8,
112 libc_lib_dir: ?[]const u8,
113 libc_static_lib_dir: ?[]const u8,
114 libc_include_dir: ?[]const u8,
115 msvc_lib_dir: ?[]const u8,
116 kernel32_lib_dir: ?[]const u8,
117 dynamic_linker: ?[]const u8,
118125 out_h_path: ?[]const u8,
119126
120127 is_test: bool,
......@@ -179,6 +186,8 @@ pub const Compilation = struct {
179186 void_type: *Type.Void,
180187 bool_type: *Type.Bool,
181188 noreturn_type: *Type.NoReturn,
189 comptime_int_type: *Type.ComptimeInt,
190 u8_type: *Type.Int,
182191
183192 void_value: *Value.Void,
184193 true_value: *Value.Bool,
......@@ -188,6 +197,7 @@ pub const Compilation = struct {
188197 target_machine: llvm.TargetMachineRef,
189198 target_data_ref: llvm.TargetDataRef,
190199 target_layout_str: [*]u8,
200 target_ptr_bits: u32,
191201
192202 /// for allocating things which have the same lifetime as this Compilation
193203 arena_allocator: std.heap.ArenaAllocator,
......@@ -195,7 +205,30 @@ pub const Compilation = struct {
195205 root_package: *Package,
196206 std_package: *Package,
197207
198 const CompileErrList = std.ArrayList(*errmsg.Msg);
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);
199232
200233 // TODO handle some of these earlier and report them in a way other than error codes
201234 pub const BuildError = error{
......@@ -240,12 +273,16 @@ pub const Compilation = struct {
240273 EnvironmentVariableNotFound,
241274 AppDataDirUnavailable,
242275 LinkFailed,
276 LibCRequiredButNotProvidedOrFound,
277 LibCMissingDynamicLinker,
278 InvalidDarwinVersionString,
279 UnsupportedLinkArchitecture,
243280 };
244281
245282 pub const Event = union(enum) {
246283 Ok,
247284 Error: BuildError,
248 Fail: []*errmsg.Msg,
285 Fail: []*Msg,
249286 };
250287
251288 pub const DarwinVersionMin = union(enum) {
......@@ -284,7 +321,6 @@ pub const Compilation = struct {
284321 build_mode: builtin.Mode,
285322 is_static: bool,
286323 zig_lib_dir: []const u8,
287 cache_dir: []const u8,
288324 ) !*Compilation {
289325 const loop = event_loop_local.loop;
290326 const comp = try event_loop_local.loop.allocator.create(Compilation{
......@@ -299,7 +335,6 @@ pub const Compilation = struct {
299335 .build_mode = build_mode,
300336 .zig_lib_dir = zig_lib_dir,
301337 .zig_std_dir = undefined,
302 .cache_dir = cache_dir,
303338 .tmp_dir = event.Future(BuildError![]u8).init(loop),
304339
305340 .name = undefined,
......@@ -318,12 +353,6 @@ pub const Compilation = struct {
318353 .verbose_link = false,
319354
320355 .linker_script = null,
321 .libc_lib_dir = null,
322 .libc_static_lib_dir = null,
323 .libc_include_dir = null,
324 .msvc_lib_dir = null,
325 .kernel32_lib_dir = null,
326 .dynamic_linker = null,
327356 .out_h_path = null,
328357 .is_test = false,
329358 .each_lib_rpath = false,
......@@ -350,7 +379,12 @@ pub const Compilation = struct {
350379 .link_out_file = null,
351380 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
352381 .prelink_group = event.Group(BuildError!void).init(loop),
382 .deinit_group = event.Group(void).init(loop),
353383 .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,
354388
355389 .meta_type = undefined,
356390 .void_type = undefined,
......@@ -360,15 +394,26 @@ pub const Compilation = struct {
360394 .false_value = undefined,
361395 .noreturn_type = undefined,
362396 .noreturn_value = undefined,
397 .comptime_int_type = undefined,
398 .u8_type = undefined,
363399
364400 .target_machine = undefined,
365401 .target_data_ref = undefined,
366402 .target_layout_str = undefined,
403 .target_ptr_bits = target.getArchPtrBitWidth(),
367404
368405 .root_package = undefined,
369406 .std_package = undefined,
407
408 .override_libc = null,
409 .destroy_handle = undefined,
410 .have_err_ret_tracing = false,
411 .primitive_type_table = undefined,
370412 });
371413 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();
372417 comp.arena_allocator.deinit();
373418 comp.loop.allocator.destroy(comp);
374419 }
......@@ -378,6 +423,7 @@ pub const Compilation = struct {
378423 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
379424 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
380425 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
426 comp.primitive_type_table = TypeTable.init(comp.arena());
381427
382428 const opt_level = switch (build_mode) {
383429 builtin.Mode.Debug => llvm.CodeGenLevelNone,
......@@ -431,123 +477,221 @@ pub const Compilation = struct {
431477
432478 try comp.initTypes();
433479
480 comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
481
434482 return comp;
435483 }
436484
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
437519 fn initTypes(comp: *Compilation) !void {
438 comp.meta_type = try comp.gpa().create(Type.MetaType{
520 comp.meta_type = try comp.arena().create(Type.MetaType{
439521 .base = Type{
522 .name = "type",
440523 .base = Value{
441524 .id = Value.Id.Type,
442 .typeof = undefined,
525 .typ = undefined,
443526 .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
444527 },
445528 .id = builtin.TypeId.Type,
529 .abi_alignment = Type.AbiAlignment.init(comp.loop),
446530 },
447531 .value = undefined,
448532 });
449533 comp.meta_type.value = &comp.meta_type.base;
450 comp.meta_type.base.base.typeof = &comp.meta_type.base;
451 errdefer comp.gpa().destroy(comp.meta_type);
534 comp.meta_type.base.base.typ = &comp.meta_type.base;
535 assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
452536
453 comp.void_type = try comp.gpa().create(Type.Void{
537 comp.void_type = try comp.arena().create(Type.Void{
454538 .base = Type{
539 .name = "void",
455540 .base = Value{
456541 .id = Value.Id.Type,
457 .typeof = &Type.MetaType.get(comp).base,
542 .typ = &Type.MetaType.get(comp).base,
458543 .ref_count = std.atomic.Int(usize).init(1),
459544 },
460545 .id = builtin.TypeId.Void,
546 .abi_alignment = Type.AbiAlignment.init(comp.loop),
461547 },
462548 });
463 errdefer comp.gpa().destroy(comp.void_type);
549 assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
464550
465 comp.noreturn_type = try comp.gpa().create(Type.NoReturn{
551 comp.noreturn_type = try comp.arena().create(Type.NoReturn{
466552 .base = Type{
553 .name = "noreturn",
467554 .base = Value{
468555 .id = Value.Id.Type,
469 .typeof = &Type.MetaType.get(comp).base,
556 .typ = &Type.MetaType.get(comp).base,
470557 .ref_count = std.atomic.Int(usize).init(1),
471558 },
472559 .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),
473575 },
474576 });
475 errdefer comp.gpa().destroy(comp.noreturn_type);
577 assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
476578
477 comp.bool_type = try comp.gpa().create(Type.Bool{
579 comp.bool_type = try comp.arena().create(Type.Bool{
478580 .base = Type{
581 .name = "bool",
479582 .base = Value{
480583 .id = Value.Id.Type,
481 .typeof = &Type.MetaType.get(comp).base,
584 .typ = &Type.MetaType.get(comp).base,
482585 .ref_count = std.atomic.Int(usize).init(1),
483586 },
484587 .id = builtin.TypeId.Bool,
588 .abi_alignment = Type.AbiAlignment.init(comp.loop),
485589 },
486590 });
487 errdefer comp.gpa().destroy(comp.bool_type);
591 assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
488592
489 comp.void_value = try comp.gpa().create(Value.Void{
593 comp.void_value = try comp.arena().create(Value.Void{
490594 .base = Value{
491595 .id = Value.Id.Void,
492 .typeof = &Type.Void.get(comp).base,
596 .typ = &Type.Void.get(comp).base,
493597 .ref_count = std.atomic.Int(usize).init(1),
494598 },
495599 });
496 errdefer comp.gpa().destroy(comp.void_value);
497600
498 comp.true_value = try comp.gpa().create(Value.Bool{
601 comp.true_value = try comp.arena().create(Value.Bool{
499602 .base = Value{
500603 .id = Value.Id.Bool,
501 .typeof = &Type.Bool.get(comp).base,
604 .typ = &Type.Bool.get(comp).base,
502605 .ref_count = std.atomic.Int(usize).init(1),
503606 },
504607 .x = true,
505608 });
506 errdefer comp.gpa().destroy(comp.true_value);
507609
508 comp.false_value = try comp.gpa().create(Value.Bool{
610 comp.false_value = try comp.arena().create(Value.Bool{
509611 .base = Value{
510612 .id = Value.Id.Bool,
511 .typeof = &Type.Bool.get(comp).base,
613 .typ = &Type.Bool.get(comp).base,
512614 .ref_count = std.atomic.Int(usize).init(1),
513615 },
514616 .x = false,
515617 });
516 errdefer comp.gpa().destroy(comp.false_value);
517618
518 comp.noreturn_value = try comp.gpa().create(Value.NoReturn{
619 comp.noreturn_value = try comp.arena().create(Value.NoReturn{
519620 .base = Value{
520621 .id = Value.Id.NoReturn,
521 .typeof = &Type.NoReturn.get(comp).base,
622 .typ = &Type.NoReturn.get(comp).base,
522623 .ref_count = std.atomic.Int(usize).init(1),
523624 },
524625 });
525 errdefer comp.gpa().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);
526666 }
527667
528 pub fn destroy(self: *Compilation) void {
668 /// This function can safely use async/await, because it manages Compilation's lifetime,
669 /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
670 async fn internalDeinit(self: *Compilation) void {
671 suspend;
672
673 await (async self.deinit_group.wait() catch unreachable);
529674 if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
675 // TODO evented I/O?
530676 os.deleteTree(self.arena(), tmp_dir) catch {};
531677 } else |_| {};
532678
533 self.noreturn_value.base.deref(self);
534 self.void_value.base.deref(self);
535 self.false_value.base.deref(self);
536 self.true_value.base.deref(self);
537 self.noreturn_type.base.base.deref(self);
538 self.void_type.base.base.deref(self);
539 self.meta_type.base.base.deref(self);
540
541679 self.events.destroy();
542680
543681 llvm.DisposeMessage(self.target_layout_str);
544682 llvm.DisposeTargetData(self.target_data_ref);
545683 llvm.DisposeTargetMachine(self.target_machine);
546684
685 self.primitive_type_table.deinit();
686
547687 self.arena_allocator.deinit();
548688 self.gpa().destroy(self);
549689 }
550690
691 pub fn destroy(self: *Compilation) void {
692 resume self.destroy_handle;
693 }
694
551695 pub fn build(self: *Compilation) !void {
552696 if (self.llvm_argv.len != 0) {
553697 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
......@@ -597,79 +741,103 @@ pub const Compilation = struct {
597741 }
598742
599743 async fn compileAndLink(self: *Compilation) !void {
600 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
601 // TODO async/await os.path.real
602 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
603 try printError("unable to get real path '{}': {}", root_src_path, err);
604 return err;
605 };
606 errdefer self.gpa().free(root_src_real_path);
744 if (self.root_src_path) |root_src_path| {
745 // TODO async/await os.path.real
746 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
747 try printError("unable to get real path '{}': {}", root_src_path, 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 }
607766
608 // TODO async/await readFileAlloc()
609 const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
610 try printError("unable to open '{}': {}", root_src_real_path, err);
611 return err;
612 };
613 errdefer self.gpa().free(source_code);
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;
614771
615 const parsed_file = try self.gpa().create(ParsedFile{
616 .tree = undefined,
617 .realpath = root_src_real_path,
618 });
619 errdefer self.gpa().destroy(parsed_file);
620
621 parsed_file.tree = try std.zig.parse(self.gpa(), source_code);
622 errdefer parsed_file.tree.deinit();
623
624 const tree = &parsed_file.tree;
625
626 // create empty struct for it
627 const decls = try Scope.Decls.create(self, null);
628 defer decls.base.deref(self);
629
630 var decl_group = event.Group(BuildError!void).init(self.loop);
631 errdefer decl_group.cancelAll();
632
633 var it = tree.root_node.decls.iterator(0);
634 while (it.next()) |decl_ptr| {
635 const decl = decl_ptr.*;
636 switch (decl.id) {
637 ast.Node.Id.Comptime => @panic("TODO"),
638 ast.Node.Id.VarDecl => @panic("TODO"),
639 ast.Node.Id.FnProto => {
640 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
641
642 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
643 try self.addCompileError(parsed_file, Span{
644 .first = fn_proto.fn_token,
645 .last = fn_proto.fn_token + 1,
646 }, "missing function name");
647 continue;
648 };
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();
649776
650 const fn_decl = try self.gpa().create(Decl.Fn{
651 .base = Decl{
652 .id = Decl.Id.Fn,
653 .name = name,
654 .visib = parseVisibToken(tree, fn_proto.visib_token),
655 .resolution = event.Future(BuildError!void).init(self.loop),
656 .resolution_in_progress = 0,
657 .parsed_file = parsed_file,
658 .parent_scope = &decls.base,
659 },
660 .value = Decl.Fn.Val{ .Unresolved = {} },
661 .fn_proto = fn_proto,
662 });
663 errdefer self.gpa().destroy(fn_decl);
664
665 try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
666 },
667 ast.Node.Id.TestDecl => @panic("TODO"),
668 else => unreachable,
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 }
669829 }
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();
670835 }
671 try await (async decl_group.wait() catch unreachable);
672 try await (async self.prelink_group.wait() catch unreachable);
836
837 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
838 error.SemanticAnalysisFailed => {},
839 else => return err,
840 };
673841
674842 const any_prelink_errors = blk: {
675843 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
......@@ -679,39 +847,108 @@ pub const Compilation = struct {
679847 };
680848
681849 if (!any_prelink_errors) {
682 try link(self);
850 try await (async link(self) catch unreachable);
683851 }
684852 }
685853
686 async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
687 const is_export = decl.isExported(&decl.parsed_file.tree);
854 /// caller takes ownership of resulting Code
855 async fn genAndAnalyzeCode(
856 comp: *Compilation,
857 scope: *Scope,
858 node: *ast.Node,
859 expected_type: ?*Type,
860 ) !*ir.Code {
861 const unanalyzed_code = try await (async ir.gen(
862 comp,
863 node,
864 scope,
865 ) catch unreachable);
866 defer unanalyzed_code.destroy(comp.gpa());
867
868 if (comp.verbose_ir) {
869 std.debug.warn("unanalyzed:\n");
870 unanalyzed_code.dump();
871 }
872
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();
883 }
884
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());
908 }
909
910 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
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
688917
689918 if (is_export) {
690919 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
691920 try self.prelink_group.call(resolveDecl, self, decl);
692921 }
922
923 add_to_table_resolved = true;
924 try await add_to_table;
693925 }
694926
695 fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {
696 const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
697 errdefer self.loop.allocator.free(text);
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();
698930
699 try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text);
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
934 }
935 }
936
937 fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
938 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
939 errdefer self.gpa().free(text);
940
941 const msg = try Msg.createFromScope(self, root, span, text);
942 errdefer msg.destroy();
943
944 try self.prelink_group.call(addCompileErrorAsync, self, msg);
700945 }
701946
702947 async fn addCompileErrorAsync(
703948 self: *Compilation,
704 parsed_file: *ParsedFile,
705 span: Span,
706 text: []u8,
949 msg: *Msg,
707950 ) !void {
708 const msg = try self.loop.allocator.create(errmsg.Msg{
709 .path = parsed_file.realpath,
710 .text = text,
711 .span = span,
712 .tree = &parsed_file.tree,
713 });
714 errdefer self.loop.allocator.destroy(msg);
951 errdefer msg.destroy();
715952
716953 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
717954 defer compile_errors.release();
......@@ -725,7 +962,7 @@ pub const Compilation = struct {
725962
726963 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
727964 try self.addCompileError(
728 decl.parsed_file,
965 decl.findRootScope(),
729966 decl.getSpan(),
730967 "exported symbol collision: '{}'",
731968 decl.name,
......@@ -762,10 +999,22 @@ pub const Compilation = struct {
762999 try self.link_libs_list.append(link_lib);
7631000 if (is_libc) {
7641001 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 }
7651007 }
7661008 return link_lib;
7671009 }
7681010
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
7691018 /// General Purpose Allocator. Must free when done.
7701019 fn gpa(self: Compilation) *mem.Allocator {
7711020 return self.loop.allocator;
......@@ -831,6 +1080,37 @@ pub const Compilation = struct {
8311080 b64_fs_encoder.encode(result[0..], rand_bytes);
8321081 return result;
8331082 }
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 }
8341114};
8351115
8361116fn printError(comptime format: []const u8, args: ...) !void {
......@@ -850,15 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
8501130 }
8511131}
8521132
853/// This declaration has been blessed as going into the final code generation.
854pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
855 if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
856
857 decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
858 decl.resolution.resolve();
859 return decl.resolution.data;
860}
861
8621133/// The function that actually does the generation.
8631134async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
8641135 switch (decl.id) {
......@@ -872,66 +1143,30 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
8721143}
8731144
8741145async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
875 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);
8761147
8771148 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
8781149 defer fndef_scope.base.deref(comp);
8791150
880 // TODO actually look at the return type of the AST
881 const return_type = &Type.Void.get(comp).base;
882 defer return_type.base.deref(comp);
883
884 const is_var_args = false;
885 const params = ([*]Type.Fn.Param)(undefined)[0..0];
886 const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
1151 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
8871152 defer fn_type.base.base.deref(comp);
8881153
8891154 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
890 errdefer symbol_name.deinit();
1155 var symbol_name_consumed = false;
1156 errdefer if (!symbol_name_consumed) symbol_name.deinit();
8911157
8921158 // The Decl.Fn owns the initial 1 reference count
8931159 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
894 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
1160 fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
1161 symbol_name_consumed = true;
8951162
896 const unanalyzed_code = (await (async ir.gen(
897 comp,
898 body_node,
1163 const analyzed_code = try await (async comp.genAndAnalyzeCode(
8991164 &fndef_scope.base,
900 Span.token(body_node.lastToken()),
901 fn_decl.base.parsed_file,
902 ) catch unreachable)) catch |err| switch (err) {
903 // This poison value should not cause the errdefers to run. It simply means
904 // that self.compile_errors is populated.
905 // TODO https://github.com/ziglang/zig/issues/769
906 error.SemanticAnalysisFailed => return {},
907 else => return err,
908 };
909 defer unanalyzed_code.destroy(comp.gpa());
910
911 if (comp.verbose_ir) {
912 std.debug.warn("unanalyzed:\n");
913 unanalyzed_code.dump();
914 }
915
916 const analyzed_code = (await (async ir.analyze(
917 comp,
918 fn_decl.base.parsed_file,
919 unanalyzed_code,
920 null,
921 ) catch unreachable)) catch |err| switch (err) {
922 // This poison value should not cause the errdefers to run. It simply means
923 // that self.compile_errors is populated.
924 // TODO https://github.com/ziglang/zig/issues/769
925 error.SemanticAnalysisFailed => return {},
926 else => return err,
927 };
1165 body_node,
1166 fn_type.return_type,
1167 ) catch unreachable);
9281168 errdefer analyzed_code.destroy(comp.gpa());
9291169
930 if (comp.verbose_ir) {
931 std.debug.warn("analyzed:\n");
932 analyzed_code.dump();
933 }
934
9351170 // Kick off rendering to LLVM module, but it doesn't block the fn decl
9361171 // analysis from being complete.
9371172 try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
......@@ -953,3 +1188,54 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
9531188fn getZigDir(allocator: *mem.Allocator) ![]u8 {
9541189 return os.getAppDataDir(allocator, "zig");
9551190}
1191
1192async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
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,
1196 };
1197 const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
1198 return_type.base.deref(comp);
1199
1200 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
1201 var params_consumed = false;
1202 defer if (params_consumed) {
1203 for (params.toSliceConst()) |param| {
1204 param.typ.base.deref(comp);
1205 }
1206 params.deinit();
1207 };
1208
1209 const is_var_args = false;
1210 {
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 }
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);
1225
1226 return fn_type;
1227}
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;
1241}
src-self-hosted/decl.zig+8-6
......@@ -3,7 +3,6 @@ const Allocator = mem.Allocator;
33const mem = std.mem;
44const ast = std.zig.ast;
55const Visib = @import("visib.zig").Visib;
6const ParsedFile = @import("parsed_file.zig").ParsedFile;
76const event = std.event;
87const Value = @import("value.zig").Value;
98const Token = std.zig.Token;
......@@ -16,8 +15,6 @@ pub const Decl = struct {
1615 name: []const u8,
1716 visib: Visib,
1817 resolution: event.Future(Compilation.BuildError!void),
19 resolution_in_progress: u8,
20 parsed_file: *ParsedFile,
2118 parent_scope: *Scope,
2219
2320 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
......@@ -48,6 +45,10 @@ pub const Decl = struct {
4845 }
4946 }
5047
48 pub fn findRootScope(base: *const Decl) *Scope.Root {
49 return base.parent_scope.findRoot();
50 }
51
5152 pub const Id = enum {
5253 Var,
5354 Fn,
......@@ -61,12 +62,13 @@ pub const Decl = struct {
6162 pub const Fn = struct {
6263 base: Decl,
6364 value: Val,
64 fn_proto: *const ast.Node.FnProto,
65 fn_proto: *ast.Node.FnProto,
6566
6667 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
67 pub const Val = union {
68 pub const Val = union(enum) {
6869 Unresolved: void,
69 Ok: *Value.Fn,
70 Fn: *Value.Fn,
71 FnProto: *Value.FnProto,
7072 };
7173
7274 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;
44const Token = std.zig.Token;
55const ast = std.zig.ast;
66const TokenIndex = std.zig.ast.TokenIndex;
7const Compilation = @import("compilation.zig").Compilation;
8const Scope = @import("scope.zig").Scope;
79
810pub const Color = enum {
911 Auto,
......@@ -16,85 +18,220 @@ pub const Span = struct {
1618 last: ast.TokenIndex,
1719
1820 pub fn token(i: TokenIndex) Span {
19 return Span {
21 return Span{
2022 .first = i,
2123 .last = i,
2224 };
2325 }
26
27 pub fn node(n: *ast.Node) Span {
28 return Span{
29 .first = n.firstToken(),
30 .last = n.lastToken(),
31 };
32 }
2433};
2534
2635pub const Msg = struct {
27 path: []const u8,
28 text: []u8,
2936 span: Span,
30 tree: *ast.Tree,
31};
37 text: []u8,
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) {
69215 try stream.print(
70 "{}:{}:{}: error: {}\n",
71 msg.path,
216 "{}:{}:{}: error: {}\n{}\n",
217 path,
72218 start_loc.line + 1,
73219 start_loc.column + 1,
74220 msg.text,
221 tree.source[start_loc.line_start..start_loc.line_end],
75222 );
76 return;
223 try stream.writeByteNTimes(' ', start_loc.column);
224 try stream.writeByteNTimes('~', last_token.end - first_token.start);
225 try stream.write("\n");
77226 }
78227
79 try stream.print(
80 "{}:{}:{}: error: {}\n{}\n",
81 msg.path,
82 start_loc.line + 1,
83 start_loc.column + 1,
84 msg.text,
85 msg.tree.source[start_loc.line_start..start_loc.line_end],
86 );
87 try stream.writeByteNTimes(' ', start_loc.column);
88 try stream.writeByteNTimes('~', last_token.end - first_token.start);
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}
228 pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
229 const color_on = switch (color) {
230 Color.Auto => file.isTty(),
231 Color.On => true,
232 Color.Off => false,
233 };
234 var stream = &std.io.FileOutStream.init(file).stream;
235 return msg.printToStream(stream, color_on);
236 }
237};
src-self-hosted/ir.zig+1551-197
......@@ -8,10 +8,10 @@ const Value = @import("value.zig").Value;
88const Type = Value.Type;
99const assert = std.debug.assert;
1010const Token = std.zig.Token;
11const ParsedFile = @import("parsed_file.zig").ParsedFile;
1211const Span = @import("errmsg.zig").Span;
1312const llvm = @import("llvm.zig");
1413const ObjectFile = @import("codegen.zig").ObjectFile;
14const Decl = @import("decl.zig").Decl;
1515
1616pub const LVal = enum {
1717 None,
......@@ -31,10 +31,10 @@ pub const IrVal = union(enum) {
3131
3232 pub fn dump(self: IrVal) void {
3333 switch (self) {
34 IrVal.Unknown => typeof.dump(),
35 IrVal.KnownType => |typeof| {
34 IrVal.Unknown => std.debug.warn("Unknown"),
35 IrVal.KnownType => |typ| {
3636 std.debug.warn("KnownType(");
37 typeof.dump();
37 typ.dump();
3838 std.debug.warn(")");
3939 },
4040 IrVal.KnownValue => |value| {
......@@ -46,7 +46,7 @@ pub const IrVal = union(enum) {
4646 }
4747};
4848
49pub const Instruction = struct {
49pub const Inst = struct {
5050 id: Id,
5151 scope: *Scope,
5252 debug_id: usize,
......@@ -59,15 +59,15 @@ pub const Instruction = struct {
5959 is_generated: bool,
6060
6161 /// the instruction that is derived from this one in analysis
62 child: ?*Instruction,
62 child: ?*Inst,
6363
6464 /// the instruction that this one derives from in analysis
65 parent: ?*Instruction,
65 parent: ?*Inst,
6666
6767 /// populated durign codegen
6868 llvm_value: ?llvm.ValueRef,
6969
70 pub fn cast(base: *Instruction, comptime T: type) ?*T {
70 pub fn cast(base: *Inst, comptime T: type) ?*T {
7171 if (base.id == comptime typeToId(T)) {
7272 return @fieldParentPtr(T, "base", base);
7373 }
......@@ -77,18 +77,18 @@ pub const Instruction = struct {
7777 pub fn typeToId(comptime T: type) Id {
7878 comptime var i = 0;
7979 inline while (i < @memberCount(Id)) : (i += 1) {
80 if (T == @field(Instruction, @memberName(Id, i))) {
80 if (T == @field(Inst, @memberName(Id, i))) {
8181 return @field(Id, @memberName(Id, i));
8282 }
8383 }
8484 unreachable;
8585 }
8686
87 pub fn dump(base: *const Instruction) void {
87 pub fn dump(base: *const Inst) void {
8888 comptime var i = 0;
8989 inline while (i < @memberCount(Id)) : (i += 1) {
9090 if (base.id == @field(Id, @memberName(Id, i))) {
91 const T = @field(Instruction, @memberName(Id, i));
91 const T = @field(Inst, @memberName(Id, i));
9292 std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id));
9393 @fieldParentPtr(T, "base", base).dump();
9494 std.debug.warn(")");
......@@ -98,32 +98,40 @@ pub const Instruction = struct {
9898 unreachable;
9999 }
100100
101 pub fn hasSideEffects(base: *const Instruction) bool {
101 pub fn hasSideEffects(base: *const Inst) bool {
102102 comptime var i = 0;
103103 inline while (i < @memberCount(Id)) : (i += 1) {
104104 if (base.id == @field(Id, @memberName(Id, i))) {
105 const T = @field(Instruction, @memberName(Id, i));
105 const T = @field(Inst, @memberName(Id, i));
106106 return @fieldParentPtr(T, "base", base).hasSideEffects();
107107 }
108108 }
109109 unreachable;
110110 }
111111
112 pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction {
113 comptime var i = 0;
114 inline while (i < @memberCount(Id)) : (i += 1) {
115 if (base.id == @field(Id, @memberName(Id, i))) {
116 const T = @field(Instruction, @memberName(Id, i));
117 return @fieldParentPtr(T, "base", base).analyze(ira);
118 }
112 pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
113 switch (base.id) {
114 Id.Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
115 Id.Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
116 Id.Call => return @fieldParentPtr(Call, "base", base).analyze(ira),
117 Id.DeclRef => return await (async @fieldParentPtr(DeclRef, "base", base).analyze(ira) catch unreachable),
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),
119125 }
120 unreachable;
121126 }
122127
123 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) {
124129 switch (base.id) {
125130 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
126131 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,
127135 Id.Ref => @panic("TODO"),
128136 Id.DeclVar => @panic("TODO"),
129137 Id.CheckVoidStmt => @panic("TODO"),
......@@ -133,14 +141,22 @@ pub const Instruction = struct {
133141 }
134142 }
135143
136 fn ref(base: *Instruction, builder: *Builder) void {
144 fn ref(base: *Inst, builder: *Builder) void {
137145 base.ref_count += 1;
138146 if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) {
139 base.owner_bb.ref();
147 base.owner_bb.ref(builder);
140148 }
141149 }
142150
143 fn getAsParam(param: *Instruction) !*Instruction {
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;
144160 const child = param.child orelse return error.SemanticAnalysisFailed;
145161 switch (child.val) {
146162 IrVal.Unknown => return error.SemanticAnalysisFailed,
......@@ -148,32 +164,72 @@ pub const Instruction = struct {
148164 }
149165 }
150166
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
151207 /// asserts that the type is known
152 fn getKnownType(self: *Instruction) *Type {
208 fn getKnownType(self: *Inst) *Type {
153209 switch (self.val) {
154 IrVal.KnownType => |typeof| return typeof,
155 IrVal.KnownValue => |value| return value.typeof,
210 IrVal.KnownType => |typ| return typ,
211 IrVal.KnownValue => |value| return value.typ,
156212 IrVal.Unknown => unreachable,
157213 }
158214 }
159215
160 pub fn setGenerated(base: *Instruction) void {
216 pub fn setGenerated(base: *Inst) void {
161217 base.is_generated = true;
162218 }
163219
164 pub fn isNoReturn(base: *const Instruction) bool {
220 pub fn isNoReturn(base: *const Inst) bool {
165221 switch (base.val) {
166222 IrVal.Unknown => return false,
167 IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn,
168 IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn,
223 IrVal.KnownValue => |x| return x.typ.id == Type.Id.NoReturn,
224 IrVal.KnownType => |typ| return typ.id == Type.Id.NoReturn,
169225 }
170226 }
171227
172 pub fn isCompTime(base: *const Instruction) bool {
228 pub fn isCompTime(base: *const Inst) bool {
173229 return base.val == IrVal.KnownValue;
174230 }
175231
176 pub fn linkToParent(self: *Instruction, parent: *Instruction) void {
232 pub fn linkToParent(self: *Inst, parent: *Inst) void {
177233 assert(self.parent == null);
178234 assert(parent.child == null);
179235 self.parent = parent;
......@@ -189,10 +245,89 @@ pub const Instruction = struct {
189245 Phi,
190246 Br,
191247 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 }
192327 };
193328
194329 pub const Const = struct {
195 base: Instruction,
330 base: Inst,
196331 params: Params,
197332
198333 const Params = struct {};
......@@ -209,7 +344,7 @@ pub const Instruction = struct {
209344 return false;
210345 }
211346
212 pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction {
347 pub fn analyze(self: *const Const, ira: *Analyze) !*Inst {
213348 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
214349 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
215350 return new_inst;
......@@ -221,11 +356,11 @@ pub const Instruction = struct {
221356 };
222357
223358 pub const Return = struct {
224 base: Instruction,
359 base: Inst,
225360 params: Params,
226361
227362 const Params = struct {
228 return_value: *Instruction,
363 return_value: *Inst,
229364 };
230365
231366 const ir_val_init = IrVal.Init.NoReturn;
......@@ -238,7 +373,7 @@ pub const Instruction = struct {
238373 return true;
239374 }
240375
241 pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction {
376 pub fn analyze(self: *const Return, ira: *Analyze) !*Inst {
242377 const value = try self.params.return_value.getAsParam();
243378 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
244379
......@@ -247,25 +382,25 @@ pub const Instruction = struct {
247382 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
248383 }
249384
250 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 {
251386 const value = self.params.return_value.llvm_value;
252387 const return_type = self.params.return_value.getKnownType();
253388
254389 if (return_type.handleIsPtr()) {
255390 @panic("TODO");
256391 } else {
257 _ = llvm.BuildRet(ofile.builder, value);
392 _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory;
258393 }
259394 return null;
260395 }
261396 };
262397
263398 pub const Ref = struct {
264 base: Instruction,
399 base: Inst,
265400 params: Params,
266401
267402 const Params = struct {
268 target: *Instruction,
403 target: *Inst,
269404 mut: Type.Pointer.Mut,
270405 volatility: Type.Pointer.Vol,
271406 };
......@@ -278,7 +413,7 @@ pub const Instruction = struct {
278413 return false;
279414 }
280415
281 pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction {
416 pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
282417 const target = try self.params.target.getAsParam();
283418
284419 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
......@@ -287,7 +422,6 @@ pub const Instruction = struct {
287422 Value.Ptr.Mut.CompTimeConst,
288423 self.params.mut,
289424 self.params.volatility,
290 val.typeof.getAbiAlignment(ira.irb.comp),
291425 );
292426 }
293427
......@@ -297,14 +431,13 @@ pub const Instruction = struct {
297431 .volatility = self.params.volatility,
298432 });
299433 const elem_type = target.getKnownType();
300 const ptr_type = Type.Pointer.get(
301 ira.irb.comp,
302 elem_type,
303 self.params.mut,
304 self.params.volatility,
305 Type.Pointer.Size.One,
306 elem_type.getAbiAlignment(ira.irb.comp),
307 );
434 const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
435 .child_type = elem_type,
436 .mut = self.params.mut,
437 .vol = self.params.volatility,
438 .size = Type.Pointer.Size.One,
439 .alignment = Type.Pointer.Align.Abi,
440 }) catch unreachable);
308441 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this
309442 // could be a ref of a global, for example
310443 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
......@@ -313,8 +446,99 @@ pub const Instruction = struct {
313446 }
314447 };
315448
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
316540 pub const DeclVar = struct {
317 base: Instruction,
541 base: Inst,
318542 params: Params,
319543
320544 const Params = struct {
......@@ -329,39 +553,46 @@ pub const Instruction = struct {
329553 return true;
330554 }
331555
332 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction {
556 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Inst {
333557 return error.Unimplemented; // TODO
334558 }
335559 };
336560
337561 pub const CheckVoidStmt = struct {
338 base: Instruction,
562 base: Inst,
339563 params: Params,
340564
341565 const Params = struct {
342 target: *Instruction,
566 target: *Inst,
343567 };
344568
345569 const ir_val_init = IrVal.Init.Unknown;
346570
347 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 }
348574
349575 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
350576 return true;
351577 }
352578
353 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction {
354 return error.Unimplemented; // TODO
579 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst {
580 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);
355586 }
356587 };
357588
358589 pub const Phi = struct {
359 base: Instruction,
590 base: Inst,
360591 params: Params,
361592
362593 const Params = struct {
363594 incoming_blocks: []*BasicBlock,
364 incoming_values: []*Instruction,
595 incoming_values: []*Inst,
365596 };
366597
367598 const ir_val_init = IrVal.Init.Unknown;
......@@ -372,18 +603,18 @@ pub const Instruction = struct {
372603 return false;
373604 }
374605
375 pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction {
606 pub fn analyze(self: *const Phi, ira: *Analyze) !*Inst {
376607 return error.Unimplemented; // TODO
377608 }
378609 };
379610
380611 pub const Br = struct {
381 base: Instruction,
612 base: Inst,
382613 params: Params,
383614
384615 const Params = struct {
385616 dest_block: *BasicBlock,
386 is_comptime: *Instruction,
617 is_comptime: *Inst,
387618 };
388619
389620 const ir_val_init = IrVal.Init.NoReturn;
......@@ -394,17 +625,41 @@ pub const Instruction = struct {
394625 return true;
395626 }
396627
397 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 {
398653 return error.Unimplemented; // TODO
399654 }
400655 };
401656
402657 pub const AddImplicitReturnType = struct {
403 base: Instruction,
658 base: Inst,
404659 params: Params,
405660
406661 pub const Params = struct {
407 target: *Instruction,
662 target: *Inst,
408663 };
409664
410665 const ir_val_init = IrVal.Init.Unknown;
......@@ -417,12 +672,117 @@ pub const Instruction = struct {
417672 return true;
418673 }
419674
420 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction {
675 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Inst {
421676 const target = try self.params.target.getAsParam();
422677 try ira.src_implicit_return_type_list.append(target);
423678 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
424679 }
425680 };
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 };
426786};
427787
428788pub const Variable = struct {
......@@ -434,8 +794,8 @@ pub const BasicBlock = struct {
434794 name_hint: [*]const u8, // must be a C string literal
435795 debug_id: usize,
436796 scope: *Scope,
437 instruction_list: std.ArrayList(*Instruction),
438 ref_instruction: ?*Instruction,
797 instruction_list: std.ArrayList(*Inst),
798 ref_instruction: ?*Inst,
439799
440800 /// for codegen
441801 llvm_block: llvm.BasicBlockRef,
......@@ -447,7 +807,7 @@ pub const BasicBlock = struct {
447807 /// the basic block that this one derives from in analysis
448808 parent: ?*BasicBlock,
449809
450 pub fn ref(self: *BasicBlock) void {
810 pub fn ref(self: *BasicBlock, builder: *Builder) void {
451811 self.ref_count += 1;
452812 }
453813
......@@ -482,6 +842,33 @@ pub const Code = struct {
482842 }
483843 }
484844 }
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 }
485872};
486873
487874pub const Builder = struct {
......@@ -489,12 +876,14 @@ pub const Builder = struct {
489876 code: *Code,
490877 current_basic_block: *BasicBlock,
491878 next_debug_id: usize,
492 parsed_file: *ParsedFile,
879 root_scope: *Scope.Root,
493880 is_comptime: bool,
881 is_async: bool,
882 begin_scope: ?*Scope,
494883
495884 pub const Error = Analyze.Error;
496885
497 pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder {
886 pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder {
498887 const code = try comp.gpa().create(Code{
499888 .basic_block_list = undefined,
500889 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
......@@ -505,11 +894,13 @@ pub const Builder = struct {
505894
506895 return Builder{
507896 .comp = comp,
508 .parsed_file = parsed_file,
897 .root_scope = root_scope,
509898 .current_basic_block = undefined,
510899 .code = code,
511900 .next_debug_id = 0,
512901 .is_comptime = false,
902 .is_async = false,
903 .begin_scope = begin_scope,
513904 };
514905 }
515906
......@@ -529,7 +920,7 @@ pub const Builder = struct {
529920 .name_hint = name_hint,
530921 .debug_id = self.next_debug_id,
531922 .scope = scope,
532 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),
923 .instruction_list = std.ArrayList(*Inst).init(self.arena()),
533924 .child = null,
534925 .parent = null,
535926 .ref_instruction = null,
......@@ -549,69 +940,210 @@ pub const Builder = struct {
549940 self.current_basic_block = basic_block;
550941 }
551942
552 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 {
553944 switch (node.id) {
554945 ast.Node.Id.Root => unreachable,
555946 ast.Node.Id.Use => unreachable,
556947 ast.Node.Id.TestDecl => unreachable,
557 ast.Node.Id.VarDecl => @panic("TODO"),
558 ast.Node.Id.Defer => @panic("TODO"),
559 ast.Node.Id.InfixOp => @panic("TODO"),
560 ast.Node.Id.PrefixOp => @panic("TODO"),
561 ast.Node.Id.SuffixOp => @panic("TODO"),
562 ast.Node.Id.Switch => @panic("TODO"),
563 ast.Node.Id.While => @panic("TODO"),
564 ast.Node.Id.For => @panic("TODO"),
565 ast.Node.Id.If => @panic("TODO"),
566 ast.Node.Id.ControlFlowExpression => return error.Unimplemented,
567 ast.Node.Id.Suspend => @panic("TODO"),
568 ast.Node.Id.VarType => @panic("TODO"),
569 ast.Node.Id.ErrorType => @panic("TODO"),
570 ast.Node.Id.FnProto => @panic("TODO"),
571 ast.Node.Id.PromiseType => @panic("TODO"),
572 ast.Node.Id.IntegerLiteral => @panic("TODO"),
573 ast.Node.Id.FloatLiteral => @panic("TODO"),
574 ast.Node.Id.StringLiteral => @panic("TODO"),
575 ast.Node.Id.MultilineStringLiteral => @panic("TODO"),
576 ast.Node.Id.CharLiteral => @panic("TODO"),
577 ast.Node.Id.BoolLiteral => @panic("TODO"),
578 ast.Node.Id.NullLiteral => @panic("TODO"),
579 ast.Node.Id.UndefinedLiteral => @panic("TODO"),
580 ast.Node.Id.ThisLiteral => @panic("TODO"),
581 ast.Node.Id.Unreachable => @panic("TODO"),
582 ast.Node.Id.Identifier => @panic("TODO"),
948 ast.Node.Id.VarDecl => return error.Unimplemented,
949 ast.Node.Id.Defer => return error.Unimplemented,
950 ast.Node.Id.InfixOp => return error.Unimplemented,
951 ast.Node.Id.PrefixOp => {
952 const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node);
953 switch (prefix_op.op) {
954 ast.Node.PrefixOp.Op.AddressOf => return error.Unimplemented,
955 ast.Node.PrefixOp.Op.ArrayType => |n| return error.Unimplemented,
956 ast.Node.PrefixOp.Op.Await => return error.Unimplemented,
957 ast.Node.PrefixOp.Op.BitNot => return error.Unimplemented,
958 ast.Node.PrefixOp.Op.BoolNot => return error.Unimplemented,
959 ast.Node.PrefixOp.Op.Cancel => return error.Unimplemented,
960 ast.Node.PrefixOp.Op.OptionalType => return error.Unimplemented,
961 ast.Node.PrefixOp.Op.Negation => return error.Unimplemented,
962 ast.Node.PrefixOp.Op.NegationWrap => return error.Unimplemented,
963 ast.Node.PrefixOp.Op.Resume => return error.Unimplemented,
964 ast.Node.PrefixOp.Op.PtrType => |ptr_info| {
965 const inst = try await (async irb.genPtrType(prefix_op, ptr_info, scope) catch unreachable);
966 return irb.lvalWrap(scope, inst, lval);
967 },
968 ast.Node.PrefixOp.Op.SliceType => |ptr_info| return error.Unimplemented,
969 ast.Node.PrefixOp.Op.Try => return error.Unimplemented,
970 }
971 },
972 ast.Node.Id.SuffixOp => {
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 },
5831021 ast.Node.Id.GroupedExpression => {
5841022 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
585 return irb.genNode(grouped_expr.expr, scope, lval);
1023 return await (async irb.genNode(grouped_expr.expr, scope, lval) catch unreachable);
5861024 },
587 ast.Node.Id.BuiltinCall => @panic("TODO"),
588 ast.Node.Id.ErrorSetDecl => @panic("TODO"),
589 ast.Node.Id.ContainerDecl => @panic("TODO"),
590 ast.Node.Id.Asm => @panic("TODO"),
591 ast.Node.Id.Comptime => @panic("TODO"),
1025 ast.Node.Id.BuiltinCall => return error.Unimplemented,
1026 ast.Node.Id.ErrorSetDecl => return error.Unimplemented,
1027 ast.Node.Id.ContainerDecl => return error.Unimplemented,
1028 ast.Node.Id.Asm => return error.Unimplemented,
1029 ast.Node.Id.Comptime => return error.Unimplemented,
5921030 ast.Node.Id.Block => {
5931031 const block = @fieldParentPtr(ast.Node.Block, "base", node);
594 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);
5951034 },
596 ast.Node.Id.DocComment => @panic("TODO"),
597 ast.Node.Id.SwitchCase => @panic("TODO"),
598 ast.Node.Id.SwitchElse => @panic("TODO"),
599 ast.Node.Id.Else => @panic("TODO"),
600 ast.Node.Id.Payload => @panic("TODO"),
601 ast.Node.Id.PointerPayload => @panic("TODO"),
602 ast.Node.Id.PointerIndexPayload => @panic("TODO"),
603 ast.Node.Id.StructField => @panic("TODO"),
604 ast.Node.Id.UnionTag => @panic("TODO"),
605 ast.Node.Id.EnumTag => @panic("TODO"),
606 ast.Node.Id.ErrorTag => @panic("TODO"),
607 ast.Node.Id.AsmInput => @panic("TODO"),
608 ast.Node.Id.AsmOutput => @panic("TODO"),
609 ast.Node.Id.AsyncAttribute => @panic("TODO"),
610 ast.Node.Id.ParamDecl => @panic("TODO"),
611 ast.Node.Id.FieldInitializer => @panic("TODO"),
1035 ast.Node.Id.DocComment => return error.Unimplemented,
1036 ast.Node.Id.SwitchCase => return error.Unimplemented,
1037 ast.Node.Id.SwitchElse => return error.Unimplemented,
1038 ast.Node.Id.Else => return error.Unimplemented,
1039 ast.Node.Id.Payload => return error.Unimplemented,
1040 ast.Node.Id.PointerPayload => return error.Unimplemented,
1041 ast.Node.Id.PointerIndexPayload => return error.Unimplemented,
1042 ast.Node.Id.StructField => return error.Unimplemented,
1043 ast.Node.Id.UnionTag => return error.Unimplemented,
1044 ast.Node.Id.EnumTag => return error.Unimplemented,
1045 ast.Node.Id.ErrorTag => return error.Unimplemented,
1046 ast.Node.Id.AsmInput => return error.Unimplemented,
1047 ast.Node.Id.AsmOutput => return error.Unimplemented,
1048 ast.Node.Id.AsyncAttribute => return error.Unimplemented,
1049 ast.Node.Id.ParamDecl => return error.Unimplemented,
1050 ast.Node.Id.FieldInitializer => return error.Unimplemented,
6121051 }
6131052 }
6141053
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);
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 });
1145 }
1146
6151147 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
6161148 if (irb.is_comptime)
6171149 return true;
......@@ -622,15 +1154,105 @@ pub const Builder = struct {
6221154 Scope.Id.CompTime => return true,
6231155 Scope.Id.FnDef => return false,
6241156 Scope.Id.Decls => unreachable,
1157 Scope.Id.Root => unreachable,
6251158 Scope.Id.Block,
6261159 Scope.Id.Defer,
6271160 Scope.Id.DeferExpr,
628 => scope = scope.parent orelse return false,
1161 => scope = scope.parent.?,
6291162 }
6301163 }
6311164 }
6321165
633 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 {
6341256 const block_scope = try Scope.Block.create(irb.comp, parent_scope);
6351257
6361258 const outer_block_scope = &block_scope.base;
......@@ -648,7 +1270,7 @@ pub const Builder = struct {
6481270 }
6491271
6501272 if (block.label) |label| {
651 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());
1273 block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena());
6521274 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
6531275 block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd");
6541276 block_scope.is_comptime = try irb.buildConstBool(
......@@ -659,7 +1281,7 @@ pub const Builder = struct {
6591281 }
6601282
6611283 var is_continuation_unreachable = false;
662 var noreturn_return_value: ?*Instruction = null;
1284 var noreturn_return_value: ?*Inst = null;
6631285
6641286 var stmt_it = block.statements.iterator(0);
6651287 while (stmt_it.next()) |statement_node_ptr| {
......@@ -667,7 +1289,7 @@ pub const Builder = struct {
6671289
6681290 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
6691291 // defer starts a new scope
670 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);
6711293 const kind = switch (defer_token.id) {
6721294 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
6731295 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
......@@ -678,7 +1300,7 @@ pub const Builder = struct {
6781300 child_scope = &defer_child_scope.base;
6791301 continue;
6801302 }
681 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);
6821304
6831305 is_continuation_unreachable = statement_value.isNoReturn();
6841306 if (is_continuation_unreachable) {
......@@ -686,16 +1308,19 @@ pub const Builder = struct {
6861308 noreturn_return_value = statement_value;
6871309 }
6881310
689 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
1311 if (statement_value.cast(Inst.DeclVar)) |decl_var| {
6901312 // variable declarations start a new scope
6911313 child_scope = decl_var.params.variable.child_scope;
6921314 } else if (!is_continuation_unreachable) {
6931315 // this statement's value must be void
6941316 _ = irb.build(
695 Instruction.CheckVoidStmt,
1317 Inst.CheckVoidStmt,
6961318 child_scope,
697 statement_value.span,
698 Instruction.CheckVoidStmt.Params{ .target = statement_value },
1319 Span{
1320 .first = statement_node.firstToken(),
1321 .last = statement_node.lastToken(),
1322 },
1323 Inst.CheckVoidStmt.Params{ .target = statement_value },
6991324 );
7001325 }
7011326 }
......@@ -707,7 +1332,7 @@ pub const Builder = struct {
7071332 }
7081333
7091334 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
710 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{
7111336 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
7121337 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
7131338 });
......@@ -718,26 +1343,216 @@ pub const Builder = struct {
7181343 try block_scope.incoming_values.append(
7191344 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
7201345 );
721 _ = 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);
7221347
723 _ = 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{
7241349 .dest_block = block_scope.end_block,
7251350 .is_comptime = block_scope.is_comptime,
7261351 });
7271352
7281353 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
7291354
730 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{
7311356 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
7321357 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
7331358 });
7341359 }
7351360
736 _ = 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);
7371362 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
7381363 }
7391364
740 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(
7411556 irb: *Builder,
7421557 inner_scope: *Scope,
7431558 outer_scope: *Scope,
......@@ -755,25 +1570,26 @@ pub const Builder = struct {
7551570 };
7561571 if (generate) {
7571572 const defer_expr_scope = defer_scope.defer_expr_scope;
758 const instruction = try irb.genNode(
1573 const instruction = try await (async irb.genNode(
7591574 defer_expr_scope.expr_node,
7601575 &defer_expr_scope.base,
7611576 LVal.None,
762 );
1577 ) catch unreachable);
7631578 if (instruction.isNoReturn()) {
7641579 is_noreturn = true;
7651580 } else {
7661581 _ = try irb.build(
767 Instruction.CheckVoidStmt,
1582 Inst.CheckVoidStmt,
7681583 &defer_expr_scope.base,
7691584 Span.token(defer_expr_scope.expr_node.lastToken()),
770 Instruction.CheckVoidStmt.Params{ .target = instruction },
1585 Inst.CheckVoidStmt.Params{ .target = instruction },
7711586 );
7721587 }
7731588 }
7741589 },
7751590 Scope.Id.FnDef,
7761591 Scope.Id.Decls,
1592 Scope.Id.Root,
7771593 => return is_noreturn,
7781594
7791595 Scope.Id.CompTime,
......@@ -785,13 +1601,13 @@ pub const Builder = struct {
7851601 }
7861602 }
7871603
788 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 {
7891605 switch (lval) {
7901606 LVal.None => return instruction,
7911607 LVal.Ptr => {
7921608 // We needed a pointer to a value, but we got a value. So we create
7931609 // an instruction which just makes a const pointer of it.
794 return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{
1610 return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{
7951611 .target = instruction,
7961612 .mut = Type.Pointer.Mut.Const,
7971613 .volatility = Type.Pointer.Vol.Non,
......@@ -811,10 +1627,10 @@ pub const Builder = struct {
8111627 span: Span,
8121628 params: I.Params,
8131629 is_generated: bool,
814 ) !*Instruction {
1630 ) !*Inst {
8151631 const inst = try self.arena().create(I{
816 .base = Instruction{
817 .id = Instruction.typeToId(I),
1632 .base = Inst{
1633 .id = Inst.typeToId(I),
8181634 .is_generated = is_generated,
8191635 .scope = scope,
8201636 .debug_id = self.next_debug_id,
......@@ -838,9 +1654,27 @@ pub const Builder = struct {
8381654 inline while (i < @memberCount(I.Params)) : (i += 1) {
8391655 const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
8401656 switch (FieldType) {
841 *Instruction => @field(inst.params, @memberName(I.Params, i)).ref(self),
842 ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self),
843 else => {},
1657 *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self),
1658 *BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self),
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)),
8441678 }
8451679 }
8461680
......@@ -855,7 +1689,7 @@ pub const Builder = struct {
8551689 scope: *Scope,
8561690 span: Span,
8571691 params: I.Params,
858 ) !*Instruction {
1692 ) !*Inst {
8591693 return self.buildExtra(I, scope, span, params, false);
8601694 }
8611695
......@@ -865,21 +1699,95 @@ pub const Builder = struct {
8651699 scope: *Scope,
8661700 span: Span,
8671701 params: I.Params,
868 ) !*Instruction {
1702 ) !*Inst {
8691703 return self.buildExtra(I, scope, span, params, true);
8701704 }
8711705
872 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction {
873 const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{});
1706 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Inst {
1707 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
8741708 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base };
8751709 return inst;
8761710 }
8771711
878 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction {
879 const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated);
1712 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst {
1713 const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated);
8801714 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };
8811715 return inst;
8821716 }
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 }
8831791};
8841792
8851793const Analyze = struct {
......@@ -888,7 +1796,7 @@ const Analyze = struct {
8881796 const_predecessor_bb: ?*BasicBlock,
8891797 parent_basic_block: *BasicBlock,
8901798 instruction_index: usize,
891 src_implicit_return_type_list: std.ArrayList(*Instruction),
1799 src_implicit_return_type_list: std.ArrayList(*Inst),
8921800 explicit_return_type: ?*Type,
8931801
8941802 pub const Error = error{
......@@ -902,8 +1810,8 @@ const Analyze = struct {
9021810 OutOfMemory,
9031811 };
9041812
905 pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze {
906 var irb = try Builder.init(comp, parsed_file);
1813 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {
1814 var irb = try Builder.init(comp, root_scope, null);
9071815 errdefer irb.abort();
9081816
9091817 return Analyze{
......@@ -912,7 +1820,7 @@ const Analyze = struct {
9121820 .const_predecessor_bb = null,
9131821 .parent_basic_block = undefined, // initialized with startBasicBlock
9141822 .instruction_index = undefined, // initialized with startBasicBlock
915 .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()),
1823 .src_implicit_return_type_list = std.ArrayList(*Inst).init(irb.arena()),
9161824 .explicit_return_type = explicit_return_type,
9171825 };
9181826 }
......@@ -921,7 +1829,7 @@ const Analyze = struct {
9211829 self.irb.abort();
9221830 }
9231831
924 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 {
9251833 if (old_bb.child) |child| {
9261834 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
9271835 return child;
......@@ -981,21 +1889,478 @@ const Analyze = struct {
9811889 }
9821890
9831891 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
984 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);
9851893 }
9861894
987 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 {
9881896 // TODO actual implementation
9891897 return &Type.Void.get(self.irb.comp).base;
9901898 }
9911899
992 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 {
9931901 const dest_type = optional_dest_type orelse return target;
994 @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);
1905 }
1906
1907 fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst {
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;
9952360 }
9962361
997 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value {
998 @panic("TODO getCompTimeValOrNullUndefOk");
2362 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value {
2363 @panic("TODO");
9992364 }
10002365
10012366 fn getCompTimeRef(
......@@ -1004,9 +2369,8 @@ const Analyze = struct {
10042369 ptr_mut: Value.Ptr.Mut,
10052370 mut: Type.Pointer.Mut,
10062371 volatility: Type.Pointer.Vol,
1007 ptr_align: u32,
1008 ) Analyze.Error!*Instruction {
1009 @panic("TODO getCompTimeRef");
2372 ) Analyze.Error!*Inst {
2373 return error.Unimplemented;
10102374 }
10112375};
10122376
......@@ -1014,43 +2378,32 @@ pub async fn gen(
10142378 comp: *Compilation,
10152379 body_node: *ast.Node,
10162380 scope: *Scope,
1017 end_span: Span,
1018 parsed_file: *ParsedFile,
10192381) !*Code {
1020 var irb = try Builder.init(comp, parsed_file);
2382 var irb = try Builder.init(comp, scope.findRoot(), scope);
10212383 errdefer irb.abort();
10222384
10232385 const entry_block = try irb.createBasicBlock(scope, c"Entry");
1024 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.
10252387 try irb.setCursorAtEndAndAppendBlock(entry_block);
10262388
1027 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);
10282390 if (!result.isNoReturn()) {
1029 _ = irb.buildGen(
1030 Instruction.AddImplicitReturnType,
1031 scope,
1032 end_span,
1033 Instruction.AddImplicitReturnType.Params{ .target = result },
1034 );
1035 _ = irb.buildGen(
1036 Instruction.Return,
1037 scope,
1038 end_span,
1039 Instruction.Return.Params{ .return_value = result },
1040 );
2391 // no need for save_err_ret_addr because this cannot return error
2392 _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true);
10412393 }
10422394
10432395 return irb.finish();
10442396}
10452397
1046pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code {
1047 var ira = try Analyze.init(comp, parsed_file, expected_type);
1048 errdefer ira.abort();
1049
2398pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
10502399 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();
10512404
10522405 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
1053 new_entry_bb.ref();
2406 new_entry_bb.ref(&ira.irb);
10542407
10552408 ira.irb.current_basic_block = new_entry_bb;
10562409
......@@ -1064,7 +2417,8 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co
10642417 continue;
10652418 }
10662419
1067 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
10682422 return_inst.linkToParent(old_instruction);
10692423 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,
10702424 // 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+494-84
......@@ -1,8 +1,12 @@
11const std = @import("std");
2const mem = std.mem;
23const c = @import("c.zig");
34const builtin = @import("builtin");
45const ObjectFormat = builtin.ObjectFormat;
56const Compilation = @import("compilation.zig").Compilation;
7const Target = @import("target.zig").Target;
8const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
9const assert = std.debug.assert;
610
711const Context = struct {
812 comp: *Compilation,
......@@ -12,9 +16,12 @@ const Context = struct {
1216
1317 link_err: error{OutOfMemory}!void,
1418 link_msg: std.Buffer,
19
20 libc: *LibCInstallation,
21 out_file_path: std.Buffer,
1522};
1623
17pub fn link(comp: *Compilation) !void {
24pub async fn link(comp: *Compilation) !void {
1825 var ctx = Context{
1926 .comp = comp,
2027 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
......@@ -22,15 +29,45 @@ pub fn link(comp: *Compilation) !void {
2229 .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
2330 .link_err = {},
2431 .link_msg = undefined,
32 .libc = undefined,
33 .out_file_path = undefined,
2534 };
2635 defer ctx.arena.deinit();
2736 ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
2837 ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
2938
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
3056 // even though we're calling LLD as a library it thinks the first
3157 // argument is its own exe name
3258 try ctx.args.append(c"lld");
3359
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
3471 try constructLinkerArgs(&ctx);
3572
3673 if (comp.verbose_link) {
......@@ -43,6 +80,7 @@ pub fn link(comp: *Compilation) !void {
4380
4481 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
4582 const args_slice = ctx.args.toSlice();
83 // Not evented I/O. LLD does its own multithreading internally.
4684 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
4785 if (!ctx.link_msg.isNull()) {
4886 // TODO capture these messages and pass them through the system, reporting them through the
......@@ -95,10 +133,7 @@ fn constructLinkerArgs(ctx: *Context) !void {
95133}
96134
97135fn constructLinkerArgsElf(ctx: *Context) !void {
98 //if (g->libc_link_lib != nullptr) {
99 // find_libc_lib_path(g);
100 //}
101
136 // TODO commented out code in this function
102137 //if (g->linker_script) {
103138 // lj->args.append("-T");
104139 // lj->args.append(g->linker_script);
......@@ -107,7 +142,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
107142 //if (g->no_rosegment_workaround) {
108143 // lj->args.append("--no-rosegment");
109144 //}
110 //lj->args.append("--gc-sections");
145 try ctx.args.append(c"--gc-sections");
111146
112147 //lj->args.append("-m");
113148 //lj->args.append(getLDMOption(&g->zig_target));
......@@ -115,14 +150,13 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
115150 //bool is_lib = g->out_type == OutTypeLib;
116151 //bool shared = !g->is_static && is_lib;
117152 //Buf *soname = nullptr;
118 //if (g->is_static) {
119 // if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb ||
120 // g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb)
121 // {
122 // lj->args.append("-Bstatic");
123 // } else {
124 // lj->args.append("-static");
125 // }
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 }
126160 //} else if (shared) {
127161 // lj->args.append("-shared");
128162
......@@ -133,23 +167,16 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
133167 // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
134168 //}
135169
136 //lj->args.append("-o");
137 //lj->args.append(buf_ptr(&lj->out_file));
170 try ctx.args.append(c"-o");
171 try ctx.args.append(ctx.out_file_path.ptr());
138172
139 //if (lj->link_in_crt) {
140 // const char *crt1o;
141 // const char *crtbegino;
142 // if (g->is_static) {
143 // crt1o = "crt1.o";
144 // crtbegino = "crtbeginT.o";
145 // } else {
146 // crt1o = "Scrt1.o";
147 // crtbegino = "crtbegin.o";
148 // }
149 // lj->args.append(get_libc_file(g, crt1o));
150 // lj->args.append(get_libc_file(g, "crti.o"));
151 // lj->args.append(get_libc_static_file(g, crtbegino));
152 //}
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 }
153180
154181 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
155182 // Buf *rpath = g->rpath_list.at(i);
......@@ -182,25 +209,23 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
182209 // lj->args.append(lib_dir);
183210 //}
184211
185 //if (g->libc_link_lib != nullptr) {
186 // lj->args.append("-L");
187 // lj->args.append(buf_ptr(g->libc_lib_dir));
188
189 // lj->args.append("-L");
190 // lj->args.append(buf_ptr(g->libc_static_lib_dir));
191 //}
192
193 //if (!g->is_static) {
194 // if (g->dynamic_linker != nullptr) {
195 // assert(buf_len(g->dynamic_linker) != 0);
196 // lj->args.append("-dynamic-linker");
197 // lj->args.append(buf_ptr(g->dynamic_linker));
198 // } else {
199 // Buf *resolved_dynamic_linker = get_dynamic_linker_path(g);
200 // lj->args.append("-dynamic-linker");
201 // lj->args.append(buf_ptr(resolved_dynamic_linker));
202 // }
203 //}
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 }
204229
205230 //if (shared) {
206231 // lj->args.append("-soname");
......@@ -241,53 +266,356 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
241266 // lj->args.append(buf_ptr(arg));
242267 //}
243268
244 //// libc dep
245 //if (g->libc_link_lib != nullptr) {
246 // if (g->is_static) {
247 // lj->args.append("--start-group");
248 // lj->args.append("-lgcc");
249 // lj->args.append("-lgcc_eh");
250 // lj->args.append("-lc");
251 // lj->args.append("-lm");
252 // lj->args.append("--end-group");
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 // }
253430 // } else {
254 // lj->args.append("-lgcc");
255 // lj->args.append("--as-needed");
256 // lj->args.append("-lgcc_s");
257 // lj->args.append("--no-as-needed");
258 // lj->args.append("-lc");
259 // lj->args.append("-lm");
260 // lj->args.append("-lgcc");
261 // lj->args.append("--as-needed");
262 // lj->args.append("-lgcc_s");
263 // lj->args.append("--no-as-needed");
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));
264458 // }
265459 //}
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));
266489
267 //// crt end
268 //if (lj->link_in_crt) {
269 // lj->args.append(get_libc_static_file(g, "crtend.o"));
270 // lj->args.append(get_libc_file(g, "crtn.o"));
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 // }
271501 //}
272502
273 //if (!g->is_native_target) {
274 // lj->args.append("--allow-shlib-undefined");
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);
275533 //}
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 }
276563
277 //if (g->zig_target.os == OsZen) {
278 // lj->args.append("-e");
279 // lj->args.append("_start");
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 //}
280569
281 // lj->args.append("--image-base=0x10000000");
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));
282580 //}
283}
284581
285fn constructLinkerArgsCoff(ctx: *Context) void {
286 @panic("TODO");
287}
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 }
288604
289fn constructLinkerArgsMachO(ctx: *Context) void {
290 @panic("TODO");
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 //}
291619}
292620
293621fn constructLinkerArgsWasm(ctx: *Context) void {
......@@ -312,3 +640,85 @@ fn addFnObjects(ctx: *Context) !void {
312640 it = node.next;
313641 }
314642}
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+39-1
......@@ -23,13 +23,20 @@ pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
2323pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
2424pub const DIBuilder = c.ZigLLVMDIBuilder;
2525
26pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
2627pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
2728pub const AddFunction = c.LLVMAddFunction;
29pub const AddGlobal = c.LLVMAddGlobal;
2830pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
2931pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
32pub const ArrayType = c.LLVMArrayType;
3033pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
3134pub const ConstAllOnes = c.LLVMConstAllOnes;
35pub const ConstArray = c.LLVMConstArray;
36pub const ConstBitCast = c.LLVMConstBitCast;
3237pub const ConstInt = c.LLVMConstInt;
38pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
39pub const ConstNeg = c.LLVMConstNeg;
3340pub const ConstNull = c.LLVMConstNull;
3441pub const ConstStringInContext = c.LLVMConstStringInContext;
3542pub const ConstStructInContext = c.LLVMConstStructInContext;
......@@ -57,6 +64,7 @@ pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
5764pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
5865pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
5966pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
67pub const GetUndef = c.LLVMGetUndef;
6068pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
6169pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
6270pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
......@@ -79,14 +87,24 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
7987pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
8088pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
8189pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
90pub const PointerType = c.LLVMPointerType;
91pub const SetAlignment = c.LLVMSetAlignment;
8292pub const SetDataLayout = c.LLVMSetDataLayout;
93pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
94pub const SetInitializer = c.LLVMSetInitializer;
95pub const SetLinkage = c.LLVMSetLinkage;
8396pub const SetTarget = c.LLVMSetTarget;
97pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
8498pub const StructTypeInContext = c.LLVMStructTypeInContext;
8599pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
100pub const TypeOf = c.LLVMTypeOf;
86101pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
87102pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
88103pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
89104
105pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
106pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
107
90108pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
91109extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
92110
......@@ -143,13 +161,28 @@ pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
143161pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
144162pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
145163
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
146179fn removeNullability(comptime T: type) type {
147180 comptime assert(@typeId(T) == builtin.TypeId.Optional);
148181 return T.Child;
149182}
150183
151184pub const BuildRet = LLVMBuildRet;
152extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef;
185extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
153186
154187pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
155188extern fn ZigLLVMTargetMachineEmitToFile(
......@@ -161,3 +194,8 @@ extern fn ZigLLVMTargetMachineEmitToFile(
161194 is_debug: bool,
162195 is_small: bool,
163196) 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+82-57
......@@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
1818const Compilation = @import("compilation.zig").Compilation;
1919const Target = @import("target.zig").Target;
2020const errmsg = @import("errmsg.zig");
21const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2122
2223var stderr_file: os.File = undefined;
2324var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
......@@ -28,13 +29,14 @@ const usage =
2829 \\
2930 \\Commands:
3031 \\
31 \\ build-exe [source] Create executable from source or object files
32 \\ build-lib [source] Create library from source or object files
33 \\ build-obj [source] Create object from source or assembly
34 \\ fmt [source] Parse file and render in canonical zig format
35 \\ targets List available compilation targets
36 \\ version Print version number and exit
37 \\ zen Print zen of zig and exit
32 \\ build-exe [source] Create executable from source or object files
33 \\ build-lib [source] Create library from source or object files
34 \\ build-obj [source] Create object from source or assembly
35 \\ fmt [source] Parse file and render in canonical zig format
36 \\ libc [paths_file] Display native libc paths file or validate one
37 \\ targets List available compilation targets
38 \\ version Print version number and exit
39 \\ zen Print zen of zig and exit
3840 \\
3941 \\
4042;
......@@ -85,6 +87,10 @@ pub fn main() !void {
8587 .name = "fmt",
8688 .exec = cmdFmt,
8789 },
90 Command{
91 .name = "libc",
92 .exec = cmdLibC,
93 },
8894 Command{
8995 .name = "targets",
9096 .exec = cmdTargets,
......@@ -130,11 +136,10 @@ const usage_build_generic =
130136 \\ --color [auto|off|on] Enable or disable colored error messages
131137 \\
132138 \\Compile Options:
139 \\ --libc [file] Provide a file which specifies libc paths
133140 \\ --assembly [source] Add assembly file to build
134 \\ --cache-dir [path] Override the cache directory
135141 \\ --emit [filetype] Emit a specific file format as compilation output
136142 \\ --enable-timing-info Print timing diagnostics
137 \\ --libc-include-dir [path] Directory where libc stdlib.h resides
138143 \\ --name [name] Override output name
139144 \\ --output [file] Override destination path
140145 \\ --output-h [file] Override generated header file path
......@@ -163,12 +168,7 @@ const usage_build_generic =
163168 \\
164169 \\Link Options:
165170 \\ --ar-path [path] Set the path to ar
166 \\ --dynamic-linker [path] Set the path to ld.so
167171 \\ --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
172172 \\ --library [lib] Link against lib
173173 \\ --forbid-library [lib] Make it an error to link against lib
174174 \\ --library-path [dir] Add a directory to the library search path
......@@ -203,14 +203,13 @@ const args_build_generic = []Flag{
203203 }),
204204
205205 Flag.ArgMergeN("--assembly", 1),
206 Flag.Arg1("--cache-dir"),
207206 Flag.Option("--emit", []const []const u8{
208207 "asm",
209208 "bin",
210209 "llvm-ir",
211210 }),
212211 Flag.Bool("--enable-timing-info"),
213 Flag.Arg1("--libc-include-dir"),
212 Flag.Arg1("--libc"),
214213 Flag.Arg1("--name"),
215214 Flag.Arg1("--output"),
216215 Flag.Arg1("--output-h"),
......@@ -234,12 +233,7 @@ const args_build_generic = []Flag{
234233 Flag.Arg1("-mllvm"),
235234
236235 Flag.Arg1("--ar-path"),
237 Flag.Arg1("--dynamic-linker"),
238236 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"),
243237 Flag.ArgMergeN("--library", 1),
244238 Flag.ArgMergeN("--forbid-library", 1),
245239 Flag.ArgMergeN("--library-path", 1),
......@@ -377,16 +371,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
377371 os.exit(1);
378372 }
379373
380 const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
381 const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
382 try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
383 os.exit(1);
384 };
385 defer allocator.free(full_cache_dir);
386
387374 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
388375 defer allocator.free(zig_lib_dir);
389376
377 var override_libc: LibCInstallation = undefined;
378
390379 var loop: event.Loop = undefined;
391380 try loop.initMultiThreaded(allocator);
392381 defer loop.deinit();
......@@ -403,10 +392,18 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
403392 build_mode,
404393 is_static,
405394 zig_lib_dir,
406 full_cache_dir,
407395 );
408396 defer comp.destroy();
409397
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
410407 comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
411408 comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
412409 comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
......@@ -430,25 +427,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
430427
431428 comp.strip = flags.present("strip");
432429
433 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
434 comp.libc_lib_dir = libc_lib_dir;
435 }
436 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
437 comp.libc_static_lib_dir = libc_static_lib_dir;
438 }
439 if (flags.single("libc-include-dir")) |libc_include_dir| {
440 comp.libc_include_dir = libc_include_dir;
441 }
442 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
443 comp.msvc_lib_dir = msvc_lib_dir;
444 }
445 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
446 comp.kernel32_lib_dir = kernel32_lib_dir;
447 }
448 if (flags.single("dynamic-linker")) |dynamic_linker| {
449 comp.dynamic_linker = dynamic_linker;
450 }
451
452430 comp.verbose_tokenize = flags.present("verbose-tokenize");
453431 comp.verbose_ast_tree = flags.present("verbose-ast-tree");
454432 comp.verbose_ast_fmt = flags.present("verbose-ast-fmt");
......@@ -484,7 +462,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
484462
485463 comp.emit_file_type = emit_type;
486464 comp.assembly_files = assembly_files;
487 comp.link_out_file = flags.single("out-file");
465 comp.link_out_file = flags.single("output");
488466 comp.link_objects = link_objects;
489467
490468 try comp.build();
......@@ -499,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
499477
500478 switch (build_event) {
501479 Compilation.Event.Ok => {
502 std.debug.warn("Build succeeded\n");
503480 return;
504481 },
505482 Compilation.Event.Error => |err| {
......@@ -508,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
508485 },
509486 Compilation.Event.Fail => |msgs| {
510487 for (msgs) |msg| {
511 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
488 defer msg.destroy();
489 msg.printToFile(&stderr_file, color) catch os.exit(1);
512490 }
513491 },
514492 }
......@@ -579,6 +557,53 @@ const Fmt = struct {
579557 }
580558};
581559
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
582607fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
583608 var flags = try Args.parse(allocator, args_fmt_spec, args);
584609 defer flags.deinit();
......@@ -622,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
622647
623648 var error_it = tree.errors.iterator(0);
624649 while (error_it.next()) |parse_error| {
625 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
626 defer allocator.destroy(msg);
650 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
651 defer msg.destroy();
627652
628 try errmsg.printToFile(&stderr_file, msg, color);
653 try msg.printToFile(&stderr_file, color);
629654 }
630655 if (tree.errors.len != 0) {
631656 os.exit(1);
......@@ -678,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
678703
679704 var error_it = tree.errors.iterator(0);
680705 while (error_it.next()) |parse_error| {
681 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);
682 defer allocator.destroy(msg);
706 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
707 defer msg.destroy();
683708
684 try errmsg.printToFile(&stderr_file, msg, color);
709 try msg.printToFile(&stderr_file, color);
685710 }
686711 if (tree.errors.len != 0) {
687712 fmt.any_error = true;
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+100-25
......@@ -8,6 +8,8 @@ const ast = std.zig.ast;
88const Value = @import("value.zig").Value;
99const ir = @import("ir.zig");
1010const Span = @import("errmsg.zig").Span;
11const assert = std.debug.assert;
12const event = std.event;
1113
1214pub const Scope = struct {
1315 id: Id,
......@@ -23,7 +25,8 @@ pub const Scope = struct {
2325 if (base.ref_count == 0) {
2426 if (base.parent) |parent| parent.deref(comp);
2527 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),
2730 Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
2831 Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
2932 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
......@@ -33,6 +36,15 @@ pub const Scope = struct {
3336 }
3437 }
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
3648 pub fn findFnDef(base: *Scope) ?*FnDef {
3749 var scope = base;
3850 while (true) {
......@@ -44,12 +56,33 @@ pub const Scope = struct {
4456 Id.Defer,
4557 Id.DeferExpr,
4658 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,
4779 => scope = scope.parent orelse return null,
4880 }
4981 }
5082 }
5183
5284 pub const Id = enum {
85 Root,
5386 Decls,
5487 Block,
5588 FnDef,
......@@ -58,42 +91,82 @@ pub const Scope = struct {
5891 DeferExpr,
5992 };
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
61126 pub const Decls = struct {
62127 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
65138 /// 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 {
67140 const self = try comp.gpa().create(Decls{
68141 .base = Scope{
69142 .id = Id.Decls,
70143 .parent = parent,
71144 .ref_count = 1,
72145 },
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),
74148 });
75 errdefer comp.gpa().destroy(self);
76
77 self.table = Decl.Table.init(comp.gpa());
78 errdefer self.table.deinit();
79
80 if (parent) |p| p.ref();
81
149 parent.ref();
82150 return self;
83151 }
84152
85 pub fn destroy(self: *Decls) void {
153 pub fn destroy(self: *Decls, comp: *Compilation) void {
86154 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;
88161 }
89162 };
90163
91164 pub const Block = struct {
92165 base: Scope,
93 incoming_values: std.ArrayList(*ir.Instruction),
166 incoming_values: std.ArrayList(*ir.Inst),
94167 incoming_blocks: std.ArrayList(*ir.BasicBlock),
95168 end_block: *ir.BasicBlock,
96 is_comptime: *ir.Instruction,
169 is_comptime: *ir.Inst,
97170
98171 safety: Safety,
99172
......@@ -125,7 +198,7 @@ pub const Scope = struct {
125198 };
126199
127200 /// 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 {
129202 const self = try comp.gpa().create(Block{
130203 .base = Scope{
131204 .id = Id.Block,
......@@ -140,7 +213,7 @@ pub const Scope = struct {
140213 });
141214 errdefer comp.gpa().destroy(self);
142215
143 if (parent) |p| p.ref();
216 parent.ref();
144217 return self;
145218 }
146219
......@@ -157,7 +230,7 @@ pub const Scope = struct {
157230
158231 /// Creates a FnDef scope with 1 reference
159232 /// Must set the fn_val later
160 pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef {
233 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
161234 const self = try comp.gpa().create(FnDef{
162235 .base = Scope{
163236 .id = Id.FnDef,
......@@ -167,7 +240,7 @@ pub const Scope = struct {
167240 .fn_val = undefined,
168241 });
169242
170 if (parent) |p| p.ref();
243 parent.ref();
171244
172245 return self;
173246 }
......@@ -181,7 +254,7 @@ pub const Scope = struct {
181254 base: Scope,
182255
183256 /// 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 {
185258 const self = try comp.gpa().create(CompTime{
186259 .base = Scope{
187260 .id = Id.CompTime,
......@@ -190,7 +263,7 @@ pub const Scope = struct {
190263 },
191264 });
192265
193 if (parent) |p| p.ref();
266 parent.ref();
194267 return self;
195268 }
196269
......@@ -212,7 +285,7 @@ pub const Scope = struct {
212285 /// Creates a Defer scope with 1 reference
213286 pub fn create(
214287 comp: *Compilation,
215 parent: ?*Scope,
288 parent: *Scope,
216289 kind: Kind,
217290 defer_expr_scope: *DeferExpr,
218291 ) !*Defer {
......@@ -229,7 +302,7 @@ pub const Scope = struct {
229302
230303 defer_expr_scope.base.ref();
231304
232 if (parent) |p| p.ref();
305 parent.ref();
233306 return self;
234307 }
235308
......@@ -242,9 +315,10 @@ pub const Scope = struct {
242315 pub const DeferExpr = struct {
243316 base: Scope,
244317 expr_node: *ast.Node,
318 reported_err: bool,
245319
246320 /// 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 {
248322 const self = try comp.gpa().create(DeferExpr{
249323 .base = Scope{
250324 .id = Id.DeferExpr,
......@@ -252,10 +326,11 @@ pub const Scope = struct {
252326 .ref_count = 1,
253327 },
254328 .expr_node = expr_node,
329 .reported_err = false,
255330 });
256331 errdefer comp.gpa().destroy(self);
257332
258 if (parent) |p| p.ref();
333 parent.ref();
259334 return self;
260335 }
261336
src-self-hosted/target.zig+445-1
......@@ -1,6 +1,13 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const llvm = @import("llvm.zig");
4const CInt = @import("c_int.zig").CInt;
5
6pub const FloatAbi = enum {
7 Hard,
8 Soft,
9 SoftFp,
10};
411
512pub const Target = union(enum) {
613 Native,
......@@ -13,7 +20,7 @@ pub const Target = union(enum) {
1320 object_format: builtin.ObjectFormat,
1421 };
1522
16 pub fn oFileExt(self: Target) []const u8 {
23 pub fn objFileExt(self: Target) []const u8 {
1724 return switch (self.getObjectFormat()) {
1825 builtin.ObjectFormat.coff => ".obj",
1926 else => ".o",
......@@ -27,6 +34,13 @@ pub const Target = union(enum) {
2734 };
2835 }
2936
37 pub fn libFileExt(self: Target, is_static: bool) []const u8 {
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
3044 pub fn getOs(self: Target) builtin.Os {
3145 return switch (self) {
3246 Target.Native => builtin.os,
......@@ -76,6 +90,56 @@ pub const Target = union(enum) {
7690 };
7791 }
7892
93 /// TODO expose the arch and subarch separately
94 pub fn isArmOrThumb(self: Target) bool {
95 return switch (self.getArch()) {
96 builtin.Arch.armv8_3a,
97 builtin.Arch.armv8_2a,
98 builtin.Arch.armv8_1a,
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
79143 pub fn initializeAll() void {
80144 llvm.InitializeAllTargets();
81145 llvm.InitializeAllTargetInfos();
......@@ -106,6 +170,257 @@ pub const Target = union(enum) {
106170 return result;
107171 }
108172
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
109424 pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
110425 var result: llvm.TargetRef = undefined;
111426 var err_msg: [*]u8 = undefined;
......@@ -115,4 +430,133 @@ pub const Target = union(enum) {
115430 }
116431 return result;
117432 }
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 }
118562};
src-self-hosted/test.zig+88-14
......@@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic;
88const errmsg = @import("errmsg.zig");
99const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
1010
11test "compile errors" {
12 var ctx: TestContext = undefined;
11var ctx: TestContext = undefined;
12
13test "stage2" {
1314 try ctx.init();
1415 defer ctx.deinit();
1516
1617 try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
18 try @import("../test/stage2/compare_output.zig").addCases(&ctx);
1719
1820 try ctx.run();
1921}
......@@ -25,7 +27,6 @@ pub const TestContext = struct {
2527 loop: std.event.Loop,
2628 event_loop_local: EventLoopLocal,
2729 zig_lib_dir: []u8,
28 zig_cache_dir: []u8,
2930 file_index: std.atomic.Int(usize),
3031 group: std.event.Group(error!void),
3132 any_err: error!void,
......@@ -38,7 +39,6 @@ pub const TestContext = struct {
3839 .loop = undefined,
3940 .event_loop_local = undefined,
4041 .zig_lib_dir = undefined,
41 .zig_cache_dir = undefined,
4242 .group = undefined,
4343 .file_index = std.atomic.Int(usize).init(0),
4444 };
......@@ -55,16 +55,12 @@ pub const TestContext = struct {
5555 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
5656 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
6158 try std.os.makePath(allocator, tmp_dir_name);
6259 errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
6360 }
6461
6562 fn deinit(self: *TestContext) void {
6663 std.os.deleteTree(allocator, tmp_dir_name) catch {};
67 allocator.free(self.zig_cache_dir);
6864 allocator.free(self.zig_lib_dir);
6965 self.event_loop_local.deinit();
7066 self.loop.deinit();
......@@ -109,7 +105,6 @@ pub const TestContext = struct {
109105 builtin.Mode.Debug,
110106 true, // is_static
111107 self.zig_lib_dir,
112 self.zig_cache_dir,
113108 );
114109 errdefer comp.destroy();
115110
......@@ -118,6 +113,84 @@ pub const TestContext = struct {
118113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
119114 }
120115
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
121194 async fn getModuleEvent(
122195 comp: *Compilation,
123196 source: []const u8,
......@@ -139,10 +212,10 @@ pub const TestContext = struct {
139212 Compilation.Event.Fail => |msgs| {
140213 assertOrPanic(msgs.len != 0);
141214 for (msgs) |msg| {
142 if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {
143 const first_token = msg.tree.tokens.at(msg.span.first);
144 const last_token = msg.tree.tokens.at(msg.span.first);
145 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);
217 const last_token = msg.getTree().tokens.at(msg.span.first);
218 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
146219 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
147220 return;
148221 }
......@@ -159,7 +232,8 @@ pub const TestContext = struct {
159232 std.debug.warn("\n====found:========\n");
160233 var stderr = try std.io.getStdErr();
161234 for (msgs) |msg| {
162 try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
235 defer msg.destroy();
236 try msg.printToFile(&stderr, errmsg.Color.Auto);
163237 }
164238 std.debug.warn("============\n");
165239 return error.TestFailed;
src-self-hosted/type.zig+392-65
......@@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope;
44const Compilation = @import("compilation.zig").Compilation;
55const Value = @import("value.zig").Value;
66const 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
911pub const Type = struct {
1012 base: Value,
1113 id: Id,
14 name: []const u8,
15 abi_alignment: AbiAlignment,
16
17 pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
1218
1319 pub const Id = builtin.TypeId;
1420
......@@ -42,33 +48,37 @@ pub const Type = struct {
4248 }
4349 }
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) {
4656 switch (base.id) {
47 Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile),
48 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile),
57 Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
58 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
4959 Id.Type => unreachable,
5060 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),
5262 Id.NoReturn => unreachable,
53 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile),
54 Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile),
55 Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile),
56 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile),
63 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
64 Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
65 Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
66 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
5767 Id.ComptimeFloat => unreachable,
5868 Id.ComptimeInt => unreachable,
5969 Id.Undefined => unreachable,
6070 Id.Null => unreachable,
61 Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile),
62 Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile),
63 Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile),
64 Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile),
65 Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile),
71 Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
72 Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
73 Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
74 Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
75 Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
6676 Id.Namespace => unreachable,
6777 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),
6979 Id.ArgTuple => unreachable,
70 Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile),
71 Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile),
80 Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
81 Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
7282 }
7383 }
7484
......@@ -151,8 +161,49 @@ pub const Type = struct {
151161 std.debug.warn("{}", @tagName(base.id));
152162 }
153163
154 pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 {
155 @panic("TODO getAbiAlignment");
164 fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
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));
156207 }
157208
158209 pub const Struct = struct {
......@@ -163,7 +214,7 @@ pub const Type = struct {
163214 comp.gpa().destroy(self);
164215 }
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 {
167218 @panic("TODO");
168219 }
169220 };
......@@ -176,28 +227,23 @@ pub const Type = struct {
176227
177228 pub const Param = struct {
178229 is_noalias: bool,
179 typeof: *Type,
230 typ: *Type,
180231 };
181232
182233 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
183234 const result = try comp.gpa().create(Fn{
184 .base = Type{
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 },
235 .base = undefined,
192236 .return_type = return_type,
193237 .params = params,
194238 .is_var_args = is_var_args,
195239 });
196240 errdefer comp.gpa().destroy(result);
197241
242 result.base.init(comp, Id.Fn, "TODO fn type name");
243
198244 result.return_type.base.ref();
199245 for (result.params) |param| {
200 param.typeof.base.ref();
246 param.typ.base.ref();
201247 }
202248 return result;
203249 }
......@@ -205,20 +251,20 @@ pub const Type = struct {
205251 pub fn destroy(self: *Fn, comp: *Compilation) void {
206252 self.return_type.base.deref(comp);
207253 for (self.params) |param| {
208 param.typeof.base.deref(comp);
254 param.typ.base.deref(comp);
209255 }
210256 comp.gpa().destroy(self);
211257 }
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 {
214260 const llvm_return_type = switch (self.return_type.id) {
215 Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,
216 else => try self.return_type.getLlvmType(ofile),
261 Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
262 else => try self.return_type.getLlvmType(allocator, llvm_context),
217263 };
218 const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len);
219 defer ofile.gpa().free(llvm_param_types);
264 const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
265 defer allocator.free(llvm_param_types);
220266 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);
222268 }
223269
224270 return llvm.FunctionType(
......@@ -272,7 +318,7 @@ pub const Type = struct {
272318 comp.gpa().destroy(self);
273319 }
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 {
276322 @panic("TODO");
277323 }
278324 };
......@@ -293,13 +339,83 @@ pub const Type = struct {
293339
294340 pub const Int = struct {
295341 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
297397 pub fn destroy(self: *Int, comp: *Compilation) void {
398 self.garbage_node = std.atomic.Stack(*Int).Node{
399 .data = self,
400 .next = undefined,
401 };
402 comp.registerGarbage(Int, &self.garbage_node);
403 }
404
405 pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
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);
298414 comp.gpa().destroy(self);
299415 }
300416
301 pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef {
302 @panic("TODO");
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;
303419 }
304420 };
305421
......@@ -310,56 +426,236 @@ pub const Type = struct {
310426 comp.gpa().destroy(self);
311427 }
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 {
314430 @panic("TODO");
315431 }
316432 };
317433 pub const Pointer = struct {
318434 base: Type,
319 mut: Mut,
320 vol: Vol,
321 size: Size,
322 alignment: u32,
435 key: Key,
436 garbage_node: std.atomic.Stack(*Pointer).Node,
437
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
324473 pub const Mut = enum {
325474 Mut,
326475 Const,
327476 };
477
328478 pub const Vol = enum {
329479 Non,
330480 Volatile,
331481 };
482
483 pub const Align = union(enum) {
484 Abi,
485 Override: u32,
486 };
487
332488 pub const Size = builtin.TypeInfo.Pointer.Size;
333489
334490 pub fn destroy(self: *Pointer, comp: *Compilation) void {
491 self.garbage_node = std.atomic.Stack(*Pointer).Node{
492 .data = self,
493 .next = undefined,
494 };
495 comp.registerGarbage(Pointer, &self.garbage_node);
496 }
497
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);
335506 comp.gpa().destroy(self);
336507 }
337508
338 pub fn get(
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(
339517 comp: *Compilation,
340 elem_type: *Type,
341 mut: Mut,
342 vol: Vol,
343 size: Size,
344 alignment: u32,
345 ) *Pointer {
346 @panic("TODO get pointer");
518 key: Key,
519 ) !*Pointer {
520 var normal_key = key;
521 switch (key.alignment) {
522 Align.Abi => {},
523 Align.Override => |alignment| {
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;
347590 }
348591
349 pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef {
350 @panic("TODO");
592 pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
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;
351595 }
352596 };
353597
354598 pub const Array = struct {
355599 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
357616 pub fn destroy(self: *Array, comp: *Compilation) void {
617 self.key.elem_type.base.deref(comp);
358618 comp.gpa().destroy(self);
359619 }
360620
361 pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {
362 @panic("TODO");
621 pub async fn get(comp: *Compilation, key: Key) !*Array {
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;
363659 }
364660 };
365661
......@@ -374,6 +670,12 @@ pub const Type = struct {
374670 pub const ComptimeInt = struct {
375671 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
377679 pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
378680 comp.gpa().destroy(self);
379681 }
......@@ -402,7 +704,7 @@ pub const Type = struct {
402704 comp.gpa().destroy(self);
403705 }
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 {
406708 @panic("TODO");
407709 }
408710 };
......@@ -414,7 +716,7 @@ pub const Type = struct {
414716 comp.gpa().destroy(self);
415717 }
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 {
418720 @panic("TODO");
419721 }
420722 };
......@@ -426,7 +728,7 @@ pub const Type = struct {
426728 comp.gpa().destroy(self);
427729 }
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 {
430732 @panic("TODO");
431733 }
432734 };
......@@ -438,7 +740,7 @@ pub const Type = struct {
438740 comp.gpa().destroy(self);
439741 }
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 {
442744 @panic("TODO");
443745 }
444746 };
......@@ -450,7 +752,7 @@ pub const Type = struct {
450752 comp.gpa().destroy(self);
451753 }
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 {
454756 @panic("TODO");
455757 }
456758 };
......@@ -478,7 +780,7 @@ pub const Type = struct {
478780 comp.gpa().destroy(self);
479781 }
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 {
482784 @panic("TODO");
483785 }
484786 };
......@@ -498,7 +800,7 @@ pub const Type = struct {
498800 comp.gpa().destroy(self);
499801 }
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 {
502804 @panic("TODO");
503805 }
504806 };
......@@ -510,8 +812,33 @@ pub const Type = struct {
510812 comp.gpa().destroy(self);
511813 }
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 {
514816 @panic("TODO");
515817 }
516818 };
517819};
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+368-4
......@@ -5,12 +5,13 @@ const Compilation = @import("compilation.zig").Compilation;
55const ObjectFile = @import("codegen.zig").ObjectFile;
66const llvm = @import("llvm.zig");
77const Buffer = std.Buffer;
8const assert = std.debug.assert;
89
910/// Values are ref-counted, heap-allocated, and copy-on-write
1011/// If there is only 1 ref then write need not copy
1112pub const Value = struct {
1213 id: Id,
13 typeof: *Type,
14 typ: *Type,
1415 ref_count: std.atomic.Int(usize),
1516
1617 /// Thread-safe
......@@ -21,23 +22,37 @@ pub const Value = struct {
2122 /// Thread-safe
2223 pub fn deref(base: *Value, comp: *Compilation) void {
2324 if (base.ref_count.decr() == 1) {
24 base.typeof.base.deref(comp);
25 base.typ.base.deref(comp);
2526 switch (base.id) {
2627 Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
2728 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
29 Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
2830 Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
2931 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
3032 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
3133 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),
3236 }
3337 }
3438 }
3539
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
3646 pub fn getRef(base: *Value) *Value {
3747 base.ref();
3848 return base;
3949 }
4050
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
4156 pub fn dump(base: *const Value) void {
4257 std.debug.warn("{}", @tagName(base.id));
4358 }
......@@ -46,24 +61,111 @@ pub const Value = struct {
4661 switch (base.id) {
4762 Id.Type => unreachable,
4863 Id.Fn => @panic("TODO"),
64 Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
4965 Id.Void => return null,
5066 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
5167 Id.NoReturn => unreachable,
52 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,
5395 }
5496 }
5597
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
56116 pub const Id = enum {
57117 Type,
58118 Fn,
59119 Void,
60120 Bool,
61121 NoReturn,
122 Array,
62123 Ptr,
124 Int,
125 FnProto,
63126 };
64127
65128 pub const Type = @import("type.zig").Type;
66129
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
67169 pub const Fn = struct {
68170 base: Value,
69171
......@@ -98,7 +200,7 @@ pub const Value = struct {
98200 const self = try comp.gpa().create(Fn{
99201 .base = Value{
100202 .id = Value.Id.Fn,
101 .typeof = &fn_type.base,
203 .typ = &fn_type.base,
102204 .ref_count = std.atomic.Int(usize).init(1),
103205 },
104206 .fndef_scope = fndef_scope,
......@@ -187,6 +289,8 @@ pub const Value = struct {
187289
188290 pub const Ptr = struct {
189291 base: Value,
292 special: Special,
293 mut: Mut,
190294
191295 pub const Mut = enum {
192296 CompTimeConst,
......@@ -194,8 +298,268 @@ pub const Value = struct {
194298 RunTime,
195299 };
196300
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
197360 pub fn destroy(self: *Ptr, comp: *Compilation) void {
198361 comp.gpa().destroy(self);
199362 }
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);
563 }
200564 };
201565};
src/analyze.cpp+3-4
......@@ -4379,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
43794379
43804380static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
43814381 if (g->win_sdk == nullptr) {
4382 if (os_find_windows_sdk(&g->win_sdk)) {
4382 if (zig_find_windows_sdk(&g->win_sdk)) {
43834383 fprintf(stderr, "unable to determine windows sdk path\n");
43844384 exit(1);
43854385 }
......@@ -4499,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) {
44994499 ZigWindowsSDK *sdk = get_windows_sdk(g);
45004500
45014501 if (g->msvc_lib_dir == nullptr) {
4502 Buf* vc_lib_dir = buf_alloc();
4503 if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
4502 if (sdk->msvc_lib_dir_ptr == nullptr) {
45044503 fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
45054504 exit(1);
45064505 }
4507 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);
45084507 }
45094508
45104509 if (g->libc_lib_dir == nullptr) {
src/link.cpp+1-1
......@@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
901901 if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {
902902 Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
903903 lj->args.append(buf_ptr(arg));
904 } else {
904 } else {
905905 lj->args.append(buf_ptr(link_lib->name));
906906 }
907907 }
src/os.cpp+4-244
......@@ -26,7 +26,6 @@
2626#include <windows.h>
2727#include <io.h>
2828#include <fcntl.h>
29#include "windows_com.hpp"
3029
3130typedef SSIZE_T ssize_t;
3231#else
......@@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {
11151114#endif
11161115}
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
13571117int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
13581118#if defined(ZIG_OS_WINDOWS)
13591119 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);
13611121 switch (platform_type) {
13621122 case ZigLLVM_x86:
13631123 buf_append_str(output_buf, "x86\\");
......@@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
13891149int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
13901150#if defined(ZIG_OS_WINDOWS)
13911151 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);
13931153 if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
13941154 return 0;
13951155 }
......@@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
14061166#if defined(ZIG_OS_WINDOWS)
14071167 {
14081168 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);
14101170 switch (platform_type) {
14111171 case ZigLLVM_x86:
14121172 buf_append_str(output_buf, "x86\\");
......@@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
14291189 }
14301190 {
14311191 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);
14331193 switch (platform_type) {
14341194 case ZigLLVM_x86:
14351195 buf_append_str(output_buf, "x86\\");
src/os.hpp+1-9
......@@ -12,6 +12,7 @@
1212#include "buffer.hpp"
1313#include "error.hpp"
1414#include "zig_llvm.h"
15#include "windows_sdk.h"
1516
1617#include <stdio.h>
1718#include <inttypes.h>
......@@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);
7980
8081int 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);
9183int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
9284int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
9385int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
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
std/event/group.zig+14-2
......@@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
66const AtomicOrder = builtin.AtomicOrder;
77const assert = std.debug.assert;
88
9/// ReturnType should be `void` or `E!void`
9/// ReturnType must be `void` or `E!void`
1010pub fn Group(comptime ReturnType: type) type {
1111 return struct {
1212 coro_stack: Stack,
......@@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type {
3838 self.alloc_stack.push(node);
3939 }
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
4150 /// 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.
4352 /// Thread-safe.
4453 pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
4554 const S = struct {
......@@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {
6776
6877 /// Wait for all the calls and promises of the group to complete.
6978 /// Thread-safe.
79 /// Safe to call any number of times.
7080 pub async fn wait(self: *Self) ReturnType {
7181 // TODO catch unreachable because the allocation can be grouped with
7282 // the coro frame allocation
......@@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type {
98108 }
99109
100110 /// 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
101113 pub fn cancelAll(self: *Self) void {
102114 while (self.coro_stack.pop()) |node| {
103115 cancel node.data;
std/event/loop.zig+1-1
......@@ -444,7 +444,7 @@ pub const Loop = struct {
444444 .next = undefined,
445445 .data = p,
446446 };
447 loop.onNextTick(&my_tick_node);
447 self.onNextTick(&my_tick_node);
448448 }
449449 }
450450
std/fmt/index.zig+6-2
......@@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
785785 return buf[0 .. buf.len - context.remaining.len];
786786}
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 {
789791 var size: usize = 0;
790792 format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
791793 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 };
793797}
794798
795799fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
std/macho.zig+1-1
......@@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
141141 }
142142
143143 // 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
146146 // Insert the sentinel. Since we don't know where the last function ends,
147147 // we arbitrarily limit it to the start address + 4 KB.
std/math/big/int.zig+18-1
......@@ -60,8 +60,9 @@ pub const Int = struct {
6060 self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
6161 }
6262
63 pub fn deinit(self: Int) void {
63 pub fn deinit(self: *Int) void {
6464 self.allocator.free(self.limbs);
65 self.* = undefined;
6566 }
6667
6768 pub fn clone(other: Int) !Int {
......@@ -332,6 +333,7 @@ pub const Int = struct {
332333 self.positive = positive;
333334 }
334335
336 /// TODO make this call format instead of the other way around
335337 pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
336338 if (base < 2 or base > 16) {
337339 return error.InvalidBase;
......@@ -414,6 +416,21 @@ pub const Int = struct {
414416 return s;
415417 }
416418
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
417434 // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
418435 pub fn cmpAbs(a: Int, b: Int) i8 {
419436 if (a.len < b.len) {
std/mem.zig+10-2
......@@ -35,6 +35,7 @@ pub const Allocator = struct {
3535 freeFn: fn (self: *Allocator, old_mem: []u8) void,
3636
3737 /// Call `destroy` with the result
38 /// TODO this is deprecated. use createOne instead
3839 pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {
3940 const T = @typeOf(init);
4041 if (@sizeOf(T) == 0) return &(T{});
......@@ -44,6 +45,14 @@ pub const Allocator = struct {
4445 return ptr;
4546 }
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
4756 /// `ptr` should be the return value of `create`
4857 pub fn destroy(self: *Allocator, ptr: var) void {
4958 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
......@@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
149158 @setRuntimeSafety(false);
150159 assert(dest.len >= source.len);
151160 var i = source.len;
152 while(i > 0){
161 while (i > 0) {
153162 i -= 1;
154163 dest[i] = source[i];
155164 }
156165}
157166
158
159167pub fn set(comptime T: type, dest: []T, value: T) void {
160168 for (dest) |*d|
161169 d.* = value;
std/os/file.zig+23-24
......@@ -109,43 +109,42 @@ pub const File = struct {
109109 Unexpected,
110110 };
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 {
113113 const path_with_null = try std.cstr.addNullByte(allocator, path);
114114 defer allocator.free(path_with_null);
115115
116116 if (is_posix) {
117 // mode is ignored and is always F_OK for now
118117 const result = posix.access(path_with_null.ptr, posix.F_OK);
119118 const err = posix.getErrno(result);
120 if (err > 0) {
121 return switch (err) {
122 posix.EACCES => error.PermissionDenied,
123 posix.EROFS => error.PermissionDenied,
124 posix.ELOOP => error.PermissionDenied,
125 posix.ETXTBSY => error.PermissionDenied,
126 posix.ENOTDIR => error.NotFound,
127 posix.ENOENT => error.NotFound,
119 switch (err) {
120 0 => return,
121 posix.EACCES => return error.PermissionDenied,
122 posix.EROFS => return error.PermissionDenied,
123 posix.ELOOP => return error.PermissionDenied,
124 posix.ETXTBSY => return error.PermissionDenied,
125 posix.ENOTDIR => return error.NotFound,
126 posix.ENOENT => return error.NotFound,
128127
129 posix.ENAMETOOLONG => error.NameTooLong,
130 posix.EINVAL => error.BadMode,
131 posix.EFAULT => error.BadPathName,
132 posix.EIO => error.Io,
133 posix.ENOMEM => error.SystemResources,
134 else => os.unexpectedErrorPosix(err),
135 };
128 posix.ENAMETOOLONG => return error.NameTooLong,
129 posix.EINVAL => unreachable,
130 posix.EFAULT => return error.BadPathName,
131 posix.EIO => return error.Io,
132 posix.ENOMEM => return error.SystemResources,
133 else => return os.unexpectedErrorPosix(err),
136134 }
137 return true;
138135 } else if (is_windows) {
139136 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
140 return true;
137 return;
141138 }
142139
143140 const err = windows.GetLastError();
144 return switch (err) {
145 windows.ERROR.FILE_NOT_FOUND => error.NotFound,
146 windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
147 else => os.unexpectedErrorWindows(err),
148 };
141 switch (err) {
142 windows.ERROR.FILE_NOT_FOUND,
143 windows.ERROR.PATH_NOT_FOUND,
144 => return error.NotFound,
145 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
146 else => return os.unexpectedErrorWindows(err),
147 }
149148 } else {
150149 @compileError("TODO implement access for this OS");
151150 }
std/os/test.zig+3-3
......@@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {
2323
2424test "access file" {
2525 try os.makePath(a, "os_test_tmp");
26 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
27 unreachable;
26 if (os.File.access(a, "os_test_tmp/file.txt")) |ok| {
27 @panic("expected error");
2828 } else |err| {
2929 assert(err == error.NotFound);
3030 }
3131
3232 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");
3434 try os.deleteTree(a, "os_test_tmp");
3535}
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+9-183
......@@ -1,190 +1,19 @@
11const std = @import("../../index.zig");
22const 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
311test "import" {
412 _ = @import("util.zig");
513}
614
715pub const ERROR = @import("error.zig");
816
9pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
10 phProv: *HCRYPTPROV,
11 pszContainer: ?LPCSTR,
12 pszProvider: ?LPCSTR,
13 dwProvType: DWORD,
14 dwFlags: DWORD,
15) BOOL;
16
17pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
18
19pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
20
21pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
22
23pub extern "kernel32" stdcallcc fn CreateDirectoryA(
24 lpPathName: LPCSTR,
25 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
26) BOOL;
27
28pub extern "kernel32" stdcallcc fn CreateFileA(
29 lpFileName: LPCSTR,
30 dwDesiredAccess: DWORD,
31 dwShareMode: DWORD,
32 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
33 dwCreationDisposition: DWORD,
34 dwFlagsAndAttributes: DWORD,
35 hTemplateFile: ?HANDLE,
36) HANDLE;
37
38pub extern "kernel32" stdcallcc fn CreatePipe(
39 hReadPipe: *HANDLE,
40 hWritePipe: *HANDLE,
41 lpPipeAttributes: *const SECURITY_ATTRIBUTES,
42 nSize: DWORD,
43) BOOL;
44
45pub extern "kernel32" stdcallcc fn CreateProcessA(
46 lpApplicationName: ?LPCSTR,
47 lpCommandLine: LPSTR,
48 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
49 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
50 bInheritHandles: BOOL,
51 dwCreationFlags: DWORD,
52 lpEnvironment: ?*c_void,
53 lpCurrentDirectory: ?LPCSTR,
54 lpStartupInfo: *STARTUPINFOA,
55 lpProcessInformation: *PROCESS_INFORMATION,
56) BOOL;
57
58pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
59 lpSymlinkFileName: LPCSTR,
60 lpTargetFileName: LPCSTR,
61 dwFlags: DWORD,
62) BOOLEAN;
63
64pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
65
66pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
67
68pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
69
70pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
71
72pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
73pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
74pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
75
76pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
77
78pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
79
80pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
81
82pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
83
84pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
85
86pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
87
88pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
89
90pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
91
92pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
93
94pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
95
96pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
97
98pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
99 in_hFile: HANDLE,
100 in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
101 out_lpFileInformation: *c_void,
102 in_dwBufferSize: DWORD,
103) BOOL;
104
105pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
106 hFile: HANDLE,
107 lpszFilePath: LPSTR,
108 cchFilePath: DWORD,
109 dwFlags: DWORD,
110) DWORD;
111
112pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
113pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
114
115pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
116pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
117
118pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
119pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
120pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
121pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
122pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
123pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
124pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
125
126pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
127
128pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
129
130pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
131
132pub extern "kernel32" stdcallcc fn MoveFileExA(
133 lpExistingFileName: LPCSTR,
134 lpNewFileName: LPCSTR,
135 dwFlags: DWORD,
136) BOOL;
137
138pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
139
140pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
141
142pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
143
144pub extern "kernel32" stdcallcc fn ReadFile(
145 in_hFile: HANDLE,
146 out_lpBuffer: *c_void,
147 in_nNumberOfBytesToRead: DWORD,
148 out_lpNumberOfBytesRead: *DWORD,
149 in_out_lpOverlapped: ?*OVERLAPPED,
150) BOOL;
151
152pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
153
154pub extern "kernel32" stdcallcc fn SetFilePointerEx(
155 in_fFile: HANDLE,
156 in_liDistanceToMove: LARGE_INTEGER,
157 out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
158 in_dwMoveMethod: DWORD,
159) BOOL;
160
161pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
162
163pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
164
165pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
166
167pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
168
169pub extern "kernel32" stdcallcc fn WriteFile(
170 in_hFile: HANDLE,
171 in_lpBuffer: *const c_void,
172 in_nNumberOfBytesToWrite: DWORD,
173 out_lpNumberOfBytesWritten: ?*DWORD,
174 in_out_lpOverlapped: ?*OVERLAPPED,
175) BOOL;
176
177//TODO: call unicode versions instead of relying on ANSI code page
178pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
179
180pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
181
182pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
183
184pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
185
186pub const PROV_RSA_FULL = 1;
187
18817pub const BOOL = c_int;
18918pub const BOOLEAN = BYTE;
19019pub const BYTE = u8;
......@@ -206,6 +35,7 @@ pub const LPSTR = [*]CHAR;
20635pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR;
20736pub const LPVOID = *c_void;
20837pub const LPWSTR = [*]WCHAR;
38pub const LPCWSTR = [*]const WCHAR;
20939pub const PVOID = *c_void;
21040pub const PWSTR = [*]WCHAR;
21141pub const SIZE_T = usize;
......@@ -442,10 +272,6 @@ pub const SYSTEM_INFO = extern struct {
442272 wProcessorRevision: WORD,
443273};
444274
445pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
446
447pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
448
449275pub const HRESULT = c_long;
450276
451277pub const KNOWNFOLDERID = GUID;
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/zig/index.zig+3
......@@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");
22pub const Token = tokenizer.Token;
33pub const Tokenizer = tokenizer.Tokenizer;
44pub const parse = @import("parse.zig").parse;
5pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral;
56pub const render = @import("render.zig").render;
67pub const ast = @import("ast.zig");
78
......@@ -10,4 +11,6 @@ test "std.zig tests" {
1011 _ = @import("parse.zig");
1112 _ = @import("render.zig");
1213 _ = @import("tokenizer.zig");
14 _ = @import("parse_string_literal.zig");
1315}
16
std/zig/parse.zig+1-1
......@@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
23562356 const token = nextToken(&tok_it, &tree);
23572357 switch (token.ptr.id) {
23582358 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);
23602360 continue;
23612361 },
23622362 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 {
7373 return null;
7474 }
7575
76 /// TODO remove this enum
7677 const StrLitKind = enum {
7778 Normal,
7879 C,
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 {
99 try ctx.testCompileError(
1010 \\fn() void {}
1111 , "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'");
1230}