From e5d5a8bc4ea6b27dc3540ad4800a1231ff50b33d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 2 Jan 2025 03:10:19 -0500 Subject: [PATCH] x86_64: implement switch jump tables --- lib/std/Thread/Condition.zig | 6 +- lib/std/Thread/Mutex.zig | 2 +- lib/std/debug.zig | 6 +- lib/std/debug/SelfInfo.zig | 8 +- lib/std/heap.zig | 2 +- lib/std/math/big/int.zig | 2 +- lib/std/os.zig | 2 +- lib/std/os/windows.zig | 2 +- lib/std/posix.zig | 2 +- lib/std/posix/test.zig | 2 +- lib/std/simd.zig | 10 +- lib/std/zig/system/NativePaths.zig | 2 +- src/Liveness.zig | 19 +- src/arch/x86_64/CodeGen.zig | 299 +++++++++++++++++++++--- src/arch/x86_64/Emit.zig | 92 +++++--- src/arch/x86_64/Lower.zig | 15 +- src/arch/x86_64/Mir.zig | 9 +- src/arch/x86_64/bits.zig | 5 +- src/arch/x86_64/encoder.zig | 103 ++++---- src/link/MachO.zig | 2 +- test/behavior/align.zig | 20 +- test/behavior/asm.zig | 2 +- test/behavior/call.zig | 1 + test/behavior/cast.zig | 4 +- test/behavior/eval.zig | 2 +- test/behavior/math.zig | 8 +- test/behavior/maximum_minimum.zig | 2 +- test/behavior/muladd.zig | 8 +- test/behavior/saturating_arithmetic.zig | 10 +- test/behavior/struct.zig | 8 +- test/behavior/var_args.zig | 10 +- test/behavior/vector.zig | 8 +- test/behavior/wrapping_arithmetic.zig | 2 +- 33 files changed, 476 insertions(+), 199 deletions(-) diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index e6c25d761c..65bfa32ad0 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -161,17 +161,17 @@ const WindowsImpl = struct { } } - if (comptime builtin.mode == .Debug) { + if (builtin.mode == .Debug) { // The internal state of the DebugMutex needs to be handled here as well. mutex.impl.locking_thread.store(0, .unordered); } const rc = os.windows.kernel32.SleepConditionVariableSRW( &self.condition, - if (comptime builtin.mode == .Debug) &mutex.impl.impl.srwlock else &mutex.impl.srwlock, + if (builtin.mode == .Debug) &mutex.impl.impl.srwlock else &mutex.impl.srwlock, timeout_ms, 0, // the srwlock was assumed to acquired in exclusive mode not shared ); - if (comptime builtin.mode == .Debug) { + if (builtin.mode == .Debug) { // The internal state of the DebugMutex needs to be handled here as well. mutex.impl.locking_thread.store(std.Thread.getCurrentId(), .unordered); } diff --git a/lib/std/Thread/Mutex.zig b/lib/std/Thread/Mutex.zig index be421c4c94..402c96a4d5 100644 --- a/lib/std/Thread/Mutex.zig +++ b/lib/std/Thread/Mutex.zig @@ -158,7 +158,7 @@ const FutexImpl = struct { // On x86, use `lock bts` instead of `lock cmpxchg` as: // - they both seem to mark the cache-line as modified regardless: https://stackoverflow.com/a/63350048 // - `lock bts` is smaller instruction-wise which makes it better for inlining - if (comptime builtin.target.cpu.arch.isX86()) { + if (builtin.target.cpu.arch.isX86()) { const locked_bit = @ctz(locked); return self.state.bitSet(locked_bit, .acquire) == 0; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 3664bd0cef..02eb60d6a7 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -179,7 +179,7 @@ pub fn dumpHexFallible(bytes: []const u8) !void { /// TODO multithreaded awareness pub fn dumpCurrentStackTrace(start_addr: ?usize) void { nosuspend { - if (comptime builtin.target.isWasm()) { + if (builtin.target.isWasm()) { if (native_os == .wasi) { const stderr = io.getStdErr().writer(); stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; @@ -267,7 +267,7 @@ pub inline fn getContext(context: *ThreadContext) bool { /// TODO multithreaded awareness pub fn dumpStackTraceFromBase(context: *ThreadContext) void { nosuspend { - if (comptime builtin.target.isWasm()) { + if (builtin.target.isWasm()) { if (native_os == .wasi) { const stderr = io.getStdErr().writer(); stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; @@ -365,7 +365,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT /// TODO multithreaded awareness pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void { nosuspend { - if (comptime builtin.target.isWasm()) { + if (builtin.target.isWasm()) { if (native_os == .wasi) { const stderr = io.getStdErr().writer(); stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index 544cf0ac6f..4dd0b4e842 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -121,13 +121,13 @@ pub fn deinit(self: *SelfInfo) void { } pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module { - if (comptime builtin.target.isDarwin()) { + if (builtin.target.isDarwin()) { return self.lookupModuleDyld(address); } else if (native_os == .windows) { return self.lookupModuleWin32(address); } else if (native_os == .haiku) { return self.lookupModuleHaiku(address); - } else if (comptime builtin.target.isWasm()) { + } else if (builtin.target.isWasm()) { return self.lookupModuleWasm(address); } else { return self.lookupModuleDl(address); @@ -138,13 +138,13 @@ pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module { // This can be called when getModuleForAddress fails, so implementations should provide // a path that doesn't rely on any side-effects of a prior successful module lookup. pub fn getModuleNameForAddress(self: *SelfInfo, address: usize) ?[]const u8 { - if (comptime builtin.target.isDarwin()) { + if (builtin.target.isDarwin()) { return self.lookupModuleNameDyld(address); } else if (native_os == .windows) { return self.lookupModuleNameWin32(address); } else if (native_os == .haiku) { return null; - } else if (comptime builtin.target.isWasm()) { + } else if (builtin.target.isWasm()) { return null; } else { return self.lookupModuleNameDl(address); diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 3d19d8daa6..33f79e265a 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -890,7 +890,7 @@ test { _ = @import("heap/memory_pool.zig"); _ = ArenaAllocator; _ = GeneralPurposeAllocator; - if (comptime builtin.target.isWasm()) { + if (builtin.target.isWasm()) { _ = WasmAllocator; _ = WasmPageAllocator; } diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 98d37d8994..2549644dbc 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2523,7 +2523,7 @@ pub const Const = struct { /// Returns the number of leading zeros in twos-complement form. pub fn clz(a: Const, bits: Limb) Limb { // Limbs are stored in little-endian order but we need to iterate big-endian. - if (!a.positive) return 0; + if (!a.positive and !a.eqlZero()) return 0; var total_limb_lz: Limb = 0; var i: usize = a.limbs.len; const bits_per_limb = @bitSizeOf(Limb); diff --git a/lib/std/os.zig b/lib/std/os.zig index 27e6577111..80f45dd59d 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -157,7 +157,7 @@ pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix. return target; }, .freebsd => { - if (comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) { + if (builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) { var kfile: std.c.kinfo_file = undefined; kfile.structsize = std.c.KINFO_FILE_SIZE; switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index ceed0618d1..cfc2403800 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1061,7 +1061,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil // us INVALID_PARAMETER. // The same reasoning for win10_rs5 as in os.renameatW() applies (FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5). var need_fallback = true; - if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs5)) { + if (builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs5)) { // Deletion with posix semantics if the filesystem supports it. var info = FILE_DISPOSITION_INFORMATION_EX{ .Flags = FILE_DISPOSITION_DELETE | diff --git a/lib/std/posix.zig b/lib/std/posix.zig index f07421fdc1..f99875437d 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -6819,7 +6819,7 @@ pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t { } }, .freebsd => { - if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt) + if (builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt) @compileError("memfd_create is unavailable on FreeBSD < 13.0"); const rc = system.memfd_create(name, flags); switch (errno(rc)) { diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 653637c0a7..89346e66fe 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -804,7 +804,7 @@ test "getrlimit and setrlimit" { // // This happens for example if RLIMIT_MEMLOCK is bigger than ~2GiB. // In that case the following the limit would be RLIM_INFINITY and the following setrlimit fails with EPERM. - if (comptime builtin.cpu.arch.isMIPS() and builtin.link_libc) { + if (builtin.cpu.arch.isMIPS() and builtin.link_libc) { if (limit.cur != linux.RLIM.INFINITY) { try posix.setrlimit(resource, limit); } diff --git a/lib/std/simd.zig b/lib/std/simd.zig index e06a873f09..b4aef7246c 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -163,7 +163,7 @@ pub fn interlace(vecs: anytype) @Vector(vectorLength(@TypeOf(vecs[0])) * vecs.le // The indices are correct. The problem seems to be with the @shuffle builtin. // On MIPS, the test that interlaces small_base gives { 0, 2, 0, 0, 64, 255, 248, 200, 0, 0 }. // Calling this with two inputs seems to work fine, but I'll let the compile error trigger for all inputs, just to be safe. - comptime if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why interlace() doesn't work on MIPS"); + if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why interlace() doesn't work on MIPS"); const VecType = @TypeOf(vecs[0]); const vecs_arr = @as([vecs.len]VecType, vecs); @@ -248,7 +248,7 @@ test "vector patterns" { try std.testing.expectEqual([8]u32{ 10, 20, 30, 40, 55, 66, 77, 88 }, join(base, other_base)); try std.testing.expectEqual([2]u32{ 20, 30 }, extract(base, 1, 2)); - if (comptime !builtin.cpu.arch.isMIPS()) { + if (!builtin.cpu.arch.isMIPS()) { try std.testing.expectEqual([8]u32{ 10, 55, 20, 66, 30, 77, 40, 88 }, interlace(.{ base, other_base })); const small_braid = interlace(small_bases); @@ -390,7 +390,7 @@ pub fn prefixScanWithFunc( comptime identity: std.meta.Child(@TypeOf(vec)), ) if (ErrorType == void) @TypeOf(vec) else ErrorType!@TypeOf(vec) { // I haven't debugged this, but it might be a cousin of sorts to what's going on with interlace. - comptime if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why prefixScan doesn't work on MIPS"); + if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why prefixScan doesn't work on MIPS"); const len = vectorLength(@TypeOf(vec)); @@ -465,9 +465,7 @@ test "vector prefix scan" { if ((builtin.cpu.arch == .armeb or builtin.cpu.arch == .thumbeb) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/22060 if (builtin.cpu.arch == .aarch64_be and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21893 - if (comptime builtin.cpu.arch.isMIPS()) { - return error.SkipZigTest; - } + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; const int_base = @Vector(4, i32){ 11, 23, 9, -21 }; const float_base = @Vector(4, f32){ 2, 0.5, -10, 6.54321 }; diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig index 3c96134556..be8e7b05dd 100644 --- a/lib/std/zig/system/NativePaths.zig +++ b/lib/std/zig/system/NativePaths.zig @@ -83,7 +83,7 @@ pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths { // TODO: consider also adding homebrew paths // TODO: consider also adding macports paths - if (comptime builtin.target.isDarwin()) { + if (builtin.target.isDarwin()) { if (std.zig.system.darwin.isSdkInstalled(arena)) sdk: { const sdk = std.zig.system.darwin.getSdk(arena, native_target) orelse break :sdk; try self.addLibDir(try std.fs.path.join(arena, &.{ sdk, "usr/lib" })); diff --git a/src/Liveness.zig b/src/Liveness.zig index e6ed782a21..e6e9c07363 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -719,32 +719,25 @@ pub const SwitchBrTable = struct { /// Caller owns the memory. pub fn getSwitchBr(l: Liveness, gpa: Allocator, inst: Air.Inst.Index, cases_len: u32) Allocator.Error!SwitchBrTable { - var index: usize = l.special.get(inst) orelse return SwitchBrTable{ - .deaths = &.{}, - }; + var index: usize = l.special.get(inst) orelse return .{ .deaths = &.{} }; const else_death_count = l.extra[index]; index += 1; - var deaths = std.ArrayList([]const Air.Inst.Index).init(gpa); - defer deaths.deinit(); - try deaths.ensureTotalCapacity(cases_len + 1); + var deaths = try gpa.alloc([]const Air.Inst.Index, cases_len); + errdefer gpa.free(deaths); var case_i: u32 = 0; while (case_i < cases_len - 1) : (case_i += 1) { const case_death_count: u32 = l.extra[index]; index += 1; - const case_deaths: []const Air.Inst.Index = @ptrCast(l.extra[index..][0..case_death_count]); + deaths[case_i] = @ptrCast(l.extra[index..][0..case_death_count]); index += case_death_count; - deaths.appendAssumeCapacity(case_deaths); } { // Else - const else_deaths: []const Air.Inst.Index = @ptrCast(l.extra[index..][0..else_death_count]); - deaths.appendAssumeCapacity(else_deaths); + deaths[case_i] = @ptrCast(l.extra[index..][0..else_death_count]); } - return SwitchBrTable{ - .deaths = try deaths.toOwnedSlice(), - }; + return .{ .deaths = deaths }; } /// Note that this information is technically redundant, but is useful for diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d2d1fedb6f..79e69c7c07 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -61,9 +61,10 @@ src_loc: Zcu.LazySrcLoc, eflags_inst: ?Air.Inst.Index = null, /// MIR Instructions -mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, +mir_instructions: std.MultiArrayList(Mir.Inst) = .empty, /// MIR extra data mir_extra: std.ArrayListUnmanaged(u32) = .empty, +mir_table: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, /// Byte offset within the source file of the ending curly. end_di_line: u32, @@ -75,8 +76,8 @@ end_di_column: u32, exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined, -const_tracking: ConstTrackingMap = .{}, -inst_tracking: InstTrackingMap = .{}, +const_tracking: ConstTrackingMap = .empty, +inst_tracking: InstTrackingMap = .empty, // Key is the block instruction blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, @@ -86,16 +87,26 @@ register_manager: RegisterManager = .{}, /// Generation of the current scope, increments by 1 for every entered scope. scope_generation: u32 = 0, -frame_allocs: std.MultiArrayList(FrameAlloc) = .{}, +frame_allocs: std.MultiArrayList(FrameAlloc) = .empty, free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .empty, -frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{}, +frame_locs: std.MultiArrayList(Mir.FrameLoc) = .empty, loops: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { /// The state to restore before branching. state: State, /// The branch target. target: Mir.Inst.Index, -}) = .{}, +}) = .empty, +loop_switches: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { + start: u31, + len: u11, + min: Value, + else_relocs: union(enum) { + @"unreachable", + forward: std.ArrayListUnmanaged(Mir.Inst.Index), + backward: Mir.Inst.Index, + }, +}) = .empty, next_temp_index: Temp.Index = @enumFromInt(0), temp_type: [Temp.Index.max]Type = undefined, @@ -904,6 +915,7 @@ pub fn generate( function.free_frame_indices.deinit(gpa); function.frame_locs.deinit(gpa); function.loops.deinit(gpa); + function.loop_switches.deinit(gpa); var block_it = function.blocks.valueIterator(); while (block_it.next()) |block| block.deinit(gpa); function.blocks.deinit(gpa); @@ -912,6 +924,7 @@ pub fn generate( function.exitlude_jump_relocs.deinit(gpa); function.mir_instructions.deinit(gpa); function.mir_extra.deinit(gpa); + function.mir_table.deinit(gpa); } try function.inst_tracking.ensureTotalCapacity(gpa, Temp.Index.max); for (0..Temp.Index.max) |temp_index| { @@ -978,6 +991,7 @@ pub fn generate( var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), .extra = try function.mir_extra.toOwnedSlice(gpa), + .table = try function.mir_table.toOwnedSlice(gpa), .frame_locs = function.frame_locs.toOwnedSlice(), }; defer mir.deinit(gpa); @@ -1012,7 +1026,6 @@ pub fn generate( }, .prev_di_pc = 0, }; - defer emit.deinit(); emit.emitMir() catch |err| switch (err) { error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), @@ -1056,6 +1069,7 @@ pub fn generateLazy( defer { function.mir_instructions.deinit(gpa); function.mir_extra.deinit(gpa); + function.mir_table.deinit(gpa); } function.genLazy(lazy_sym) catch |err| switch (err) { @@ -1067,6 +1081,7 @@ pub fn generateLazy( var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), .extra = try function.mir_extra.toOwnedSlice(gpa), + .table = try function.mir_table.toOwnedSlice(gpa), .frame_locs = function.frame_locs.toOwnedSlice(), }; defer mir.deinit(gpa); @@ -1093,7 +1108,6 @@ pub fn generateLazy( .prev_di_loc = undefined, // no debug info yet .prev_di_pc = undefined, // no debug info yet }; - defer emit.deinit(); emit.emitMir() catch |err| switch (err) { error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}), @@ -1161,6 +1175,7 @@ fn formatWipMir( .mir = .{ .instructions = data.self.mir_instructions.slice(), .extra = data.self.mir_extra.items, + .table = data.self.mir_table.items, .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(), }, .cc = .auto, @@ -20748,25 +20763,195 @@ fn lowerBlock(self: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index self.getValueIfFree(tracking.short, inst); } -fn lowerSwitchBr(self: *CodeGen, inst: Air.Inst.Index, switch_br: Air.UnwrappedSwitch, condition: MCValue) !void { +fn lowerSwitchBr( + self: *CodeGen, + inst: Air.Inst.Index, + switch_br: Air.UnwrappedSwitch, + condition: MCValue, + condition_dies: bool, + is_loop: bool, +) !void { const zcu = self.pt.zcu; const condition_ty = self.typeOf(switch_br.operand); - const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.cases_len + 1); - defer self.gpa.free(liveness.deaths); - const signedness = switch (condition_ty.zigTypeTag(zcu)) { - .bool, .pointer => .unsigned, - .int, .@"enum", .error_set => condition_ty.intInfo(zcu).signedness, - else => unreachable, + const ExpectedContents = extern struct { + liveness_deaths: [1 << 8 | 1]Air.Inst.Index, + bigint_limbs: [std.math.big.int.calcTwosCompLimbCount(1 << 8)]std.math.big.Limb, + relocs: [1 << 6]Mir.Inst.Index, }; + var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = + std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + const allocator = stack.get(); self.scope_generation += 1; const state = try self.saveState(); - var it = switch_br.iterateCases(); - while (it.next()) |case| { - var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); - defer self.gpa.free(relocs); + const liveness = try self.liveness.getSwitchBr(allocator, inst, switch_br.cases_len + 1); + defer allocator.free(liveness.deaths); + + if (!self.mod.pic and self.target.ofmt == .elf) table: { + var prong_items: u32 = 0; + var min: ?Value = null; + var max: ?Value = null; + { + var cases_it = switch_br.iterateCases(); + while (cases_it.next()) |case| { + prong_items += @intCast(case.items.len + case.ranges.len); + for (case.items) |item| { + const val = Value.fromInterned(item.toInterned().?); + if (min == null or val.compareHetero(.lt, min.?, zcu)) min = val; + if (max == null or val.compareHetero(.gt, max.?, zcu)) max = val; + } + for (case.ranges) |range| { + const low = Value.fromInterned(range[0].toInterned().?); + if (min == null or low.compareHetero(.lt, min.?, zcu)) min = low; + const high = Value.fromInterned(range[1].toInterned().?); + if (max == null or high.compareHetero(.gt, max.?, zcu)) max = high; + } + } + } + // This condition also triggers for switches with no non-else prongs and switches on bool. + if (prong_items < 1 << 2 or prong_items > 1 << 8) break :table; + + var min_space: Value.BigIntSpace = undefined; + const min_bigint = min.?.toBigInt(&min_space, zcu); + var max_space: Value.BigIntSpace = undefined; + const max_bigint = max.?.toBigInt(&max_space, zcu); + const limbs = try allocator.alloc( + std.math.big.Limb, + @max(min_bigint.limbs.len, max_bigint.limbs.len) + 1, + ); + defer allocator.free(limbs); + const table_len = table_len: { + var table_len_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; + table_len_bigint.sub(max_bigint, min_bigint); + assert(table_len_bigint.positive); // min <= max + break :table_len @as(u11, table_len_bigint.toConst().to(u10) catch break :table) + 1; // no more than a 1024 entry table + }; + assert(prong_items <= table_len); // each prong item introduces at least one unique integer to the range + if (prong_items < table_len >> 2) break :table; // no more than 75% waste + + const condition_index = if (condition_dies and condition.isModifiable()) condition else condition_index: { + const condition_index = try self.allocTempRegOrMem(condition_ty, true); + try self.genCopy(condition_ty, condition_index, condition, .{}); + break :condition_index condition_index; + }; + try self.spillEflagsIfOccupied(); + if (min.?.orderAgainstZero(zcu).compare(.neq)) try self.genBinOpMir( + .{ ._, .sub }, + condition_ty, + condition_index, + .{ .air_ref = Air.internedToRef(min.?.toIntern()) }, + ); + const else_reloc = if (switch_br.else_body_len > 0) else_reloc: { + try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition_index, .{ .immediate = table_len - 1 }); + break :else_reloc try self.asmJccReloc(.a, undefined); + } else undefined; + const table_start: u31 = @intCast(self.mir_table.items.len); + { + const condition_index_reg = if (condition_index.isRegister()) + condition_index.getReg().? + else + try self.copyToTmpRegister(.usize, condition_index); + const condition_index_lock = self.register_manager.lockReg(condition_index_reg); + defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock); + try self.truncateRegister(condition_ty, condition_index_reg); + const ptr_size = @divExact(self.target.ptrBitWidth(), 8); + try self.asmMemory(.{ ._, .jmp }, .{ + .base = .table, + .mod = .{ .rm = .{ + .size = .ptr, + .index = registerAlias(condition_index_reg, ptr_size), + .scale = .fromFactor(@intCast(ptr_size)), + .disp = table_start * ptr_size, + } }, + }); + } + const else_reloc_marker: u32 = 0; + assert(self.mir_instructions.len > else_reloc_marker); + try self.mir_table.appendNTimes(self.gpa, else_reloc_marker, table_len); + if (is_loop) try self.loop_switches.putNoClobber(self.gpa, inst, .{ + .start = table_start, + .len = table_len, + .min = min.?, + .else_relocs = if (switch_br.else_body_len > 0) .{ .forward = .empty } else .@"unreachable", + }); + defer if (is_loop) { + var loop_switch_data = self.loop_switches.fetchRemove(inst).?.value; + switch (loop_switch_data.else_relocs) { + .@"unreachable", .backward => {}, + .forward => |*else_relocs| else_relocs.deinit(self.gpa), + } + }; + var cases_it = switch_br.iterateCases(); + while (cases_it.next()) |case| { + { + const table = self.mir_table.items[table_start..][0..table_len]; + for (case.items) |item| { + const val = Value.fromInterned(item.toInterned().?); + var val_space: Value.BigIntSpace = undefined; + const val_bigint = val.toBigInt(&val_space, zcu); + var index_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; + index_bigint.sub(val_bigint, min_bigint); + table[index_bigint.toConst().to(u10) catch unreachable] = @intCast(self.mir_instructions.len); + } + for (case.ranges) |range| { + var low_space: Value.BigIntSpace = undefined; + const low_bigint = Value.fromInterned(range[0].toInterned().?).toBigInt(&low_space, zcu); + var high_space: Value.BigIntSpace = undefined; + const high_bigint = Value.fromInterned(range[1].toInterned().?).toBigInt(&high_space, zcu); + var index_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; + index_bigint.sub(low_bigint, min_bigint); + const start = index_bigint.toConst().to(u10) catch unreachable; + index_bigint.sub(high_bigint, min_bigint); + const end = @as(u11, index_bigint.toConst().to(u10) catch unreachable) + 1; + @memset(table[start..end], @intCast(self.mir_instructions.len)); + } + } + + for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); + + try self.genBodyBlock(case.body); + try self.restoreState(state, &.{}, .{ + .emit_instructions = false, + .update_tracking = true, + .resurrect = true, + .close_scope = true, + }); + } + if (switch_br.else_body_len > 0) { + const else_body = cases_it.elseBody(); + + const else_deaths = liveness.deaths.len - 1; + for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand); + + self.performReloc(else_reloc); + if (is_loop) { + const loop_switch_data = self.loop_switches.getPtr(inst).?; + for (loop_switch_data.else_relocs.forward.items) |reloc| self.performReloc(reloc); + loop_switch_data.else_relocs.forward.deinit(self.gpa); + loop_switch_data.else_relocs = .{ .backward = @intCast(self.mir_instructions.len) }; + } + for (self.mir_table.items[table_start..][0..table_len]) |*entry| if (entry.* == else_reloc_marker) { + entry.* = @intCast(self.mir_instructions.len); + }; + + try self.genBodyBlock(else_body); + try self.restoreState(state, &.{}, .{ + .emit_instructions = false, + .update_tracking = true, + .resurrect = true, + .close_scope = true, + }); + } + return; + } + + const signedness = if (condition_ty.isAbiInt(zcu)) condition_ty.intInfo(zcu).signedness else .unsigned; + var cases_it = switch_br.iterateCases(); + while (cases_it.next()) |case| { + var relocs = try allocator.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); + defer allocator.free(relocs); try self.spillEflagsIfOccupied(); for (case.items, relocs[0..case.items.len]) |item, *reloc| { @@ -20849,9 +21034,8 @@ fn lowerSwitchBr(self: *CodeGen, inst: Air.Inst.Index, switch_br: Air.UnwrappedS // Relocate the "skip" branch to fall through to the next case. self.performReloc(skip_case_reloc); } - if (switch_br.else_body_len > 0) { - const else_body = it.elseBody(); + const else_body = cases_it.elseBody(); const else_deaths = liveness.deaths.len - 1; for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand); @@ -20873,11 +21057,11 @@ fn airSwitchBr(self: *CodeGen, inst: Air.Inst.Index) !void { // If the condition dies here in this switch instruction, process // that death now instead of later as this has an effect on // whether it needs to be spilled in the branches - if (self.liveness.operandDies(inst, 0)) { + const condition_dies = self.liveness.operandDies(inst, 0); + if (condition_dies) { if (switch_br.operand.toIndex()) |op_inst| try self.processDeath(op_inst); } - - try self.lowerSwitchBr(inst, switch_br, condition); + try self.lowerSwitchBr(inst, switch_br, condition, condition_dies, false); // We already took care of pl_op.operand earlier, so there's nothing left to do } @@ -20915,7 +21099,7 @@ fn airLoopSwitchBr(self: *CodeGen, inst: Air.Inst.Index) !void { // Stop tracking block result without forgetting tracking info try self.freeValue(mat_cond); - try self.lowerSwitchBr(inst, switch_br, mat_cond); + try self.lowerSwitchBr(inst, switch_br, mat_cond, true, true); try self.processDeath(inst); } @@ -20924,8 +21108,67 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void { const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br; const block_ty = self.typeOfIndex(br.block_inst); - const block_tracking = self.inst_tracking.getPtr(br.block_inst).?; const loop_data = self.loops.getPtr(br.block_inst).?; + if (self.loop_switches.getPtr(br.block_inst)) |table| { + // Process operand death so that it is properly accounted for in the State below. + const condition_dies = self.liveness.operandDies(inst, 0); + + try self.restoreState(loop_data.state, &.{}, .{ + .emit_instructions = true, + .update_tracking = false, + .resurrect = false, + .close_scope = false, + }); + + const condition_ty = self.typeOf(br.operand); + const condition = try self.resolveInst(br.operand); + const condition_index = if (condition_dies and condition.isModifiable()) condition else condition_index: { + const condition_index = try self.allocTempRegOrMem(condition_ty, true); + try self.genCopy(condition_ty, condition_index, condition, .{}); + break :condition_index condition_index; + }; + try self.spillEflagsIfOccupied(); + if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir( + .{ ._, .sub }, + condition_ty, + condition_index, + .{ .air_ref = Air.internedToRef(table.min.toIntern()) }, + ); + switch (table.else_relocs) { + .@"unreachable" => {}, + .forward => |*else_relocs| { + try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition_index, .{ .immediate = table.len - 1 }); + try else_relocs.append(self.gpa, try self.asmJccReloc(.a, undefined)); + }, + .backward => |else_reloc| { + try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition_index, .{ .immediate = table.len - 1 }); + _ = try self.asmJccReloc(.a, else_reloc); + }, + } + { + const condition_index_reg = if (condition_index.isRegister()) + condition_index.getReg().? + else + try self.copyToTmpRegister(.usize, condition_index); + const condition_index_lock = self.register_manager.lockReg(condition_index_reg); + defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock); + try self.truncateRegister(condition_ty, condition_index_reg); + const ptr_size = @divExact(self.target.ptrBitWidth(), 8); + try self.asmMemory(.{ ._, .jmp }, .{ + .base = .table, + .mod = .{ .rm = .{ + .size = .ptr, + .index = registerAlias(condition_index_reg, ptr_size), + .scale = .fromFactor(@intCast(ptr_size)), + .disp = @intCast(table.start * ptr_size), + } }, + }); + } + + return self.finishAir(inst, .none, .{ br.operand, .none, .none }); + } + + const block_tracking = self.inst_tracking.getPtr(br.block_inst).?; done: { try self.getValue(block_tracking.short, null); const src_mcv = try self.resolveInst(br.operand); @@ -22543,6 +22786,7 @@ fn genSetMem( .none => .{ .immediate = @bitCast(@as(i64, disp)) }, .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } }, .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, + .table => unreachable, .reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } }, }; switch (src_mcv) { @@ -22652,6 +22896,7 @@ fn genSetMem( .index = frame_index, .off = disp, }).compare(.gte, src_align), + .table => unreachable, .reloc => false, })).write( self, @@ -23260,6 +23505,7 @@ fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void { const ptr_lock = switch (ptr_mem.base) { .none, .frame, .reloc => null, .reg => |reg| self.register_manager.lockReg(reg), + .table => unreachable, }; defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); @@ -23327,6 +23573,7 @@ fn atomicOp( const mem_lock = switch (ptr_mem.base) { .none, .frame, .reloc => null, .reg => |reg| self.register_manager.lockReg(reg), + .table => unreachable, }; defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 6e0d75f883..bd5efec81c 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -10,22 +10,21 @@ prev_di_loc: Loc, /// Relative to the beginning of `code`. prev_di_pc: usize, -code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, -relocs: std.ArrayListUnmanaged(Reloc) = .empty, - pub const Error = Lower.Error || error{ EmitFail, } || link.File.UpdateDebugInfoError; pub fn emitMir(emit: *Emit) Error!void { const gpa = emit.lower.bin_file.comp.gpa; + const code_offset_mapping = try emit.lower.allocator.alloc(u32, emit.lower.mir.instructions.len); + defer emit.lower.allocator.free(code_offset_mapping); + var relocs: std.ArrayListUnmanaged(Reloc) = .empty; + defer relocs.deinit(emit.lower.allocator); + var table_relocs: std.ArrayListUnmanaged(TableReloc) = .empty; + defer table_relocs.deinit(emit.lower.allocator); for (0..emit.lower.mir.instructions.len) |mir_i| { const mir_index: Mir.Inst.Index = @intCast(mir_i); - try emit.code_offset_mapping.putNoClobber( - emit.lower.allocator, - mir_index, - @intCast(emit.code.items.len), - ); + code_offset_mapping[mir_index] = @intCast(emit.code.items.len); const lowered = try emit.lower.lowerMir(mir_index); var lowered_relocs = lowered.relocs; for (lowered.insts, 0..) |lowered_inst, lowered_index| { @@ -89,13 +88,17 @@ pub fn emitMir(emit: *Emit) Error!void { lowered_relocs[0].lowered_inst_index == lowered_index) : ({ lowered_relocs = lowered_relocs[1..]; }) switch (lowered_relocs[0].target) { - .inst => |target| try emit.relocs.append(emit.lower.allocator, .{ + .inst => |target| try relocs.append(emit.lower.allocator, .{ .source = start_offset, .source_offset = end_offset - 4, .target = target, .target_offset = lowered_relocs[0].off, .length = @intCast(end_offset - start_offset), }), + .table => try table_relocs.append(emit.lower.allocator, .{ + .source_offset = end_offset - 4, + .target_offset = lowered_relocs[0].off, + }), .linker_extern_fn => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { // Add relocation to the decl. const zo = elf_file.zigObjectPtr().?; @@ -103,7 +106,7 @@ pub fn emitMir(emit: *Emit) Error!void { const r_type = @intFromEnum(std.elf.R_X86_64.PLT32); try atom_ptr.addReloc(gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_info = @as(u64, sym_index) << 32 | r_type, .r_addend = lowered_relocs[0].off - 4, }, zo); } else if (emit.lower.bin_file.cast(.macho)) |macho_file| { @@ -150,7 +153,7 @@ pub fn emitMir(emit: *Emit) Error!void { const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD); try atom.addReloc(gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_info = @as(u64, sym_index) << 32 | r_type, .r_addend = lowered_relocs[0].off - 4, }, zo); }, @@ -161,7 +164,7 @@ pub fn emitMir(emit: *Emit) Error!void { const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32); try atom.addReloc(gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_info = @as(u64, sym_index) << 32 | r_type, .r_addend = lowered_relocs[0].off, }, zo); }, @@ -176,7 +179,7 @@ pub fn emitMir(emit: *Emit) Error!void { @intFromEnum(std.elf.R_X86_64.PC32); try atom.addReloc(gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_info = @as(u64, sym_index) << 32 | r_type, .r_addend = lowered_relocs[0].off - 4, }, zo); } else { @@ -186,7 +189,7 @@ pub fn emitMir(emit: *Emit) Error!void { @intFromEnum(std.elf.R_X86_64.@"32"); try atom.addReloc(gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_info = @as(u64, sym_index) << 32 | r_type, .r_addend = lowered_relocs[0].off, }, zo); } @@ -412,7 +415,7 @@ pub fn emitMir(emit: *Emit) Error!void { loc_buf[0] = switch (mem.base()) { .none => .{ .constu = 0 }, .reg => |reg| .{ .breg = reg.dwarfNum() }, - .frame => unreachable, + .frame, .table => unreachable, .reloc => |sym_index| .{ .addr = .{ .sym = sym_index } }, }; break :base &loc_buf[0]; @@ -463,13 +466,40 @@ pub fn emitMir(emit: *Emit) Error!void { } } } - try emit.fixupRelocs(); -} + { + // TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size. + // This should be reversed like it is done in aarch64 MIR emit code: start with the smallest + // possible resolution, i.e., 8bit, and iteratively converge on the minimum required resolution + // until the entire decl is correctly emitted with all JMP/CALL instructions within range. + for (relocs.items) |reloc| { + const target = code_offset_mapping[reloc.target]; + const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length)) + reloc.target_offset; + std.mem.writeInt(i32, emit.code.items[reloc.source_offset..][0..4], @intCast(disp), .little); + } + } + if (emit.lower.mir.table.len > 0) { + if (emit.lower.bin_file.cast(.elf)) |elf_file| { + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; -pub fn deinit(emit: *Emit) void { - emit.relocs.deinit(emit.lower.allocator); - emit.code_offset_mapping.deinit(emit.lower.allocator); - emit.* = undefined; + const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8); + var table_offset = std.mem.alignForward(u32, @intCast(emit.code.items.len), ptr_size); + for (table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{ + .r_offset = table_reloc.source_offset, + .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"), + .r_addend = @as(i64, table_offset) + table_reloc.target_offset, + }, zo); + for (emit.lower.mir.table) |entry| { + try atom.addReloc(gpa, .{ + .r_offset = table_offset, + .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"64"), + .r_addend = code_offset_mapping[entry], + }, zo); + table_offset += ptr_size; + } + try emit.code.appendNTimes(gpa, 0, table_offset - emit.code.items.len); + } else unreachable; + } } fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { @@ -481,7 +511,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { const Reloc = struct { /// Offset of the instruction. - source: usize, + source: u32, /// Offset of the relocation within the instruction. source_offset: u32, /// Target of the relocation. @@ -492,18 +522,12 @@ const Reloc = struct { length: u5, }; -fn fixupRelocs(emit: *Emit) Error!void { - // TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size. - // This should be reversed like it is done in aarch64 MIR emit code: start with the smallest - // possible resolution, i.e., 8bit, and iteratively converge on the minimum required resolution - // until the entire decl is correctly emitted with all JMP/CALL instructions within range. - for (emit.relocs.items) |reloc| { - const target = emit.code_offset_mapping.get(reloc.target) orelse - return emit.fail("JMP/CALL relocation target not found!", .{}); - const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length)) + reloc.target_offset; - std.mem.writeInt(i32, emit.code.items[reloc.source_offset..][0..4], @intCast(disp), .little); - } -} +const TableReloc = struct { + /// Offset of the relocation. + source_offset: u32, + /// Offset from the start of the table. + target_offset: i32, +}; const Loc = struct { line: u32, diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index bfe699a825..55582100ea 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -57,6 +57,7 @@ pub const Reloc = struct { const Target = union(enum) { inst: Mir.Inst.Index, + table, linker_reloc: u32, linker_tlsld: u32, linker_dtpoff: u32, @@ -348,7 +349,7 @@ pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error { return error.LowerFail; } -pub fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate { +pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate { return switch (ops) { .rri_s, .ri_s, @@ -379,8 +380,16 @@ pub fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate { }; } -pub fn mem(lower: Lower, payload: u32) Memory { - return lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode(); +pub fn mem(lower: *Lower, payload: u32) Memory { + var m = lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode(); + switch (m) { + .sib => |*sib| switch (sib.base) { + else => {}, + .table => sib.disp = lower.reloc(.table, sib.disp).signed, + }, + else => {}, + } + return m; } fn reloc(lower: *Lower, target: Reloc.Target, off: i32) Immediate { diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index c5f29d3a0c..595f79e8dd 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -9,6 +9,7 @@ instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, +table: []const Inst.Index, frame_locs: std.MultiArrayList(FrameLoc).Slice, pub const Inst = struct { @@ -1237,7 +1238,7 @@ pub const Memory = struct { size: bits.Memory.Size, index: Register, scale: bits.Memory.Scale, - _: u16 = undefined, + _: u15 = undefined, }; pub fn encode(mem: bits.Memory) Memory { @@ -1260,7 +1261,7 @@ pub const Memory = struct { }, }, .base = switch (mem.base) { - .none => undefined, + .none, .table => undefined, .reg => |reg| @intFromEnum(reg), .frame => |frame_index| @intFromEnum(frame_index), .reloc => |sym_index| sym_index, @@ -1289,6 +1290,7 @@ pub const Memory = struct { .none => .none, .reg => .{ .reg = @enumFromInt(mem.base) }, .frame => .{ .frame = @enumFromInt(mem.base) }, + .table => .table, .reloc => .{ .reloc = mem.base }, }, .scale_index = switch (mem.info.index) { @@ -1317,6 +1319,7 @@ pub const Memory = struct { pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); gpa.free(mir.extra); + gpa.free(mir.table); mir.frame_locs.deinit(gpa); mir.* = undefined; } @@ -1352,7 +1355,7 @@ pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffse pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory { return switch (mem.info.base) { - .none, .reg, .reloc => mem, + .none, .reg, .table, .reloc => mem, .frame => if (mir.frame_locs.len > 0) .{ .info = .{ .base = .reg, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 500dc488e6..8f13620730 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -482,17 +482,18 @@ pub const Memory = struct { base: Base = .none, mod: Mod = .{ .rm = .{} }, - pub const Base = union(enum(u2)) { + pub const Base = union(enum(u3)) { none, reg: Register, frame: FrameIndex, + table, reloc: u32, pub const Tag = @typeInfo(Base).@"union".tag_type.?; pub fn isExtended(self: Base) bool { return switch (self) { - .none, .frame, .reloc => false, // rsp, rbp, and rip are not extended + .none, .frame, .table, .reloc => false, // rsp, rbp, and rip are not extended .reg => |reg| reg.isExtended(), }; } diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 048fb6508d..bf0c0c0467 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -138,7 +138,7 @@ pub const Instruction = struct { .moffs => true, .rip => false, .sib => |s| switch (s.base) { - .none, .frame, .reloc => false, + .none, .frame, .table, .reloc => false, .reg => |reg| reg.class() == .segment, }, }; @@ -161,9 +161,9 @@ pub const Instruction = struct { pub fn disp(mem: Memory) Immediate { return switch (mem) { - .sib => |s| Immediate.s(s.disp), - .rip => |r| Immediate.s(r.disp), - .moffs => |m| Immediate.u(m.offset), + .sib => |s| .s(s.disp), + .rip => |r| .s(r.disp), + .moffs => |m| .u(m.offset), }; } @@ -277,6 +277,7 @@ pub const Instruction = struct { .none => any = false, .reg => |reg| try writer.print("{s}", .{@tagName(reg)}), .frame => |frame_index| try writer.print("{}", .{frame_index}), + .table => try writer.print("Table", .{}), .reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}), } if (mem.scaleIndex()) |si| { @@ -614,7 +615,7 @@ pub const Instruction = struct { switch (mem) { .moffs => unreachable, .sib => |sib| switch (sib.base) { - .none => { + .none, .table => { try encoder.modRm_SIBDisp0(operand_enc); if (mem.scaleIndex()) |si| { const scale = math.log2_int(u4, si.scale); @@ -1191,7 +1192,7 @@ const TestEncode = struct { ) !void { var stream = std.io.fixedBufferStream(&enc.buffer); var count_writer = std.io.countingWriter(stream.writer()); - const inst = try Instruction.new(.none, mnemonic, ops); + const inst: Instruction = try .new(.none, mnemonic, ops); try inst.encode(count_writer.writer(), .{}); enc.index = count_writer.bytes_written; } @@ -1205,9 +1206,9 @@ test "encode" { var buf = std.ArrayList(u8).init(testing.allocator); defer buf.deinit(); - const inst = try Instruction.new(.none, .mov, &.{ + const inst: Instruction = try .new(.none, .mov, &.{ .{ .reg = .rbx }, - .{ .imm = Instruction.Immediate.u(4) }, + .{ .imm = .u(4) }, }); try inst.encode(buf.writer(), .{}); try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items); @@ -1217,47 +1218,47 @@ test "lower I encoding" { var enc = TestEncode{}; try enc.encode(.push, &.{ - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10"); try enc.encode(.push, &.{ - .{ .imm = Instruction.Immediate.u(0x1000) }, + .{ .imm = .u(0x1000) }, }); try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000"); try enc.encode(.push, &.{ - .{ .imm = Instruction.Immediate.u(0x10000000) }, + .{ .imm = .u(0x10000000) }, }); try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000"); try enc.encode(.adc, &.{ .{ .reg = .rax }, - .{ .imm = Instruction.Immediate.u(0x10000000) }, + .{ .imm = .u(0x10000000) }, }); try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000"); try enc.encode(.add, &.{ .{ .reg = .al }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10"); try enc.encode(.add, &.{ .{ .reg = .rax }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); try enc.encode(.sbb, &.{ .{ .reg = .ax }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10"); try enc.encode(.xor, &.{ .{ .reg = .al }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10"); } @@ -1267,43 +1268,43 @@ test "lower MI encoding" { try enc.encode(.mov, &.{ .{ .reg = .r12 }, - .{ .imm = Instruction.Immediate.u(0x1000) }, + .{ .imm = .u(0x1000) }, }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); try enc.encode(.mov, &.{ .{ .reg = .r12 }, - .{ .imm = Instruction.Immediate.u(0x1000) }, + .{ .imm = .u(0x1000) }, }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try enc.encode(.mov, &.{ .{ .reg = .r12 }, - .{ .imm = Instruction.Immediate.u(0x1000) }, + .{ .imm = .u(0x1000) }, }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try enc.encode(.mov, &.{ .{ .reg = .rax }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 } }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.initRip(.qword, 0x10) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings( "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", @@ -1313,19 +1314,19 @@ test "lower MI encoding" { try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) }, - .{ .imm = Instruction.Immediate.s(-16) }, + .{ .imm = .s(-16) }, }); try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); @@ -1335,7 +1336,7 @@ test "lower MI encoding" { .disp = 0x10000000, .scale_index = .{ .scale = 2, .index = .rcx }, }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings( "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", @@ -1345,43 +1346,43 @@ test "lower MI encoding" { try enc.encode(.adc, &.{ .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); try enc.encode(.adc, &.{ .{ .mem = Instruction.Memory.initRip(.qword, 0) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10"); try enc.encode(.adc, &.{ .{ .reg = .rax }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); try enc.encode(.add, &.{ .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); try enc.encode(.add, &.{ .{ .reg = .rax }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); try enc.encode(.add, &.{ .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, - .{ .imm = Instruction.Immediate.s(-0x10) }, + .{ .imm = .s(-0x10) }, }); try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); try enc.encode(.@"and", &.{ .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings( "\x83\x24\x25\x00\x00\x00\x10\x10", @@ -1391,7 +1392,7 @@ test "lower MI encoding" { try enc.encode(.@"and", &.{ .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings( "\x26\x83\x24\x25\x00\x00\x00\x10\x10", @@ -1401,7 +1402,7 @@ test "lower MI encoding" { try enc.encode(.@"and", &.{ .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings( "\x41\x83\xA4\x24\x00\x00\x00\x10\x10", @@ -1411,7 +1412,7 @@ test "lower MI encoding" { try enc.encode(.sub, &.{ .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings( "\x41\x83\xAB\x00\x00\x00\x10\x10", @@ -1630,14 +1631,14 @@ test "lower RMI encoding" { try enc.encode(.imul, &.{ .{ .reg = .r11 }, .{ .reg = .r12 }, - .{ .imm = Instruction.Immediate.s(-2) }, + .{ .imm = .s(-2) }, }); try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2"); try enc.encode(.imul, &.{ .{ .reg = .r11 }, .{ .mem = Instruction.Memory.initRip(.qword, -16) }, - .{ .imm = Instruction.Immediate.s(-1024) }, + .{ .imm = .s(-1024) }, }); try expectEqualHexStrings( "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF", @@ -1648,7 +1649,7 @@ test "lower RMI encoding" { try enc.encode(.imul, &.{ .{ .reg = .bx }, .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, - .{ .imm = Instruction.Immediate.s(-1024) }, + .{ .imm = .s(-1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\xFC", @@ -1659,7 +1660,7 @@ test "lower RMI encoding" { try enc.encode(.imul, &.{ .{ .reg = .bx }, .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, - .{ .imm = Instruction.Immediate.u(1024) }, + .{ .imm = .u(1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\x04", @@ -1775,7 +1776,7 @@ test "lower M encoding" { try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); try enc.encode(.call, &.{ - .{ .imm = Instruction.Immediate.s(0) }, + .{ .imm = .s(0) }, }); try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); @@ -1834,7 +1835,7 @@ test "lower OI encoding" { try enc.encode(.mov, &.{ .{ .reg = .rax }, - .{ .imm = Instruction.Immediate.u(0x1000000000000000) }, + .{ .imm = .u(0x1000000000000000) }, }); try expectEqualHexStrings( "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", @@ -1844,7 +1845,7 @@ test "lower OI encoding" { try enc.encode(.mov, &.{ .{ .reg = .r11 }, - .{ .imm = Instruction.Immediate.u(0x1000000000000000) }, + .{ .imm = .u(0x1000000000000000) }, }); try expectEqualHexStrings( "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", @@ -1854,19 +1855,19 @@ test "lower OI encoding" { try enc.encode(.mov, &.{ .{ .reg = .r11d }, - .{ .imm = Instruction.Immediate.u(0x10000000) }, + .{ .imm = .u(0x10000000) }, }); try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000"); try enc.encode(.mov, &.{ .{ .reg = .r11w }, - .{ .imm = Instruction.Immediate.u(0x1000) }, + .{ .imm = .u(0x1000) }, }); try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000"); try enc.encode(.mov, &.{ .{ .reg = .r11b }, - .{ .imm = Instruction.Immediate.u(0x10) }, + .{ .imm = .u(0x10) }, }); try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10"); } @@ -1940,7 +1941,7 @@ test "lower NP encoding" { } fn invalidInstruction(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void { - const err = Instruction.new(.none, mnemonic, ops); + const err: Instruction = .new(.none, mnemonic, ops); try testing.expectError(error.InvalidInstruction, err); } @@ -1988,12 +1989,12 @@ test "invalid instruction" { .{ .reg = .r12d }, }); try invalidInstruction(.push, &.{ - .{ .imm = Instruction.Immediate.u(0x1000000000000000) }, + .{ .imm = .u(0x1000000000000000) }, }); } fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void { - try testing.expectError(error.CannotEncode, Instruction.new(.none, mnemonic, ops)); + try testing.expectError(error.CannotEncode, .new(.none, mnemonic, ops)); } test "cannot encode" { @@ -2177,7 +2178,7 @@ const Assembler = struct { pub fn assemble(as: *Assembler, writer: anytype) !void { while (try as.next()) |parsed_inst| { - const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops); + const inst: Instruction = try .new(.none, parsed_inst.mnemonic, &parsed_inst.ops); try inst.encode(writer, .{}); } } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e36fd4e80a..a5d4379004 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3548,7 +3548,7 @@ pub fn getTarget(self: MachO) std.Target { pub fn invalidateKernelCache(dir: fs.Dir, sub_path: []const u8) !void { const tracy = trace(@src()); defer tracy.end(); - if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) { + if (builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) { try dir.copyFile(sub_path, dir, sub_path, .{}); } } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index e1b8e3a18f..e6917eb649 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -277,8 +277,8 @@ test "function alignment" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - // function alignment is a compile error on wasm32/wasm64 - if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; + // function alignment is a compile error on wasm + if (native_arch.isWasm()) return error.SkipZigTest; const S = struct { fn alignExpr() align(@sizeOf(usize) * 2) i32 { @@ -307,8 +307,8 @@ test "implicitly decreasing fn alignment" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - // function alignment is a compile error on wasm32/wasm64 - if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; + // function alignment is a compile error on wasm + if (native_arch.isWasm()) return error.SkipZigTest; try testImplicitlyDecreaseFnAlign(alignedSmall, 1234); try testImplicitlyDecreaseFnAlign(alignedBig, 5678); @@ -331,9 +331,9 @@ test "@alignCast functions" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - // function alignment is a compile error on wasm32/wasm64 - if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; - if (native_arch == .thumb or native_arch == .thumbeb) return error.SkipZigTest; + // function alignment is a compile error on wasm + if (native_arch.isWasm()) return error.SkipZigTest; + if (native_arch.isThumb()) return error.SkipZigTest; try expect(fnExpectsOnly1(simple4) == 0x19); } @@ -496,9 +496,9 @@ test "align(N) on functions" { return error.SkipZigTest; } - // function alignment is a compile error on wasm32/wasm64 - if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; - if (native_arch == .thumb or native_arch == .thumbeb) return error.SkipZigTest; + // function alignment is a compile error on wasm + if (native_arch.isWasm()) return error.SkipZigTest; + if (native_arch.isThumb()) return error.SkipZigTest; try expect((@intFromPtr(&overaligned_fn) & (0x1000 - 1)) == 0); } diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index e82242f425..992f18282e 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -178,7 +178,7 @@ test "rw constraint (x86_64)" { } test "asm modifiers (AArch64)" { - if (builtin.target.cpu.arch != .aarch64) return error.SkipZigTest; + if (!builtin.target.cpu.arch.isAARCH64()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly diff --git a/test/behavior/call.zig b/test/behavior/call.zig index ed0e07a85b..c8239ac53e 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -660,6 +660,7 @@ test "arguments pointed to on stack into tailcall" { switch (builtin.cpu.arch) { .wasm32, + .wasm64, .mips, .mipsel, .mips64, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 37db3ba941..84c634bb0e 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -124,7 +124,7 @@ test "@floatFromInt(f80)" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @@ -1362,7 +1362,7 @@ test "cast f16 to wider types" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index dd3de9bb9f..4c67d29273 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -522,7 +522,7 @@ test "runtime 128 bit integer division" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 789eeaef66..ffd0310ab9 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -785,7 +785,7 @@ test "128-bit multiplication" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; { @@ -1374,7 +1374,7 @@ test "remainder division" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { @@ -1527,7 +1527,7 @@ test "@round f80" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @@ -1540,7 +1540,7 @@ test "@round f128" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig index d7d494a9ad..53b3d92406 100644 --- a/test/behavior/maximum_minimum.zig +++ b/test/behavior/maximum_minimum.zig @@ -122,7 +122,7 @@ test "@min/max for floats" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index ec07f203ec..2bebdd30f0 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -58,7 +58,7 @@ test "@mulAdd f80" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @@ -79,7 +79,7 @@ test "@mulAdd f128" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @@ -189,7 +189,7 @@ test "vector f80" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try comptime vector80(); @@ -216,7 +216,7 @@ test "vector f128" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try comptime vector128(); diff --git a/test/behavior/saturating_arithmetic.zig b/test/behavior/saturating_arithmetic.zig index ea3d51f3e6..d93899ad48 100644 --- a/test/behavior/saturating_arithmetic.zig +++ b/test/behavior/saturating_arithmetic.zig @@ -164,10 +164,10 @@ test "saturating multiplication <= 32 bits" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .wasm32) { + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isWasm()) { // https://github.com/ziglang/zig/issues/9660 return error.SkipZigTest; } @@ -264,10 +264,10 @@ test "saturating multiplication" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .wasm32) { + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isWasm()) { // https://github.com/ziglang/zig/issues/9660 return error.SkipZigTest; } @@ -311,7 +311,7 @@ test "saturating shift-left" { try testSatShl(i8, 127, 1, 127); try testSatShl(i8, -128, 1, -128); // TODO: remove this check once #9668 is completed - if (builtin.cpu.arch != .wasm32) { + if (!builtin.cpu.arch.isWasm()) { // skip testing ints > 64 bits on wasm due to miscompilation / wasmtime ci error try testSatShl(i128, maxInt(i128), 64, maxInt(i128)); try testSatShl(u128, maxInt(u128), 64, maxInt(u128)); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 74a77f3e21..95a31326ff 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -418,8 +418,8 @@ test "packed struct 24bits" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; // TODO - if (comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO + if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; // TODO + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; @@ -818,7 +818,7 @@ test "non-packed struct with u128 entry in union" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const U = union(enum) { @@ -941,7 +941,7 @@ test "tuple assigned to variable" { test "comptime struct field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO const T = struct { a: i32, diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index b5370b7813..c4b92f9473 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -100,7 +100,7 @@ test "simple variadic function" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) { + if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -161,7 +161,7 @@ test "coerce reference to var arg" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) { + if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -194,7 +194,7 @@ test "variadic functions" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) { + if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -239,7 +239,7 @@ test "copy VaList" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) { + if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -273,7 +273,7 @@ test "unused VaList arg" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) { + if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 6b03ac90e3..6af4b5b4b6 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -101,7 +101,7 @@ test "vector float operators" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { @@ -754,7 +754,7 @@ test "vector reduce operation" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21091 @@ -989,7 +989,7 @@ test "saturating multiplication" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO: once #9660 has been solved, remove this line - if (builtin.target.cpu.arch == .wasm32) return error.SkipZigTest; + if (builtin.target.cpu.arch.isWasm()) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -1256,7 +1256,7 @@ test "byte vector initialized in inline function" { if (builtin.cpu.arch == .aarch64_be and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (comptime builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and - builtin.cpu.features.isEnabled(@intFromEnum(std.Target.x86.Feature.avx512f))) + std.Target.x86.featureSetHas(builtin.cpu.features, .avx512f)) { // TODO https://github.com/ziglang/zig/issues/13279 return error.SkipZigTest; diff --git a/test/behavior/wrapping_arithmetic.zig b/test/behavior/wrapping_arithmetic.zig index 958be2f6f0..f1fbc0de51 100644 --- a/test/behavior/wrapping_arithmetic.zig +++ b/test/behavior/wrapping_arithmetic.zig @@ -83,7 +83,7 @@ test "wrapping multiplication" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO: once #9660 has been solved, remove this line - if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; + if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; const S = struct { fn doTheTest() !void {