authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-27 14:06:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-27 14:19:53-07:00
loga8e964eadd3496330043985cacaaee7db92886c6
tree57768a2f87b76b9f80231ab924d2bdb481a22d2a
parentba71b96fe6c0e01b8445a2f7bd49541a07c360db

stage2: `zig test` now works with the LLVM backend

Frontend improvements: * When compiling in `zig test` mode, put a task on the work queue to analyze the main package root file. Normally, start code does `_ = import("root");` to make Zig analyze the user's code, however in the case of `zig test`, the root source file is the test runner. Without this change, no tests are picked up. * In the main pipeline, once semantic analysis is finished, if there are no compile errors, populate the `test_functions` Decl with the set of test functions picked up from semantic analysis. * Value: add `array` and `slice` Tags. LLVM backend improvements: * Fix incremental updates of globals. Previously the value of a global would not get replaced with a new value. * Fix LLVM type of arrays. They were incorrectly sending the ABI size as the element count. * Remove the FuncGen parameter from genTypedValue. This function is for generating global constants and there is no function available when it is being called. - The `ref_val` case is now commented out. I'd like to eliminate `ref_val` as one of the possible Value Tags. Instead it should always be done via `decl_ref`. * Implement constant value generation for slices, arrays, and structs. * Constant value generation for functions supports the `decl_ref` tag.

6 files changed, 332 insertions(+), 82 deletions(-)

