From f82c26eb04590fc6083a3520b470333078cfc571 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 Aug 2021 18:07:30 +0200 Subject: [PATCH 1/9] macho: don't embed codesig unless targeting aarch64-macos When developing an iOS app for example, the developer is required to use Apple's codesign utility to generate a valid signature as done by Xcode. --- src/link/MachO.zig | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 10e079d4f1..ac98be1df3 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -54,6 +54,11 @@ d_sym: ?DebugSymbols = null, /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. page_size: u16, +/// TODO Should we figure out embedding code signatures for other Apple platforms as part of the linker? +/// Or should this be a separate tool? +/// https://github.com/ziglang/zig/issues/9567 +requires_adhoc_codesig: bool, + /// We commit 0x1000 = 4096 bytes of space to the header and /// the table of load commands. This should be plenty for any /// potential future extensions. @@ -400,6 +405,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { .file = null, }, .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000, + .requires_adhoc_codesig = options.target.cpu.arch == .aarch64 and options.target.os.tag == .macos, }; return self; @@ -459,7 +465,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { try ds.flushModule(self.base.allocator, self.base.options); } - if (target.cpu.arch == .aarch64) { + if (self.requires_adhoc_codesig) { // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values // written out to the file. @@ -492,11 +498,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { assert(!self.strtab_dirty); assert(!self.strtab_needs_relocation); - if (target.cpu.arch == .aarch64) { - switch (output_mode) { - .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last - else => {}, - } + if (self.requires_adhoc_codesig) { + try self.writeCodeSignature(); // code signing always comes last } } @@ -2841,7 +2844,7 @@ fn addDataInCodeLC(self: *MachO) !void { } fn addCodeSignatureLC(self: *MachO) !void { - if (self.code_signature_cmd_index == null and self.base.options.target.cpu.arch == .aarch64) { + if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) { self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ .LinkeditData = .{ @@ -2935,14 +2938,14 @@ fn flushZld(self: *MachO) !void { seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); } - if (self.base.options.target.cpu.arch == .aarch64) { + if (self.requires_adhoc_codesig) { try self.writeCodeSignaturePadding(); } try self.writeLoadCommands(); try self.writeHeader(); - if (self.base.options.target.cpu.arch == .aarch64) { + if (self.requires_adhoc_codesig) { try self.writeCodeSignature(); } } @@ -4454,7 +4457,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd }); self.load_commands_dirty = true; } - if (self.code_signature_cmd_index == null) { + if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) { self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ .LinkeditData = .{ @@ -5719,8 +5722,8 @@ fn writeStringTableZld(self: *MachO) !void { try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); - if (symtab.strsize > self.strtab.items.len and self.base.options.target.cpu.arch == .x86_64) { - // This is the last section, so we need to pad it out. + if (symtab.strsize > self.strtab.items.len) { + // This is potentially the last section, so we need to pad it out. try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); } } From d5f7963331e031326b8063673a2c43581565b538 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 Aug 2021 18:29:19 +0200 Subject: [PATCH 2/9] macho: adhoc code sign binaries targeting aarch64-xxx-simulator --- src/link/MachO.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ac98be1df3..abe4bae3a9 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -396,6 +396,13 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { const self = try gpa.create(MachO); + const cpu_arch = options.target.cpu.arch; + const os_tag = options.target.os.tag; + const abi = options.target.abi; + const page_size = if (cpu_arch == .aarch64) 0x4000 else 0x1000; + // Adhoc code signature is required when targeting aarch64-macos either directly or indirectly via the simulator + // ABI such as aarch64-ios-simulator, etc. + const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator); self.* = .{ .base = .{ @@ -404,8 +411,8 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { .allocator = gpa, .file = null, }, - .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000, - .requires_adhoc_codesig = options.target.cpu.arch == .aarch64 and options.target.os.tag == .macos, + .page_size = page_size, + .requires_adhoc_codesig = requires_adhoc_codesig, }; return self; From e2303840de713e34fe6b2b16c5e9866e6dad9080 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 Aug 2021 23:50:39 +0200 Subject: [PATCH 3/9] Fix AST and build errors --- src/link/MachO.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index abe4bae3a9..3b6eccbc3a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -399,7 +399,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { const cpu_arch = options.target.cpu.arch; const os_tag = options.target.os.tag; const abi = options.target.abi; - const page_size = if (cpu_arch == .aarch64) 0x4000 else 0x1000; + const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000; // Adhoc code signature is required when targeting aarch64-macos either directly or indirectly via the simulator // ABI such as aarch64-ios-simulator, etc. const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator); @@ -446,7 +446,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { defer tracy.end(); const output_mode = self.base.options.output_mode; - const target = self.base.options.target; switch (output_mode) { .Exe => { From 5cd1d42a351aa77d2a030e59cbd2b9abf7c44444 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 19 Aug 2021 07:20:50 +0200 Subject: [PATCH 4/9] Add mask before truncating dereferenced bit pointers (#9584) --- src/stage1/codegen.cpp | 13 ++++---- test/behavior.zig | 1 + test/behavior/bugs/9584.zig | 60 +++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 test/behavior/bugs/9584.zig diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 359af18e82..c44081c770 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -3831,10 +3831,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, Stage1Air *executable, LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); + LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); + LLVMValueRef mask = LLVMConstAllOnes(LLVMIntType(size_in_bits)); + mask = LLVMConstZExt(mask, LLVMTypeOf(containing_int)); + LLVMValueRef masked_value = LLVMBuildAnd(g->builder, shifted_value, mask, ""); + if (handle_is_ptr(g, child_type)) { LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); - LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); + LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, masked_value, same_size_int, ""); LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, result_loc, LLVMPointerType(same_size_int, 0), ""); LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); @@ -3842,12 +3846,11 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, Stage1Air *executable, } if (child_type->id == ZigTypeIdFloat) { - LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); - LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); + LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, masked_value, same_size_int, ""); return LLVMBuildBitCast(g->builder, truncated_int, get_llvm_type(g, child_type), ""); } - return LLVMBuildTrunc(g->builder, shifted_value, get_llvm_type(g, child_type), ""); + return LLVMBuildTrunc(g->builder, masked_value, get_llvm_type(g, child_type), ""); } static bool value_is_all_undef_array(CodeGen *g, ZigValue *const_val, size_t len) { diff --git a/test/behavior.zig b/test/behavior.zig index 7c6e98daa9..bb55ec83f1 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -71,6 +71,7 @@ test { _ = @import("behavior/bugs/7047.zig"); _ = @import("behavior/bugs/7003.zig"); _ = @import("behavior/bugs/7250.zig"); + _ = @import("behavior/bugs/9584.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/529.zig"); diff --git a/test/behavior/bugs/9584.zig b/test/behavior/bugs/9584.zig new file mode 100644 index 0000000000..63a607981e --- /dev/null +++ b/test/behavior/bugs/9584.zig @@ -0,0 +1,60 @@ +const std = @import("std"); + +const A = packed struct { + a: bool, + b: bool, + c: bool, + d: bool, + + e: bool, + f: bool, + g: bool, + h: bool, +}; + +const X = union { + x: A, + y: u64, +}; + +pub fn a( + x0: i32, + x1: i32, + x2: i32, + x3: i32, + x4: i32, + flag_a: bool, + flag_b: bool, +) !void { + _ = x0; + _ = x1; + _ = x2; + _ = x3; + _ = x4; + _ = flag_a; + // With this bug present, `flag_b` would actually contain the value 17. + // Note: this bug only presents itself on debug mode. + try std.testing.expect(@ptrCast(*const u8, &flag_b).* == 1); +} + +pub fn b(x: *X) !void { + try a(0, 1, 2, 3, 4, x.x.a, x.x.b); +} + +test "bug 9584" { + var flags = A{ + .a = false, + .b = true, + .c = false, + .d = false, + + .e = false, + .f = true, + .g = false, + .h = false, + }; + var x = X{ + .x = flags, + }; + try b(&x); +} From 2129cc5c54e76268177f3d6e8d7598a86496e265 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Wed, 18 Aug 2021 22:09:25 -0400 Subject: [PATCH 5/9] stage2: fix typo this made errors go from stuff like: > type comptime_int cannot represent integer value 40 to > type u5 cannot represent integer value 40 which makes much more sense --- src/Sema.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 0813f749e2..8b6f6d4a9f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8323,7 +8323,7 @@ fn coerceNum( return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{}); } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { if (!val.intFitsInType(dest_type, target)) { - return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ inst_ty, val }); + return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val }); } return try sema.addConstant(dest_type, val); } From d785dc49aa6fc422d936928e504e50a63fa37759 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Tue, 17 Aug 2021 10:49:17 -0400 Subject: [PATCH 6/9] stage2: add error set type equality --- src/type.zig | 23 ++++++++++++++++------- test/cases.zig | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/type.zig b/src/type.zig index 28b87a8afe..41f392c04a 100644 --- a/src/type.zig +++ b/src/type.zig @@ -534,15 +534,24 @@ pub const Type = extern union { return a_data.error_set.eql(b_data.error_set) and a_data.payload.eql(b_data.payload); }, .ErrorSet => { - const a_is_anyerror = a.tag() == .anyerror; - const b_is_anyerror = b.tag() == .anyerror; + if (a.tag() == .anyerror and b.tag() == .anyerror) { + return true; + } - if (a_is_anyerror and b_is_anyerror) return true; - if (a_is_anyerror or b_is_anyerror) return false; + if (a.tag() == .error_set and b.tag() == .error_set) { + return a.castTag(.error_set).?.data.owner_decl == b.castTag(.error_set).?.data.owner_decl; + } - std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ - a.tag(), b.tag(), - }); + if (a.tag() == .error_set_inferred and b.tag() == .error_set_inferred) { + return a.castTag(.error_set_inferred).?.data.func == b.castTag(.error_set_inferred).?.data.func; + } + + if (a.tag() == .error_set_single and b.tag() == .error_set_single) { + const a_data = a.castTag(.error_set_single).?.data; + const b_data = b.castTag(.error_set_single).?.data; + return std.mem.eql(u8, a_data, b_data); + } + return false; }, .Opaque, .Float, diff --git a/test/cases.zig b/test/cases.zig index ffff88f7d8..302246065f 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -1567,6 +1567,24 @@ pub fn addCases(ctx: *TestContext) !void { ":2:20: note: '||' merges error sets; 'or' performs boolean OR", }); } + { + var case = ctx.exe("error set equality", linux_x64); + + case.addCompareOutput( + \\pub fn main() void { + \\ assert(@TypeOf(error.Foo) == @TypeOf(error.Foo)); + \\ assert(@TypeOf(error.Bar) != @TypeOf(error.Foo)); + \\ assert(anyerror == anyerror); + \\ assert(error{Foo} != error{Foo}); + \\ // TODO put inferred error sets here when @typeInfo works + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + } { var case = ctx.exe("inline assembly", linux_x64); From 7e7d67d8eed45bcf3908edd2f4ca864144fffad5 Mon Sep 17 00:00:00 2001 From: Meghan Date: Thu, 19 Aug 2021 04:12:11 -0700 Subject: [PATCH 7/9] std.fmt: add support for printing slices strings (#9562) --- lib/std/fmt.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 9ab111496c..5cbdc57832 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -544,6 +544,13 @@ pub fn formatType( return formatText(value, actual_fmt, options, writer); } } + if (comptime std.meta.trait.isZigString(info.child)) { + for (value) |item, i| { + if (i != 0) try formatText(", ", actual_fmt, options, writer); + try formatText(item, actual_fmt, options, writer); + } + return; + } @compileError("Unknown format string: '" ++ actual_fmt ++ "' for type '" ++ @typeName(T) ++ "'"); }, .Enum, .Union, .Struct => { From 62fe4a0ba8a08b9477e772841d3ae128937f0752 Mon Sep 17 00:00:00 2001 From: Justin Whear Date: Thu, 19 Aug 2021 12:18:23 -0700 Subject: [PATCH 8/9] std.rand.Random: add enumValue() (#9583) * add Random.enumValue() * edits suggested by review * applied zig fmt * Rewrite to use std.enums.values Implemented pfgithub's suggestion to rewrite against this function, greatly simplifying the implementation. Co-authored-by: Justin Whear --- lib/std/rand.zig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 66f758cb48..7d967d3715 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -47,6 +47,19 @@ pub const Random = struct { return r.int(u1) != 0; } + /// Returns a random value from an enum, evenly distributed. + pub fn enumValue(r: *Random, comptime EnumType: type) EnumType { + if (comptime !std.meta.trait.is(.Enum)(EnumType)) { + @compileError("Random.enumValue requires an enum type, not a " ++ @typeName(EnumType)); + } + + // We won't use int -> enum casting because enum elements can have + // arbitrary values. Instead we'll randomly pick one of the type's values. + const values = std.enums.values(EnumType); + const index = r.uintLessThan(usize, values.len); + return values[index]; + } + /// Returns a random int `i` such that `0 <= i <= maxInt(T)`. /// `i` is evenly distributed. pub fn int(r: *Random, comptime T: type) T { @@ -377,6 +390,23 @@ fn testRandomBoolean() !void { try expect(r.random.boolean() == true); } +test "Random enum" { + try testRandomEnumValue(); + comptime try testRandomEnumValue(); +} +fn testRandomEnumValue() !void { + const TestEnum = enum { + First, + Second, + Third, + }; + var r = SequentialPrng.init(); + r.next_value = 0; + try expect(r.random.enumValue(TestEnum) == TestEnum.First); + try expect(r.random.enumValue(TestEnum) == TestEnum.First); + try expect(r.random.enumValue(TestEnum) == TestEnum.First); +} + test "Random intLessThan" { @setEvalBranchQuota(10000); try testRandomIntLessThan(); From df10e998ee4a935f49943fb5c0ef134f336c6ee3 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Wed, 18 Aug 2021 21:29:32 -0400 Subject: [PATCH 9/9] stage2 x86_64: enable bitwise and + or and add tests --- src/codegen.zig | 2 ++ test/cases.zig | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/codegen.zig b/src/codegen.zig index d5b106dbe3..11c007dbed 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1247,6 +1247,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and), + .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs), else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}), }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1256,6 +1257,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or), + .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs), else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}), }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); diff --git a/test/cases.zig b/test/cases.zig index 302246065f..e8479b792e 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -1535,6 +1535,48 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + { + var case = ctx.exe("runtime bitwise and", linux_x64); + + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u32 = 10; + \\ var j: u32 = 11; + \\ assert(i & 1 == 0); + \\ assert(j & 1 == 1); + \\ var m1: u32 = 0b1111; + \\ var m2: u32 = 0b0000; + \\ assert(m1 & 0b1010 == 0b1010); + \\ assert(m2 & 0b1010 == 0b0000); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + } + { + var case = ctx.exe("runtime bitwise or", linux_x64); + + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u32 = 10; + \\ var j: u32 = 11; + \\ assert(i | 1 == 11); + \\ assert(j | 1 == 11); + \\ var m1: u32 = 0b1111; + \\ var m2: u32 = 0b0000; + \\ assert(m1 | 0b1010 == 0b1111); + \\ assert(m2 | 0b1010 == 0b1010); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + } { var case = ctx.exe("merge error sets", linux_x64);