authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-08 09:15:35-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-08 09:15:35-04:00
log1de242cd732b63bbd8b24579b42b0ccfaf40e193
tree894348970d0f8a4eaf415dd147184fd7d2997318
parenta9b37ac63724a79805f00b4e6966ebb8329151a2
parentb5dedd7c00604bc687aab373a430058d9a97cdf9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17416 from jacobly0/x86_64

x86_64: implement more `f80` operations and other features

15 files changed, 1606 insertions(+), 805 deletions(-)

src/arch/x86_64/CodeGen.zig+1497-736
......@@ -32,10 +32,10 @@ const Target = std.Target;
3232const Type = @import("../../type.zig").Type;
3333const TypedValue = @import("../../TypedValue.zig");
3434const Value = @import("../../value.zig").Value;
35const Instruction = @import("encoder.zig").Instruction;
3536
3637const abi = @import("abi.zig");
3738const bits = @import("bits.zig");
38const encoder = @import("encoder.zig");
3939const errUnionErrorOffset = codegen.errUnionErrorOffset;
4040const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
4141
......@@ -47,9 +47,6 @@ const RegisterManager = abi.RegisterManager;
4747const RegisterLock = RegisterManager.RegisterLock;
4848const FrameIndex = bits.FrameIndex;
4949
50const gp = abi.RegisterClass.gp;
51const sse = abi.RegisterClass.sse;
52
5350const InnerError = CodeGenError || error{OutOfRegisters};
5451
5552gpa: Allocator,
......@@ -61,6 +58,15 @@ target: *const std.Target,
6158owner: Owner,
6259err_msg: ?*ErrorMsg,
6360args: []MCValue,
61va_info: union {
62 sysv: struct {
63 gp_count: u32,
64 fp_count: u32,
65 overflow_arg_area: FrameAddr,
66 reg_save_area: FrameAddr,
67 },
68 win64: struct {},
69},
6470ret_mcv: InstTracking,
6571fn_type: Type,
6672arg_index: u32,
......@@ -532,8 +538,8 @@ const InstTracking = struct {
532538 };
533539 }
534540
535 fn trackSpill(self: *InstTracking, function: *Self, inst: Air.Inst.Index) void {
536 function.freeValue(self.short);
541 fn trackSpill(self: *InstTracking, function: *Self, inst: Air.Inst.Index) !void {
542 try function.freeValue(self.short);
537543 self.reuseFrame();
538544 tracking_log.debug("%{d} => {} (spilled)", .{ inst, self.* });
539545 }
......@@ -618,8 +624,9 @@ const InstTracking = struct {
618624 }
619625 }
620626
621 fn die(self: *InstTracking, function: *Self, inst: Air.Inst.Index) void {
622 function.freeValue(self.short);
627 fn die(self: *InstTracking, function: *Self, inst: Air.Inst.Index) !void {
628 if (self.short == .dead) return;
629 try function.freeValue(self.short);
623630 self.short = .{ .dead = function.scope_generation };
624631 tracking_log.debug("%{d} => {} (death)", .{ inst, self.* });
625632 }
......@@ -634,6 +641,40 @@ const InstTracking = struct {
634641 tracking_log.debug("%{d} => {} (reuse %{d})", .{ new_inst, self.*, old_inst });
635642 }
636643
644 fn liveOut(self: *InstTracking, function: *Self, inst: Air.Inst.Index) void {
645 for (self.short.getRegs()) |reg| {
646 if (function.register_manager.isRegFree(reg)) {
647 tracking_log.debug("%{d} => {} (live-out)", .{ inst, self.* });
648 continue;
649 }
650
651 const index = RegisterManager.indexOfRegIntoTracked(reg).?;
652 const tracked_inst = function.register_manager.registers[index];
653 const tracking = function.getResolvedInstValue(tracked_inst);
654
655 // Disable death.
656 var found_reg = false;
657 var remaining_reg: Register = .none;
658 for (tracking.short.getRegs()) |tracked_reg| if (tracked_reg.id() == reg.id()) {
659 assert(!found_reg);
660 found_reg = true;
661 } else {
662 assert(remaining_reg == .none);
663 remaining_reg = tracked_reg;
664 };
665 assert(found_reg);
666 tracking.short = switch (remaining_reg) {
667 .none => .{ .dead = function.scope_generation },
668 else => .{ .register = remaining_reg },
669 };
670
671 // Perform side-effects of freeValue manually.
672 function.register_manager.freeReg(reg);
673
674 tracking_log.debug("%{d} => {} (live-out %{d})", .{ inst, self.*, tracked_inst });
675 }
676 }
677
637678 pub fn format(
638679 self: InstTracking,
639680 comptime _: []const u8,
......@@ -713,6 +754,7 @@ pub fn generate(
713754 .owner = .{ .func_index = func_index },
714755 .err_msg = null,
715756 .args = undefined, // populated after `resolveCallingConventionValues`
757 .va_info = undefined, // populated after `resolveCallingConventionValues`
716758 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
717759 .fn_type = fn_type,
718760 .arg_index = 0,
......@@ -753,6 +795,7 @@ pub fn generate(
753795 );
754796
755797 const fn_info = mod.typeToFunc(fn_type).?;
798 const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*);
756799 var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) {
757800 error.CodegenFail => return Result{ .fail = function.err_msg.? },
758801 error.OutOfRegisters => return Result{
......@@ -787,6 +830,16 @@ pub fn generate(
787830 .alignment = call_info.stack_align,
788831 }),
789832 );
833 function.va_info = switch (cc) {
834 .SysV => .{ .sysv = .{
835 .gp_count = call_info.gp_count,
836 .fp_count = call_info.fp_count,
837 .overflow_arg_area = .{ .index = .args_frame, .off = call_info.stack_byte_count },
838 .reg_save_area = undefined,
839 } },
840 .Win64 => .{ .win64 = .{} },
841 else => undefined,
842 };
790843
791844 function.gen() catch |err| switch (err) {
792845 error.CodegenFail => return Result{ .fail = function.err_msg.? },
......@@ -807,7 +860,7 @@ pub fn generate(
807860 .lower = .{
808861 .allocator = bin_file.allocator,
809862 .mir = mir,
810 .cc = abi.resolveCallingConvention(fn_info.cc, function.target.*),
863 .cc = cc,
811864 .src_loc = src_loc,
812865 },
813866 .bin_file = bin_file,
......@@ -862,6 +915,7 @@ pub fn generateLazy(
862915 .owner = .{ .lazy_sym = lazy_sym },
863916 .err_msg = null,
864917 .args = undefined,
918 .va_info = undefined,
865919 .ret_mcv = undefined,
866920 .fn_type = undefined,
867921 .arg_index = undefined,
......@@ -980,7 +1034,6 @@ fn formatWipMir(
9801034 _: std.fmt.FormatOptions,
9811035 writer: anytype,
9821036) @TypeOf(writer).Error!void {
983 const mod = data.self.bin_file.options.module.?;
9841037 var lower = Lower{
9851038 .allocator = data.self.gpa,
9861039 .mir = .{
......@@ -988,7 +1041,7 @@ fn formatWipMir(
9881041 .extra = data.self.mir_extra.items,
9891042 .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(),
9901043 },
991 .cc = mod.typeToFunc(data.self.fn_type).?.cc,
1044 .cc = .Unspecified,
9921045 .src_loc = data.self.src_loc,
9931046 };
9941047 for ((lower.lowerMir(data.inst) catch |err| switch (err) {
......@@ -1144,7 +1197,7 @@ fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void {
11441197 } },
11451198 .z_and_np, .nz_or_p => .{ .rr = .{
11461199 .r1 = reg,
1147 .r2 = (try self.register_manager.allocReg(null, gp)).to8(),
1200 .r2 = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to8(),
11481201 } },
11491202 },
11501203 });
......@@ -1184,7 +1237,7 @@ fn asmSetccMemory(self: *Self, m: Memory, cc: bits.Condition) !void {
11841237 .payload = payload,
11851238 } },
11861239 .z_and_np, .nz_or_p => .{ .rx = .{
1187 .r1 = (try self.register_manager.allocReg(null, gp)).to8(),
1240 .r1 = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to8(),
11881241 .payload = payload,
11891242 } },
11901243 },
......@@ -1222,6 +1275,17 @@ fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Ins
12221275 });
12231276}
12241277
1278fn asmReloc(self: *Self, tag: Mir.Inst.FixedTag, target: Mir.Inst.Index) !void {
1279 _ = try self.addInst(.{
1280 .tag = tag[1],
1281 .ops = .inst,
1282 .data = .{ .inst = .{
1283 .fixes = tag[0],
1284 .inst = target,
1285 } },
1286 });
1287}
1288
12251289fn asmPlaceholder(self: *Self) !Mir.Inst.Index {
12261290 return self.addInst(.{
12271291 .tag = .pseudo,
......@@ -1629,7 +1693,8 @@ fn asmMemoryRegisterImmediate(
16291693
16301694fn gen(self: *Self) InnerError!void {
16311695 const mod = self.bin_file.options.module.?;
1632 const cc = self.fn_type.fnCallingConvention(mod);
1696 const fn_info = mod.typeToFunc(self.fn_type).?;
1697 const cc = abi.resolveCallingConvention(fn_info.cc, self.target.*);
16331698 if (cc != .Naked) {
16341699 try self.asmRegister(.{ ._, .push }, .rbp);
16351700 const backpatch_push_callee_preserved_regs = try self.asmPlaceholder();
......@@ -1659,6 +1724,42 @@ fn gen(self: *Self) InnerError!void {
16591724 else => unreachable,
16601725 }
16611726
1727 if (fn_info.is_var_args) switch (cc) {
1728 .SysV => {
1729 const info = &self.va_info.sysv;
1730 const reg_save_area_fi = try self.allocFrameIndex(FrameAlloc.init(.{
1731 .size = abi.SysV.c_abi_int_param_regs.len * 8 +
1732 abi.SysV.c_abi_sse_param_regs.len * 16,
1733 .alignment = .@"16",
1734 }));
1735 info.reg_save_area = .{ .index = reg_save_area_fi };
1736
1737 for (abi.SysV.c_abi_int_param_regs[info.gp_count..], info.gp_count..) |reg, reg_i|
1738 try self.genSetMem(
1739 .{ .frame = reg_save_area_fi },
1740 @intCast(reg_i * 8),
1741 Type.usize,
1742 .{ .register = reg },
1743 );
1744
1745 try self.asmRegisterImmediate(.{ ._, .cmp }, .al, Immediate.u(info.fp_count));
1746 const skip_sse_reloc = try self.asmJccReloc(undefined, .na);
1747
1748 const vec_2_f64 = try mod.vectorType(.{ .len = 2, .child = .f64_type });
1749 for (abi.SysV.c_abi_sse_param_regs[info.fp_count..], info.fp_count..) |reg, reg_i|
1750 try self.genSetMem(
1751 .{ .frame = reg_save_area_fi },
1752 @intCast(abi.SysV.c_abi_int_param_regs.len * 8 + reg_i * 16),
1753 vec_2_f64,
1754 .{ .register = reg },
1755 );
1756
1757 try self.performReloc(skip_sse_reloc);
1758 },
1759 .Win64 => return self.fail("TODO implement gen var arg function for Win64", .{}),
1760 else => unreachable,
1761 };
1762
16621763 try self.asmPseudo(.pseudo_dbg_prologue_end_none);
16631764
16641765 try self.genBody(self.air.getMainBody());
......@@ -2033,10 +2134,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
20332134 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
20342135 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
20352136
2036 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
2037 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
2038 .c_va_end => return self.fail("TODO implement c_va_end", .{}),
2039 .c_va_start => return self.fail("TODO implement c_va_start", .{}),
2137 .c_va_arg => try self.airVaArg(inst),
2138 .c_va_copy => try self.airVaCopy(inst),
2139 .c_va_end => try self.airVaEnd(inst),
2140 .c_va_start => try self.airVaStart(inst),
20402141
20412142 .wasm_memory_size => unreachable,
20422143 .wasm_memory_grow => unreachable,
......@@ -2074,7 +2175,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
20742175 switch (lazy_sym.ty.zigTypeTag(mod)) {
20752176 .Enum => {
20762177 const enum_ty = lazy_sym.ty;
2077 wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)});
2178 wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(mod)});
20782179
20792180 const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*);
20802181 const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
......@@ -2084,10 +2185,10 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
20842185 const ret_reg = param_regs[0];
20852186 const enum_mcv = MCValue{ .register = param_regs[1] };
20862187
2087 var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount(mod));
2188 var exitlude_jump_relocs = try self.gpa.alloc(Mir.Inst.Index, enum_ty.enumFieldCount(mod));
20882189 defer self.gpa.free(exitlude_jump_relocs);
20892190
2090 const data_reg = try self.register_manager.allocReg(null, gp);
2191 const data_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
20912192 const data_lock = self.register_manager.lockRegAssumeUnused(data_reg);
20922193 defer self.register_manager.unlockReg(data_lock);
20932194 try self.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = enum_ty });
......@@ -2122,7 +2223,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
21222223 },
21232224 else => return self.fail(
21242225 "TODO implement {s} for {}",
2125 .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(self.bin_file.options.module.?) },
2226 .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(mod) },
21262227 ),
21272228 }
21282229}
......@@ -2132,9 +2233,12 @@ fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void {
21322233 self.register_manager.getRegAssumeFree(reg, inst);
21332234}
21342235
2135fn freeValue(self: *Self, value: MCValue) void {
2236fn freeValue(self: *Self, value: MCValue) !void {
21362237 switch (value) {
2137 .register => |reg| self.register_manager.freeReg(reg),
2238 .register => |reg| {
2239 if (reg.class() == .x87) try self.asmRegister(.{ .f_, .free }, reg);
2240 self.register_manager.freeReg(reg);
2241 },
21382242 .register_pair => |regs| for (regs) |reg| self.register_manager.freeReg(reg),
21392243 .register_offset => |reg_off| self.register_manager.freeReg(reg_off.reg),
21402244 .register_overflow => |reg_ov| {
......@@ -2146,13 +2250,13 @@ fn freeValue(self: *Self, value: MCValue) void {
21462250 }
21472251}
21482252
2149fn feed(self: *Self, bt: *Liveness.BigTomb, operand: Air.Inst.Ref) void {
2150 if (bt.feed()) if (Air.refToIndex(operand)) |inst| self.processDeath(inst);
2253fn feed(self: *Self, bt: *Liveness.BigTomb, operand: Air.Inst.Ref) !void {
2254 if (bt.feed()) if (Air.refToIndex(operand)) |inst| try self.processDeath(inst);
21512255}
21522256
21532257/// Asserts there is already capacity to insert into top branch inst_table.
2154fn processDeath(self: *Self, inst: Air.Inst.Index) void {
2155 self.inst_tracking.getPtr(inst).?.die(self, inst);
2258fn processDeath(self: *Self, inst: Air.Inst.Index) !void {
2259 try self.inst_tracking.getPtr(inst).?.die(self, inst);
21562260}
21572261
21582262/// Called when there are no operands, and the instruction is always unreferenced.
......@@ -2177,13 +2281,18 @@ fn finishAirResult(self: *Self, inst: Air.Inst.Index, result: MCValue) void {
21772281 self.finishAirBookkeeping();
21782282}
21792283
2180fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
2284fn finishAir(
2285 self: *Self,
2286 inst: Air.Inst.Index,
2287 result: MCValue,
2288 operands: [Liveness.bpi - 1]Air.Inst.Ref,
2289) !void {
21812290 var tomb_bits = self.liveness.getTombBits(inst);
21822291 for (operands) |op| {
21832292 const dies = @as(u1, @truncate(tomb_bits)) != 0;
21842293 tomb_bits >>= 1;
21852294 if (!dies) continue;
2186 self.processDeath(Air.refToIndexAllowNone(op) orelse continue);
2295 try self.processDeath(Air.refToIndexAllowNone(op) orelse continue);
21872296 }
21882297 self.finishAirResult(inst, result);
21892298}
......@@ -2349,7 +2458,7 @@ fn allocRegOrMemAdvanced(self: *Self, ty: Type, inst: ?Air.Inst.Index, reg_ok: b
23492458 },
23502459 else => 8,
23512460 })) {
2352 if (self.register_manager.tryAllocReg(inst, regClassForType(ty, mod))) |reg| {
2461 if (self.register_manager.tryAllocReg(inst, self.regClassForType(ty))) |reg| {
23532462 return MCValue{ .register = registerAlias(reg, abi_size) };
23542463 }
23552464 }
......@@ -2359,10 +2468,15 @@ fn allocRegOrMemAdvanced(self: *Self, ty: Type, inst: ?Air.Inst.Index, reg_ok: b
23592468 return .{ .load_frame = .{ .index = frame_index } };
23602469}
23612470
2362fn regClassForType(ty: Type, mod: *Module) RegisterManager.RegisterBitSet {
2471fn regClassForType(self: *Self, ty: Type) RegisterManager.RegisterBitSet {
2472 const mod = self.bin_file.options.module.?;
23632473 return switch (ty.zigTypeTag(mod)) {
2364 .Float, .Vector => sse,
2365 else => gp,
2474 .Float => switch (ty.floatBits(self.target.*)) {
2475 80 => abi.RegisterClass.x87,
2476 else => abi.RegisterClass.sse,
2477 },
2478 .Vector => abi.RegisterClass.sse,
2479 else => abi.RegisterClass.gp,
23662480 };
23672481}
23682482
......@@ -2409,7 +2523,7 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt
24092523 for (
24102524 self.inst_tracking.keys()[state.inst_tracking_len..],
24112525 self.inst_tracking.values()[state.inst_tracking_len..],
2412 ) |inst, *tracking| tracking.die(self, inst);
2526 ) |inst, *tracking| try tracking.die(self, inst);
24132527 self.inst_tracking.shrinkRetainingCapacity(state.inst_tracking_len);
24142528 }
24152529
......@@ -2417,7 +2531,7 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt
24172531 self.inst_tracking.keys()[0..state.inst_tracking_len],
24182532 self.inst_tracking.values()[0..state.inst_tracking_len],
24192533 ) |inst, *tracking| tracking.resurrect(inst, state.scope_generation);
2420 for (deaths) |death| self.processDeath(death);
2534 for (deaths) |death| try self.processDeath(death);
24212535
24222536 for (0..state.registers.len) |index| {
24232537 const current_maybe_inst = if (self.register_manager.free_registers.isSet(index))
......@@ -2444,7 +2558,7 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt
24442558 }
24452559 if (opts.update_tracking) {
24462560 if (current_maybe_inst) |current_inst| {
2447 self.inst_tracking.getPtr(current_inst).?.trackSpill(self, current_inst);
2561 try self.inst_tracking.getPtr(current_inst).?.trackSpill(self, current_inst);
24482562 }
24492563 {
24502564 const reg = RegisterManager.regAtTrackedIndex(@intCast(index));
......@@ -2463,7 +2577,7 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt
24632577 try self.inst_tracking.getPtr(inst).?.spill(self, inst);
24642578 if (opts.update_tracking) if (self.eflags_inst) |inst| {
24652579 self.eflags_inst = null;
2466 self.inst_tracking.getPtr(inst).?.trackSpill(self, inst);
2580 try self.inst_tracking.getPtr(inst).?.trackSpill(self, inst);
24672581 };
24682582
24692583 if (opts.update_tracking and std.debug.runtime_safety) {
......@@ -2481,7 +2595,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
24812595 if (tracked_reg.id() == reg.id()) break;
24822596 } else unreachable; // spilled reg not tracked with spilled instruciton
24832597 try tracking.spill(self, inst);
2484 tracking.trackSpill(self, inst);
2598 try tracking.trackSpill(self, inst);
24852599}
24862600
24872601pub fn spillEflagsIfOccupied(self: *Self) !void {
......@@ -2490,7 +2604,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {
24902604 const tracking = self.inst_tracking.getPtr(inst).?;
24912605 assert(tracking.getCondition() != null);
24922606 try tracking.spill(self, inst);
2493 tracking.trackSpill(self, inst);
2607 try tracking.trackSpill(self, inst);
24942608 }
24952609}
24962610
......@@ -2502,8 +2616,7 @@ pub fn spillRegisters(self: *Self, registers: []const Register) !void {
25022616/// allocated. A second call to `copyToTmpRegister` may return the same register.
25032617/// This can have a side effect of spilling instructions to the stack to free up a register.
25042618fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
2505 const mod = self.bin_file.options.module.?;
2506 const reg = try self.register_manager.allocReg(null, regClassForType(ty, mod));
2619 const reg = try self.register_manager.allocReg(null, self.regClassForType(ty));
25072620 try self.genSetReg(reg, ty, mcv);
25082621 return reg;
25092622}
......@@ -2518,8 +2631,7 @@ fn copyToRegisterWithInstTracking(
25182631 ty: Type,
25192632 mcv: MCValue,
25202633) !MCValue {
2521 const mod = self.bin_file.options.module.?;
2522 const reg: Register = try self.register_manager.allocReg(reg_owner, regClassForType(ty, mod));
2634 const reg: Register = try self.register_manager.allocReg(reg_owner, self.regClassForType(ty));
25232635 try self.genSetReg(reg, ty, mcv);
25242636 return MCValue{ .register = reg };
25252637}
......@@ -2568,7 +2680,7 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
25682680 80, 128 => true,
25692681 else => unreachable,
25702682 },
2571 80 => switch (dst_bits) {
2683 80 => switch (src_bits) {
25722684 128 => true,
25732685 else => unreachable,
25742686 },
......@@ -2872,9 +2984,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
28722984 else => null,
28732985 },
28742986 else => null,
2875 }) orelse return self.fail("TODO implement airTrunc for {}", .{
2876 dst_ty.fmt(self.bin_file.options.module.?),
2877 });
2987 }) orelse return self.fail("TODO implement airTrunc for {}", .{dst_ty.fmt(mod)});
28782988
28792989 const elem_ty = src_ty.childType(mod);
28802990 const mask_val = try mod.intValue(elem_ty, @as(u64, math.maxInt(u64)) >> @intCast(64 - dst_info.bits));
......@@ -3076,7 +3186,7 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
30763186 };
30773187 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
30783188
3079 const limit_reg = try self.register_manager.allocReg(null, gp);
3189 const limit_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
30803190 const limit_mcv = MCValue{ .register = limit_reg };
30813191 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
30823192 defer self.register_manager.unlockReg(limit_lock);
......@@ -3159,7 +3269,7 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
31593269 };
31603270 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
31613271
3162 const limit_reg = try self.register_manager.allocReg(null, gp);
3272 const limit_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
31633273 const limit_mcv = MCValue{ .register = limit_reg };
31643274 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
31653275 defer self.register_manager.unlockReg(limit_lock);
......@@ -3237,7 +3347,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
32373347 };
32383348 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
32393349
3240 const limit_reg = try self.register_manager.allocReg(null, gp);
3350 const limit_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
32413351 const limit_mcv = MCValue{ .register = limit_reg };
32423352 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
32433353 defer self.register_manager.unlockReg(limit_lock);
......@@ -3425,7 +3535,8 @@ fn genSetFrameTruncatedOverflowCompare(
34253535
34263536 const rest_ty = try mod.intType(.unsigned, int_info.bits - hi_limb_bits);
34273537
3428 const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, gp);
3538 const temp_regs =
3539 try self.register_manager.allocRegs(3, .{ null, null, null }, abi.RegisterClass.gp);
34293540 const temp_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs);
34303541 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock);
34313542
......@@ -3843,7 +3954,7 @@ fn airUnwrapErrUnionErrPtr(self: *Self, inst: Air.Inst.Index) !void {
38433954 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
38443955 defer self.register_manager.unlockReg(src_lock);
38453956
3846 const dst_reg = try self.register_manager.allocReg(inst, gp);
3957 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
38473958 const dst_mcv = MCValue{ .register = dst_reg };
38483959 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
38493960 defer self.register_manager.unlockReg(dst_lock);
......@@ -3883,7 +3994,7 @@ fn airUnwrapErrUnionPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
38833994 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
38843995 src_reg
38853996 else
3886 try self.register_manager.allocReg(inst, gp);
3997 try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
38873998 const dst_mcv = MCValue{ .register = dst_reg };
38883999 const dst_lock = self.register_manager.lockReg(dst_reg);
38894000 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -3934,7 +4045,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
39344045 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
39354046 src_reg
39364047 else
3937 try self.register_manager.allocReg(inst, gp);
4048 try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
39384049 const dst_lock = self.register_manager.lockReg(dst_reg);
39394050 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
39404051
......@@ -4107,7 +4218,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
41074218 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
41084219 src_reg
41094220 else
4110 try self.register_manager.allocReg(inst, gp);
4221 try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
41114222 const dst_mcv = MCValue{ .register = dst_reg };
41124223 const dst_lock = self.register_manager.lockReg(dst_reg);
41134224 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -4144,7 +4255,7 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi
41444255 .immediate => |imm| {
41454256 // Optimisation: if index MCValue is an immediate, we can multiply in `comptime`
41464257 // and set the register directly to the scaled offset as an immediate.
4147 const reg = try self.register_manager.allocReg(null, gp);
4258 const reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
41484259 try self.genSetReg(reg, index_ty, .{ .immediate = imm * elem_size });
41494260 break :blk reg;
41504261 },
......@@ -4184,7 +4295,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
41844295 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
41854296 defer self.register_manager.unlockReg(offset_reg_lock);
41864297
4187 const addr_reg = try self.register_manager.allocReg(null, gp);
4298 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
41884299 try self.genSetReg(addr_reg, Type.usize, slice_mcv);
41894300 // TODO we could allocate register here, but need to expect addr register and potentially
41904301 // offset register.
......@@ -4241,7 +4352,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
42414352 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
42424353 defer self.register_manager.unlockReg(offset_reg_lock);
42434354
4244 const addr_reg = try self.register_manager.allocReg(null, gp);
4355 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
42454356 switch (array) {
42464357 .register => {
42474358 const frame_index = try self.allocFrameIndex(FrameAlloc.initType(array_ty, mod));
......@@ -4478,7 +4589,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
44784589 };
44794590 defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock);
44804591
4481 const dst_reg = try self.register_manager.allocReg(inst, gp);
4592 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
44824593 const dst_mcv = MCValue{ .register = dst_reg };
44834594 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
44844595 defer self.register_manager.unlockReg(dst_lock);
......@@ -4502,7 +4613,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
45024613 try self.genBinOpMir(.{ ._, .sub }, dst_ty, dst_mcv, .{ .immediate = extra_bits });
45034614 }
45044615 } else if (src_bits <= 128) {
4505 const tmp_reg = try self.register_manager.allocReg(null, gp);
4616 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
45064617 const tmp_mcv = MCValue{ .register = tmp_reg };
45074618 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
45084619 defer self.register_manager.unlockReg(tmp_lock);
......@@ -4525,7 +4636,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
45254636 .{ .immediate = 128 - src_bits },
45264637 );
45274638 }
4528 } else return self.fail("TODO airClz of {}", .{src_ty.fmt(self.bin_file.options.module.?)});
4639 } else return self.fail("TODO airClz of {}", .{src_ty.fmt(mod)});
45294640 break :result dst_mcv;
45304641 }
45314642
......@@ -4608,7 +4719,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
46084719 };
46094720 defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock);
46104721
4611 const dst_reg = try self.register_manager.allocReg(inst, gp);
4722 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
46124723 const dst_mcv = MCValue{ .register = dst_reg };
46134724 const dst_lock = self.register_manager.lockReg(dst_reg);
46144725 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -4635,7 +4746,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
46354746 } else mat_src_mcv;
46364747 try self.genBinOpMir(.{ ._, .tzcnt }, wide_ty, dst_mcv, masked_mcv);
46374748 } else if (src_bits <= 128) {
4638 const tmp_reg = try self.register_manager.allocReg(null, gp);
4749 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
46394750 const tmp_mcv = MCValue{ .register = tmp_reg };
46404751 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
46414752 defer self.register_manager.unlockReg(tmp_lock);
......@@ -4654,12 +4765,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
46544765 try self.genBinOpMir(.{ ._, .add }, dst_ty, dst_mcv, .{ .immediate = 64 });
46554766 try self.genBinOpMir(.{ ._, .tzcnt }, Type.u64, tmp_mcv, mat_src_mcv);
46564767 try self.asmCmovccRegisterRegister(dst_reg.to32(), tmp_reg.to32(), .nc);
4657 } else return self.fail("TODO airCtz of {}", .{src_ty.fmt(self.bin_file.options.module.?)});
4768 } else return self.fail("TODO airCtz of {}", .{src_ty.fmt(mod)});
46584769 break :result dst_mcv;
46594770 }
46604771
4661 if (src_bits > 64)
4662 return self.fail("TODO airCtz of {}", .{src_ty.fmt(self.bin_file.options.module.?)});
4772 if (src_bits > 64) return self.fail("TODO airCtz of {}", .{src_ty.fmt(mod)});
46634773
46644774 const width_reg = try self.copyToTmpRegister(dst_ty, .{ .immediate = src_bits });
46654775 const width_lock = self.register_manager.lockRegAssumeUnused(width_reg);
......@@ -4710,7 +4820,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
47104820 if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
47114821 src_mcv
47124822 else
4713 .{ .register = try self.register_manager.allocReg(inst, gp) };
4823 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.gp) };
47144824
47154825 const popcnt_ty = if (src_abi_size > 1) src_ty else Type.u16;
47164826 try self.genBinOpMir(.{ ._, .popcnt }, popcnt_ty, dst_mcv, mat_src_mcv);
......@@ -4731,7 +4841,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
47314841 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
47324842 defer self.register_manager.unlockReg(dst_lock);
47334843
4734 const tmp_reg = try self.register_manager.allocReg(null, gp);
4844 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
47354845 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
47364846 defer self.register_manager.unlockReg(tmp_lock);
47374847
......@@ -4739,7 +4849,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
47394849 const dst = registerAlias(dst_reg, src_abi_size);
47404850 const tmp = registerAlias(tmp_reg, src_abi_size);
47414851 const imm = if (src_abi_size > 4)
4742 try self.register_manager.allocReg(null, gp)
4852 try self.register_manager.allocReg(null, abi.RegisterClass.gp)
47434853 else
47444854 undefined;
47454855
......@@ -4840,7 +4950,7 @@ fn byteSwap(self: *Self, inst: Air.Inst.Index, src_ty: Type, src_mcv: MCValue, m
48404950 const dst_mcv: MCValue = if (mem_ok)
48414951 try self.allocRegOrMem(inst, true)
48424952 else
4843 .{ .register = try self.register_manager.allocReg(inst, gp) };
4953 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.gp) };
48444954 if (dst_mcv.isRegister()) {
48454955 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_mcv.register);
48464956 defer self.register_manager.unlockReg(dst_lock);
......@@ -4855,7 +4965,7 @@ fn byteSwap(self: *Self, inst: Air.Inst.Index, src_ty: Type, src_mcv: MCValue, m
48554965 return dst_mcv;
48564966 }
48574967
4858 const dst_reg = try self.register_manager.allocReg(inst, gp);
4968 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
48594969 const dst_mcv = MCValue{ .register = dst_reg };
48604970 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
48614971 defer self.register_manager.unlockReg(dst_lock);
......@@ -4898,7 +5008,7 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
48985008 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
48995009 defer self.register_manager.unlockReg(dst_lock);
49005010
4901 const tmp_reg = try self.register_manager.allocReg(null, gp);
5011 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
49025012 const tmp_lock = self.register_manager.lockReg(tmp_reg);
49035013 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);
49045014
......@@ -4906,7 +5016,7 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
49065016 const dst = registerAlias(dst_reg, src_abi_size);
49075017 const tmp = registerAlias(tmp_reg, src_abi_size);
49085018 const imm = if (src_abi_size > 4)
4909 try self.register_manager.allocReg(null, gp)
5019 try self.register_manager.allocReg(null, abi.RegisterClass.gp)
49105020 else
49115021 undefined;
49125022
......@@ -4998,109 +5108,129 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
49985108fn floatSign(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Type) !void {
49995109 const mod = self.bin_file.options.module.?;
50005110 const tag = self.air.instructions.items(.tag)[inst];
5001 const abi_size: u32 = switch (ty.abiSize(mod)) {
5002 1...16 => 16,
5003 17...32 => 32,
5004 else => return self.fail("TODO implement floatSign for {}", .{
5005 ty.fmt(mod),
5006 }),
5007 };
5008 const scalar_bits = ty.scalarType(mod).floatBits(self.target.*);
5009 if (scalar_bits == 80) return self.fail("TODO implement floatSign for {}", .{
5010 ty.fmt(mod),
5011 });
50125111
5013 const src_mcv = try self.resolveInst(operand);
5014 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
5015 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
5112 const result = result: {
5113 const scalar_bits = ty.scalarType(mod).floatBits(self.target.*);
5114 if (scalar_bits == 80) {
5115 if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement floatSign for {}", .{
5116 ty.fmt(mod),
5117 });
50165118
5017 const dst_mcv: MCValue = if (src_mcv.isRegister() and self.reuseOperand(inst, operand, 0, src_mcv))
5018 src_mcv
5019 else if (self.hasFeature(.avx))
5020 .{ .register = try self.register_manager.allocReg(inst, sse) }
5021 else
5022 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5023 const dst_reg = dst_mcv.getReg().?;
5024 const dst_lock = self.register_manager.lockReg(dst_reg);
5025 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5119 const src_mcv = try self.resolveInst(operand);
5120 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
5121 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
50265122
5027 const vec_ty = try mod.vectorType(.{
5028 .len = @divExact(abi_size * 8, scalar_bits),
5029 .child = (try mod.intType(.signed, scalar_bits)).ip_index,
5030 });
5123 const dst_mcv: MCValue = .{ .register = .st0 };
5124 if (std.meta.eql(src_mcv, dst_mcv) and self.reuseOperand(inst, operand, 0, src_mcv))
5125 try self.register_manager.getReg(.st0, inst);
50315126
5032 const sign_val = switch (tag) {
5033 .neg => try vec_ty.minInt(mod, vec_ty),
5034 .abs => try vec_ty.maxInt(mod, vec_ty),
5035 else => unreachable,
5036 };
5127 try self.genCopy(ty, dst_mcv, src_mcv);
5128 switch (tag) {
5129 .neg => try self.asmOpOnly(.{ .f_, .chs }),
5130 .abs => try self.asmOpOnly(.{ .f_, .abs }),
5131 else => unreachable,
5132 }
5133 break :result dst_mcv;
5134 }
50375135
5038 const sign_mcv = try self.genTypedValue(.{ .ty = vec_ty, .val = sign_val });
5039 const sign_mem = if (sign_mcv.isMemory())
5040 sign_mcv.mem(Memory.PtrSize.fromSize(abi_size))
5041 else
5042 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5043 .base = .{ .reg = try self.copyToTmpRegister(Type.usize, sign_mcv.address()) },
5136 const abi_size: u32 = switch (ty.abiSize(mod)) {
5137 1...16 => 16,
5138 17...32 => 32,
5139 else => return self.fail("TODO implement floatSign for {}", .{
5140 ty.fmt(mod),
5141 }),
5142 };
5143
5144 const src_mcv = try self.resolveInst(operand);
5145 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
5146 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
5147
5148 const dst_mcv: MCValue = if (src_mcv.isRegister() and
5149 self.reuseOperand(inst, operand, 0, src_mcv))
5150 src_mcv
5151 else if (self.hasFeature(.avx))
5152 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }
5153 else
5154 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5155 const dst_reg = dst_mcv.getReg().?;
5156 const dst_lock = self.register_manager.lockReg(dst_reg);
5157 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5158
5159 const vec_ty = try mod.vectorType(.{
5160 .len = @divExact(abi_size * 8, scalar_bits),
5161 .child = (try mod.intType(.signed, scalar_bits)).ip_index,
50445162 });
50455163
5046 if (self.hasFeature(.avx)) try self.asmRegisterRegisterMemory(
5047 switch (scalar_bits) {
5048 16, 128 => if (abi_size <= 16 or self.hasFeature(.avx2)) switch (tag) {
5049 .neg => .{ .vp_, .xor },
5050 .abs => .{ .vp_, .@"and" },
5051 else => unreachable,
5052 } else switch (tag) {
5053 .neg => .{ .v_ps, .xor },
5054 .abs => .{ .v_ps, .@"and" },
5055 else => unreachable,
5056 },
5057 32 => switch (tag) {
5058 .neg => .{ .v_ps, .xor },
5059 .abs => .{ .v_ps, .@"and" },
5060 else => unreachable,
5061 },
5062 64 => switch (tag) {
5063 .neg => .{ .v_pd, .xor },
5064 .abs => .{ .v_pd, .@"and" },
5065 else => unreachable,
5066 },
5067 80 => return self.fail("TODO implement floatSign for {}", .{
5068 ty.fmt(self.bin_file.options.module.?),
5069 }),
5164 const sign_val = switch (tag) {
5165 .neg => try vec_ty.minInt(mod, vec_ty),
5166 .abs => try vec_ty.maxInt(mod, vec_ty),
50705167 else => unreachable,
5071 },
5072 registerAlias(dst_reg, abi_size),
5073 registerAlias(if (src_mcv.isRegister())
5074 src_mcv.getReg().?
5168 };
5169
5170 const sign_mcv = try self.genTypedValue(.{ .ty = vec_ty, .val = sign_val });
5171 const sign_mem = if (sign_mcv.isMemory())
5172 sign_mcv.mem(Memory.PtrSize.fromSize(abi_size))
50755173 else
5076 try self.copyToTmpRegister(ty, src_mcv), abi_size),
5077 sign_mem,
5078 ) else try self.asmRegisterMemory(
5079 switch (scalar_bits) {
5080 16, 128 => switch (tag) {
5081 .neg => .{ .p_, .xor },
5082 .abs => .{ .p_, .@"and" },
5083 else => unreachable,
5084 },
5085 32 => switch (tag) {
5086 .neg => .{ ._ps, .xor },
5087 .abs => .{ ._ps, .@"and" },
5174 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5175 .base = .{ .reg = try self.copyToTmpRegister(Type.usize, sign_mcv.address()) },
5176 });
5177
5178 if (self.hasFeature(.avx)) try self.asmRegisterRegisterMemory(
5179 switch (scalar_bits) {
5180 16, 128 => if (abi_size <= 16 or self.hasFeature(.avx2)) switch (tag) {
5181 .neg => .{ .vp_, .xor },
5182 .abs => .{ .vp_, .@"and" },
5183 else => unreachable,
5184 } else switch (tag) {
5185 .neg => .{ .v_ps, .xor },
5186 .abs => .{ .v_ps, .@"and" },
5187 else => unreachable,
5188 },
5189 32 => switch (tag) {
5190 .neg => .{ .v_ps, .xor },
5191 .abs => .{ .v_ps, .@"and" },
5192 else => unreachable,
5193 },
5194 64 => switch (tag) {
5195 .neg => .{ .v_pd, .xor },
5196 .abs => .{ .v_pd, .@"and" },
5197 else => unreachable,
5198 },
5199 80 => return self.fail("TODO implement floatSign for {}", .{ty.fmt(mod)}),
50885200 else => unreachable,
50895201 },
5090 64 => switch (tag) {
5091 .neg => .{ ._pd, .xor },
5092 .abs => .{ ._pd, .@"and" },
5202 registerAlias(dst_reg, abi_size),
5203 registerAlias(if (src_mcv.isRegister())
5204 src_mcv.getReg().?
5205 else
5206 try self.copyToTmpRegister(ty, src_mcv), abi_size),
5207 sign_mem,
5208 ) else try self.asmRegisterMemory(
5209 switch (scalar_bits) {
5210 16, 128 => switch (tag) {
5211 .neg => .{ .p_, .xor },
5212 .abs => .{ .p_, .@"and" },
5213 else => unreachable,
5214 },
5215 32 => switch (tag) {
5216 .neg => .{ ._ps, .xor },
5217 .abs => .{ ._ps, .@"and" },
5218 else => unreachable,
5219 },
5220 64 => switch (tag) {
5221 .neg => .{ ._pd, .xor },
5222 .abs => .{ ._pd, .@"and" },
5223 else => unreachable,
5224 },
5225 80 => return self.fail("TODO implement floatSign for {}", .{ty.fmt(mod)}),
50935226 else => unreachable,
50945227 },
5095 80 => return self.fail("TODO implement floatSign for {}", .{
5096 ty.fmt(self.bin_file.options.module.?),
5097 }),
5098 else => unreachable,
5099 },
5100 registerAlias(dst_reg, abi_size),
5101 sign_mem,
5102 );
5103 return self.finishAir(inst, dst_mcv, .{ operand, .none, .none });
5228 registerAlias(dst_reg, abi_size),
5229 sign_mem,
5230 );
5231 break :result dst_mcv;
5232 };
5233 return self.finishAir(inst, result, .{ operand, .none, .none });
51045234}
51055235
51065236fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5133,21 +5263,29 @@ fn airRound(self: *Self, inst: Air.Inst.Index, mode: RoundMode) !void {
51335263 const un_op = self.air.instructions.items(.data)[inst].un_op;
51345264 const ty = self.typeOf(un_op);
51355265
5136 const src_mcv = try self.resolveInst(un_op);
5137 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
5138 src_mcv
5139 else
5140 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5141 const dst_reg = dst_mcv.getReg().?;
5142 const dst_lock = self.register_manager.lockReg(dst_reg);
5143 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5144 try self.genRound(ty, dst_reg, src_mcv, mode);
5145 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
5266 const result = result: {
5267 switch (try self.genRoundLibcall(ty, .{ .air_ref = un_op }, mode)) {
5268 .none => {},
5269 else => |dst_mcv| break :result dst_mcv,
5270 }
5271
5272 const src_mcv = try self.resolveInst(un_op);
5273 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
5274 src_mcv
5275 else
5276 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5277 const dst_reg = dst_mcv.getReg().?;
5278 const dst_lock = self.register_manager.lockReg(dst_reg);
5279 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5280 try self.genRound(ty, dst_reg, src_mcv, mode);
5281 break :result dst_mcv;
5282 };
5283 return self.finishAir(inst, result, .{ un_op, .none, .none });
51465284}
51475285
5148fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: RoundMode) !void {
5286fn getRoundTag(self: *Self, ty: Type) ?Mir.Inst.FixedTag {
51495287 const mod = self.bin_file.options.module.?;
5150 const mir_tag = @as(?Mir.Inst.FixedTag, if (self.hasFeature(.sse4_1)) switch (ty.zigTypeTag(mod)) {
5288 return if (self.hasFeature(.sse4_1)) switch (ty.zigTypeTag(mod)) {
51515289 .Float => switch (ty.floatBits(self.target.*)) {
51525290 32 => if (self.hasFeature(.avx)) .{ .v_ss, .round } else .{ ._ss, .round },
51535291 64 => if (self.hasFeature(.avx)) .{ .v_sd, .round } else .{ ._sd, .round },
......@@ -5174,27 +5312,38 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: Ro
51745312 else => null,
51755313 },
51765314 else => unreachable,
5177 } else null) orelse {
5178 if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement genRound for {}", .{
5179 ty.fmt(self.bin_file.options.module.?),
5180 });
5315 } else null;
5316}
51815317
5182 var callee: ["__trunc?".len]u8 = undefined;
5183 const res = try self.genCall(.{ .lib = .{
5184 .return_type = ty.toIntern(),
5185 .param_types = &.{ty.toIntern()},
5186 .callee = std.fmt.bufPrint(&callee, "{s}{s}{s}", .{
5187 floatLibcAbiPrefix(ty),
5188 switch (mode.mode) {
5189 .down => "floor",
5190 .up => "ceil",
5191 .zero => "trunc",
5192 else => unreachable,
5193 },
5194 floatLibcAbiSuffix(ty),
5195 }) catch unreachable,
5196 } }, &.{ty}, &.{src_mcv});
5197 return self.genSetReg(dst_reg, ty, res);
5318fn genRoundLibcall(self: *Self, ty: Type, src_mcv: MCValue, mode: RoundMode) !MCValue {
5319 const mod = self.bin_file.options.module.?;
5320 if (self.getRoundTag(ty)) |_| return .none;
5321
5322 if (ty.zigTypeTag(mod) != .Float)
5323 return self.fail("TODO implement genRound for {}", .{ty.fmt(mod)});
5324
5325 var callee: ["__trunc?".len]u8 = undefined;
5326 return try self.genCall(.{ .lib = .{
5327 .return_type = ty.toIntern(),
5328 .param_types = &.{ty.toIntern()},
5329 .callee = std.fmt.bufPrint(&callee, "{s}{s}{s}", .{
5330 floatLibcAbiPrefix(ty),
5331 switch (mode.mode) {
5332 .down => "floor",
5333 .up => "ceil",
5334 .zero => "trunc",
5335 else => unreachable,
5336 },
5337 floatLibcAbiSuffix(ty),
5338 }) catch unreachable,
5339 } }, &.{ty}, &.{src_mcv});
5340}
5341
5342fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: RoundMode) !void {
5343 const mod = self.bin_file.options.module.?;
5344 const mir_tag = self.getRoundTag(ty) orelse {
5345 const result = try self.genRoundLibcall(ty, src_mcv, mode);
5346 return self.genSetReg(dst_reg, ty, result);
51985347 };
51995348 const abi_size: u32 = @intCast(ty.abiSize(mod));
52005349 const dst_alias = registerAlias(dst_reg, abi_size);
......@@ -5236,46 +5385,104 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
52365385 const mod = self.bin_file.options.module.?;
52375386 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
52385387 const ty = self.typeOf(ty_op.operand);
5239 const scalar_ty = ty.scalarType(mod);
52405388
5241 switch (scalar_ty.zigTypeTag(mod)) {
5242 .Int => if (ty.zigTypeTag(mod) == .Vector) {
5243 return self.fail("TODO implement airAbs for {}", .{ty.fmt(mod)});
5244 } else {
5245 if (ty.abiSize(mod) > 8) {
5246 return self.fail("TODO implement abs for integer abi sizes larger than 8", .{});
5247 }
5248 const src_mcv = try self.resolveInst(ty_op.operand);
5249 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5389 const result: MCValue = result: {
5390 const mir_tag = @as(?Mir.Inst.FixedTag, switch (ty.zigTypeTag(mod)) {
5391 else => null,
5392 .Int => {
5393 if (ty.abiSize(mod) > 8) {
5394 return self.fail("TODO implement abs for integer abi sizes larger than 8", .{});
5395 }
5396 const src_mcv = try self.resolveInst(ty_op.operand);
5397 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
52505398
5251 try self.genUnOpMir(.{ ._, .neg }, ty, dst_mcv);
5399 try self.genUnOpMir(.{ ._, .neg }, ty, dst_mcv);
52525400
5253 const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(mod))), 2);
5254 switch (src_mcv) {
5255 .register => |val_reg| try self.asmCmovccRegisterRegister(
5256 registerAlias(dst_mcv.register, cmov_abi_size),
5257 registerAlias(val_reg, cmov_abi_size),
5258 .l,
5259 ),
5260 .memory, .indirect, .load_frame => try self.asmCmovccRegisterMemory(
5261 registerAlias(dst_mcv.register, cmov_abi_size),
5262 src_mcv.mem(Memory.PtrSize.fromSize(cmov_abi_size)),
5263 .l,
5264 ),
5265 else => {
5266 const val_reg = try self.copyToTmpRegister(ty, src_mcv);
5267 try self.asmCmovccRegisterRegister(
5401 const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(mod))), 2);
5402 switch (src_mcv) {
5403 .register => |val_reg| try self.asmCmovccRegisterRegister(
52685404 registerAlias(dst_mcv.register, cmov_abi_size),
52695405 registerAlias(val_reg, cmov_abi_size),
52705406 .l,
5271 );
5407 ),
5408 .memory, .indirect, .load_frame => try self.asmCmovccRegisterMemory(
5409 registerAlias(dst_mcv.register, cmov_abi_size),
5410 src_mcv.mem(Memory.PtrSize.fromSize(cmov_abi_size)),
5411 .l,
5412 ),
5413 else => {
5414 const val_reg = try self.copyToTmpRegister(ty, src_mcv);
5415 try self.asmCmovccRegisterRegister(
5416 registerAlias(dst_mcv.register, cmov_abi_size),
5417 registerAlias(val_reg, cmov_abi_size),
5418 .l,
5419 );
5420 },
5421 }
5422 break :result dst_mcv;
5423 },
5424 .Float => return self.floatSign(inst, ty_op.operand, ty),
5425 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
5426 else => null,
5427 .Int => switch (ty.childType(mod).intInfo(mod).bits) {
5428 else => null,
5429 8 => switch (ty.vectorLen(mod)) {
5430 else => null,
5431 1...16 => if (self.hasFeature(.avx))
5432 .{ .vp_b, .abs }
5433 else if (self.hasFeature(.ssse3))
5434 .{ .p_b, .abs }
5435 else
5436 null,
5437 17...32 => if (self.hasFeature(.avx2)) .{ .vp_b, .abs } else null,
5438 },
5439 16 => switch (ty.vectorLen(mod)) {
5440 else => null,
5441 1...8 => if (self.hasFeature(.avx))
5442 .{ .vp_w, .abs }
5443 else if (self.hasFeature(.ssse3))
5444 .{ .p_w, .abs }
5445 else
5446 null,
5447 9...16 => if (self.hasFeature(.avx2)) .{ .vp_w, .abs } else null,
5448 },
5449 32 => switch (ty.vectorLen(mod)) {
5450 else => null,
5451 1...4 => if (self.hasFeature(.avx))
5452 .{ .vp_d, .abs }
5453 else if (self.hasFeature(.ssse3))
5454 .{ .p_d, .abs }
5455 else
5456 null,
5457 5...8 => if (self.hasFeature(.avx2)) .{ .vp_d, .abs } else null,
5458 },
52725459 },
5273 }
5274 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
5275 },
5276 .Float => return self.floatSign(inst, ty_op.operand, ty),
5277 else => unreachable,
5278 }
5460 .Float => return self.floatSign(inst, ty_op.operand, ty),
5461 },
5462 }) orelse return self.fail("TODO implement airAbs for {}", .{ty.fmt(mod)});
5463
5464 const abi_size: u32 = @intCast(ty.abiSize(mod));
5465 const src_mcv = try self.resolveInst(ty_op.operand);
5466 const dst_reg = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
5467 src_mcv.getReg().?
5468 else
5469 try self.register_manager.allocReg(inst, self.regClassForType(ty));
5470 const dst_alias = registerAlias(dst_reg, abi_size);
5471 if (src_mcv.isMemory()) try self.asmRegisterMemory(
5472 mir_tag,
5473 dst_alias,
5474 src_mcv.mem(self.memPtrSize(ty)),
5475 ) else try self.asmRegisterRegister(
5476 mir_tag,
5477 dst_alias,
5478 registerAlias(if (src_mcv.isRegister())
5479 src_mcv.getReg().?
5480 else
5481 try self.copyToTmpRegister(ty, src_mcv), abi_size),
5482 );
5483 break :result .{ .register = dst_reg };
5484 };
5485 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
52795486}
52805487
52815488fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5284,19 +5491,43 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {
52845491 const ty = self.typeOf(un_op);
52855492 const abi_size: u32 = @intCast(ty.abiSize(mod));
52865493
5287 const src_mcv = try self.resolveInst(un_op);
5288 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
5289 src_mcv
5290 else
5291 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5292 const dst_reg = registerAlias(dst_mcv.getReg().?, abi_size);
5293 const dst_lock = self.register_manager.lockReg(dst_reg);
5294 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5295
52965494 const result: MCValue = result: {
5495 switch (ty.zigTypeTag(mod)) {
5496 .Float => {
5497 const float_bits = ty.floatBits(self.target.*);
5498 if (switch (float_bits) {
5499 16 => !self.hasFeature(.f16c),
5500 32, 64 => false,
5501 80, 128 => true,
5502 else => unreachable,
5503 }) {
5504 var callee: ["__sqrt?".len]u8 = undefined;
5505 break :result try self.genCall(.{ .lib = .{
5506 .return_type = ty.toIntern(),
5507 .param_types = &.{ty.toIntern()},
5508 .callee = std.fmt.bufPrint(&callee, "{s}sqrt{s}", .{
5509 floatLibcAbiPrefix(ty),
5510 floatLibcAbiSuffix(ty),
5511 }) catch unreachable,
5512 } }, &.{ty}, &.{.{ .air_ref = un_op }});
5513 }
5514 },
5515 else => {},
5516 }
5517
5518 const src_mcv = try self.resolveInst(un_op);
5519 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
5520 src_mcv
5521 else
5522 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5523 const dst_reg = registerAlias(dst_mcv.getReg().?, abi_size);
5524 const dst_lock = self.register_manager.lockReg(dst_reg);
5525 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5526
52975527 const mir_tag = @as(?Mir.Inst.FixedTag, switch (ty.zigTypeTag(mod)) {
52985528 .Float => switch (ty.floatBits(self.target.*)) {
5299 16 => if (self.hasFeature(.f16c)) {
5529 16 => {
5530 assert(self.hasFeature(.f16c));
53005531 const mat_src_reg = if (src_mcv.isRegister())
53015532 src_mcv.getReg().?
53025533 else
......@@ -5310,10 +5541,9 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {
53105541 Immediate.u(0b1_00),
53115542 );
53125543 break :result dst_mcv;
5313 } else null,
5544 },
53145545 32 => if (self.hasFeature(.avx)) .{ .v_ss, .sqrt } else .{ ._ss, .sqrt },
53155546 64 => if (self.hasFeature(.avx)) .{ .v_sd, .sqrt } else .{ ._sd, .sqrt },
5316 80, 128 => null,
53175547 else => unreachable,
53185548 },
53195549 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
......@@ -5387,21 +5617,9 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {
53875617 else => unreachable,
53885618 },
53895619 else => unreachable,
5390 }) orelse {
5391 if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement airSqrt for {}", .{
5392 ty.fmt(mod),
5393 });
5394
5395 var callee: ["__sqrt?".len]u8 = undefined;
5396 break :result try self.genCall(.{ .lib = .{
5397 .return_type = ty.toIntern(),
5398 .param_types = &.{ty.toIntern()},
5399 .callee = std.fmt.bufPrint(&callee, "{s}sqrt{s}", .{
5400 floatLibcAbiPrefix(ty),
5401 floatLibcAbiSuffix(ty),
5402 }) catch unreachable,
5403 } }, &.{ty}, &.{src_mcv});
5404 };
5620 }) orelse return self.fail("TODO implement airSqrt for {}", .{
5621 ty.fmt(mod),
5622 });
54055623 switch (mir_tag[0]) {
54065624 .v_ss, .v_sd => if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(
54075625 mir_tag,
......@@ -5518,9 +5736,7 @@ fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) Inn
55185736 const val_bit_off = ptr_info.packed_offset.bit_offset % limb_abi_bits;
55195737 const val_extra_bits = self.regExtraBits(val_ty);
55205738
5521 if (val_abi_size > 8) return self.fail("TODO implement packed load of {}", .{
5522 val_ty.fmt(self.bin_file.options.module.?),
5523 });
5739 if (val_abi_size > 8) return self.fail("TODO implement packed load of {}", .{val_ty.fmt(mod)});
55245740
55255741 const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
55265742 const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg);
......@@ -5528,7 +5744,7 @@ fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) Inn
55285744
55295745 const dst_reg = switch (dst_mcv) {
55305746 .register => |reg| reg,
5531 else => try self.register_manager.allocReg(null, gp),
5747 else => try self.register_manager.allocReg(null, abi.RegisterClass.gp),
55325748 };
55335749 const dst_lock = self.register_manager.lockReg(dst_reg);
55345750 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -5547,7 +5763,8 @@ fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) Inn
55475763 );
55485764 try self.asmRegisterImmediate(.{ ._r, .sh }, load_reg, Immediate.u(val_bit_off));
55495765 } else {
5550 const tmp_reg = registerAlias(try self.register_manager.allocReg(null, gp), val_abi_size);
5766 const tmp_reg =
5767 registerAlias(try self.register_manager.allocReg(null, abi.RegisterClass.gp), val_abi_size);
55515768 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
55525769 defer self.register_manager.unlockReg(tmp_lock);
55535770
......@@ -5632,8 +5849,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
56325849 const ptr_ty = self.typeOf(ty_op.operand);
56335850 const elem_size = elem_ty.abiSize(mod);
56345851
5635 const elem_rc = regClassForType(elem_ty, mod);
5636 const ptr_rc = regClassForType(ptr_ty, mod);
5852 const elem_rc = self.regClassForType(elem_ty);
5853 const ptr_rc = self.regClassForType(ptr_ty);
56375854
56385855 const ptr_mcv = try self.resolveInst(ty_op.operand);
56395856 const dst_mcv = if (elem_size <= 8 and elem_rc.supersetOf(ptr_rc) and
......@@ -5688,13 +5905,13 @@ fn packedStore(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) In
56885905 } else if (math.cast(i32, @as(i64, @bitCast(part_mask_not)))) |small| {
56895906 try self.asmMemoryImmediate(.{ ._, .@"and" }, limb_mem, Immediate.s(small));
56905907 } else {
5691 const part_mask_reg = try self.register_manager.allocReg(null, gp);
5908 const part_mask_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
56925909 try self.asmRegisterImmediate(.{ ._, .mov }, part_mask_reg, Immediate.u(part_mask_not));
56935910 try self.asmMemoryRegister(.{ ._, .@"and" }, limb_mem, part_mask_reg);
56945911 }
56955912
56965913 if (src_bit_size <= 64) {
5697 const tmp_reg = try self.register_manager.allocReg(null, gp);
5914 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
56985915 const tmp_mcv = MCValue{ .register = tmp_reg };
56995916 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
57005917 defer self.register_manager.unlockReg(tmp_lock);
......@@ -5721,9 +5938,7 @@ fn packedStore(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) In
57215938 limb_mem,
57225939 registerAlias(tmp_reg, limb_abi_size),
57235940 );
5724 } else return self.fail("TODO: implement packed store of {}", .{
5725 src_ty.fmt(self.bin_file.options.module.?),
5726 });
5941 } else return self.fail("TODO: implement packed store of {}", .{src_ty.fmt(mod)});
57275942 }
57285943}
57295944
......@@ -5761,7 +5976,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
57615976
57625977 try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv);
57635978 },
5764 .air_ref => |ptr_ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ptr_ref)),
5979 .air_ref => |ptr_ref| try self.store(ptr_ty, try self.resolveInst(ptr_ref), src_mcv),
57655980 }
57665981}
57675982
......@@ -5832,11 +6047,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
58326047 const index = extra.field_index;
58336048
58346049 const container_ty = self.typeOf(operand);
5835 const container_rc = regClassForType(container_ty, mod);
6050 const container_rc = self.regClassForType(container_ty);
58366051 const field_ty = container_ty.structFieldType(index, mod);
58376052 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) break :result .none;
5838 const field_rc = regClassForType(field_ty, mod);
5839 const field_is_gp = field_rc.supersetOf(gp);
6053 const field_rc = self.regClassForType(field_ty);
6054 const field_is_gp = field_rc.supersetOf(abi.RegisterClass.gp);
58406055
58416056 const src_mcv = try self.resolveInst(operand);
58426057 const field_off: u32 = switch (container_ty.containerLayout(mod)) {
......@@ -5860,8 +6075,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
58606075 @intCast(field_ty.bitSize(mod)),
58616076 );
58626077
5863 const dst_reg =
5864 try self.register_manager.allocReg(if (field_is_gp) inst else null, gp);
6078 const dst_reg = try self.register_manager.allocReg(
6079 if (field_is_gp) inst else null,
6080 abi.RegisterClass.gp,
6081 );
58656082 const dst_mcv = MCValue{ .register = dst_reg };
58666083 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
58676084 defer self.register_manager.unlockReg(dst_lock);
......@@ -5890,7 +6107,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
58906107 return self.fail("TODO implement struct_field_val with large packed field", .{});
58916108 }
58926109
5893 const dst_reg = try self.register_manager.allocReg(if (field_is_gp) inst else null, gp);
6110 const dst_reg = try self.register_manager.allocReg(
6111 if (field_is_gp) inst else null,
6112 abi.RegisterClass.gp,
6113 );
58946114 const field_extra_bits = self.regExtraBits(field_ty);
58956115 const load_abi_size =
58966116 if (field_bit_off < field_extra_bits) field_abi_size else field_abi_size * 2;
......@@ -5907,7 +6127,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
59076127 try self.asmRegisterImmediate(.{ ._r, .sh }, load_reg, Immediate.u(field_bit_off));
59086128 } else {
59096129 const tmp_reg = registerAlias(
5910 try self.register_manager.allocReg(null, gp),
6130 try self.register_manager.allocReg(null, abi.RegisterClass.gp),
59116131 field_abi_size,
59126132 );
59136133 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
......@@ -5967,7 +6187,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
59676187 );
59686188 if (self.regExtraBits(field_ty) > 0) try self.truncateRegister(field_ty, dst_reg);
59696189
5970 break :result if (field_rc.supersetOf(gp))
6190 break :result if (field_rc.supersetOf(abi.RegisterClass.gp))
59716191 dst_mcv
59726192 else
59736193 try self.copyToRegisterWithInstTracking(inst, field_ty, dst_mcv);
......@@ -5988,7 +6208,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
59886208 self.eflags_inst = inst;
59896209 break :result .{ .eflags = ro.eflags };
59906210 } else {
5991 const dst_reg = try self.register_manager.allocReg(inst, gp);
6211 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
59926212 try self.asmSetccRegister(dst_reg.to8(), ro.eflags);
59936213 break :result .{ .register = dst_reg.to8() };
59946214 },
......@@ -6024,9 +6244,8 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air:
60246244 const mod = self.bin_file.options.module.?;
60256245 const src_ty = self.typeOf(src_air);
60266246 const src_mcv = try self.resolveInst(src_air);
6027 if (src_ty.zigTypeTag(mod) == .Vector) {
6028 return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)});
6029 }
6247 if (src_ty.zigTypeTag(mod) == .Vector)
6248 return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(mod)});
60306249
60316250 switch (src_mcv) {
60326251 .eflags => |cc| switch (tag) {
......@@ -6086,10 +6305,7 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air:
60866305fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MCValue) !void {
60876306 const mod = self.bin_file.options.module.?;
60886307 const abi_size: u32 = @intCast(dst_ty.abiSize(mod));
6089 if (abi_size > 8) return self.fail("TODO implement {} for {}", .{
6090 mir_tag,
6091 dst_ty.fmt(self.bin_file.options.module.?),
6092 });
6308 if (abi_size > 8) return self.fail("TODO implement {} for {}", .{ mir_tag, dst_ty.fmt(mod) });
60936309 switch (dst_mcv) {
60946310 .none,
60956311 .unreach,
......@@ -6109,7 +6325,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
61096325 .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
61106326 .register_pair => unreachable, // unimplemented
61116327 .memory, .load_got, .load_direct, .load_tlv => {
6112 const addr_reg = try self.register_manager.allocReg(null, gp);
6328 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
61136329 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
61146330 defer self.register_manager.unlockReg(addr_reg_lock);
61156331
......@@ -6211,7 +6427,7 @@ fn genShiftBinOpMir(
62116427 }),
62126428 }
62136429 } else if (abi_size <= 16) {
6214 const tmp_reg = try self.register_manager.allocReg(null, gp);
6430 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
62156431 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
62166432 defer self.register_manager.unlockReg(tmp_lock);
62176433
......@@ -6289,11 +6505,11 @@ fn genShiftBinOpMir(
62896505 }
62906506 },
62916507 else => {
6292 const first_reg = try self.register_manager.allocReg(null, gp);
6508 const first_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
62936509 const first_lock = self.register_manager.lockRegAssumeUnused(first_reg);
62946510 defer self.register_manager.unlockReg(first_lock);
62956511
6296 const second_reg = try self.register_manager.allocReg(null, gp);
6512 const second_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
62976513 const second_lock = self.register_manager.lockRegAssumeUnused(second_reg);
62986514 defer self.register_manager.unlockReg(second_lock);
62996515
......@@ -6445,7 +6661,7 @@ fn genMulDivBinOp(
64456661 .mul, .mul_wrap => dst_abi_size != src_abi_size and dst_abi_size != src_abi_size * 2,
64466662 .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_abi_size != src_abi_size,
64476663 } or src_abi_size > 8) return self.fail("TODO implement genMulDivBinOp from {} to {}", .{
6448 src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?),
6664 src_ty.fmt(mod), dst_ty.fmt(mod),
64496665 });
64506666 const ty = if (dst_abi_size <= 8) dst_ty else src_ty;
64516667 const abi_size = if (dst_abi_size <= 8) dst_abi_size else src_abi_size;
......@@ -6615,31 +6831,53 @@ fn genBinOp(
66156831 const rhs_ty = self.typeOf(rhs_air);
66166832 const abi_size: u32 = @intCast(lhs_ty.abiSize(mod));
66176833
6618 if (lhs_ty.isRuntimeFloat() and switch (lhs_ty.floatBits(self.target.*)) {
6834 if (lhs_ty.isRuntimeFloat() and (air_tag == .rem or switch (lhs_ty.floatBits(self.target.*)) {
66196835 16 => !self.hasFeature(.f16c),
66206836 32, 64 => false,
66216837 80, 128 => true,
66226838 else => unreachable,
6623 }) {
6839 })) {
66246840 var callee: ["__add?f3".len]u8 = undefined;
6625 return self.genCall(.{ .lib = .{
6841 const result = try self.genCall(.{ .lib = .{
66266842 .return_type = lhs_ty.toIntern(),
66276843 .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() },
66286844 .callee = switch (air_tag) {
6629 .add, .sub, .mul, .div_float => std.fmt.bufPrint(&callee, "__{s}{c}f3", .{
6845 .add,
6846 .sub,
6847 .mul,
6848 .div_float,
6849 .div_trunc,
6850 .div_floor,
6851 => std.fmt.bufPrint(&callee, "__{s}{c}f3", .{
66306852 @tagName(air_tag)[0..3],
66316853 floatCompilerRtAbiName(lhs_ty.floatBits(self.target.*)),
66326854 }),
6633 .min, .max => std.fmt.bufPrint(&callee, "{s}f{s}{s}", .{
6855 .rem, .min, .max => std.fmt.bufPrint(&callee, "{s}f{s}{s}", .{
66346856 floatLibcAbiPrefix(lhs_ty),
6635 @tagName(air_tag),
6857 switch (air_tag) {
6858 .rem => "mod",
6859 .min => "min",
6860 .max => "max",
6861 else => unreachable,
6862 },
66366863 floatLibcAbiSuffix(lhs_ty),
66376864 }),
66386865 else => return self.fail("TODO implement genBinOp for {s} {}", .{
6639 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
6866 @tagName(air_tag), lhs_ty.fmt(mod),
66406867 }),
66416868 } catch unreachable,
66426869 } }, &.{ lhs_ty, rhs_ty }, &.{ .{ .air_ref = lhs_air }, .{ .air_ref = rhs_air } });
6870 return switch (air_tag) {
6871 .div_trunc, .div_floor => try self.genRoundLibcall(lhs_ty, result, .{
6872 .mode = switch (air_tag) {
6873 .div_trunc => .zero,
6874 .div_floor => .down,
6875 else => unreachable,
6876 },
6877 .precision = .inexact,
6878 }),
6879 else => result,
6880 };
66436881 }
66446882
66456883 if ((lhs_ty.scalarType(mod).isRuntimeFloat() and
......@@ -6655,11 +6893,11 @@ fn genBinOp(
66556893 if (!self.hasFeature(.avx) and self.hasFeature(.sse4_1)) mask: {
66566894 try self.register_manager.getReg(.xmm0, null);
66576895 break :mask .xmm0;
6658 } else try self.register_manager.allocReg(null, sse),
6896 } else try self.register_manager.allocReg(null, abi.RegisterClass.sse),
66596897 abi_size,
66606898 ) else null,
66616899 .rem, .mod => return self.fail("TODO implement genBinOp for {s} {}", .{
6662 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
6900 @tagName(air_tag), lhs_ty.fmt(mod),
66636901 }),
66646902 };
66656903 const mask_lock =
......@@ -6896,7 +7134,7 @@ fn genBinOp(
68967134 },
68977135
68987136 else => return self.fail("TODO implement genBinOp for {s} {}", .{
6899 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7137 @tagName(air_tag), lhs_ty.fmt(mod),
69007138 }),
69017139 }
69027140 return dst_mcv;
......@@ -6908,7 +7146,8 @@ fn genBinOp(
69087146 .Float => switch (lhs_ty.floatBits(self.target.*)) {
69097147 16 => {
69107148 assert(self.hasFeature(.f16c));
6911 const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128();
7149 const tmp_reg =
7150 (try self.register_manager.allocReg(null, abi.RegisterClass.sse)).to128();
69127151 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
69137152 defer self.register_manager.unlockReg(tmp_lock);
69147153
......@@ -6943,6 +7182,23 @@ fn genBinOp(
69437182 dst_reg,
69447183 tmp_reg,
69457184 );
7185 switch (air_tag) {
7186 .div_trunc, .div_floor => try self.asmRegisterRegisterRegisterImmediate(
7187 .{ .v_ss, .round },
7188 dst_reg,
7189 dst_reg,
7190 dst_reg,
7191 Immediate.u(@as(u5, @bitCast(RoundMode{
7192 .mode = switch (air_tag) {
7193 .div_trunc => .zero,
7194 .div_floor => .down,
7195 else => unreachable,
7196 },
7197 .precision = .inexact,
7198 }))),
7199 ),
7200 else => {},
7201 }
69467202 try self.asmRegisterRegisterImmediate(
69477203 .{ .v_, .cvtps2ph },
69487204 dst_reg,
......@@ -7215,7 +7471,10 @@ fn genBinOp(
72157471 assert(self.hasFeature(.f16c));
72167472 switch (lhs_ty.vectorLen(mod)) {
72177473 1 => {
7218 const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128();
7474 const tmp_reg = (try self.register_manager.allocReg(
7475 null,
7476 abi.RegisterClass.sse,
7477 )).to128();
72197478 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
72207479 defer self.register_manager.unlockReg(tmp_lock);
72217480
......@@ -7259,7 +7518,10 @@ fn genBinOp(
72597518 return dst_mcv;
72607519 },
72617520 2 => {
7262 const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128();
7521 const tmp_reg = (try self.register_manager.allocReg(
7522 null,
7523 abi.RegisterClass.sse,
7524 )).to128();
72637525 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
72647526 defer self.register_manager.unlockReg(tmp_lock);
72657527
......@@ -7307,7 +7569,10 @@ fn genBinOp(
73077569 return dst_mcv;
73087570 },
73097571 3...4 => {
7310 const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128();
7572 const tmp_reg = (try self.register_manager.allocReg(
7573 null,
7574 abi.RegisterClass.sse,
7575 )).to128();
73117576 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
73127577 defer self.register_manager.unlockReg(tmp_lock);
73137578
......@@ -7347,7 +7612,10 @@ fn genBinOp(
73477612 return dst_mcv;
73487613 },
73497614 5...8 => {
7350 const tmp_reg = (try self.register_manager.allocReg(null, sse)).to256();
7615 const tmp_reg = (try self.register_manager.allocReg(
7616 null,
7617 abi.RegisterClass.sse,
7618 )).to256();
73517619 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
73527620 defer self.register_manager.unlockReg(tmp_lock);
73537621
......@@ -7470,7 +7738,7 @@ fn genBinOp(
74707738 },
74717739 },
74727740 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7473 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7741 @tagName(air_tag), lhs_ty.fmt(mod),
74747742 });
74757743
74767744 const lhs_copy_reg = if (maybe_mask_reg) |_| registerAlias(
......@@ -7487,7 +7755,10 @@ fn genBinOp(
74877755 mir_tag,
74887756 dst_reg,
74897757 lhs_reg,
7490 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),
7758 src_mcv.mem(switch (lhs_ty.zigTypeTag(mod)) {
7759 else => Memory.PtrSize.fromSize(abi_size),
7760 .Vector => Memory.PtrSize.fromBitSize(dst_reg.bitSize()),
7761 }),
74917762 ) else try self.asmRegisterRegisterRegister(
74927763 mir_tag,
74937764 dst_reg,
......@@ -7502,7 +7773,10 @@ fn genBinOp(
75027773 if (src_mcv.isMemory()) try self.asmRegisterMemory(
75037774 mir_tag,
75047775 dst_reg,
7505 src_mcv.mem(Memory.PtrSize.fromSize(abi_size)),
7776 src_mcv.mem(switch (lhs_ty.zigTypeTag(mod)) {
7777 else => Memory.PtrSize.fromSize(abi_size),
7778 .Vector => Memory.PtrSize.fromBitSize(dst_reg.bitSize()),
7779 }),
75067780 ) else try self.asmRegisterRegister(
75077781 mir_tag,
75087782 dst_reg,
......@@ -7515,16 +7789,14 @@ fn genBinOp(
75157789
75167790 switch (air_tag) {
75177791 .add, .add_wrap, .sub, .sub_wrap, .mul, .mul_wrap, .div_float, .div_exact => {},
7518 .div_trunc, .div_floor => try self.genRound(
7519 lhs_ty,
7520 dst_reg,
7521 .{ .register = dst_reg },
7522 .{ .mode = switch (air_tag) {
7792 .div_trunc, .div_floor => try self.genRound(lhs_ty, dst_reg, .{ .register = dst_reg }, .{
7793 .mode = switch (air_tag) {
75237794 .div_trunc => .zero,
75247795 .div_floor => .down,
75257796 else => unreachable,
7526 }, .precision = .inexact },
7527 ),
7797 },
7798 .precision = .inexact,
7799 }),
75287800 .bit_and, .bit_or, .xor => {},
75297801 .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) {
75307802 const rhs_copy_reg = registerAlias(src_mcv.getReg().?, abi_size);
......@@ -7556,7 +7828,7 @@ fn genBinOp(
75567828 },
75577829 else => unreachable,
75587830 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7559 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7831 @tagName(air_tag), lhs_ty.fmt(mod),
75607832 }),
75617833 mask_reg,
75627834 rhs_copy_reg,
......@@ -7588,7 +7860,7 @@ fn genBinOp(
75887860 },
75897861 else => unreachable,
75907862 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7591 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7863 @tagName(air_tag), lhs_ty.fmt(mod),
75927864 }),
75937865 dst_reg,
75947866 dst_reg,
......@@ -7624,7 +7896,7 @@ fn genBinOp(
76247896 },
76257897 else => unreachable,
76267898 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7627 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7899 @tagName(air_tag), lhs_ty.fmt(mod),
76287900 }),
76297901 mask_reg,
76307902 mask_reg,
......@@ -7655,7 +7927,7 @@ fn genBinOp(
76557927 },
76567928 else => unreachable,
76577929 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7658 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7930 @tagName(air_tag), lhs_ty.fmt(mod),
76597931 }),
76607932 dst_reg,
76617933 lhs_copy_reg.?,
......@@ -7686,7 +7958,7 @@ fn genBinOp(
76867958 },
76877959 else => unreachable,
76887960 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7689 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7961 @tagName(air_tag), lhs_ty.fmt(mod),
76907962 }),
76917963 dst_reg,
76927964 mask_reg,
......@@ -7716,7 +7988,7 @@ fn genBinOp(
77167988 },
77177989 else => unreachable,
77187990 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7719 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
7991 @tagName(air_tag), lhs_ty.fmt(mod),
77207992 }),
77217993 mask_reg,
77227994 lhs_copy_reg.?,
......@@ -7746,7 +8018,7 @@ fn genBinOp(
77468018 },
77478019 else => unreachable,
77488020 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{
7749 @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?),
8021 @tagName(air_tag), lhs_ty.fmt(mod),
77508022 }),
77518023 dst_reg,
77528024 mask_reg,
......@@ -7940,7 +8212,8 @@ fn genBinOpMir(
79408212 const dst_info: OpInfo = switch (dst_mcv) {
79418213 else => unreachable,
79428214 .memory, .load_got, .load_direct, .load_tlv => dst: {
7943 const dst_addr_reg = (try self.register_manager.allocReg(null, gp)).to64();
8215 const dst_addr_reg =
8216 (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64();
79448217 const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg);
79458218 errdefer self.register_manager.unlockReg(dst_addr_lock);
79468219
......@@ -7985,7 +8258,8 @@ fn genBinOpMir(
79858258 else => unreachable,
79868259 }
79878260
7988 const src_addr_reg = (try self.register_manager.allocReg(null, gp)).to64();
8261 const src_addr_reg =
8262 (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64();
79898263 const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg);
79908264 errdefer self.register_manager.unlockReg(src_addr_lock);
79918265
......@@ -8428,8 +8702,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
84288702 const ret = try self.genCall(.{ .air = pl_op.operand }, arg_tys, arg_vals);
84298703
84308704 var bt = self.liveness.iterateBigTomb(inst);
8431 self.feed(&bt, pl_op.operand);
8432 for (arg_refs) |arg_ref| self.feed(&bt, arg_ref);
8705 try self.feed(&bt, pl_op.operand);
8706 for (arg_refs) |arg_ref| try self.feed(&bt, arg_ref);
84338707
84348708 const result = if (self.liveness.isUnused(inst)) .unreach else ret;
84358709 return self.finishAirResult(inst, result);
......@@ -8520,7 +8794,10 @@ fn genCall(self: *Self, info: union(enum) {
85208794 try self.spillRegisters(&regs);
85218795 try arg_locks.appendSlice(&self.register_manager.lockRegs(2, regs));
85228796 },
8523 .load_frame => try self.genCopy(arg_ty, dst_arg, src_arg),
8797 .load_frame => {
8798 try self.genCopy(arg_ty, dst_arg, src_arg);
8799 try self.freeValue(src_arg);
8800 },
85248801 else => unreachable,
85258802 };
85268803
......@@ -8546,6 +8823,9 @@ fn genCall(self: *Self, info: union(enum) {
85468823 else => unreachable,
85478824 };
85488825
8826 if (fn_info.is_var_args)
8827 try self.asmRegisterImmediate(.{ ._, .mov }, .al, Immediate.u(call_info.fp_count));
8828
85498829 // Due to incremental compilation, how function calls are generated depends
85508830 // on linking.
85518831 switch (info) {
......@@ -8611,6 +8891,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
86118891 const mod = self.bin_file.options.module.?;
86128892 const un_op = self.air.instructions.items(.data)[inst].un_op;
86138893 const operand = try self.resolveInst(un_op);
8894
86148895 const ret_ty = self.fn_type.fnReturnType(mod);
86158896 switch (self.ret_mcv.short) {
86168897 .none => {},
......@@ -8625,6 +8906,8 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
86258906 },
86268907 else => unreachable,
86278908 }
8909 self.ret_mcv.liveOut(self, inst);
8910
86288911 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
86298912 // which is available if the jump is 127 bytes or less forward.
86308913 const jmp_reloc = try self.asmJmpReloc(undefined);
......@@ -8635,6 +8918,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
86358918fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
86368919 const un_op = self.air.instructions.items(.data)[inst].un_op;
86378920 const ptr = try self.resolveInst(un_op);
8921
86388922 const ptr_ty = self.typeOf(un_op);
86398923 switch (self.ret_mcv.short) {
86408924 .none => {},
......@@ -8642,6 +8926,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
86428926 .indirect => |reg_off| try self.genSetReg(reg_off.reg, ptr_ty, ptr),
86438927 else => unreachable,
86448928 }
8929 self.ret_mcv.liveOut(self, inst);
8930
86458931 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
86468932 // which is available if the jump is 127 bytes or less forward.
86478933 const jmp_reloc = try self.asmJmpReloc(undefined);
......@@ -8792,8 +9078,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
87929078 else => unreachable,
87939079 }
87949080
8795 const dst_addr_reg =
8796 (try self.register_manager.allocReg(null, gp)).to64();
9081 const dst_addr_reg = (try self.register_manager.allocReg(
9082 null,
9083 abi.RegisterClass.gp,
9084 )).to64();
87979085 const dst_addr_lock =
87989086 self.register_manager.lockRegAssumeUnused(dst_addr_reg);
87999087 errdefer self.register_manager.unlockReg(dst_addr_lock);
......@@ -8848,8 +9136,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
88489136 else => unreachable,
88499137 }
88509138
8851 const src_addr_reg =
8852 (try self.register_manager.allocReg(null, gp)).to64();
9139 const src_addr_reg = (try self.register_manager.allocReg(
9140 null,
9141 abi.RegisterClass.gp,
9142 )).to64();
88539143 const src_addr_lock =
88549144 self.register_manager.lockRegAssumeUnused(src_addr_reg);
88559145 errdefer self.register_manager.unlockReg(src_addr_lock);
......@@ -8868,8 +9158,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
88689158 defer if (src_info) |info|
88699159 self.register_manager.unlockReg(info.addr_lock);
88709160
8871 const regs =
8872 try self.register_manager.allocRegs(2, .{ null, null }, gp);
9161 const regs = try self.register_manager.allocRegs(
9162 2,
9163 .{ null, null },
9164 abi.RegisterClass.gp,
9165 );
88739166 const acc_reg = regs[0].to64();
88749167 const locks = self.register_manager.lockRegsAssumeUnused(2, regs);
88759168 defer for (locks) |lock| self.register_manager.unlockReg(lock);
......@@ -8956,12 +9249,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
89569249 switch (ty.floatBits(self.target.*)) {
89579250 16 => {
89589251 assert(self.hasFeature(.f16c));
8959 const tmp1_reg = (try self.register_manager.allocReg(null, sse)).to128();
9252 const tmp1_reg =
9253 (try self.register_manager.allocReg(null, abi.RegisterClass.sse)).to128();
89609254 const tmp1_mcv = MCValue{ .register = tmp1_reg };
89619255 const tmp1_lock = self.register_manager.lockRegAssumeUnused(tmp1_reg);
89629256 defer self.register_manager.unlockReg(tmp1_lock);
89639257
8964 const tmp2_reg = (try self.register_manager.allocReg(null, sse)).to128();
9258 const tmp2_reg =
9259 (try self.register_manager.allocReg(null, abi.RegisterClass.sse)).to128();
89659260 const tmp2_mcv = MCValue{ .register = tmp2_reg };
89669261 const tmp2_lock = self.register_manager.lockRegAssumeUnused(tmp2_reg);
89679262 defer self.register_manager.unlockReg(tmp2_lock);
......@@ -9024,7 +9319,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
90249319 const mod = self.bin_file.options.module.?;
90259320 const un_op = self.air.instructions.items(.data)[inst].un_op;
90269321
9027 const addr_reg = try self.register_manager.allocReg(null, gp);
9322 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
90289323 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
90299324 defer self.register_manager.unlockReg(addr_lock);
90309325 try self.genLazySymbolRef(.lea, addr_reg, link.File.LazySymbol.initDecl(.const_data, null, mod));
......@@ -9086,13 +9381,13 @@ fn genTry(
90869381 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);
90879382
90889383 if (self.liveness.operandDies(inst, 0)) {
9089 if (Air.refToIndex(err_union)) |err_union_inst| self.processDeath(err_union_inst);
9384 if (Air.refToIndex(err_union)) |err_union_inst| try self.processDeath(err_union_inst);
90909385 }
90919386
90929387 self.scope_generation += 1;
90939388 const state = try self.saveState();
90949389
9095 for (liveness_cond_br.else_deaths) |operand| self.processDeath(operand);
9390 for (liveness_cond_br.else_deaths) |operand| try self.processDeath(operand);
90969391 try self.genBody(body);
90979392 try self.restoreState(state, &.{}, .{
90989393 .emit_instructions = false,
......@@ -9103,7 +9398,7 @@ fn genTry(
91039398
91049399 try self.performReloc(reloc);
91059400
9106 for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand);
9401 for (liveness_cond_br.then_deaths) |operand| try self.processDeath(operand);
91079402
91089403 const result = if (self.liveness.isUnused(inst))
91099404 .unreach
......@@ -9126,8 +9421,8 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
91269421}
91279422
91289423fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
9129 const ty_fn = self.air.instructions.items(.data)[inst].ty_fn;
91309424 const mod = self.bin_file.options.module.?;
9425 const ty_fn = self.air.instructions.items(.data)[inst].ty_fn;
91319426 const func = mod.funcInfo(ty_fn.func);
91329427 // TODO emit debug info for function change
91339428 _ = func;
......@@ -9153,7 +9448,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
91539448 return self.finishAir(inst, .unreach, .{ operand, .none, .none });
91549449}
91559450
9156fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
9451fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !Mir.Inst.Index {
91579452 const mod = self.bin_file.options.module.?;
91589453 const abi_size = ty.abiSize(mod);
91599454 switch (mcv) {
......@@ -9196,13 +9491,13 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
91969491 // that death now instead of later as this has an effect on
91979492 // whether it needs to be spilled in the branches
91989493 if (self.liveness.operandDies(inst, 0)) {
9199 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
9494 if (Air.refToIndex(pl_op.operand)) |op_inst| try self.processDeath(op_inst);
92009495 }
92019496
92029497 self.scope_generation += 1;
92039498 const state = try self.saveState();
92049499
9205 for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand);
9500 for (liveness_cond_br.then_deaths) |operand| try self.processDeath(operand);
92069501 try self.genBody(then_body);
92079502 try self.restoreState(state, &.{}, .{
92089503 .emit_instructions = false,
......@@ -9213,7 +9508,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
92139508
92149509 try self.performReloc(reloc);
92159510
9216 for (liveness_cond_br.else_deaths) |operand| self.processDeath(operand);
9511 for (liveness_cond_br.else_deaths) |operand| try self.processDeath(operand);
92179512 try self.genBody(else_body);
92189513 try self.restoreState(state, &.{}, .{
92199514 .emit_instructions = false,
......@@ -9285,7 +9580,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
92859580 .load_direct,
92869581 .load_tlv,
92879582 => {
9288 const addr_reg = (try self.register_manager.allocReg(null, gp)).to64();
9583 const addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64();
92899584 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
92909585 defer self.register_manager.unlockReg(addr_reg_lock);
92919586
......@@ -9535,7 +9830,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
95359830 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
95369831 const loop = self.air.extraData(Air.Block, ty_pl.payload);
95379832 const body = self.air.extra[loop.end..][0..loop.data.body_len];
9538 const jmp_target: u32 = @intCast(self.mir_instructions.len);
9833 const jmp_target: Mir.Inst.Index = @intCast(self.mir_instructions.len);
95399834
95409835 self.scope_generation += 1;
95419836 const state = try self.saveState();
......@@ -9580,7 +9875,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
95809875
95819876 if (std.debug.runtime_safety) assert(self.inst_tracking.getIndex(inst).? == inst_tracking_i);
95829877 const tracking = &self.inst_tracking.values()[inst_tracking_i];
9583 if (self.liveness.isUnused(inst)) tracking.die(self, inst);
9878 if (self.liveness.isUnused(inst)) try tracking.die(self, inst);
95849879 self.getValue(tracking.short, inst);
95859880 self.finishAirBookkeeping();
95869881}
......@@ -9599,7 +9894,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
95999894 // that death now instead of later as this has an effect on
96009895 // whether it needs to be spilled in the branches
96019896 if (self.liveness.operandDies(inst, 0)) {
9602 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
9897 if (Air.refToIndex(pl_op.operand)) |op_inst| try self.processDeath(op_inst);
96039898 }
96049899
96059900 self.scope_generation += 1;
......@@ -9612,7 +9907,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
96129907 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
96139908 extra_index = case.end + items.len + case_body.len;
96149909
9615 var relocs = try self.gpa.alloc(u32, items.len);
9910 var relocs = try self.gpa.alloc(Mir.Inst.Index, items.len);
96169911 defer self.gpa.free(relocs);
96179912
96189913 try self.spillEflagsIfOccupied();
......@@ -9622,7 +9917,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
96229917 reloc.* = try self.asmJccReloc(undefined, if (i < relocs.len - 1) .e else .ne);
96239918 }
96249919
9625 for (liveness.deaths[case_i]) |operand| self.processDeath(operand);
9920 for (liveness.deaths[case_i]) |operand| try self.processDeath(operand);
96269921
96279922 for (relocs[0 .. relocs.len - 1]) |reloc| try self.performReloc(reloc);
96289923 try self.genBody(case_body);
......@@ -9640,7 +9935,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
96409935 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
96419936
96429937 const else_deaths = liveness.deaths.len - 1;
9643 for (liveness.deaths[else_deaths]) |operand| self.processDeath(operand);
9938 for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand);
96449939
96459940 try self.genBody(else_body);
96469941 try self.restoreState(state, &.{}, .{
......@@ -9704,7 +9999,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
97049999
970510000 // Process operand death so that it is properly accounted for in the State below.
970610001 if (self.liveness.operandDies(inst, 0)) {
9707 if (Air.refToIndex(br.operand)) |op_inst| self.processDeath(op_inst);
10002 if (Air.refToIndex(br.operand)) |op_inst| try self.processDeath(op_inst);
970810003 }
970910004
971010005 if (first_br) {
......@@ -9718,7 +10013,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
971810013 });
971910014
972010015 // Stop tracking block result without forgetting tracking info
9721 self.freeValue(block_tracking.short);
10016 try self.freeValue(block_tracking.short);
972210017
972310018 // Emit a jump with a relocation. It will be patched up after the block ends.
972410019 // Leave the jump offset undefined
......@@ -9754,9 +10049,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
975410049
975510050 var outputs_extra_i = extra_i;
975610051 for (outputs) |output| {
9757 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
9758 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
9759 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
10052 const extra_bytes = mem.sliceAsBytes(self.air.extra[extra_i..]);
10053 const constraint = mem.sliceTo(mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
10054 const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
976010055 // This equation accounts for the fact that even if we have exactly 4 bytes
976110056 // for the string, we still use the next u32 for the null terminator.
976210057 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
......@@ -9769,22 +10064,35 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
976910064 .none => self.typeOfIndex(inst),
977010065 else => self.typeOf(output).childType(mod),
977110066 };
9772 const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint, "=r"))
9773 self.register_manager.tryAllocReg(maybe_inst, regClassForType(ty, mod)) orelse
10067 const is_read = switch (constraint[0]) {
10068 '=' => false,
10069 '+' => read: {
10070 if (output == .none) return self.fail(
10071 "read-write constraint unsupported for asm result: '{s}'",
10072 .{constraint},
10073 );
10074 break :read true;
10075 },
10076 else => return self.fail("invalid constraint: '{s}'", .{constraint}),
10077 };
10078 const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint[1..], "r"))
10079 self.register_manager.tryAllocReg(maybe_inst, self.regClassForType(ty)) orelse
977410080 return self.fail("ran out of registers lowering inline asm", .{})
9775 else if (mem.eql(u8, constraint, "=m"))
10081 else if (mem.eql(u8, constraint[1..], "m"))
977610082 if (output != .none) null else return self.fail(
9777 "memory constraint unsupported for asm result",
9778 .{},
9779 )
9780 else if (mem.eql(u8, constraint, "=g"))
9781 self.register_manager.tryAllocReg(maybe_inst, regClassForType(ty, mod)) orelse
9782 if (output != .none) null else return self.fail(
9783 "ran out of register lowering inline asm",
9784 .{},
10083 "memory constraint unsupported for asm result: '{s}'",
10084 .{constraint},
978510085 )
9786 else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}"))
9787 parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse
10086 else if (mem.eql(u8, constraint[1..], "g") or
10087 mem.eql(u8, constraint[1..], "rm") or mem.eql(u8, constraint[1..], "mr") or
10088 mem.eql(u8, constraint[1..], "r,m") or mem.eql(u8, constraint[1..], "m,r"))
10089 self.register_manager.tryAllocReg(maybe_inst, self.regClassForType(ty)) orelse
10090 if (output != .none)
10091 null
10092 else
10093 return self.fail("ran out of registers lowering inline asm", .{})
10094 else if (mem.startsWith(u8, constraint[1..], "{") and mem.endsWith(u8, constraint[1..], "}"))
10095 parseRegName(constraint[1 + "{".len .. constraint.len - "}".len]) orelse
978810096 return self.fail("invalid register constraint: '{s}'", .{constraint})
978910097 else
979010098 return self.fail("invalid constraint: '{s}'", .{constraint});
......@@ -9801,16 +10109,17 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
980110109 if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
980210110 _ = self.register_manager.lockRegAssumeUnused(reg);
980310111 };
9804 if (!std.mem.eql(u8, name, "_"))
10112 if (!mem.eql(u8, name, "_"))
980510113 arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len));
980610114 args.appendAssumeCapacity(arg_mcv);
980710115 if (output == .none) result = arg_mcv;
10116 if (is_read) try self.load(arg_mcv, self.typeOf(output), .{ .air_ref = output });
980810117 }
980910118
981010119 for (inputs) |input| {
9811 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
9812 const constraint = std.mem.sliceTo(input_bytes, 0);
9813 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
10120 const input_bytes = mem.sliceAsBytes(self.air.extra[extra_i..]);
10121 const constraint = mem.sliceTo(input_bytes, 0);
10122 const name = mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
981410123 // This equation accounts for the fact that even if we have exactly 4 bytes
981510124 // for the string, we still use the next u32 for the null terminator.
981610125 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
......@@ -9832,14 +10141,17 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
983210141 break :arg temp_mcv;
983310142 },
983410143 }
9835 const addr_reg = self.register_manager.tryAllocReg(null, gp) orelse {
10144 const addr_reg = self.register_manager.tryAllocReg(null, abi.RegisterClass.gp) orelse {
983610145 const temp_mcv = try self.allocTempRegOrMem(ty, false);
983710146 try self.genCopy(ty, temp_mcv, input_mcv);
983810147 break :arg temp_mcv;
983910148 };
984010149 try self.genSetReg(addr_reg, Type.usize, input_mcv.address());
984110150 break :arg .{ .indirect = .{ .reg = addr_reg } };
9842 } else if (mem.eql(u8, constraint, "g")) arg: {
10151 } else if (mem.eql(u8, constraint, "g") or
10152 mem.eql(u8, constraint, "rm") or mem.eql(u8, constraint, "mr") or
10153 mem.eql(u8, constraint, "r,m") or mem.eql(u8, constraint, "m,r"))
10154 arg: {
984310155 switch (input_mcv) {
984410156 .register, .indirect, .load_frame => break :arg input_mcv,
984510157 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
......@@ -9861,7 +10173,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
986110173 if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
986210174 _ = self.register_manager.lockReg(reg);
986310175 };
9864 if (!std.mem.eql(u8, name, "_"))
10176 if (!mem.eql(u8, name, "_"))
986510177 arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len));
986610178 args.appendAssumeCapacity(arg_mcv);
986710179 }
......@@ -9869,7 +10181,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
986910181 {
987010182 var clobber_i: u32 = 0;
987110183 while (clobber_i < clobbers_len) : (clobber_i += 1) {
9872 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
10184 const clobber = mem.sliceTo(mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
987310185 // This equation accounts for the fact that even if we have exactly 4 bytes
987410186 // for the string, we still use the next u32 for the null terminator.
987510187 extra_i += clobber.len / 4 + 1;
......@@ -9878,44 +10190,141 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
987810190 }
987910191 }
988010192
10193 const Label = struct {
10194 target: Mir.Inst.Index = undefined,
10195 pending_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
10196
10197 const Kind = enum { definition, reference };
10198
10199 fn isValid(kind: Kind, name: []const u8) bool {
10200 for (name, 0..) |c, i| switch (c) {
10201 else => return false,
10202 '$' => if (i == 0) return false,
10203 '.' => {},
10204 '0'...'9' => if (i == 0) switch (kind) {
10205 .definition => if (name.len != 1) return false,
10206 .reference => {
10207 if (name.len != 2) return false;
10208 switch (name[1]) {
10209 else => return false,
10210 'B', 'F', 'b', 'f' => {},
10211 }
10212 },
10213 },
10214 '@', 'A'...'Z', '_', 'a'...'z' => {},
10215 };
10216 return name.len > 0;
10217 }
10218 };
10219 var labels: std.StringHashMapUnmanaged(Label) = .{};
10220 defer {
10221 var label_it = labels.valueIterator();
10222 while (label_it.next()) |label| label.pending_relocs.deinit(self.gpa);
10223 labels.deinit(self.gpa);
10224 }
10225
988110226 const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
988210227 var line_it = mem.tokenizeAny(u8, asm_source, "\n\r;");
9883 while (line_it.next()) |line| {
10228 next_line: while (line_it.next()) |line| {
988410229 var mnem_it = mem.tokenizeAny(u8, line, " \t");
9885 const mnem_str = mnem_it.next() orelse continue;
9886 if (mem.startsWith(u8, mnem_str, "#")) continue;
9887
9888 const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b"))
9889 .byte
9890 else if (mem.endsWith(u8, mnem_str, "w"))
9891 .word
9892 else if (mem.endsWith(u8, mnem_str, "l"))
9893 .dword
9894 else if (mem.endsWith(u8, mnem_str, "q"))
9895 .qword
9896 else
9897 null;
9898 const mnem_tag = Mir.Inst.FixedTag{ ._, mnem: {
9899 if (mnem_size) |_| {
9900 if (std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])) |mnem| {
9901 break :mnem mnem;
9902 }
10230 var prefix: Instruction.Prefix = .none;
10231 const mnem_str = while (mnem_it.next()) |mnem_str| {
10232 if (mem.startsWith(u8, mnem_str, "#")) continue :next_line;
10233 if (std.meta.stringToEnum(Instruction.Prefix, mnem_str)) |pre| {
10234 if (prefix != .none) return self.fail("extra prefix: '{s}'", .{mnem_str});
10235 prefix = pre;
10236 continue;
10237 }
10238 if (!mem.endsWith(u8, mnem_str, ":")) break mnem_str;
10239 const label_name = mnem_str[0 .. mnem_str.len - ":".len];
10240 if (!Label.isValid(.definition, label_name))
10241 return self.fail("invalid label: '{s}'", .{label_name});
10242 const label_gop = try labels.getOrPut(self.gpa, label_name);
10243 if (!label_gop.found_existing) label_gop.value_ptr.* = .{} else {
10244 const anon = std.ascii.isDigit(label_name[0]);
10245 if (!anon and label_gop.value_ptr.pending_relocs.items.len == 0)
10246 return self.fail("redefined label: '{s}'", .{label_name});
10247 for (label_gop.value_ptr.pending_relocs.items) |pending_reloc|
10248 try self.performReloc(pending_reloc);
10249 if (anon)
10250 label_gop.value_ptr.pending_relocs.clearRetainingCapacity()
10251 else
10252 label_gop.value_ptr.pending_relocs.clearAndFree(self.gpa);
990310253 }
9904 break :mnem std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
9905 return self.fail("invalid mnemonic: '{s}'", .{mnem_str});
9906 } };
9907
9908 var op_it = mem.tokenizeScalar(u8, mnem_it.rest(), ',');
9909 var ops = [1]encoder.Instruction.Operand{.none} ** 4;
9910 for (&ops) |*op| {
9911 const op_str = mem.trim(u8, op_it.next() orelse break, " \t");
9912 if (mem.startsWith(u8, op_str, "#")) break;
10254 label_gop.value_ptr.target = @intCast(self.mir_instructions.len);
10255 } else continue;
10256
10257 var mnem_size: ?Memory.PtrSize = null;
10258 const mnem_tag = mnem: {
10259 mnem_size = if (mem.endsWith(u8, mnem_str, "b"))
10260 .byte
10261 else if (mem.endsWith(u8, mnem_str, "w"))
10262 .word
10263 else if (mem.endsWith(u8, mnem_str, "l"))
10264 .dword
10265 else if (mem.endsWith(u8, mnem_str, "q"))
10266 .qword
10267 else if (mem.endsWith(u8, mnem_str, "t"))
10268 .tbyte
10269 else
10270 break :mnem null;
10271 break :mnem std.meta.stringToEnum(Instruction.Mnemonic, mnem_str[0 .. mnem_str.len - 1]);
10272 } orelse mnem: {
10273 mnem_size = null;
10274 break :mnem std.meta.stringToEnum(Instruction.Mnemonic, mnem_str);
10275 } orelse return self.fail("invalid mnemonic: '{s}'", .{mnem_str});
10276 const mnem_name = @tagName(mnem_tag);
10277 const mnem_fixed_tag: Mir.Inst.FixedTag = for (std.enums.values(Mir.Inst.Fixes)) |fixes| {
10278 const fixes_name = @tagName(fixes);
10279 const space_i = mem.indexOfScalar(u8, fixes_name, ' ');
10280 const fixes_prefix = if (space_i) |i|
10281 std.meta.stringToEnum(Instruction.Prefix, fixes_name[0..i]).?
10282 else
10283 .none;
10284 if (fixes_prefix != prefix) continue;
10285 const pattern = fixes_name[if (space_i) |i| i + " ".len else 0..];
10286 const wildcard_i = mem.indexOfScalar(u8, pattern, '_').?;
10287 const mnem_prefix = pattern[0..wildcard_i];
10288 const mnem_suffix = pattern[wildcard_i + "_".len ..];
10289 if (!mem.startsWith(u8, mnem_name, mnem_prefix)) continue;
10290 if (!mem.endsWith(u8, mnem_name, mnem_suffix)) continue;
10291 break .{ fixes, std.meta.stringToEnum(
10292 Mir.Inst.Tag,
10293 mnem_name[mnem_prefix.len .. mnem_name.len - mnem_suffix.len],
10294 ) orelse continue };
10295 } else {
10296 assert(prefix != .none);
10297 return self.fail("invalid prefix for mnemonic: '{s} {s}'", .{
10298 @tagName(prefix), mnem_str,
10299 });
10300 };
10301
10302 const Operand = union(enum) {
10303 none,
10304 reg: Register,
10305 mem: Memory,
10306 imm: Immediate,
10307 inst: Mir.Inst.Index,
10308 };
10309 var ops: [4]Operand = .{.none} ** 4;
10310
10311 var last_op = false;
10312 var op_it = mem.splitScalar(u8, mnem_it.rest(), ',');
10313 next_op: for (&ops) |*op| {
10314 const op_str = while (!last_op) {
10315 const full_str = op_it.next() orelse break :next_op;
10316 const trim_str = mem.trim(u8, if (mem.indexOfScalar(u8, full_str, '#')) |hash| hash: {
10317 last_op = true;
10318 break :hash full_str[0..hash];
10319 } else full_str, " \t");
10320 if (trim_str.len > 0) break trim_str;
10321 } else break;
991310322 if (mem.startsWith(u8, op_str, "%%")) {
991410323 const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');
991510324 const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse
991610325 return self.fail("invalid register: '{s}'", .{op_str});
991710326 if (colon) |colon_pos| {
9918 const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch
10327 const disp = std.fmt.parseInt(i32, op_str[colon_pos + ":".len ..], 0) catch
991910328 return self.fail("invalid displacement: '{s}'", .{op_str});
992010329 op.* = .{ .mem = Memory.sib(
992110330 mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
......@@ -9929,33 +10338,33 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
992910338 } else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) {
993010339 const colon = mem.indexOfScalarPos(u8, op_str, "%[".len, ':');
993110340 const modifier = if (colon) |colon_pos|
9932 op_str[colon_pos + 1 .. op_str.len - "]".len]
10341 op_str[colon_pos + ":".len .. op_str.len - "]".len]
993310342 else
993410343 "";
993510344 op.* = switch (args.items[
993610345 arg_map.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse
993710346 return self.fail("no matching constraint: '{s}'", .{op_str})
993810347 ]) {
9939 .register => |reg| if (std.mem.eql(u8, modifier, ""))
10348 .register => |reg| if (mem.eql(u8, modifier, ""))
994010349 .{ .reg = reg }
994110350 else
994210351 return self.fail("invalid modifier: '{s}'", .{modifier}),
9943 .memory => |addr| if (std.mem.eql(u8, modifier, "") or
9944 std.mem.eql(u8, modifier, "P"))
10352 .memory => |addr| if (mem.eql(u8, modifier, "") or
10353 mem.eql(u8, modifier, "P"))
994510354 .{ .mem = Memory.sib(
994610355 mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
994710356 .{ .base = .{ .reg = .ds }, .disp = @intCast(@as(i64, @bitCast(addr))) },
994810357 ) }
994910358 else
995010359 return self.fail("invalid modifier: '{s}'", .{modifier}),
9951 .indirect => |reg_off| if (std.mem.eql(u8, modifier, ""))
10360 .indirect => |reg_off| if (mem.eql(u8, modifier, ""))
995210361 .{ .mem = Memory.sib(
995310362 mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
995410363 .{ .base = .{ .reg = reg_off.reg }, .disp = reg_off.off },
995510364 ) }
995610365 else
995710366 return self.fail("invalid modifier: '{s}'", .{modifier}),
9958 .load_frame => |frame_addr| if (std.mem.eql(u8, modifier, ""))
10367 .load_frame => |frame_addr| if (mem.eql(u8, modifier, ""))
995910368 .{ .mem = Memory.sib(
996010369 mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
996110370 .{ .base = .{ .frame = frame_addr.index }, .disp = frame_addr.off },
......@@ -9980,64 +10389,113 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
998010389 }
998110390 op.* = .{ .imm = Immediate.u(u) };
998210391 } else |_| return self.fail("invalid immediate: '{s}'", .{op_str});
10392 } else if (mem.endsWith(u8, op_str, ")")) {
10393 const open = mem.indexOfScalar(u8, op_str, '(') orelse
10394 return self.fail("invalid operand: '{s}'", .{op_str});
10395 var sib_it = mem.splitScalar(u8, op_str[open + "(".len .. op_str.len - ")".len], ',');
10396 const base_str = sib_it.next() orelse
10397 return self.fail("invalid memory operand: '{s}'", .{op_str});
10398 if (base_str.len > 0 and !mem.startsWith(u8, base_str, "%%"))
10399 return self.fail("invalid memory operand: '{s}'", .{op_str});
10400 const index_str = sib_it.next() orelse "";
10401 if (index_str.len > 0 and !mem.startsWith(u8, base_str, "%%"))
10402 return self.fail("invalid memory operand: '{s}'", .{op_str});
10403 const scale_str = sib_it.next() orelse "";
10404 if (index_str.len == 0 and scale_str.len > 0)
10405 return self.fail("invalid memory operand: '{s}'", .{op_str});
10406 const scale = if (scale_str.len > 0) switch (std.fmt.parseInt(u4, scale_str, 10) catch
10407 return self.fail("invalid scale: '{s}'", .{op_str})) {
10408 1, 2, 4, 8 => |scale| scale,
10409 else => return self.fail("invalid scale: '{s}'", .{op_str}),
10410 } else 1;
10411 if (sib_it.next()) |_| return self.fail("invalid memory operand: '{s}'", .{op_str});
10412 op.* = .{ .mem = Memory.sib(mnem_size orelse
10413 return self.fail("unknown size: '{s}'", .{op_str}), .{
10414 .disp = if (open > 0) std.fmt.parseInt(i32, op_str[0..open], 0) catch
10415 return self.fail("invalid displacement: '{s}'", .{op_str}) else 0,
10416 .base = if (base_str.len > 0) .{ .reg = parseRegName(base_str["%%".len..]) orelse
10417 return self.fail("invalid base register: '{s}'", .{base_str}) } else .none,
10418 .scale_index = if (index_str.len > 0) .{
10419 .index = parseRegName(index_str["%%".len..]) orelse
10420 return self.fail("invalid index register: '{s}'", .{op_str}),
10421 .scale = scale,
10422 } else null,
10423 }) };
10424 } else if (Label.isValid(.reference, op_str)) {
10425 const anon = std.ascii.isDigit(op_str[0]);
10426 const label_gop = try labels.getOrPut(self.gpa, op_str[0..if (anon) 1 else op_str.len]);
10427 if (!label_gop.found_existing) label_gop.value_ptr.* = .{};
10428 if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing)
10429 return self.fail("undefined label: '{s}'", .{op_str});
10430 const pending_relocs = &label_gop.value_ptr.pending_relocs;
10431 if (if (anon)
10432 op_str[1] == 'f' or op_str[1] == 'F'
10433 else
10434 !label_gop.found_existing or pending_relocs.items.len > 0)
10435 try pending_relocs.append(self.gpa, @intCast(self.mir_instructions.len));
10436 op.* = .{ .inst = label_gop.value_ptr.target };
998310437 } else return self.fail("invalid operand: '{s}'", .{op_str});
998410438 } else if (op_it.next()) |op_str| return self.fail("extra operand: '{s}'", .{op_str});
998510439
998610440 (switch (ops[0]) {
9987 .none => self.asmOpOnly(mnem_tag),
10441 .none => self.asmOpOnly(mnem_fixed_tag),
998810442 .reg => |reg0| switch (ops[1]) {
9989 .none => self.asmRegister(mnem_tag, reg0),
10443 .none => self.asmRegister(mnem_fixed_tag, reg0),
999010444 .reg => |reg1| switch (ops[2]) {
9991 .none => self.asmRegisterRegister(mnem_tag, reg1, reg0),
10445 .none => self.asmRegisterRegister(mnem_fixed_tag, reg1, reg0),
999210446 .reg => |reg2| switch (ops[3]) {
9993 .none => self.asmRegisterRegisterRegister(mnem_tag, reg2, reg1, reg0),
10447 .none => self.asmRegisterRegisterRegister(mnem_fixed_tag, reg2, reg1, reg0),
999410448 else => error.InvalidInstruction,
999510449 },
999610450 .mem => |mem2| switch (ops[3]) {
9997 .none => self.asmMemoryRegisterRegister(mnem_tag, mem2, reg1, reg0),
10451 .none => self.asmMemoryRegisterRegister(mnem_fixed_tag, mem2, reg1, reg0),
999810452 else => error.InvalidInstruction,
999910453 },
1000010454 else => error.InvalidInstruction,
1000110455 },
1000210456 .mem => |mem1| switch (ops[2]) {
10003 .none => self.asmMemoryRegister(mnem_tag, mem1, reg0),
10457 .none => self.asmMemoryRegister(mnem_fixed_tag, mem1, reg0),
1000410458 else => error.InvalidInstruction,
1000510459 },
1000610460 else => error.InvalidInstruction,
1000710461 },
1000810462 .mem => |mem0| switch (ops[1]) {
10009 .none => self.asmMemory(mnem_tag, mem0),
10463 .none => self.asmMemory(mnem_fixed_tag, mem0),
1001010464 .reg => |reg1| switch (ops[2]) {
10011 .none => self.asmRegisterMemory(mnem_tag, reg1, mem0),
10465 .none => self.asmRegisterMemory(mnem_fixed_tag, reg1, mem0),
1001210466 else => error.InvalidInstruction,
1001310467 },
1001410468 else => error.InvalidInstruction,
1001510469 },
1001610470 .imm => |imm0| switch (ops[1]) {
10017 .none => self.asmImmediate(mnem_tag, imm0),
10471 .none => self.asmImmediate(mnem_fixed_tag, imm0),
1001810472 .reg => |reg1| switch (ops[2]) {
10019 .none => self.asmRegisterImmediate(mnem_tag, reg1, imm0),
10473 .none => self.asmRegisterImmediate(mnem_fixed_tag, reg1, imm0),
1002010474 .reg => |reg2| switch (ops[3]) {
10021 .none => self.asmRegisterRegisterImmediate(mnem_tag, reg2, reg1, imm0),
10475 .none => self.asmRegisterRegisterImmediate(mnem_fixed_tag, reg2, reg1, imm0),
1002210476 else => error.InvalidInstruction,
1002310477 },
1002410478 .mem => |mem2| switch (ops[3]) {
10025 .none => self.asmMemoryRegisterImmediate(mnem_tag, mem2, reg1, imm0),
10479 .none => self.asmMemoryRegisterImmediate(mnem_fixed_tag, mem2, reg1, imm0),
1002610480 else => error.InvalidInstruction,
1002710481 },
1002810482 else => error.InvalidInstruction,
1002910483 },
1003010484 .mem => |mem1| switch (ops[2]) {
10031 .none => self.asmMemoryImmediate(mnem_tag, mem1, imm0),
10485 .none => self.asmMemoryImmediate(mnem_fixed_tag, mem1, imm0),
1003210486 else => error.InvalidInstruction,
1003310487 },
1003410488 else => error.InvalidInstruction,
1003510489 },
10490 .inst => |inst0| switch (ops[1]) {
10491 .none => self.asmReloc(mnem_fixed_tag, inst0),
10492 else => error.InvalidInstruction,
10493 },
1003610494 }) catch |err| switch (err) {
1003710495 error.InvalidInstruction => return self.fail(
10038 "Invalid instruction: '{s} {s} {s} {s} {s}'",
10496 "invalid instruction: '{s} {s} {s} {s} {s}'",
1003910497 .{
10040 @tagName(mnem_tag[1]),
10498 mnem_str,
1004110499 @tagName(ops[0]),
1004210500 @tagName(ops[1]),
1004310501 @tagName(ops[2]),
......@@ -10048,18 +10506,22 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1004810506 };
1004910507 }
1005010508
10051 for (outputs, args.items[0..outputs.len]) |output, mcv| {
10052 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]);
10509 var label_it = labels.iterator();
10510 while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0)
10511 return self.fail("undefined label: '{s}'", .{label.key_ptr.*});
10512
10513 for (outputs, args.items[0..outputs.len]) |output, arg_mcv| {
10514 const extra_bytes = mem.sliceAsBytes(self.air.extra[outputs_extra_i..]);
1005310515 const constraint =
10054 std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0);
10055 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
10516 mem.sliceTo(mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0);
10517 const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
1005610518 // This equation accounts for the fact that even if we have exactly 4 bytes
1005710519 // for the string, we still use the next u32 for the null terminator.
1005810520 outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
1005910521
1006010522 if (output == .none) continue;
10061 if (mcv != .register) continue;
10062 try self.store(self.typeOf(output), try self.resolveInst(output), mcv);
10523 if (arg_mcv != .register) continue;
10524 try self.store(self.typeOf(output), .{ .air_ref = output }, arg_mcv);
1006310525 }
1006410526
1006510527 simple: {
......@@ -10077,13 +10539,14 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1007710539 return self.finishAir(inst, result, buf);
1007810540 }
1007910541 var bt = self.liveness.iterateBigTomb(inst);
10080 for (outputs) |output| if (output != .none) self.feed(&bt, output);
10081 for (inputs) |input| self.feed(&bt, input);
10542 for (outputs) |output| if (output != .none) try self.feed(&bt, output);
10543 for (inputs) |input| try self.feed(&bt, input);
1008210544 return self.finishAirResult(inst, result);
1008310545}
1008410546
1008510547const MoveStrategy = union(enum) {
1008610548 move: Mir.Inst.FixedTag,
10549 x87_load_store,
1008710550 insert_extract: InsertExtract,
1008810551 vex_insert_extract: InsertExtract,
1008910552
......@@ -10091,6 +10554,44 @@ const MoveStrategy = union(enum) {
1009110554 insert: Mir.Inst.FixedTag,
1009210555 extract: Mir.Inst.FixedTag,
1009310556 };
10557
10558 pub fn read(strat: MoveStrategy, self: *Self, dst_reg: Register, src_mem: Memory) !void {
10559 switch (strat) {
10560 .move => |tag| try self.asmRegisterMemory(tag, dst_reg, src_mem),
10561 .x87_load_store => {
10562 try self.asmMemory(.{ .f_, .ld }, src_mem);
10563 try self.asmRegister(.{ .f_p, .st }, @enumFromInt(@intFromEnum(dst_reg) + 1));
10564 },
10565 .insert_extract => |ie| try self.asmRegisterMemoryImmediate(
10566 ie.insert,
10567 dst_reg,
10568 src_mem,
10569 Immediate.u(0),
10570 ),
10571 .vex_insert_extract => |ie| try self.asmRegisterRegisterMemoryImmediate(
10572 ie.insert,
10573 dst_reg,
10574 dst_reg,
10575 src_mem,
10576 Immediate.u(0),
10577 ),
10578 }
10579 }
10580 pub fn write(strat: MoveStrategy, self: *Self, dst_mem: Memory, src_reg: Register) !void {
10581 switch (strat) {
10582 .move => |tag| try self.asmMemoryRegister(tag, dst_mem, src_reg),
10583 .x87_load_store => {
10584 try self.asmRegister(.{ .f_, .ld }, src_reg);
10585 try self.asmMemory(.{ .f_p, .st }, dst_mem);
10586 },
10587 .insert_extract, .vex_insert_extract => |ie| try self.asmMemoryRegisterImmediate(
10588 ie.extract,
10589 dst_mem,
10590 src_reg,
10591 Immediate.u(0),
10592 ),
10593 }
10594 }
1009410595};
1009510596fn moveStrategy(self: *Self, ty: Type, aligned: bool) !MoveStrategy {
1009610597 const mod = self.bin_file.options.module.?;
......@@ -10106,6 +10607,7 @@ fn moveStrategy(self: *Self, ty: Type, aligned: bool) !MoveStrategy {
1010610607 } },
1010710608 32 => return .{ .move = if (self.hasFeature(.avx)) .{ .v_ss, .mov } else .{ ._ss, .mov } },
1010810609 64 => return .{ .move = if (self.hasFeature(.avx)) .{ .v_sd, .mov } else .{ ._sd, .mov } },
10610 80 => return .x87_load_store,
1010910611 128 => return .{ .move = if (self.hasFeature(.avx))
1011010612 if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu }
1011110613 else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } },
......@@ -10274,10 +10776,12 @@ fn moveStrategy(self: *Self, ty: Type, aligned: bool) !MoveStrategy {
1027410776 else => {},
1027510777 },
1027610778 }
10277 return self.fail("TODO moveStrategy for {}", .{ty.fmt(self.bin_file.options.module.?)});
10779 return self.fail("TODO moveStrategy for {}", .{ty.fmt(mod)});
1027810780}
1027910781
1028010782fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void {
10783 const mod = self.bin_file.options.module.?;
10784
1028110785 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
1028210786 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
1028310787
......@@ -10315,18 +10819,31 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
1031510819 .off = -dst_reg_off.off,
1031610820 } },
1031710821 }),
10318 .register_pair => |dst_regs| for (dst_regs, 0..) |dst_reg, dst_reg_i| switch (src_mcv) {
10319 .register_pair => |src_regs| try self.genSetReg(
10320 dst_reg,
10321 Type.usize,
10322 .{ .register = src_regs[dst_reg_i] },
10323 ),
10324 else => try self.genSetReg(
10325 dst_reg,
10326 Type.usize,
10327 src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(),
10328 ),
10329 .air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)),
10822 .register_pair => |dst_regs| {
10823 switch (src_mcv) {
10824 .air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)),
10825 else => {},
10826 }
10827 const classes = mem.sliceTo(&abi.classifySystemV(ty, mod, .other), .none);
10828 for (dst_regs, classes, 0..) |dst_reg, class, dst_reg_i| {
10829 const class_ty = switch (class) {
10830 .integer => Type.usize,
10831 .sse => Type.f64,
10832 else => unreachable,
10833 };
10834 switch (src_mcv) {
10835 .register_pair => |src_regs| try self.genSetReg(
10836 dst_reg,
10837 class_ty,
10838 .{ .register = src_regs[dst_reg_i] },
10839 ),
10840 else => try self.genSetReg(
10841 dst_reg,
10842 class_ty,
10843 src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(),
10844 ),
10845 }
10846 }
1033010847 },
1033110848 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),
1033210849 .memory, .load_direct, .load_got, .load_tlv => {
......@@ -10355,7 +10872,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
1035510872fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerError!void {
1035610873 const mod = self.bin_file.options.module.?;
1035710874 const abi_size: u32 = @intCast(ty.abiSize(mod));
10358 if (abi_size * 8 > dst_reg.bitSize())
10875 if (ty.bitSize(mod) > dst_reg.bitSize())
1035910876 return self.fail("genSetReg called with a value larger than dst_reg", .{});
1036010877 switch (src_mcv) {
1036110878 .none,
......@@ -10457,9 +10974,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
1045710974 80 => null,
1045810975 else => unreachable,
1045910976 },
10460 }) orelse return self.fail("TODO implement genSetReg for {}", .{
10461 ty.fmt(self.bin_file.options.module.?),
10462 }),
10977 }) orelse return self.fail("TODO implement genSetReg for {}", .{ty.fmt(mod)}),
1046310978 registerAlias(dst_reg, abi_size),
1046410979 registerAlias(src_reg, abi_size),
1046510980 ),
......@@ -10471,9 +10986,21 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
1047110986 .indirect,
1047210987 .load_frame,
1047310988 .lea_frame,
10474 => {
10475 const dst_alias = registerAlias(dst_reg, abi_size);
10476 const src_mem = Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) {
10989 => try @as(MoveStrategy, switch (src_mcv) {
10990 .register_offset => |reg_off| switch (reg_off.off) {
10991 0 => return self.genSetReg(dst_reg, ty, .{ .register = reg_off.reg }),
10992 else => .{ .move = .{ ._, .lea } },
10993 },
10994 .indirect => try self.moveStrategy(ty, false),
10995 .load_frame => |frame_addr| try self.moveStrategy(
10996 ty,
10997 self.getFrameAddrAlignment(frame_addr).compare(.gte, ty.abiAlignment(mod)),
10998 ),
10999 .lea_frame => .{ .move = .{ ._, .lea } },
11000 else => unreachable,
11001 }).read(self, registerAlias(dst_reg, abi_size), Memory.sib(
11002 self.memPtrSize(ty),
11003 switch (src_mcv) {
1047711004 .register_offset, .indirect => |reg_off| .{
1047811005 .base = .{ .reg = reg_off.reg },
1047911006 .disp = reg_off.off,
......@@ -10483,64 +11010,18 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
1048311010 .disp = frame_addr.off,
1048411011 },
1048511012 else => unreachable,
10486 });
10487 switch (@as(MoveStrategy, switch (src_mcv) {
10488 .register_offset => |reg_off| switch (reg_off.off) {
10489 0 => return self.genSetReg(dst_reg, ty, .{ .register = reg_off.reg }),
10490 else => .{ .move = .{ ._, .lea } },
10491 },
10492 .indirect => try self.moveStrategy(ty, false),
10493 .load_frame => |frame_addr| try self.moveStrategy(
10494 ty,
10495 self.getFrameAddrAlignment(frame_addr).compare(.gte, ty.abiAlignment(mod)),
10496 ),
10497 .lea_frame => .{ .move = .{ ._, .lea } },
10498 else => unreachable,
10499 })) {
10500 .move => |tag| try self.asmRegisterMemory(tag, dst_alias, src_mem),
10501 .insert_extract => |ie| try self.asmRegisterMemoryImmediate(
10502 ie.insert,
10503 dst_alias,
10504 src_mem,
10505 Immediate.u(0),
10506 ),
10507 .vex_insert_extract => |ie| try self.asmRegisterRegisterMemoryImmediate(
10508 ie.insert,
10509 dst_alias,
10510 dst_alias,
10511 src_mem,
10512 Immediate.u(0),
10513 ),
10514 }
10515 },
11013 },
11014 )),
1051611015 .memory, .load_direct, .load_got, .load_tlv => {
1051711016 switch (src_mcv) {
10518 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| {
10519 const dst_alias = registerAlias(dst_reg, abi_size);
10520 const src_mem = Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
10521 .base = .{ .reg = .ds },
10522 .disp = small_addr,
10523 });
10524 switch (try self.moveStrategy(ty, ty.abiAlignment(mod).check(
10525 @as(u32, @bitCast(small_addr)),
10526 ))) {
10527 .move => |tag| try self.asmRegisterMemory(tag, dst_alias, src_mem),
10528 .insert_extract => |ie| try self.asmRegisterMemoryImmediate(
10529 ie.insert,
10530 dst_alias,
10531 src_mem,
10532 Immediate.u(0),
10533 ),
10534 .vex_insert_extract => |ie| try self.asmRegisterRegisterMemoryImmediate(
10535 ie.insert,
10536 dst_alias,
10537 dst_alias,
10538 src_mem,
10539 Immediate.u(0),
10540 ),
10541 }
10542 return;
10543 },
11017 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
11018 return (try self.moveStrategy(
11019 ty,
11020 ty.abiAlignment(mod).check(@as(u32, @bitCast(small_addr))),
11021 )).read(self, registerAlias(dst_reg, abi_size), Memory.sib(
11022 self.memPtrSize(ty),
11023 .{ .base = .{ .reg = .ds }, .disp = small_addr },
11024 )),
1054411025 .load_direct => |sym_index| switch (ty.zigTypeTag(mod)) {
1054511026 else => {
1054611027 const atom_index = try self.owner.getSymbolIndex(self);
......@@ -10567,26 +11048,11 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
1056711048 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
1056811049 defer self.register_manager.unlockReg(addr_lock);
1056911050
10570 const dst_alias = registerAlias(dst_reg, abi_size);
10571 const src_mem = Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
10572 .base = .{ .reg = addr_reg },
10573 });
10574 switch (try self.moveStrategy(ty, false)) {
10575 .move => |tag| try self.asmRegisterMemory(tag, dst_alias, src_mem),
10576 .insert_extract => |ie| try self.asmRegisterMemoryImmediate(
10577 ie.insert,
10578 dst_alias,
10579 src_mem,
10580 Immediate.u(0),
10581 ),
10582 .vex_insert_extract => |ie| try self.asmRegisterRegisterMemoryImmediate(
10583 ie.insert,
10584 dst_alias,
10585 dst_alias,
10586 src_mem,
10587 Immediate.u(0),
10588 ),
10589 }
11051 try (try self.moveStrategy(ty, false)).read(
11052 self,
11053 registerAlias(dst_reg, abi_size),
11054 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .{ .reg = addr_reg } }),
11055 );
1059011056 },
1059111057 .lea_direct, .lea_got => |sym_index| {
1059211058 const atom_index = try self.owner.getSymbolIndex(self);
......@@ -10683,39 +11149,23 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
1068311149 },
1068411150 },
1068511151 .eflags => |cc| try self.asmSetccMemory(Memory.sib(.byte, .{ .base = base, .disp = disp }), cc),
10686 .register => |src_reg| {
10687 const dst_mem = Memory.sib(
10688 Memory.PtrSize.fromSize(abi_size),
10689 .{ .base = base, .disp = disp },
10690 );
10691 const src_alias = registerAlias(src_reg, abi_size);
10692 switch (try self.moveStrategy(ty, switch (base) {
10693 .none => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
10694 .reg => |reg| switch (reg) {
10695 .es, .cs, .ss, .ds => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
10696 else => false,
10697 },
10698 .frame => |frame_index| self.getFrameAddrAlignment(
10699 .{ .index = frame_index, .off = disp },
10700 ).compare(.gte, ty.abiAlignment(mod)),
10701 })) {
10702 .move => |tag| try self.asmMemoryRegister(tag, dst_mem, src_alias),
10703 .insert_extract, .vex_insert_extract => |ie| try self.asmMemoryRegisterImmediate(
10704 ie.extract,
10705 dst_mem,
10706 src_alias,
10707 Immediate.u(0),
10708 ),
10709 }
10710 },
11152 .register => |src_reg| try (try self.moveStrategy(ty, switch (base) {
11153 .none => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
11154 .reg => |reg| switch (reg) {
11155 .es, .cs, .ss, .ds => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
11156 else => false,
11157 },
11158 .frame => |frame_index| self.getFrameAddrAlignment(
11159 .{ .index = frame_index, .off = disp },
11160 ).compare(.gte, ty.abiAlignment(mod)),
11161 })).write(
11162 self,
11163 Memory.sib(self.memPtrSize(ty), .{ .base = base, .disp = disp }),
11164 registerAlias(src_reg, abi_size),
11165 ),
1071111166 .register_pair => |src_regs| for (src_regs, 0..) |src_reg, src_reg_i| {
1071211167 const part_size = @min(abi_size - src_reg_i * 8, 8);
10713 const dst_mem = Memory.sib(
10714 Memory.PtrSize.fromSize(part_size),
10715 .{ .base = base, .disp = disp + @as(i32, @intCast(src_reg_i * 8)) },
10716 );
10717 const src_alias = registerAlias(src_reg, part_size);
10718 switch (try self.moveStrategy(ty, switch (base) {
11168 try (try self.moveStrategy(ty, switch (base) {
1071911169 .none => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
1072011170 .reg => |reg| switch (reg) {
1072111171 .es, .cs, .ss, .ds => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
......@@ -10724,15 +11174,10 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
1072411174 .frame => |frame_index| self.getFrameAddrAlignment(
1072511175 .{ .index = frame_index, .off = disp },
1072611176 ).compare(.gte, ty.abiAlignment(mod)),
10727 })) {
10728 .move => |tag| try self.asmMemoryRegister(tag, dst_mem, src_alias),
10729 .insert_extract, .vex_insert_extract => |ie| try self.asmMemoryRegisterImmediate(
10730 ie.extract,
10731 dst_mem,
10732 src_alias,
10733 Immediate.u(0),
10734 ),
10735 }
11177 })).write(self, Memory.sib(
11178 Memory.PtrSize.fromSize(part_size),
11179 .{ .base = base, .disp = disp + @as(i32, @intCast(src_reg_i * 8)) },
11180 ), registerAlias(src_reg, part_size));
1073611181 },
1073711182 .register_overflow => |ro| {
1073811183 try self.genSetMem(
......@@ -10953,8 +11398,8 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
1095311398 const src_ty = self.typeOf(ty_op.operand);
1095411399
1095511400 const result = result: {
10956 const dst_rc = regClassForType(dst_ty, mod);
10957 const src_rc = regClassForType(src_ty, mod);
11401 const dst_rc = self.regClassForType(dst_ty);
11402 const src_rc = self.regClassForType(src_ty);
1095811403 const src_mcv = try self.resolveInst(ty_op.operand);
1095911404
1096011405 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
......@@ -10973,11 +11418,15 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
1097311418 break :dst dst_mcv;
1097411419 };
1097511420
11421 if (dst_ty.isRuntimeFloat()) break :result dst_mcv;
11422
1097611423 const dst_signedness =
1097711424 if (dst_ty.isAbiInt(mod)) dst_ty.intInfo(mod).signedness else .unsigned;
10978 const src_signedness =
10979 if (src_ty.isAbiInt(mod)) src_ty.intInfo(mod).signedness else .unsigned;
10980 if (dst_signedness == src_signedness) break :result dst_mcv;
11425 if (!src_ty.isRuntimeFloat() or src_ty.floatBits(self.target.*) != 80) {
11426 const src_signedness =
11427 if (src_ty.isAbiInt(mod)) src_ty.intInfo(mod).signedness else .unsigned;
11428 if (dst_signedness == src_signedness) break :result dst_mcv;
11429 }
1098111430
1098211431 const abi_size: u16 = @intCast(dst_ty.abiSize(mod));
1098311432 const bit_size: u16 = @intCast(dst_ty.bitSize(mod));
......@@ -11049,9 +11498,13 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
1104911498 const result = result: {
1105011499 if (switch (dst_bits) {
1105111500 16, 80, 128 => true,
11052 32, 64 => src_size > 8 and src_size < 16,
11501 32, 64 => src_size > 8,
1105311502 else => unreachable,
1105411503 }) {
11504 if (src_bits > 128) return self.fail("TODO implement airFloatFromInt from {} to {}", .{
11505 src_ty.fmt(mod), dst_ty.fmt(mod),
11506 });
11507
1105511508 var callee: ["__floatun?i?f".len]u8 = undefined;
1105611509 break :result try self.genCall(.{ .lib = .{
1105711510 .return_type = dst_ty.toIntern(),
......@@ -11067,10 +11520,6 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
1106711520 } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }});
1106811521 }
1106911522
11070 if (src_size > 8) return self.fail("TODO implement airFloatFromInt from {} to {}", .{
11071 src_ty.fmt(mod), dst_ty.fmt(mod),
11072 });
11073
1107411523 const src_mcv = try self.resolveInst(ty_op.operand);
1107511524 const src_reg = if (src_mcv.isRegister())
1107611525 src_mcv.getReg().?
......@@ -11081,7 +11530,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
1108111530
1108211531 if (src_bits < src_size * 8) try self.truncateRegister(src_ty, src_reg);
1108311532
11084 const dst_reg = try self.register_manager.allocReg(inst, regClassForType(dst_ty, mod));
11533 const dst_reg = try self.register_manager.allocReg(inst, self.regClassForType(dst_ty));
1108511534 const dst_mcv = MCValue{ .register = dst_reg };
1108611535 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1108711536 defer self.register_manager.unlockReg(dst_lock);
......@@ -11128,9 +11577,13 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
1112811577 const result = result: {
1112911578 if (switch (src_bits) {
1113011579 16, 80, 128 => true,
11131 32, 64 => dst_size > 8 and dst_size < 16,
11580 32, 64 => dst_size > 8,
1113211581 else => unreachable,
1113311582 }) {
11583 if (dst_bits > 128) return self.fail("TODO implement airIntFromFloat from {} to {}", .{
11584 src_ty.fmt(mod), dst_ty.fmt(mod),
11585 });
11586
1113411587 var callee: ["__fixuns?f?i".len]u8 = undefined;
1113511588 break :result try self.genCall(.{ .lib = .{
1113611589 .return_type = dst_ty.toIntern(),
......@@ -11146,10 +11599,6 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
1114611599 } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }});
1114711600 }
1114811601
11149 if (dst_size > 8) return self.fail("TODO implement airIntFromFloat from {} to {}", .{
11150 src_ty.fmt(mod), dst_ty.fmt(mod),
11151 });
11152
1115311602 const src_mcv = try self.resolveInst(ty_op.operand);
1115411603 const src_reg = if (src_mcv.isRegister())
1115511604 src_mcv.getReg().?
......@@ -11158,7 +11607,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
1115811607 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
1115911608 defer self.register_manager.unlockReg(src_lock);
1116011609
11161 const dst_reg = try self.register_manager.allocReg(inst, regClassForType(dst_ty, mod));
11610 const dst_reg = try self.register_manager.allocReg(inst, self.regClassForType(dst_ty));
1116211611 const dst_mcv = MCValue{ .register = dst_reg };
1116311612 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1116411613 defer self.register_manager.unlockReg(dst_lock);
......@@ -11339,7 +11788,7 @@ fn atomicOp(
1133911788 .SeqCst => .xchg,
1134011789 };
1134111790
11342 const dst_reg = try self.register_manager.allocReg(null, gp);
11791 const dst_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1134311792 const dst_mcv = MCValue{ .register = dst_reg };
1134411793 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1134511794 defer self.register_manager.unlockReg(dst_lock);
......@@ -11361,13 +11810,13 @@ fn atomicOp(
1136111810 return if (unused) .unreach else dst_mcv;
1136211811 },
1136311812 .loop => _ = if (val_abi_size <= 8) {
11364 const tmp_reg = try self.register_manager.allocReg(null, gp);
11813 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1136511814 const tmp_mcv = MCValue{ .register = tmp_reg };
1136611815 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
1136711816 defer self.register_manager.unlockReg(tmp_lock);
1136811817
1136911818 try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(.rax, val_abi_size), ptr_mem);
11370 const loop: u32 = @intCast(self.mir_instructions.len);
11819 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
1137111820 if (rmw_op != std.builtin.AtomicRmwOp.Xchg) {
1137211821 try self.genSetReg(tmp_reg, val_ty, .{ .register = .rax });
1137311822 }
......@@ -11441,7 +11890,7 @@ fn atomicOp(
1144111890 .scale_index = ptr_mem.scaleIndex(),
1144211891 .disp = ptr_mem.sib.disp + 8,
1144311892 }));
11444 const loop: u32 = @intCast(self.mir_instructions.len);
11893 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
1144511894 const val_mem_mcv: MCValue = switch (val_mcv) {
1144611895 .memory, .indirect, .load_frame => val_mcv,
1144711896 else => .{ .indirect = .{
......@@ -11486,7 +11935,7 @@ fn atomicOp(
1148611935 try self.asmRegisterMemory(.{ ._, .xor }, .rcx, val_hi_mem);
1148711936 },
1148811937 else => return self.fail("TODO implement x86 atomic loop for {} {s}", .{
11489 val_ty.fmt(self.bin_file.options.module.?), @tagName(op),
11938 val_ty.fmt(mod), @tagName(op),
1149011939 }),
1149111940 };
1149211941 try self.asmMemory(.{ .@"lock _16b", .cmpxchg }, ptr_mem);
......@@ -11635,7 +12084,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
1163512084
1163612085 // Used to store the number of elements for comparison.
1163712086 // After comparison, updated to store number of bytes needed to copy.
11638 const len_reg = try self.register_manager.allocReg(null, gp);
12087 const len_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1163912088 const len_mcv: MCValue = .{ .register = len_reg };
1164012089 const len_lock = self.register_manager.lockRegAssumeUnused(len_reg);
1164112090 defer self.register_manager.unlockReg(len_lock);
......@@ -11645,7 +12094,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
1164512094 const skip_reloc = try self.asmJccReloc(undefined, .z);
1164612095 try self.store(slice_ptr_ty, ptr, src_val);
1164712096
11648 const second_elem_ptr_reg = try self.register_manager.allocReg(null, gp);
12097 const second_elem_ptr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1164912098 const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg };
1165012099 const second_elem_ptr_lock = self.register_manager.lockRegAssumeUnused(second_elem_ptr_reg);
1165112100 defer self.register_manager.unlockReg(second_elem_ptr_lock);
......@@ -11674,7 +12123,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
1167412123 assert(len != 0); // prevented by Sema
1167512124 try self.store(elem_ptr_ty, dst_ptr, src_val);
1167612125
11677 const second_elem_ptr_reg = try self.register_manager.allocReg(null, gp);
12126 const second_elem_ptr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1167812127 const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg };
1167912128 const second_elem_ptr_lock = self.register_manager.lockRegAssumeUnused(second_elem_ptr_reg);
1168012129 defer self.register_manager.unlockReg(second_elem_ptr_lock);
......@@ -11781,16 +12230,16 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
1178112230 const err_lock = self.register_manager.lockRegAssumeUnused(err_reg);
1178212231 defer self.register_manager.unlockReg(err_lock);
1178312232
11784 const addr_reg = try self.register_manager.allocReg(null, gp);
12233 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1178512234 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
1178612235 defer self.register_manager.unlockReg(addr_lock);
1178712236 try self.genLazySymbolRef(.lea, addr_reg, link.File.LazySymbol.initDecl(.const_data, null, mod));
1178812237
11789 const start_reg = try self.register_manager.allocReg(null, gp);
12238 const start_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1179012239 const start_lock = self.register_manager.lockRegAssumeUnused(start_reg);
1179112240 defer self.register_manager.unlockReg(start_lock);
1179212241
11793 const end_reg = try self.register_manager.allocReg(null, gp);
12242 const end_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
1179412243 const end_lock = self.register_manager.lockRegAssumeUnused(end_reg);
1179512244 defer self.register_manager.unlockReg(end_lock);
1179612245
......@@ -11858,7 +12307,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
1185812307 const mod = self.bin_file.options.module.?;
1185912308 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1186012309 const vector_ty = self.typeOfIndex(inst);
11861 const dst_rc = regClassForType(vector_ty, mod);
12310 const dst_rc = self.regClassForType(vector_ty);
1186212311 const scalar_ty = vector_ty.scalarType(mod);
1186312312
1186412313 const src_mcv = try self.resolveInst(ty_op.operand);
......@@ -12046,9 +12495,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
1204612495 else => unreachable,
1204712496 },
1204812497 }
12049 return self.fail("TODO implement airSplat for {}", .{
12050 vector_ty.fmt(self.bin_file.options.module.?),
12051 });
12498 return self.fail("TODO implement airSplat for {}", .{vector_ty.fmt(mod)});
1205212499 };
1205312500 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1205412501}
......@@ -12214,7 +12661,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
1221412661 return self.finishAir(inst, result, buf);
1221512662 }
1221612663 var bt = self.liveness.iterateBigTomb(inst);
12217 for (elements) |elem| self.feed(&bt, elem);
12664 for (elements) |elem| try self.feed(&bt, elem);
1221812665 return self.finishAirResult(inst, result);
1221912666}
1222012667
......@@ -12274,146 +12721,411 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
1227412721 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
1227512722 const ty = self.typeOfIndex(inst);
1227612723
12277 if (!self.hasFeature(.fma)) return self.fail("TODO implement airMulAdd for {}", .{
12278 ty.fmt(self.bin_file.options.module.?),
12279 });
12280
1228112724 const ops = [3]Air.Inst.Ref{ extra.lhs, extra.rhs, pl_op.operand };
12282 var mcvs: [3]MCValue = undefined;
12283 var locks = [1]?RegisterManager.RegisterLock{null} ** 3;
12284 defer for (locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
12285 var order = [1]u2{0} ** 3;
12286 var unused = std.StaticBitSet(3).initFull();
12287 for (ops, &mcvs, &locks, 0..) |op, *mcv, *lock, op_i| {
12288 const op_index: u2 = @intCast(op_i);
12289 mcv.* = try self.resolveInst(op);
12290 if (unused.isSet(0) and mcv.isRegister() and self.reuseOperand(inst, op, op_index, mcv.*)) {
12291 order[op_index] = 1;
12292 unused.unset(0);
12293 } else if (unused.isSet(2) and mcv.isMemory()) {
12294 order[op_index] = 3;
12295 unused.unset(2);
12725 const result = result: {
12726 if (switch (ty.scalarType(mod).floatBits(self.target.*)) {
12727 16, 80, 128 => true,
12728 32, 64 => !self.hasFeature(.fma),
12729 else => unreachable,
12730 }) {
12731 if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement airMulAdd for {}", .{
12732 ty.fmt(mod),
12733 });
12734
12735 var callee: ["__fma?".len]u8 = undefined;
12736 break :result try self.genCall(.{ .lib = .{
12737 .return_type = ty.toIntern(),
12738 .param_types = &.{ ty.toIntern(), ty.toIntern(), ty.toIntern() },
12739 .callee = std.fmt.bufPrint(&callee, "{s}fma{s}", .{
12740 floatLibcAbiPrefix(ty),
12741 floatLibcAbiSuffix(ty),
12742 }) catch unreachable,
12743 } }, &.{ ty, ty, ty }, &.{
12744 .{ .air_ref = extra.lhs }, .{ .air_ref = extra.rhs }, .{ .air_ref = pl_op.operand },
12745 });
1229612746 }
12297 switch (mcv.*) {
12298 .register => |reg| lock.* = self.register_manager.lockReg(reg),
12299 else => {},
12747
12748 var mcvs: [3]MCValue = undefined;
12749 var locks = [1]?RegisterManager.RegisterLock{null} ** 3;
12750 defer for (locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
12751 var order = [1]u2{0} ** 3;
12752 var unused = std.StaticBitSet(3).initFull();
12753 for (ops, &mcvs, &locks, 0..) |op, *mcv, *lock, op_i| {
12754 const op_index: u2 = @intCast(op_i);
12755 mcv.* = try self.resolveInst(op);
12756 if (unused.isSet(0) and mcv.isRegister() and self.reuseOperand(inst, op, op_index, mcv.*)) {
12757 order[op_index] = 1;
12758 unused.unset(0);
12759 } else if (unused.isSet(2) and mcv.isMemory()) {
12760 order[op_index] = 3;
12761 unused.unset(2);
12762 }
12763 switch (mcv.*) {
12764 .register => |reg| lock.* = self.register_manager.lockReg(reg),
12765 else => {},
12766 }
12767 }
12768 for (&order, &mcvs, &locks) |*mop_index, *mcv, *lock| {
12769 if (mop_index.* != 0) continue;
12770 mop_index.* = 1 + @as(u2, @intCast(unused.toggleFirstSet().?));
12771 if (mop_index.* > 1 and mcv.isRegister()) continue;
12772 const reg = try self.copyToTmpRegister(ty, mcv.*);
12773 mcv.* = .{ .register = reg };
12774 if (lock.*) |old_lock| self.register_manager.unlockReg(old_lock);
12775 lock.* = self.register_manager.lockRegAssumeUnused(reg);
1230012776 }
12301 }
12302 for (&order, &mcvs, &locks) |*mop_index, *mcv, *lock| {
12303 if (mop_index.* != 0) continue;
12304 mop_index.* = 1 + @as(u2, @intCast(unused.toggleFirstSet().?));
12305 if (mop_index.* > 1 and mcv.isRegister()) continue;
12306 const reg = try self.copyToTmpRegister(ty, mcv.*);
12307 mcv.* = .{ .register = reg };
12308 if (lock.*) |old_lock| self.register_manager.unlockReg(old_lock);
12309 lock.* = self.register_manager.lockRegAssumeUnused(reg);
12310 }
1231112777
12312 const mir_tag = @as(?Mir.Inst.FixedTag, if (mem.eql(u2, &order, &.{ 1, 3, 2 }) or
12313 mem.eql(u2, &order, &.{ 3, 1, 2 }))
12314 switch (ty.zigTypeTag(mod)) {
12315 .Float => switch (ty.floatBits(self.target.*)) {
12316 32 => .{ .v_ss, .fmadd132 },
12317 64 => .{ .v_sd, .fmadd132 },
12318 16, 80, 128 => null,
12319 else => unreachable,
12320 },
12321 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
12322 .Float => switch (ty.childType(mod).floatBits(self.target.*)) {
12323 32 => switch (ty.vectorLen(mod)) {
12324 1 => .{ .v_ss, .fmadd132 },
12325 2...8 => .{ .v_ps, .fmadd132 },
12326 else => null,
12327 },
12328 64 => switch (ty.vectorLen(mod)) {
12329 1 => .{ .v_sd, .fmadd132 },
12330 2...4 => .{ .v_pd, .fmadd132 },
12331 else => null,
12332 },
12778 const mir_tag = @as(?Mir.Inst.FixedTag, if (mem.eql(u2, &order, &.{ 1, 3, 2 }) or
12779 mem.eql(u2, &order, &.{ 3, 1, 2 }))
12780 switch (ty.zigTypeTag(mod)) {
12781 .Float => switch (ty.floatBits(self.target.*)) {
12782 32 => .{ .v_ss, .fmadd132 },
12783 64 => .{ .v_sd, .fmadd132 },
1233312784 16, 80, 128 => null,
1233412785 else => unreachable,
1233512786 },
12336 else => unreachable,
12337 },
12338 else => unreachable,
12339 }
12340 else if (mem.eql(u2, &order, &.{ 2, 1, 3 }) or mem.eql(u2, &order, &.{ 1, 2, 3 }))
12341 switch (ty.zigTypeTag(mod)) {
12342 .Float => switch (ty.floatBits(self.target.*)) {
12343 32 => .{ .v_ss, .fmadd213 },
12344 64 => .{ .v_sd, .fmadd213 },
12345 16, 80, 128 => null,
12346 else => unreachable,
12347 },
12348 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
12349 .Float => switch (ty.childType(mod).floatBits(self.target.*)) {
12350 32 => switch (ty.vectorLen(mod)) {
12351 1 => .{ .v_ss, .fmadd213 },
12352 2...8 => .{ .v_ps, .fmadd213 },
12353 else => null,
12354 },
12355 64 => switch (ty.vectorLen(mod)) {
12356 1 => .{ .v_sd, .fmadd213 },
12357 2...4 => .{ .v_pd, .fmadd213 },
12358 else => null,
12787 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
12788 .Float => switch (ty.childType(mod).floatBits(self.target.*)) {
12789 32 => switch (ty.vectorLen(mod)) {
12790 1 => .{ .v_ss, .fmadd132 },
12791 2...8 => .{ .v_ps, .fmadd132 },
12792 else => null,
12793 },
12794 64 => switch (ty.vectorLen(mod)) {
12795 1 => .{ .v_sd, .fmadd132 },
12796 2...4 => .{ .v_pd, .fmadd132 },
12797 else => null,
12798 },
12799 16, 80, 128 => null,
12800 else => unreachable,
1235912801 },
12360 16, 80, 128 => null,
1236112802 else => unreachable,
1236212803 },
1236312804 else => unreachable,
12364 },
12365 else => unreachable,
12366 }
12367 else if (mem.eql(u2, &order, &.{ 2, 3, 1 }) or mem.eql(u2, &order, &.{ 3, 2, 1 }))
12368 switch (ty.zigTypeTag(mod)) {
12369 .Float => switch (ty.floatBits(self.target.*)) {
12370 32 => .{ .v_ss, .fmadd231 },
12371 64 => .{ .v_sd, .fmadd231 },
12372 16, 80, 128 => null,
12373 else => unreachable,
12374 },
12375 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
12376 .Float => switch (ty.childType(mod).floatBits(self.target.*)) {
12377 32 => switch (ty.vectorLen(mod)) {
12378 1 => .{ .v_ss, .fmadd231 },
12379 2...8 => .{ .v_ps, .fmadd231 },
12380 else => null,
12381 },
12382 64 => switch (ty.vectorLen(mod)) {
12383 1 => .{ .v_sd, .fmadd231 },
12384 2...4 => .{ .v_pd, .fmadd231 },
12385 else => null,
12805 }
12806 else if (mem.eql(u2, &order, &.{ 2, 1, 3 }) or mem.eql(u2, &order, &.{ 1, 2, 3 }))
12807 switch (ty.zigTypeTag(mod)) {
12808 .Float => switch (ty.floatBits(self.target.*)) {
12809 32 => .{ .v_ss, .fmadd213 },
12810 64 => .{ .v_sd, .fmadd213 },
12811 16, 80, 128 => null,
12812 else => unreachable,
12813 },
12814 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
12815 .Float => switch (ty.childType(mod).floatBits(self.target.*)) {
12816 32 => switch (ty.vectorLen(mod)) {
12817 1 => .{ .v_ss, .fmadd213 },
12818 2...8 => .{ .v_ps, .fmadd213 },
12819 else => null,
12820 },
12821 64 => switch (ty.vectorLen(mod)) {
12822 1 => .{ .v_sd, .fmadd213 },
12823 2...4 => .{ .v_pd, .fmadd213 },
12824 else => null,
12825 },
12826 16, 80, 128 => null,
12827 else => unreachable,
1238612828 },
12829 else => unreachable,
12830 },
12831 else => unreachable,
12832 }
12833 else if (mem.eql(u2, &order, &.{ 2, 3, 1 }) or mem.eql(u2, &order, &.{ 3, 2, 1 }))
12834 switch (ty.zigTypeTag(mod)) {
12835 .Float => switch (ty.floatBits(self.target.*)) {
12836 32 => .{ .v_ss, .fmadd231 },
12837 64 => .{ .v_sd, .fmadd231 },
1238712838 16, 80, 128 => null,
1238812839 else => unreachable,
1238912840 },
12841 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) {
12842 .Float => switch (ty.childType(mod).floatBits(self.target.*)) {
12843 32 => switch (ty.vectorLen(mod)) {
12844 1 => .{ .v_ss, .fmadd231 },
12845 2...8 => .{ .v_ps, .fmadd231 },
12846 else => null,
12847 },
12848 64 => switch (ty.vectorLen(mod)) {
12849 1 => .{ .v_sd, .fmadd231 },
12850 2...4 => .{ .v_pd, .fmadd231 },
12851 else => null,
12852 },
12853 16, 80, 128 => null,
12854 else => unreachable,
12855 },
12856 else => unreachable,
12857 },
1239012858 else => unreachable,
12391 },
12392 else => unreachable,
12393 }
12394 else
12395 unreachable) orelse return self.fail("TODO implement airMulAdd for {}", .{
12396 ty.fmt(self.bin_file.options.module.?),
12397 });
12859 }
12860 else
12861 unreachable) orelse return self.fail("TODO implement airMulAdd for {}", .{ty.fmt(mod)});
1239812862
12399 var mops: [3]MCValue = undefined;
12400 for (order, mcvs) |mop_index, mcv| mops[mop_index - 1] = mcv;
12863 var mops: [3]MCValue = undefined;
12864 for (order, mcvs) |mop_index, mcv| mops[mop_index - 1] = mcv;
1240112865
12402 const abi_size: u32 = @intCast(ty.abiSize(mod));
12403 const mop1_reg = registerAlias(mops[0].getReg().?, abi_size);
12404 const mop2_reg = registerAlias(mops[1].getReg().?, abi_size);
12405 if (mops[2].isRegister()) try self.asmRegisterRegisterRegister(
12406 mir_tag,
12407 mop1_reg,
12408 mop2_reg,
12409 registerAlias(mops[2].getReg().?, abi_size),
12410 ) else try self.asmRegisterRegisterMemory(
12411 mir_tag,
12412 mop1_reg,
12413 mop2_reg,
12414 mops[2].mem(Memory.PtrSize.fromSize(abi_size)),
12415 );
12416 return self.finishAir(inst, mops[0], ops);
12866 const abi_size: u32 = @intCast(ty.abiSize(mod));
12867 const mop1_reg = registerAlias(mops[0].getReg().?, abi_size);
12868 const mop2_reg = registerAlias(mops[1].getReg().?, abi_size);
12869 if (mops[2].isRegister()) try self.asmRegisterRegisterRegister(
12870 mir_tag,
12871 mop1_reg,
12872 mop2_reg,
12873 registerAlias(mops[2].getReg().?, abi_size),
12874 ) else try self.asmRegisterRegisterMemory(
12875 mir_tag,
12876 mop1_reg,
12877 mop2_reg,
12878 mops[2].mem(Memory.PtrSize.fromSize(abi_size)),
12879 );
12880 break :result mops[0];
12881 };
12882 return self.finishAir(inst, result, ops);
12883}
12884
12885fn airVaStart(self: *Self, inst: Air.Inst.Index) !void {
12886 const mod = self.bin_file.options.module.?;
12887 const va_list_ty = self.air.instructions.items(.data)[inst].ty;
12888 const ptr_anyopaque_ty = try mod.singleMutPtrType(Type.anyopaque);
12889
12890 const result: MCValue = switch (abi.resolveCallingConvention(
12891 self.fn_type.fnCallingConvention(mod),
12892 self.target.*,
12893 )) {
12894 .SysV => result: {
12895 const info = self.va_info.sysv;
12896 const dst_fi = try self.allocFrameIndex(FrameAlloc.initType(va_list_ty, mod));
12897 var field_off: u31 = 0;
12898 // gp_offset: c_uint,
12899 try self.genSetMem(
12900 .{ .frame = dst_fi },
12901 field_off,
12902 Type.c_uint,
12903 .{ .immediate = info.gp_count * 8 },
12904 );
12905 field_off += @intCast(Type.c_uint.abiSize(mod));
12906 // fp_offset: c_uint,
12907 try self.genSetMem(
12908 .{ .frame = dst_fi },
12909 field_off,
12910 Type.c_uint,
12911 .{ .immediate = abi.SysV.c_abi_int_param_regs.len * 8 + info.fp_count * 16 },
12912 );
12913 field_off += @intCast(Type.c_uint.abiSize(mod));
12914 // overflow_arg_area: *anyopaque,
12915 try self.genSetMem(
12916 .{ .frame = dst_fi },
12917 field_off,
12918 ptr_anyopaque_ty,
12919 .{ .lea_frame = info.overflow_arg_area },
12920 );
12921 field_off += @intCast(ptr_anyopaque_ty.abiSize(mod));
12922 // reg_save_area: *anyopaque,
12923 try self.genSetMem(
12924 .{ .frame = dst_fi },
12925 field_off,
12926 ptr_anyopaque_ty,
12927 .{ .lea_frame = info.reg_save_area },
12928 );
12929 field_off += @intCast(ptr_anyopaque_ty.abiSize(mod));
12930 break :result .{ .load_frame = .{ .index = dst_fi } };
12931 },
12932 .Win64 => return self.fail("TODO implement c_va_start for Win64", .{}),
12933 else => unreachable,
12934 };
12935 return self.finishAir(inst, result, .{ .none, .none, .none });
12936}
12937
12938fn airVaArg(self: *Self, inst: Air.Inst.Index) !void {
12939 const mod = self.bin_file.options.module.?;
12940 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
12941 const ty = self.typeOfIndex(inst);
12942 const promote_ty = self.promoteVarArg(ty);
12943 const ptr_anyopaque_ty = try mod.singleMutPtrType(Type.anyopaque);
12944 const unused = self.liveness.isUnused(inst);
12945
12946 const result: MCValue = switch (abi.resolveCallingConvention(
12947 self.fn_type.fnCallingConvention(mod),
12948 self.target.*,
12949 )) {
12950 .SysV => result: {
12951 try self.spillEflagsIfOccupied();
12952
12953 const tmp_regs =
12954 try self.register_manager.allocRegs(2, .{ null, null }, abi.RegisterClass.gp);
12955 const offset_reg = tmp_regs[0].to32();
12956 const addr_reg = tmp_regs[1].to64();
12957 const tmp_locks = self.register_manager.lockRegsAssumeUnused(2, tmp_regs);
12958 defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock);
12959
12960 const promote_mcv = try self.allocTempRegOrMem(promote_ty, true);
12961 const promote_lock = switch (promote_mcv) {
12962 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
12963 else => null,
12964 };
12965 defer if (promote_lock) |lock| self.register_manager.unlockReg(lock);
12966
12967 const ptr_arg_list_reg =
12968 try self.copyToTmpRegister(self.typeOf(ty_op.operand), .{ .air_ref = ty_op.operand });
12969 const ptr_arg_list_lock = self.register_manager.lockRegAssumeUnused(ptr_arg_list_reg);
12970 defer self.register_manager.unlockReg(ptr_arg_list_lock);
12971
12972 const gp_offset: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 0 } };
12973 const fp_offset: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 4 } };
12974 const overflow_arg_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 8 } };
12975 const reg_save_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 16 } };
12976
12977 const classes = mem.sliceTo(&abi.classifySystemV(promote_ty, mod, .arg), .none);
12978 switch (classes[0]) {
12979 .integer => {
12980 assert(classes.len == 1);
12981
12982 try self.genSetReg(offset_reg, Type.c_uint, gp_offset);
12983 try self.asmRegisterImmediate(.{ ._, .cmp }, offset_reg, Immediate.u(
12984 abi.SysV.c_abi_int_param_regs.len * 8,
12985 ));
12986 const mem_reloc = try self.asmJccReloc(undefined, .ae);
12987
12988 try self.genSetReg(addr_reg, ptr_anyopaque_ty, reg_save_area);
12989 if (!unused)
12990 try self.asmRegisterMemory(.{ ._, .lea }, addr_reg, Memory.sib(.qword, .{
12991 .base = .{ .reg = addr_reg },
12992 .scale_index = .{ .scale = 1, .index = offset_reg.to64() },
12993 }));
12994 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg, Memory.sib(.qword, .{
12995 .base = .{ .reg = offset_reg.to64() },
12996 .disp = 8,
12997 }));
12998 try self.genCopy(Type.c_uint, gp_offset, .{ .register = offset_reg });
12999 const done_reloc = try self.asmJmpReloc(undefined);
13000
13001 try self.performReloc(mem_reloc);
13002 try self.genSetReg(addr_reg, ptr_anyopaque_ty, overflow_arg_area);
13003 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg.to64(), Memory.sib(.qword, .{
13004 .base = .{ .reg = addr_reg },
13005 .disp = @intCast(@max(promote_ty.abiSize(mod), 8)),
13006 }));
13007 try self.genCopy(
13008 ptr_anyopaque_ty,
13009 overflow_arg_area,
13010 .{ .register = offset_reg.to64() },
13011 );
13012
13013 try self.performReloc(done_reloc);
13014 if (!unused) try self.genCopy(promote_ty, promote_mcv, .{
13015 .indirect = .{ .reg = addr_reg },
13016 });
13017 },
13018 .sse => {
13019 assert(classes.len == 1);
13020
13021 try self.genSetReg(offset_reg, Type.c_uint, fp_offset);
13022 try self.asmRegisterImmediate(.{ ._, .cmp }, offset_reg, Immediate.u(
13023 abi.SysV.c_abi_int_param_regs.len * 8 + abi.SysV.c_abi_sse_param_regs.len * 16,
13024 ));
13025 const mem_reloc = try self.asmJccReloc(undefined, .ae);
13026
13027 try self.genSetReg(addr_reg, ptr_anyopaque_ty, reg_save_area);
13028 if (!unused)
13029 try self.asmRegisterMemory(.{ ._, .lea }, addr_reg, Memory.sib(.qword, .{
13030 .base = .{ .reg = addr_reg },
13031 .scale_index = .{ .scale = 1, .index = offset_reg.to64() },
13032 }));
13033 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg, Memory.sib(.qword, .{
13034 .base = .{ .reg = offset_reg.to64() },
13035 .disp = 16,
13036 }));
13037 try self.genCopy(Type.c_uint, fp_offset, .{ .register = offset_reg });
13038 const done_reloc = try self.asmJmpReloc(undefined);
13039
13040 try self.performReloc(mem_reloc);
13041 try self.genSetReg(addr_reg, ptr_anyopaque_ty, overflow_arg_area);
13042 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg.to64(), Memory.sib(.qword, .{
13043 .base = .{ .reg = addr_reg },
13044 .disp = @intCast(@max(promote_ty.abiSize(mod), 8)),
13045 }));
13046 try self.genCopy(
13047 ptr_anyopaque_ty,
13048 overflow_arg_area,
13049 .{ .register = offset_reg.to64() },
13050 );
13051
13052 try self.performReloc(done_reloc);
13053 if (!unused) try self.genCopy(promote_ty, promote_mcv, .{
13054 .indirect = .{ .reg = addr_reg },
13055 });
13056 },
13057 .memory => {
13058 assert(classes.len == 1);
13059 unreachable;
13060 },
13061 else => return self.fail("TODO implement c_va_arg for {} on SysV", .{
13062 promote_ty.fmt(mod),
13063 }),
13064 }
13065
13066 if (unused) break :result .unreach;
13067 if (ty.toIntern() == promote_ty.toIntern()) break :result promote_mcv;
13068
13069 if (!promote_ty.isRuntimeFloat()) {
13070 const dst_mcv = try self.allocRegOrMem(inst, true);
13071 try self.genCopy(ty, dst_mcv, promote_mcv);
13072 break :result dst_mcv;
13073 }
13074
13075 assert(ty.toIntern() == .f32_type and promote_ty.toIntern() == .f64_type);
13076 const dst_mcv = if (promote_mcv.isRegister())
13077 promote_mcv
13078 else
13079 try self.copyToRegisterWithInstTracking(inst, ty, promote_mcv);
13080 const dst_reg = dst_mcv.getReg().?.to128();
13081 const dst_lock = self.register_manager.lockReg(dst_reg);
13082 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
13083
13084 if (self.hasFeature(.avx)) if (promote_mcv.isMemory()) try self.asmRegisterRegisterMemory(
13085 .{ .v_ss, .cvtsd2 },
13086 dst_reg,
13087 dst_reg,
13088 promote_mcv.mem(.qword),
13089 ) else try self.asmRegisterRegisterRegister(
13090 .{ .v_ss, .cvtsd2 },
13091 dst_reg,
13092 dst_reg,
13093 (if (promote_mcv.isRegister())
13094 promote_mcv.getReg().?
13095 else
13096 try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(),
13097 ) else if (promote_mcv.isMemory()) try self.asmRegisterMemory(
13098 .{ ._ss, .cvtsd2 },
13099 dst_reg,
13100 promote_mcv.mem(.qword),
13101 ) else try self.asmRegisterRegister(
13102 .{ ._ss, .cvtsd2 },
13103 dst_reg,
13104 (if (promote_mcv.isRegister())
13105 promote_mcv.getReg().?
13106 else
13107 try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(),
13108 );
13109 break :result promote_mcv;
13110 },
13111 .Win64 => return self.fail("TODO implement c_va_arg for Win64", .{}),
13112 else => unreachable,
13113 };
13114 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13115}
13116
13117fn airVaCopy(self: *Self, inst: Air.Inst.Index) !void {
13118 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
13119 const ptr_va_list_ty = self.typeOf(ty_op.operand);
13120
13121 const dst_mcv = try self.allocRegOrMem(inst, true);
13122 try self.load(dst_mcv, ptr_va_list_ty, .{ .air_ref = ty_op.operand });
13123 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
13124}
13125
13126fn airVaEnd(self: *Self, inst: Air.Inst.Index) !void {
13127 const un_op = self.air.instructions.items(.data)[inst].un_op;
13128 return self.finishAir(inst, .unreach, .{ un_op, .none, .none });
1241713129}
1241813130
1241913131fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
......@@ -12494,6 +13206,8 @@ const CallMCValues = struct {
1249413206 return_value: InstTracking,
1249513207 stack_byte_count: u31,
1249613208 stack_align: Alignment,
13209 gp_count: u32,
13210 fp_count: u32,
1249713211
1249813212 fn deinit(self: *CallMCValues, func: *Self) void {
1249913213 func.gpa.free(self.args);
......@@ -12517,8 +13231,8 @@ fn resolveCallingConventionValues(
1251713231 for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*dest, src| {
1251813232 dest.* = src.toType();
1251913233 }
12520 // TODO: promote var arg types
12521 for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty| param_ty.* = arg_ty;
13234 for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty|
13235 param_ty.* = self.promoteVarArg(arg_ty);
1252213236
1252313237 var result: CallMCValues = .{
1252413238 .args = try self.gpa.alloc(MCValue, param_types.len),
......@@ -12526,6 +13240,8 @@ fn resolveCallingConventionValues(
1252613240 .return_value = undefined,
1252713241 .stack_byte_count = 0,
1252813242 .stack_align = undefined,
13243 .gp_count = 0,
13244 .fp_count = 0,
1252913245 };
1253013246 errdefer self.gpa.free(result.args);
1253113247
......@@ -12539,10 +13255,10 @@ fn resolveCallingConventionValues(
1253913255 result.stack_align = .@"8";
1254013256 },
1254113257 .C, .SysV, .Win64 => {
12542 var ret_int_reg_i: usize = 0;
12543 var ret_sse_reg_i: usize = 0;
12544 var param_int_reg_i: usize = 0;
12545 var param_sse_reg_i: usize = 0;
13258 var ret_int_reg_i: u32 = 0;
13259 var ret_sse_reg_i: u32 = 0;
13260 var param_int_reg_i: u32 = 0;
13261 var param_sse_reg_i: u32 = 0;
1254613262 result.stack_align = .@"16";
1254713263
1254813264 switch (resolved_cc) {
......@@ -12566,7 +13282,7 @@ fn resolveCallingConventionValues(
1256613282
1256713283 const classes = switch (resolved_cc) {
1256813284 .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),
12569 .Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},
13285 .Win64 => &.{abi.classifyWindows(ret_ty, mod)},
1257013286 else => unreachable,
1257113287 };
1257213288 for (classes) |class| switch (class) {
......@@ -12639,9 +13355,10 @@ fn resolveCallingConventionValues(
1263913355 var arg_mcv: [2]MCValue = undefined;
1264013356 var arg_mcv_i: usize = 0;
1264113357
12642 const classes = switch (self.target.os.tag) {
12643 .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)},
12644 else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),
13358 const classes = switch (resolved_cc) {
13359 .SysV => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),
13360 .Win64 => &.{abi.classifyWindows(ty, mod)},
13361 else => unreachable,
1264513362 };
1264613363 for (classes) |class| switch (class) {
1264713364 .integer => {
......@@ -12700,6 +13417,10 @@ fn resolveCallingConventionValues(
1270013417 } };
1270113418 result.stack_byte_count += param_size;
1270213419 }
13420 assert(param_int_reg_i <= 6);
13421 result.gp_count = param_int_reg_i;
13422 assert(param_sse_reg_i <= 16);
13423 result.fp_count = param_sse_reg_i;
1270313424 },
1270413425 .Unspecified => {
1270513426 result.stack_align = .@"16";
......@@ -12805,6 +13526,14 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
1280513526 };
1280613527}
1280713528
13529fn memPtrSize(self: *Self, ty: Type) Memory.PtrSize {
13530 const mod = self.bin_file.options.module.?;
13531 return switch (ty.zigTypeTag(mod)) {
13532 .Float => Memory.PtrSize.fromBitSize(ty.floatBits(self.target.*)),
13533 else => Memory.PtrSize.fromSize(@intCast(ty.abiSize(mod))),
13534 };
13535}
13536
1280813537/// Truncates the value in the register in place.
1280913538/// Clobbers any remaining bits.
1281013539fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
......@@ -12943,3 +13672,35 @@ fn floatLibcAbiSuffix(ty: Type) []const u8 {
1294313672 else => unreachable,
1294413673 };
1294513674}
13675
13676fn promoteVarArg(self: *Self, ty: Type) Type {
13677 const mod = self.bin_file.options.module.?;
13678 switch (ty.zigTypeTag(mod)) {
13679 .Bool => return Type.c_int,
13680 else => {
13681 const int_info = ty.intInfo(mod);
13682 for ([_]Type{
13683 Type.c_int, Type.c_uint,
13684 Type.c_long, Type.c_ulong,
13685 Type.c_longlong, Type.c_ulonglong,
13686 }) |promote_ty| {
13687 const promote_info = promote_ty.intInfo(mod);
13688 if (int_info.signedness == .signed and promote_info.signedness == .unsigned) continue;
13689 if (int_info.bits + @intFromBool(int_info.signedness == .unsigned and
13690 promote_info.signedness == .signed) <= promote_info.bits) return promote_ty;
13691 }
13692 unreachable;
13693 },
13694 .Float => switch (ty.floatBits(self.target.*)) {
13695 32, 64 => return Type.f64,
13696 else => |float_bits| {
13697 assert(float_bits == self.target.c_type_bit_size(.longdouble));
13698 return Type.c_longdouble;
13699 },
13700 },
13701 .Pointer => {
13702 assert(!ty.isSlice(mod));
13703 return ty;
13704 },
13705 }
13706}
src/arch/x86_64/Encoding.zig+5-1
......@@ -260,7 +260,7 @@ pub const Mnemonic = enum {
260260 ud2,
261261 xadd, xchg, xor,
262262 // X87
263 fisttp, fld,
263 fabs, fchs, ffree, fisttp, fld, fst, fstp,
264264 // MMX
265265 movd, movq,
266266 packssdw, packsswb, packuswb,
......@@ -316,6 +316,8 @@ pub const Mnemonic = enum {
316316 xorpd,
317317 // SSE3
318318 movddup, movshdup, movsldup,
319 // SSSE3
320 pabsb, pabsd, pabsw,
319321 // SSE4.1
320322 blendpd, blendps, blendvpd, blendvps,
321323 extractps,
......@@ -353,6 +355,7 @@ pub const Mnemonic = enum {
353355 vmovupd, vmovups,
354356 vmulpd, vmulps, vmulsd, vmulss,
355357 vorpd, vorps,
358 vpabsb, vpabsd, vpabsw,
356359 vpackssdw, vpacksswb, vpackusdw, vpackuswb,
357360 vpaddb, vpaddd, vpaddq, vpaddsb, vpaddsw, vpaddusb, vpaddusw, vpaddw,
358361 vpand, vpandn,
......@@ -750,6 +753,7 @@ pub const Feature = enum {
750753 sse2,
751754 sse3,
752755 sse4_1,
756 ssse3,
753757 x87,
754758};
755759
src/arch/x86_64/Mir.zig+13-4
......@@ -342,14 +342,10 @@ pub const Inst = struct {
342342 div,
343343 ///
344344 int3,
345 /// Store integer with truncation
346 istt,
347345 /// Conditional jump
348346 j,
349347 /// Jump
350348 jmp,
351 /// Load floating-point value
352 ld,
353349 /// Load effective address
354350 lea,
355351 /// Load string
......@@ -446,6 +442,19 @@ pub const Inst = struct {
446442 /// Bitwise logical xor of packed double-precision floating-point values
447443 xor,
448444
445 /// Absolute value
446 abs,
447 /// Change sign
448 chs,
449 /// Free floating-point register
450 free,
451 /// Store integer with truncation
452 istt,
453 /// Load floating-point value
454 ld,
455 /// Store floating-point value
456 st,
457
449458 /// Pack with signed saturation
450459 ackssw,
451460 /// Pack with signed saturation
src/arch/x86_64/abi.zig+19-17
......@@ -64,7 +64,7 @@ pub fn classifyWindows(ty: Type, mod: *Module) Class {
6464 }
6565}
6666
67pub const Context = enum { ret, arg, other };
67pub const Context = enum { ret, arg, field, other };
6868
6969/// There are a maximum of 8 possible return slots. Returned values are in
7070/// the beginning of the array; unused slots are filled with .none.
......@@ -120,7 +120,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
120120 },
121121 .Float => switch (ty.floatBits(target)) {
122122 16 => {
123 if (ctx == .other) {
123 if (ctx == .field) {
124124 result[0] = .memory;
125125 } else {
126126 // TODO clang doesn't allow __fp16 as .ret or .arg
......@@ -140,7 +140,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
140140 // "Arguments of types __float128, _Decimal128 and __m128 are
141141 // split into two halves. The least significant ones belong
142142 // to class SSE, the most significant one to class SSEUP."
143 if (ctx == .other) {
143 if (ctx == .field) {
144144 result[0] = .memory;
145145 return result;
146146 }
......@@ -229,7 +229,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
229229 if (field_align != .none and field_align.compare(.lt, field_ty.abiAlignment(mod)))
230230 return memory_class;
231231 const field_size = field_ty.abiSize(mod);
232 const field_class_array = classifySystemV(field_ty, mod, .other);
232 const field_class_array = classifySystemV(field_ty, mod, .field);
233233 const field_class = std.mem.sliceTo(&field_class_array, .none);
234234 if (byte_i + field_size <= 8) {
235235 // Combine this field with the previous one.
......@@ -347,7 +347,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
347347 return memory_class;
348348 }
349349 // Combine this field with the previous one.
350 const field_class = classifySystemV(field_ty.toType(), mod, .other);
350 const field_class = classifySystemV(field_ty.toType(), mod, .field);
351351 for (&result, 0..) |*result_item, i| {
352352 const field_item = field_class[i];
353353 // "If both classes are equal, this is the resulting class."
......@@ -444,7 +444,7 @@ pub const SysV = struct {
444444 /// These registers need to be preserved (saved on the stack) and restored by the caller before
445445 /// the caller relinquishes control to a subroutine via call instruction (or similar).
446446 /// In other words, these registers are free to use by the callee.
447 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
447 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
448448
449449 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
450450 pub const c_abi_sse_param_regs = sse_avx_regs[0..8].*;
......@@ -459,7 +459,7 @@ pub const Win64 = struct {
459459 /// These registers need to be preserved (saved on the stack) and restored by the caller before
460460 /// the caller relinquishes control to a subroutine via call instruction (or similar).
461461 /// In other words, these registers are free to use by the callee.
462 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
462 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
463463
464464 pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
465465 pub const c_abi_sse_param_regs = sse_avx_regs[0..4].*;
......@@ -531,30 +531,32 @@ pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention) []const Register
531531const gp_regs = [_]Register{
532532 .rax, .rcx, .rdx, .rbx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
533533};
534const x87_regs = [_]Register{
535 .st0, .st1, .st2, .st3, .st4, .st5, .st6, .st7,
536};
534537const sse_avx_regs = [_]Register{
535538 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
536539 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
537540};
538const allocatable_regs = gp_regs ++ sse_avx_regs;
539pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
541const allocatable_regs = gp_regs ++ x87_regs[0 .. x87_regs.len - 1] ++ sse_avx_regs;
542pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, allocatable_regs);
540543
541544// Register classes
542545const RegisterBitSet = RegisterManager.RegisterBitSet;
543546pub const RegisterClass = struct {
544547 pub const gp: RegisterBitSet = blk: {
545548 var set = RegisterBitSet.initEmpty();
546 set.setRangeValue(.{
547 .start = 0,
548 .end = gp_regs.len,
549 }, true);
549 for (allocatable_regs, 0..) |reg, index| if (reg.class() == .general_purpose) set.set(index);
550 break :blk set;
551 };
552 pub const x87: RegisterBitSet = blk: {
553 var set = RegisterBitSet.initEmpty();
554 for (allocatable_regs, 0..) |reg, index| if (reg.class() == .x87) set.set(index);
550555 break :blk set;
551556 };
552557 pub const sse: RegisterBitSet = blk: {
553558 var set = RegisterBitSet.initEmpty();
554 set.setRangeValue(.{
555 .start = gp_regs.len,
556 .end = allocatable_regs.len,
557 }, true);
559 for (allocatable_regs, 0..) |reg, index| if (reg.class() == .sse) set.set(index);
558560 break :blk set;
559561 };
560562};
src/arch/x86_64/encodings.zig+34-3
......@@ -829,13 +829,28 @@ pub const table = [_]Entry{
829829 .{ .xor, .rm, &.{ .r64, .rm64 }, &.{ 0x33 }, 0, .long, .none },
830830
831831 // X87
832 .{ .fabs, .np, &.{}, &.{ 0xd9, 0xe1 }, 0, .none, .x87 },
833
834 .{ .fchs, .np, &.{}, &.{ 0xd9, 0xe0 }, 0, .none, .x87 },
835
836 .{ .ffree, .o, &.{ .st }, &.{ 0xdd, 0xc0 }, 0, .none, .x87 },
837
832838 .{ .fisttp, .m, &.{ .m16 }, &.{ 0xdf }, 1, .none, .x87 },
833839 .{ .fisttp, .m, &.{ .m32 }, &.{ 0xdb }, 1, .none, .x87 },
834840 .{ .fisttp, .m, &.{ .m64 }, &.{ 0xdd }, 1, .none, .x87 },
835841
836 .{ .fld, .m, &.{ .m32 }, &.{ 0xd9 }, 0, .none, .x87 },
837 .{ .fld, .m, &.{ .m64 }, &.{ 0xdd }, 0, .none, .x87 },
838 .{ .fld, .m, &.{ .m80 }, &.{ 0xdb }, 5, .none, .x87 },
842 .{ .fld, .m, &.{ .m32 }, &.{ 0xd9 }, 0, .none, .x87 },
843 .{ .fld, .m, &.{ .m64 }, &.{ 0xdd }, 0, .none, .x87 },
844 .{ .fld, .m, &.{ .m80 }, &.{ 0xdb }, 5, .none, .x87 },
845 .{ .fld, .o, &.{ .st }, &.{ 0xd9, 0xc0 }, 0, .none, .x87 },
846
847 .{ .fst, .m, &.{ .m32 }, &.{ 0xd9 }, 2, .none, .x87 },
848 .{ .fst, .m, &.{ .m64 }, &.{ 0xdd }, 2, .none, .x87 },
849 .{ .fst, .o, &.{ .st }, &.{ 0xdd, 0xd0 }, 0, .none, .x87 },
850 .{ .fstp, .m, &.{ .m32 }, &.{ 0xd9 }, 3, .none, .x87 },
851 .{ .fstp, .m, &.{ .m64 }, &.{ 0xdd }, 3, .none, .x87 },
852 .{ .fstp, .m, &.{ .m80 }, &.{ 0xdb }, 7, .none, .x87 },
853 .{ .fstp, .o, &.{ .st }, &.{ 0xdd, 0xd8 }, 0, .none, .x87 },
839854
840855 // SSE
841856 .{ .addps, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x0f, 0x58 }, 0, .none, .sse },
......@@ -1093,6 +1108,14 @@ pub const table = [_]Entry{
10931108
10941109 .{ .movsldup, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0xf3, 0x0f, 0x12 }, 0, .none, .sse3 },
10951110
1111 // SSSE3
1112 .{ .pabsb, .rm, &.{ .mm, .mm_m64 }, &.{ 0x0f, 0x38, 0x1c }, 0, .none, .ssse3 },
1113 .{ .pabsb, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x1c }, 0, .none, .ssse3 },
1114 .{ .pabsd, .rm, &.{ .mm, .mm_m64 }, &.{ 0x0f, 0x38, 0x1e }, 0, .none, .ssse3 },
1115 .{ .pabsd, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x1e }, 0, .none, .ssse3 },
1116 .{ .pabsw, .rm, &.{ .mm, .mm_m64 }, &.{ 0x0f, 0x38, 0x1d }, 0, .none, .ssse3 },
1117 .{ .pabsw, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x1d }, 0, .none, .ssse3 },
1118
10961119 // SSE4.1
10971120 .{ .blendpd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0d }, 0, .none, .sse4_1 },
10981121
......@@ -1353,6 +1376,10 @@ pub const table = [_]Entry{
13531376 .{ .vorps, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x0f, 0x56 }, 0, .vex_128_wig, .avx },
13541377 .{ .vorps, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x0f, 0x56 }, 0, .vex_256_wig, .avx },
13551378
1379 .{ .vpabsb, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x1c }, 0, .vex_128_wig, .avx },
1380 .{ .vpabsd, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x1e }, 0, .vex_128_wig, .avx },
1381 .{ .vpabsw, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x1d }, 0, .vex_128_wig, .avx },
1382
13561383 .{ .vpacksswb, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x63 }, 0, .vex_128_wig, .avx },
13571384 .{ .vpackssdw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x6b }, 0, .vex_128_wig, .avx },
13581385
......@@ -1522,6 +1549,10 @@ pub const table = [_]Entry{
15221549 .{ .vbroadcastss, .rm, &.{ .ymm, .xmm }, &.{ 0x66, 0x0f, 0x38, 0x18 }, 0, .vex_256_w0, .avx2 },
15231550 .{ .vbroadcastsd, .rm, &.{ .ymm, .xmm }, &.{ 0x66, 0x0f, 0x38, 0x19 }, 0, .vex_256_w0, .avx2 },
15241551
1552 .{ .vpabsb, .rm, &.{ .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x1c }, 0, .vex_256_wig, .avx2 },
1553 .{ .vpabsd, .rm, &.{ .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x1e }, 0, .vex_256_wig, .avx2 },
1554 .{ .vpabsw, .rm, &.{ .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x1d }, 0, .vex_256_wig, .avx2 },
1555
15251556 .{ .vpacksswb, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x63 }, 0, .vex_256_wig, .avx2 },
15261557 .{ .vpackssdw, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x6b }, 0, .vex_256_wig, .avx2 },
15271558
src/register_manager.zig+4-2
......@@ -91,11 +91,13 @@ pub fn RegisterManager(
9191 return null;
9292 }
9393
94 pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt {
94 pub fn indexOfRegIntoTracked(
95 reg: Register,
96 ) ?std.math.IntFittingRange(0, tracked_registers.len) {
9597 return indexOfReg(tracked_registers, reg);
9698 }
9799
98 pub fn regAtTrackedIndex(index: RegisterBitSet.ShiftInt) Register {
100 pub fn regAtTrackedIndex(index: std.math.IntFittingRange(0, tracked_registers.len)) Register {
99101 return tracked_registers[index];
100102 }
101103
test/behavior/abs.zig+2-2
......@@ -95,7 +95,7 @@ test "@abs floats" {
9595 try comptime testAbsFloats(f64);
9696 try testAbsFloats(f64);
9797 try comptime testAbsFloats(f80);
98 if (builtin.zig_backend != .stage2_x86_64 and builtin.zig_backend != .stage2_wasm) try testAbsFloats(f80);
98 if (builtin.zig_backend != .stage2_wasm) try testAbsFloats(f80);
9999 try comptime testAbsFloats(f128);
100100 if (builtin.zig_backend != .stage2_wasm) try testAbsFloats(f128);
101101}
......@@ -280,7 +280,7 @@ test "@abs float vectors" {
280280 try testAbsFloatVectors(f16, 16);
281281 try comptime testAbsFloatVectors(f16, 17);
282282
283 try testAbsFloatVectors(f32, 17);
283 try testAbsFloatVectors(f32, 1);
284284 try comptime testAbsFloatVectors(f32, 1);
285285 try testAbsFloatVectors(f32, 1);
286286 try comptime testAbsFloatVectors(f32, 2);
test/behavior/asm.zig+2-4
......@@ -42,7 +42,6 @@ test "module level assembly" {
4242
4343test "output constraint modifiers" {
4444 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4645 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4746 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4847 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -66,7 +65,6 @@ test "output constraint modifiers" {
6665
6766test "alternative constraints" {
6867 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
69 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7068 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7169 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7270 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -163,8 +161,8 @@ export fn derp() i32 {
163161}
164162
165163test "rw constraint (x86_64)" {
166 if (builtin.target.cpu.arch != .x86_64 or builtin.zig_backend != .stage2_llvm)
167 return error.SkipZigTest;
164 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
165 if (builtin.target.cpu.arch != .x86_64) return error.SkipZigTest;
168166
169167 var res: i32 = 5;
170168 asm ("addl %[b], %[a]"
test/behavior/cast.zig+2-2
......@@ -118,11 +118,11 @@ test "@floatFromInt" {
118118
119119test "@floatFromInt(f80)" {
120120 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
121 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
122121 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
123122 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
124123 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
125124 if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
125 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
126126
127127 const S = struct {
128128 fn doTheTest(comptime Int: type) !void {
......@@ -1476,9 +1476,9 @@ test "pointer to empty struct literal to mutable slice" {
14761476test "coerce between pointers of compatible differently-named floats" {
14771477 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
14781478 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1479 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14801479 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
14811480 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1481 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
14821482
14831483 if (builtin.os.tag == .windows) {
14841484 // https://github.com/ziglang/zig/issues/12396
test/behavior/floatop.zig+17-17
......@@ -28,7 +28,7 @@ test "add f32/f64" {
2828}
2929
3030test "add f80/f128/c_longdouble" {
31 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
31 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
3232
3333 try testAdd(f80);
3434 try comptime testAdd(f80);
......@@ -59,7 +59,7 @@ test "sub f32/f64" {
5959}
6060
6161test "sub f80/f128/c_longdouble" {
62 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
62 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
6363
6464 try testSub(f80);
6565 try comptime testSub(f80);
......@@ -90,7 +90,7 @@ test "mul f32/f64" {
9090}
9191
9292test "mul f80/f128/c_longdouble" {
93 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
93 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
9494
9595 try testMul(f80);
9696 try comptime testMul(f80);
......@@ -270,7 +270,7 @@ test "@sqrt f80/f128/c_longdouble" {
270270 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
271271 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
272272 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
273 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
273 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
274274
275275 if (builtin.os.tag == .freebsd) {
276276 // TODO https://github.com/ziglang/zig/issues/10875
......@@ -378,7 +378,7 @@ test "@sin f80/f128/c_longdouble" {
378378 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
379379 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
380380 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
381 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
381 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
382382
383383 try testSin(f80);
384384 comptime try testSin(f80);
......@@ -447,7 +447,7 @@ test "@cos f80/f128/c_longdouble" {
447447 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
448448 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
449449 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
450 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
450 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
451451
452452 try testCos(f80);
453453 try comptime testCos(f80);
......@@ -516,7 +516,7 @@ test "@tan f80/f128/c_longdouble" {
516516 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
517517 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
518518 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
519 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
519 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
520520
521521 try testTan(f80);
522522 try comptime testTan(f80);
......@@ -585,7 +585,7 @@ test "@exp f80/f128/c_longdouble" {
585585 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
586586 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
587587 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
588 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
588 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
589589
590590 try testExp(f80);
591591 try comptime testExp(f80);
......@@ -654,7 +654,7 @@ test "@exp2 f80/f128/c_longdouble" {
654654 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
655655 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
656656 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
657 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
657 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
658658
659659 try testExp2(f80);
660660 try comptime testExp2(f80);
......@@ -723,7 +723,7 @@ test "@log f80/f128/c_longdouble" {
723723 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
724724 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
725725 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
726 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
726 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
727727
728728 try testLog(f80);
729729 try comptime testLog(f80);
......@@ -790,7 +790,7 @@ test "@log2 f80/f128/c_longdouble" {
790790 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
791791 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
792792 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
793 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
793 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
794794
795795 try testLog2(f80);
796796 try comptime testLog2(f80);
......@@ -863,7 +863,7 @@ test "@log10 f80/f128/c_longdouble" {
863863 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
864864 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
865865 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
866 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
866 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
867867
868868 try testLog10(f80);
869869 try comptime testLog10(f80);
......@@ -929,7 +929,7 @@ test "@abs f80/f128/c_longdouble" {
929929 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
930930 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
931931 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
932 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
932 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
933933
934934 try testFabs(f80);
935935 try comptime testFabs(f80);
......@@ -1029,7 +1029,7 @@ test "@floor f80/f128/c_longdouble" {
10291029 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10301030 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
10311031 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1032 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1032 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
10331033
10341034 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
10351035 // https://github.com/ziglang/zig/issues/12602
......@@ -1111,7 +1111,7 @@ test "@ceil f80/f128/c_longdouble" {
11111111 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11121112 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
11131113 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1114 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1114 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
11151115
11161116 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
11171117 // https://github.com/ziglang/zig/issues/12602
......@@ -1203,7 +1203,7 @@ test "@trunc f80/f128/c_longdouble" {
12031203 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12041204 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
12051205 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1206 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1206 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
12071207
12081208 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
12091209 // https://github.com/ziglang/zig/issues/12602
......@@ -1294,7 +1294,7 @@ test "neg f80/f128/c_longdouble" {
12941294 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12951295 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
12961296 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1297 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1297 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
12981298
12991299 try testNeg(f80);
13001300 try comptime testNeg(f80);
test/behavior/math.zig+3-3
......@@ -495,11 +495,11 @@ fn testDivision() !void {
495495}
496496
497497test "division half-precision floats" {
498 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
499498 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
500499 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
501500 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
502501 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
502 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
503503
504504 try testDivisionFP16();
505505 try comptime testDivisionFP16();
......@@ -1332,6 +1332,7 @@ test "remainder division" {
13321332 try comptime remdiv(f80);
13331333 try comptime remdiv(f128);
13341334 try remdiv(f16);
1335 try remdiv(f32);
13351336 try remdiv(f64);
13361337 try remdiv(f80);
13371338 try remdiv(f128);
......@@ -1356,7 +1357,7 @@ test "float remainder division using @rem" {
13561357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13571358 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
13581359 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1359 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1360 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
13601361
13611362 try comptime frem(f16);
13621363 try comptime frem(f32);
......@@ -1468,7 +1469,6 @@ test "@round f32/f64" {
14681469test "@round f80" {
14691470 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
14701471 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1471 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14721472 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
14731473 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
14741474 if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
test/behavior/maximum_minimum.zig+2-1
......@@ -276,7 +276,8 @@ test "@min/@max notices bounds from vector types when element of comptime-known
276276 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
277277 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
278278 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
279 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
279 if (builtin.zig_backend == .stage2_x86_64 and
280 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
280281
281282 var x: @Vector(2, u32) = .{ 1_000_000, 12345 };
282283 const y: @Vector(2, u16) = .{ 10, undefined };
test/behavior/muladd.zig+3-3
......@@ -32,11 +32,11 @@ fn testMulAdd() !void {
3232}
3333
3434test "@mulAdd f16" {
35 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
3635 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
3736 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3837 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
3938 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
4040
4141 try comptime testMulAdd16();
4242 try testMulAdd16();
......@@ -50,12 +50,12 @@ fn testMulAdd16() !void {
5050}
5151
5252test "@mulAdd f80" {
53 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
5453 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
5554 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5655 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5756 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
5857 if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
58 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
5959
6060 try comptime testMulAdd80();
6161 try testMulAdd80();
......@@ -69,12 +69,12 @@ fn testMulAdd80() !void {
6969}
7070
7171test "@mulAdd f128" {
72 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7372 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7473 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7574 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7675 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
7776 if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
77 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
7878
7979 try comptime testMulAdd128();
8080 try testMulAdd128();
test/behavior/var_args.zig-4
......@@ -108,7 +108,6 @@ test "simple variadic function" {
108108 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
109109 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
110110 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
111 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
112111 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
113112 if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
114113 // https://github.com/ziglang/zig/issues/14096
......@@ -158,7 +157,6 @@ test "variadic functions" {
158157 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
159158 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
160159 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
161 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
162160 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
163161 if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
164162 // https://github.com/ziglang/zig/issues/14096
......@@ -202,7 +200,6 @@ test "copy VaList" {
202200 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
203201 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
204202 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
205 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
206203 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
207204 if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
208205 // https://github.com/ziglang/zig/issues/14096
......@@ -235,7 +232,6 @@ test "unused VaList arg" {
235232 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
236233 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
237234 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
238 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
239235 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
240236 if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
241237 // https://github.com/ziglang/zig/issues/14096
test/behavior/widening.zig+3-6
......@@ -2,7 +2,6 @@ const std = @import("std");
22const expect = std.testing.expect;
33const mem = std.mem;
44const builtin = @import("builtin");
5const has_f80_rt = @import("builtin").cpu.arch == .x86_64;
65
76test "integer widening" {
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -40,11 +39,11 @@ test "implicit unsigned integer to signed integer" {
4039}
4140
4241test "float widening" {
43 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4442 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4543 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4644 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
4745 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
46 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
4847
4948 var a: f16 = 12.34;
5049 var b: f32 = a;
......@@ -53,10 +52,8 @@ test "float widening" {
5352 try expect(a == b);
5453 try expect(b == c);
5554 try expect(c == d);
56 if (has_f80_rt) {
57 var e: f80 = c;
58 try expect(c == e);
59 }
55 var e: f80 = c;
56 try expect(c == e);
6057}
6158
6259test "float widening f16 to f128" {