src/Compilation.zig+15-19
...@@ -1709,7 +1709,9 @@ pub fn update(self: *Compilation) !void {...@@ -1709,7 +1709,9 @@ pub fn update(self: *Compilation) !void {
1709 // in the start code, but when using the stage1 backend that won't happen,1709 // in the start code, but when using the stage1 backend that won't happen,
1710 // so in order to run AstGen on the root source file we put it into the1710 // so in order to run AstGen on the root source file we put it into the
1711 // import_table here.1711 // import_table here.
1712 if (use_stage1) {1712 // Likewise, in the case of `zig test`, the test runner is the root source file,
1713 // and so there is nothing to import the main file.
1714 if (use_stage1 or self.bin_file.options.is_test) {
1713 _ = try module.importPkg(module.main_pkg);1715 _ = try module.importPkg(module.main_pkg);
1714 }1716 }
17151717
...@@ -1725,6 +1727,9 @@ pub fn update(self: *Compilation) !void {...@@ -1725,6 +1727,9 @@ pub fn update(self: *Compilation) !void {
17251727
1726 if (!use_stage1) {1728 if (!use_stage1) {
1727 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });1729 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
1730 if (self.bin_file.options.is_test) {
1731 try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });
1732 }
1728 }1733 }
1729 }1734 }
17301735
...@@ -2053,24 +2058,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2053,24 +2058,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2053 assert(decl.has_tv);2058 assert(decl.has_tv);
2054 assert(decl.ty.hasCodeGenBits());2059 assert(decl.ty.hasCodeGenBits());
20552060
2056 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {2061 try module.linkerUpdateDecl(decl);
2057 error.OutOfMemory => return error.OutOfMemory,
2058 error.AnalysisFail => {
2059 decl.analysis = .codegen_failure;
2060 continue;
2061 },
2062 else => {
2063 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2064 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2065 gpa,
2066 decl.srcLoc(),
2067 "unable to codegen: {s}",
2068 .{@errorName(err)},
2069 ));
2070 decl.analysis = .codegen_failure_retryable;
2071 continue;
2072 },
2073 };
2074 },2062 },
2075 },2063 },
2076 .codegen_func => |func| switch (func.owner_decl.analysis) {2064 .codegen_func => |func| switch (func.owner_decl.analysis) {
...@@ -2396,6 +2384,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2396,6 +2384,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2396 };2384 };
2397 },2385 },
2398 };2386 };
2387
2388 if (self.bin_file.options.is_test and self.totalErrorCount() == 0) {
2389 // The `test_functions` decl has been intentionally postponed until now,
2390 // at which point we must populate it with the list of test functions that
2391 // have been discovered and not filtered out.
2392 const mod = self.bin_file.options.module.?;
2393 try mod.populateTestFunctions();
2394 }
2399}2395}
24002396
2401const AstGenSrc = union(enum) {2397const AstGenSrc = union(enum) {
src/Module.zig+122-13
...@@ -112,6 +112,8 @@ compile_log_text: ArrayListUnmanaged(u8) = .{},...@@ -112,6 +112,8 @@ compile_log_text: ArrayListUnmanaged(u8) = .{},
112112
113emit_h: ?*GlobalEmitH,113emit_h: ?*GlobalEmitH,
114114
115test_functions: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
116
115/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.117/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
116pub const GlobalEmitH = struct {118pub const GlobalEmitH = struct {
117 /// Where to put the output.119 /// Where to put the output.
...@@ -282,6 +284,7 @@ pub const Decl = struct {...@@ -282,6 +284,7 @@ pub const Decl = struct {
282 pub fn destroy(decl: *Decl, module: *Module) void {284 pub fn destroy(decl: *Decl, module: *Module) void {
283 const gpa = module.gpa;285 const gpa = module.gpa;
284 log.debug("destroy {*} ({s})", .{ decl, decl.name });286 log.debug("destroy {*} ({s})", .{ decl, decl.name });
287 _ = module.test_functions.swapRemove(decl);
285 if (decl.deletion_flag) {288 if (decl.deletion_flag) {
286 assert(module.deletion_set.swapRemove(decl));289 assert(module.deletion_set.swapRemove(decl));
287 }290 }
...@@ -3319,6 +3322,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi...@@ -3319,6 +3322,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3319 // the test name filter.3322 // the test name filter.
3320 if (!mod.comp.bin_file.options.is_test) break :blk false;3323 if (!mod.comp.bin_file.options.is_test) break :blk false;
3321 if (decl_pkg != mod.main_pkg) break :blk false;3324 if (decl_pkg != mod.main_pkg) break :blk false;
3325 try mod.test_functions.put(gpa, new_decl, {});
3322 break :blk true;3326 break :blk true;
3323 },3327 },
3324 else => blk: {3328 else => blk: {
...@@ -3326,6 +3330,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi...@@ -3326,6 +3330,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3326 if (!mod.comp.bin_file.options.is_test) break :blk false;3330 if (!mod.comp.bin_file.options.is_test) break :blk false;
3327 if (decl_pkg != mod.main_pkg) break :blk false;3331 if (decl_pkg != mod.main_pkg) break :blk false;
3328 // TODO check the name against --test-filter3332 // TODO check the name against --test-filter
3333 try mod.test_functions.put(gpa, new_decl, {});
3329 break :blk true;3334 break :blk true;
3330 },3335 },
3331 };3336 };
...@@ -3765,17 +3770,38 @@ pub fn createAnonymousDeclNamed(...@@ -3765,17 +3770,38 @@ pub fn createAnonymousDeclNamed(
3765 scope: *Scope,3770 scope: *Scope,
3766 typed_value: TypedValue,3771 typed_value: TypedValue,
3767 name: [:0]u8,3772 name: [:0]u8,
3773) !*Decl {
3774 return mod.createAnonymousDeclFromDeclNamed(scope.ownerDecl().?, typed_value, name);
3775}
3776
3777pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl {
3778 return mod.createAnonymousDeclFromDecl(scope.ownerDecl().?, typed_value);
3779}
3780
3781pub fn createAnonymousDeclFromDecl(mod: *Module, owner_decl: *Decl, tv: TypedValue) !*Decl {
3782 const name_index = mod.getNextAnonNameIndex();
3783 const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{
3784 owner_decl.name, name_index,
3785 });
3786 return mod.createAnonymousDeclFromDeclNamed(owner_decl, tv, name);
3787}
3788
3789/// Takes ownership of `name` even if it returns an error.
3790pub fn createAnonymousDeclFromDeclNamed(
3791 mod: *Module,
3792 owner_decl: *Decl,
3793 typed_value: TypedValue,
3794 name: [:0]u8,
3768) !*Decl {3795) !*Decl {
3769 errdefer mod.gpa.free(name);3796 errdefer mod.gpa.free(name);
37703797
3771 const scope_decl = scope.ownerDecl().?;3798 const namespace = owner_decl.namespace;
3772 const namespace = scope_decl.namespace;
3773 try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1);3799 try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1);
37743800
3775 const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node);3801 const new_decl = try mod.allocateNewDecl(namespace, owner_decl.src_node);
37763802
3777 new_decl.name = name;3803 new_decl.name = name;
3778 new_decl.src_line = scope_decl.src_line;3804 new_decl.src_line = owner_decl.src_line;
3779 new_decl.ty = typed_value.ty;3805 new_decl.ty = typed_value.ty;
3780 new_decl.val = typed_value.val;3806 new_decl.val = typed_value.val;
3781 new_decl.has_tv = true;3807 new_decl.has_tv = true;
...@@ -3796,15 +3822,6 @@ pub fn createAnonymousDeclNamed(...@@ -3796,15 +3822,6 @@ pub fn createAnonymousDeclNamed(
3796 return new_decl;3822 return new_decl;
3797}3823}
37983824
3799pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl {
3800 const scope_decl = scope.ownerDecl().?;
3801 const name_index = mod.getNextAnonNameIndex();
3802 const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{
3803 scope_decl.name, name_index,
3804 });
3805 return mod.createAnonymousDeclNamed(scope, typed_value, name);
3806}
3807
3808pub fn getNextAnonNameIndex(mod: *Module) usize {3825pub fn getNextAnonNameIndex(mod: *Module) usize {
3809 return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic);3826 return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic);
3810}3827}
...@@ -4801,3 +4818,95 @@ pub fn processExports(mod: *Module) !void {...@@ -4801,3 +4818,95 @@ pub fn processExports(mod: *Module) !void {
4801 };4818 };
4802 }4819 }
4803}4820}
4821
4822pub fn populateTestFunctions(mod: *Module) !void {
4823 const gpa = mod.gpa;
4824 const builtin_pkg = mod.main_pkg.table.get("builtin").?;
4825 const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
4826 const builtin_namespace = builtin_file.root_decl.?.namespace;
4827 const decl = builtin_namespace.decls.get("test_functions").?;
4828 var buf: Type.Payload.ElemType = undefined;
4829 const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();
4830
4831 const array_decl = d: {
4832 // Add mod.test_functions to an array decl then make the test_functions
4833 // decl reference it as a slice.
4834 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
4835 errdefer new_decl_arena.deinit();
4836 const arena = &new_decl_arena.allocator;
4837
4838 const test_fn_vals = try arena.alloc(Value, mod.test_functions.count());
4839 const array_decl = try mod.createAnonymousDeclFromDecl(decl, .{
4840 .ty = try Type.Tag.array.create(arena, .{
4841 .len = test_fn_vals.len,
4842 .elem_type = try tmp_test_fn_ty.copy(arena),
4843 }),
4844 .val = try Value.Tag.array.create(arena, test_fn_vals),
4845 });
4846 for (mod.test_functions.keys()) |test_decl, i| {
4847 const test_name_slice = mem.sliceTo(test_decl.name, 0);
4848 const test_name_decl = n: {
4849 var name_decl_arena = std.heap.ArenaAllocator.init(gpa);
4850 errdefer name_decl_arena.deinit();
4851 const bytes = try name_decl_arena.allocator.dupe(u8, test_name_slice);
4852 const test_name_decl = try mod.createAnonymousDeclFromDecl(array_decl, .{
4853 .ty = try Type.Tag.array_u8.create(&name_decl_arena.allocator, bytes.len),
4854 .val = try Value.Tag.bytes.create(&name_decl_arena.allocator, bytes),
4855 });
4856 try test_name_decl.finalizeNewArena(&name_decl_arena);
4857 break :n test_name_decl;
4858 };
4859 try mod.linkerUpdateDecl(test_name_decl);
4860
4861 const field_vals = try arena.create([3]Value);
4862 field_vals.* = .{
4863 try Value.Tag.slice.create(arena, .{
4864 .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl),
4865 .len = try Value.Tag.int_u64.create(arena, test_name_slice.len),
4866 }), // name
4867 try Value.Tag.decl_ref.create(arena, test_decl), // func
4868 Value.initTag(.null_value), // async_frame_size
4869 };
4870 test_fn_vals[i] = try Value.Tag.@"struct".create(arena, field_vals);
4871 }
4872
4873 try array_decl.finalizeNewArena(&new_decl_arena);
4874 break :d array_decl;
4875 };
4876 try mod.linkerUpdateDecl(array_decl);
4877
4878 {
4879 var arena_instance = decl.value_arena.?.promote(gpa);
4880 defer decl.value_arena.?.* = arena_instance.state;
4881 const arena = &arena_instance.allocator;
4882
4883 decl.ty = try Type.Tag.const_slice.create(arena, try tmp_test_fn_ty.copy(arena));
4884 decl.val = try Value.Tag.slice.create(arena, .{
4885 .ptr = try Value.Tag.decl_ref.create(arena, array_decl),
4886 .len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()),
4887 });
4888 }
4889 try mod.linkerUpdateDecl(decl);
4890}
4891
4892pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void {
4893 mod.comp.bin_file.updateDecl(mod, decl) catch |err| switch (err) {
4894 error.OutOfMemory => return error.OutOfMemory,
4895 error.AnalysisFail => {
4896 decl.analysis = .codegen_failure;
4897 return;
4898 },
4899 else => {
4900 const gpa = mod.gpa;
4901 try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
4902 mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
4903 gpa,
4904 decl.srcLoc(),
4905 "unable to codegen: {s}",
4906 .{@errorName(err)},
4907 ));
4908 decl.analysis = .codegen_failure_retryable;
4909 return;
4910 },
4911 };
4912}
src/codegen/llvm.zig+91-43
...@@ -500,7 +500,18 @@ pub const DeclGen = struct {...@@ -500,7 +500,18 @@ pub const DeclGen = struct {
500 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {500 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
501 _ = try self.resolveLlvmFunction(extern_fn.data);501 _ = try self.resolveLlvmFunction(extern_fn.data);
502 } else {502 } else {
503 _ = try self.resolveGlobalDecl(decl);503 const global = try self.resolveGlobalDecl(decl);
504 assert(decl.has_tv);
505 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
506 const variable = payload.data;
507 break :init_val variable.init;
508 } else init_val: {
509 global.setGlobalConstant(.True);
510 break :init_val decl.val;
511 };
512
513 const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val });
514 llvm.setInitializer(global, llvm_init);
504 }515 }
505 }516 }
506517
...@@ -548,25 +559,11 @@ pub const DeclGen = struct {...@@ -548,25 +559,11 @@ pub const DeclGen = struct {
548 }559 }
549560
550 fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value {561 fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
551 if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val;562 const llvm_module = self.object.llvm_module;
552563 if (llvm_module.getNamedGlobal(decl.name)) |val| return val;
553 assert(decl.has_tv);
554
555 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.564 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
556 const llvm_type = try self.llvmType(decl.ty);565 const llvm_type = try self.llvmType(decl.ty);
557 const global = self.llvmModule().addGlobal(llvm_type, decl.name);566 return llvm_module.addGlobal(llvm_type, decl.name);
558 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
559 const variable = payload.data;
560 break :init_val variable.init;
561 } else init_val: {
562 global.setGlobalConstant(.True);
563 break :init_val decl.val;
564 };
565
566 const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }, null);
567 llvm.setInitializer(global, llvm_init);
568
569 return global;
570 }567 }
571568
572 fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {569 fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
...@@ -596,7 +593,8 @@ pub const DeclGen = struct {...@@ -596,7 +593,8 @@ pub const DeclGen = struct {
596 },593 },
597 .Array => {594 .Array => {
598 const elem_type = try self.llvmType(t.elemType());595 const elem_type = try self.llvmType(t.elemType());
599 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));596 const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);
597 return elem_type.arrayType(@intCast(c_uint, total_len));
600 },598 },
601 .Optional => {599 .Optional => {
602 if (!t.isPtrLikeOptional()) {600 if (!t.isPtrLikeOptional()) {
...@@ -674,8 +672,7 @@ pub const DeclGen = struct {...@@ -674,8 +672,7 @@ pub const DeclGen = struct {
674 }672 }
675 }673 }
676674
677 // TODO: figure out a way to remove the FuncGen argument675 fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
678 fn genTypedValue(self: *DeclGen, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
679 const llvm_type = try self.llvmType(tv.ty);676 const llvm_type = try self.llvmType(tv.ty);
680677
681 if (tv.val.isUndef())678 if (tv.val.isUndef())
...@@ -711,20 +708,36 @@ pub const DeclGen = struct {...@@ -711,20 +708,36 @@ pub const DeclGen = struct {
711 usize_type.constNull(),708 usize_type.constNull(),
712 };709 };
713710
714 // TODO: consider using buildInBoundsGEP2 for opaque pointers711 return val.constInBoundsGEP(&indices, indices.len);
715 return fg.?.builder.buildInBoundsGEP(val, &indices, 2, "");
716 },712 },
717 .ref_val => {713 .ref_val => {
718 const elem_value = tv.val.castTag(.ref_val).?.data;714 //const elem_value = tv.val.castTag(.ref_val).?.data;
719 const elem_type = tv.ty.castPointer().?.data;715 //const elem_type = tv.ty.castPointer().?.data;
720 const alloca = fg.?.buildAlloca(try self.llvmType(elem_type));716 //const alloca = fg.?.buildAlloca(try self.llvmType(elem_type));
721 _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);717 //_ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);
722 return alloca;718 //return alloca;
719 // TODO eliminate the ref_val Value Tag
720 return self.todo("implement const of pointer tag ref_val", .{});
723 },721 },
724 .variable => {722 .variable => {
725 const variable = tv.val.castTag(.variable).?.data;723 const variable = tv.val.castTag(.variable).?.data;
726 return self.resolveGlobalDecl(variable.owner_decl);724 return self.resolveGlobalDecl(variable.owner_decl);
727 },725 },
726 .slice => {
727 const slice = tv.val.castTag(.slice).?.data;
728 var buf: Type.Payload.ElemType = undefined;
729 const fields: [2]*const llvm.Value = .{
730 try self.genTypedValue(.{
731 .ty = tv.ty.slicePtrFieldType(&buf),
732 .val = slice.ptr,
733 }),
734 try self.genTypedValue(.{
735 .ty = Type.initTag(.usize),
736 .val = slice.len,
737 }),
738 };
739 return self.context.constStruct(&fields, fields.len, .False);
740 },
728 else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }),741 else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }),
729 },742 },
730 .Array => {743 .Array => {
...@@ -734,10 +747,28 @@ pub const DeclGen = struct {...@@ -734,10 +747,28 @@ pub const DeclGen = struct {
734 return self.todo("handle other sentinel values", .{});747 return self.todo("handle other sentinel values", .{});
735 } else false;748 } else false;
736749
737 return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));750 return self.context.constString(
738 } else {751 payload.data.ptr,
739 return self.todo("handle more array values", .{});752 @intCast(c_uint, payload.data.len),
753 llvm.Bool.fromBool(!zero_sentinel),
754 );
755 }
756 if (tv.val.castTag(.array)) |payload| {
757 const gpa = self.gpa;
758 const elem_ty = tv.ty.elemType();
759 const elem_vals = payload.data;
760 const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len);
761 defer gpa.free(llvm_elems);
762 for (elem_vals) |elem_val, i| {
763 llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val });
764 }
765 const llvm_elem_ty = try self.llvmType(elem_ty);
766 return llvm_elem_ty.constArray(
767 llvm_elems.ptr,
768 @intCast(c_uint, llvm_elems.len),
769 );
740 }770 }
771 return self.todo("handle more array values", .{});
741 },772 },
742 .Optional => {773 .Optional => {
743 if (!tv.ty.isPtrLikeOptional()) {774 if (!tv.ty.isPtrLikeOptional()) {
...@@ -750,26 +781,25 @@ pub const DeclGen = struct {...@@ -750,26 +781,25 @@ pub const DeclGen = struct {
750 llvm_child_type.constNull(),781 llvm_child_type.constNull(),
751 self.context.intType(1).constNull(),782 self.context.intType(1).constNull(),
752 };783 };
753 return self.context.constStruct(&optional_values, 2, .False);784 return self.context.constStruct(&optional_values, optional_values.len, .False);
754 } else {785 } else {
755 var optional_values: [2]*const llvm.Value = .{786 var optional_values: [2]*const llvm.Value = .{
756 try self.genTypedValue(.{ .ty = child_type, .val = tv.val }, fg),787 try self.genTypedValue(.{ .ty = child_type, .val = tv.val }),
757 self.context.intType(1).constAllOnes(),788 self.context.intType(1).constAllOnes(),
758 };789 };
759 return self.context.constStruct(&optional_values, 2, .False);790 return self.context.constStruct(&optional_values, optional_values.len, .False);
760 }791 }
761 } else {792 } else {
762 return self.todo("implement const of optional pointer", .{});793 return self.todo("implement const of optional pointer", .{});
763 }794 }
764 },795 },
765 .Fn => {796 .Fn => {
766 const fn_decl = if (tv.val.castTag(.extern_fn)) |extern_fn|797 const fn_decl = switch (tv.val.tag()) {
767 extern_fn.data798 .extern_fn => tv.val.castTag(.extern_fn).?.data,
768 else if (tv.val.castTag(.function)) |func_payload|799 .function => tv.val.castTag(.function).?.data.owner_decl,
769 func_payload.data.owner_decl800 .decl_ref => tv.val.castTag(.decl_ref).?.data,
770 else801 else => unreachable,
771 unreachable;802 };
772
773 return self.resolveLlvmFunction(fn_decl);803 return self.resolveLlvmFunction(fn_decl);
774 },804 },
775 .ErrorSet => {805 .ErrorSet => {
...@@ -793,11 +823,29 @@ pub const DeclGen = struct {...@@ -793,11 +823,29 @@ pub const DeclGen = struct {
793823
794 if (!payload_type.hasCodeGenBits()) {824 if (!payload_type.hasCodeGenBits()) {
795 // We use the error type directly as the type.825 // We use the error type directly as the type.
796 return self.genTypedValue(.{ .ty = error_type, .val = sub_val }, fg);826 return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
797 }827 }
798828
799 return self.todo("implement error union const of type '{}'", .{tv.ty});829 return self.todo("implement error union const of type '{}'", .{tv.ty});
800 },830 },
831 .Struct => {
832 const fields_len = tv.ty.structFieldCount();
833 const field_vals = tv.val.castTag(.@"struct").?.data;
834 const gpa = self.gpa;
835 const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len);
836 defer gpa.free(llvm_fields);
837 for (llvm_fields) |*llvm_field, i| {
838 llvm_field.* = try self.genTypedValue(.{
839 .ty = tv.ty.structFieldType(i),
840 .val = field_vals[i],
841 });
842 }
843 return self.context.constStruct(
844 llvm_fields.ptr,
845 @intCast(c_uint, llvm_fields.len),
846 .False,
847 );
848 },
801 else => return self.todo("implement const of type '{}'", .{tv.ty}),849 else => return self.todo("implement const of type '{}'", .{tv.ty}),
802 }850 }
803 }851 }
...@@ -869,7 +917,7 @@ pub const FuncGen = struct {...@@ -869,7 +917,7 @@ pub const FuncGen = struct {
869917
870 fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value {918 fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value {
871 if (self.air.value(inst)) |val| {919 if (self.air.value(inst)) |val| {
872 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }, self);920 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val });
873 }921 }
874 const inst_index = Air.refToIndex(inst).?;922 const inst_index = Air.refToIndex(inst).?;
875 if (self.func_inst_table.get(inst_index)) |value| return value;923 if (self.func_inst_table.get(inst_index)) |value| return value;
src/codegen/llvm/bindings.zig+14-2
...@@ -49,7 +49,12 @@ pub const Context = opaque {...@@ -49,7 +49,12 @@ pub const Context = opaque {
49 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value;49 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value;
5050
51 pub const constStruct = LLVMConstStructInContext;51 pub const constStruct = LLVMConstStructInContext;
52 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: Bool) *const Value;52 extern fn LLVMConstStructInContext(
53 C: *const Context,
54 ConstantVals: [*]const *const Value,
55 Count: c_uint,
56 Packed: Bool,
57 ) *const Value;
5358
54 pub const createBasicBlock = LLVMCreateBasicBlockInContext;59 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
55 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;60 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
...@@ -100,6 +105,13 @@ pub const Value = opaque {...@@ -100,6 +105,13 @@ pub const Value = opaque {
100105
101 pub const setAliasee = LLVMAliasSetAliasee;106 pub const setAliasee = LLVMAliasSetAliasee;
102 extern fn LLVMAliasSetAliasee(Alias: *const Value, Aliasee: *const Value) void;107 extern fn LLVMAliasSetAliasee(Alias: *const Value, Aliasee: *const Value) void;
108
109 pub const constInBoundsGEP = LLVMConstInBoundsGEP;
110 extern fn LLVMConstInBoundsGEP(
111 ConstantVal: *const Value,
112 ConstantIndices: [*]const *const Value,
113 NumIndices: c_uint,
114 ) *const Value;
103};115};
104116
105pub const Type = opaque {117pub const Type = opaque {
...@@ -113,7 +125,7 @@ pub const Type = opaque {...@@ -113,7 +125,7 @@ pub const Type = opaque {
113 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;125 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;
114126
115 pub const constArray = LLVMConstArray;127 pub const constArray = LLVMConstArray;
116 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value;128 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value;
117129
118 pub const getUndef = LLVMGetUndef;130 pub const getUndef = LLVMGetUndef;
119 extern fn LLVMGetUndef(Ty: *const Type) *const Value;131 extern fn LLVMGetUndef(Ty: *const Type) *const Value;
src/type.zig+22
...@@ -1526,6 +1526,8 @@ pub const Type = extern union {...@@ -1526,6 +1526,8 @@ pub const Type = extern union {
1526 .var_args_param => unreachable,1526 .var_args_param => unreachable,
15271527
1528 .@"struct" => {1528 .@"struct" => {
1529 const s = self.castTag(.@"struct").?.data;
1530 assert(s.status == .have_layout);
1529 @panic("TODO abiSize struct");1531 @panic("TODO abiSize struct");
1530 },1532 },
1531 .enum_simple, .enum_full, .enum_nonexhaustive => {1533 .enum_simple, .enum_full, .enum_nonexhaustive => {
...@@ -2768,6 +2770,26 @@ pub const Type = extern union {...@@ -2768,6 +2770,26 @@ pub const Type = extern union {
2768 }2770 }
2769 }2771 }
27702772
2773 pub fn structFieldCount(ty: Type) usize {
2774 switch (ty.tag()) {
2775 .@"struct" => {
2776 const struct_obj = ty.castTag(.@"struct").?.data;
2777 return struct_obj.fields.count();
2778 },
2779 else => unreachable,
2780 }
2781 }
2782
2783 pub fn structFieldType(ty: Type, index: usize) Type {
2784 switch (ty.tag()) {
2785 .@"struct" => {
2786 const struct_obj = ty.castTag(.@"struct").?.data;
2787 return struct_obj.fields.values()[index].ty;
2788 },
2789 else => unreachable,
2790 }
2791 }
2792
2771 pub fn declSrcLoc(ty: Type) Module.SrcLoc {2793 pub fn declSrcLoc(ty: Type) Module.SrcLoc {
2772 switch (ty.tag()) {2794 switch (ty.tag()) {
2773 .enum_full, .enum_nonexhaustive => {2795 .enum_full, .enum_nonexhaustive => {
src/value.zig+68-5
...@@ -112,6 +112,10 @@ pub const Value = extern union {...@@ -112,6 +112,10 @@ pub const Value = extern union {
112 /// This value is repeated some number of times. The amount of times to repeat112 /// This value is repeated some number of times. The amount of times to repeat
113 /// is stored externally.113 /// is stored externally.
114 repeated,114 repeated,
115 /// Each element stored as a `Value`.
116 array,
117 /// Pointer and length as sub `Value` objects.
118 slice,
115 float_16,119 float_16,
116 float_32,120 float_32,
117 float_64,121 float_64,
...@@ -217,6 +221,9 @@ pub const Value = extern union {...@@ -217,6 +221,9 @@ pub const Value = extern union {
217 .enum_literal,221 .enum_literal,
218 => Payload.Bytes,222 => Payload.Bytes,
219223
224 .array => Payload.Array,
225 .slice => Payload.Slice,
226
220 .enum_field_index => Payload.U32,227 .enum_field_index => Payload.U32,
221228
222 .ty => Payload.Ty,229 .ty => Payload.Ty,
...@@ -442,6 +449,28 @@ pub const Value = extern union {...@@ -442,6 +449,28 @@ pub const Value = extern union {
442 };449 };
443 return Value{ .ptr_otherwise = &new_payload.base };450 return Value{ .ptr_otherwise = &new_payload.base };
444 },451 },
452 .array => {
453 const payload = self.castTag(.array).?;
454 const new_payload = try allocator.create(Payload.Array);
455 new_payload.* = .{
456 .base = payload.base,
457 .data = try allocator.alloc(Value, payload.data.len),
458 };
459 std.mem.copy(Value, new_payload.data, payload.data);
460 return Value{ .ptr_otherwise = &new_payload.base };
461 },
462 .slice => {
463 const payload = self.castTag(.slice).?;
464 const new_payload = try allocator.create(Payload.Slice);
465 new_payload.* = .{
466 .base = payload.base,
467 .data = .{
468 .ptr = try payload.data.ptr.copy(allocator),
469 .len = try payload.data.len.copy(allocator),
470 },
471 };
472 return Value{ .ptr_otherwise = &new_payload.base };
473 },
445 .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),474 .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),
446 .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),475 .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
447 .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),476 .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
...@@ -605,6 +634,8 @@ pub const Value = extern union {...@@ -605,6 +634,8 @@ pub const Value = extern union {
605 try out_stream.writeAll("(repeated) ");634 try out_stream.writeAll("(repeated) ");
606 val = val.castTag(.repeated).?.data;635 val = val.castTag(.repeated).?.data;
607 },636 },
637 .array => return out_stream.writeAll("(array)"),
638 .slice => return out_stream.writeAll("(slice)"),
608 .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}),639 .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}),
609 .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}),640 .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}),
610 .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}),641 .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}),
...@@ -729,6 +760,8 @@ pub const Value = extern union {...@@ -729,6 +760,8 @@ pub const Value = extern union {
729 .field_ptr,760 .field_ptr,
730 .bytes,761 .bytes,
731 .repeated,762 .repeated,
763 .array,
764 .slice,
732 .float_16,765 .float_16,
733 .float_32,766 .float_32,
734 .float_64,767 .float_64,
...@@ -1075,6 +1108,8 @@ pub const Value = extern union {...@@ -1075,6 +1108,8 @@ pub const Value = extern union {
1075 return orderAgainstZero(lhs).compare(op);1108 return orderAgainstZero(lhs).compare(op);
1076 }1109 }
10771110
1111 /// TODO we can't compare value equality without also knowing the type to treat
1112 /// the values as
1078 pub fn eql(a: Value, b: Value) bool {1113 pub fn eql(a: Value, b: Value) bool {
1079 const a_tag = a.tag();1114 const a_tag = a.tag();
1080 const b_tag = b.tag();1115 const b_tag = b.tag();
...@@ -1109,6 +1144,8 @@ pub const Value = extern union {...@@ -1109,6 +1144,8 @@ pub const Value = extern union {
1109 return @truncate(u32, self.hash());1144 return @truncate(u32, self.hash());
1110 }1145 }
11111146
1147 /// TODO we can't hash without also knowing the type of the value.
1148 /// we have to hash as if there were a canonical value memory layout.
1112 pub fn hash(self: Value) u64 {1149 pub fn hash(self: Value) u64 {
1113 var hasher = std.hash.Wyhash.init(0);1150 var hasher = std.hash.Wyhash.init(0);
11141151
...@@ -1203,6 +1240,15 @@ pub const Value = extern union {...@@ -1203,6 +1240,15 @@ pub const Value = extern union {
1203 const payload = self.castTag(.bytes).?;1240 const payload = self.castTag(.bytes).?;
1204 hasher.update(payload.data);1241 hasher.update(payload.data);
1205 },1242 },
1243 .repeated => {
1244 @panic("TODO Value.hash for repeated");
1245 },
1246 .array => {
1247 @panic("TODO Value.hash for array");
1248 },
1249 .slice => {
1250 @panic("TODO Value.hash for slice");
1251 },
1206 .int_u64 => {1252 .int_u64 => {
1207 const payload = self.castTag(.int_u64).?;1253 const payload = self.castTag(.int_u64).?;
1208 std.hash.autoHash(&hasher, payload.data);1254 std.hash.autoHash(&hasher, payload.data);
...@@ -1211,10 +1257,6 @@ pub const Value = extern union {...@@ -1211,10 +1257,6 @@ pub const Value = extern union {
1211 const payload = self.castTag(.int_i64).?;1257 const payload = self.castTag(.int_i64).?;
1212 std.hash.autoHash(&hasher, payload.data);1258 std.hash.autoHash(&hasher, payload.data);
1213 },1259 },
1214 .repeated => {
1215 const payload = self.castTag(.repeated).?;
1216 std.hash.autoHash(&hasher, payload.data.hash());
1217 },
1218 .ref_val => {1260 .ref_val => {
1219 const payload = self.castTag(.ref_val).?;1261 const payload = self.castTag(.ref_val).?;
1220 std.hash.autoHash(&hasher, payload.data.hash());1262 std.hash.autoHash(&hasher, payload.data.hash());
...@@ -1340,6 +1382,8 @@ pub const Value = extern union {...@@ -1340,6 +1382,8 @@ pub const Value = extern union {
1340 return switch (val.tag()) {1382 return switch (val.tag()) {
1341 .empty_array => 0,1383 .empty_array => 0,
1342 .bytes => val.castTag(.bytes).?.data.len,1384 .bytes => val.castTag(.bytes).?.data.len,
1385 .array => val.castTag(.array).?.data.len,
1386 .slice => val.castTag(.slice).?.data.len.toUnsignedInt(),
1343 .ref_val => sliceLen(val.castTag(.ref_val).?.data),1387 .ref_val => sliceLen(val.castTag(.ref_val).?.data),
1344 .decl_ref => {1388 .decl_ref => {
1345 const decl = val.castTag(.decl_ref).?.data;1389 const decl = val.castTag(.decl_ref).?.data;
...@@ -1364,6 +1408,9 @@ pub const Value = extern union {...@@ -1364,6 +1408,9 @@ pub const Value = extern union {
1364 // No matter the index; all the elements are the same!1408 // No matter the index; all the elements are the same!
1365 .repeated => return self.castTag(.repeated).?.data,1409 .repeated => return self.castTag(.repeated).?.data,
13661410
1411 .array => return self.castTag(.array).?.data[index],
1412 .slice => return self.castTag(.slice).?.data.ptr.elemValue(allocator, index),
1413
1367 else => unreachable,1414 else => unreachable,
1368 }1415 }
1369 }1416 }
...@@ -1450,7 +1497,8 @@ pub const Value = extern union {...@@ -1450,7 +1497,8 @@ pub const Value = extern union {
1450 }1497 }
14511498
1452 /// Valid for all types. Asserts the value is not undefined.1499 /// Valid for all types. Asserts the value is not undefined.
1453 pub fn isType(self: Value) bool {1500 /// TODO this function is a code smell and should be deleted
1501 fn isType(self: Value) bool {
1454 return switch (self.tag()) {1502 return switch (self.tag()) {
1455 .ty,1503 .ty,
1456 .int_type,1504 .int_type,
...@@ -1528,6 +1576,8 @@ pub const Value = extern union {...@@ -1528,6 +1576,8 @@ pub const Value = extern union {
1528 .field_ptr,1576 .field_ptr,
1529 .bytes,1577 .bytes,
1530 .repeated,1578 .repeated,
1579 .array,
1580 .slice,
1531 .float_16,1581 .float_16,
1532 .float_32,1582 .float_32,
1533 .float_64,1583 .float_64,
...@@ -1638,6 +1688,19 @@ pub const Value = extern union {...@@ -1638,6 +1688,19 @@ pub const Value = extern union {
1638 data: []const u8,1688 data: []const u8,
1639 };1689 };
16401690
1691 pub const Array = struct {
1692 base: Payload,
1693 data: []Value,
1694 };
1695
1696 pub const Slice = struct {
1697 base: Payload,
1698 data: struct {
1699 ptr: Value,
1700 len: Value,
1701 },
1702 };
1703
1641 pub const Ty = struct {1704 pub const Ty = struct {
1642 base: Payload,1705 base: Payload,
1643 data: Type,1706 data: Type,