authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-19 06:49:50-04:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-21 08:49:54+01:00
logf95faac5aeeebe0b77ff7023513cdd3e34b71de1
tree77b8094d7da9c171dc29794fa2640e86c11e18bc
parent24f0900ecba3ea67b7c6df31836ed40de22b7ab8

x86_64: (re)implement optional ops

Note that this commit also changes the layout of optional for all other backends using `src/codegen.zig` without updating them!

19 files changed, 243 insertions(+), 149 deletions(-)

src/arch/x86_64/CodeGen.zig+199-121
......@@ -1871,55 +1871,74 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
18711871
18721872fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
18731873 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1874 if (self.liveness.isUnused(inst)) {
1875 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1876 }
1877
1878 const payload_ty = self.air.typeOfIndex(inst);
1879 const optional_ty = self.air.typeOf(ty_op.operand);
1880 const operand = try self.resolveInst(ty_op.operand);
18811874 const result: MCValue = result: {
1882 if (!payload_ty.hasRuntimeBits()) break :result MCValue.none;
1883 if (optional_ty.isPtrLikeOptional()) {
1884 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1885 break :result operand;
1875 if (self.liveness.isUnused(inst)) break :result .none;
1876
1877 const pl_ty = self.air.typeOfIndex(inst);
1878 const opt_mcv = try self.resolveInst(ty_op.operand);
1879
1880 if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv)) {
1881 switch (opt_mcv) {
1882 .register => |reg| try self.truncateRegister(pl_ty, reg),
1883 else => {},
18861884 }
1887 break :result try self.copyToRegisterWithInstTracking(inst, payload_ty, operand);
1885 break :result opt_mcv;
18881886 }
18891887
1890 const offset = optional_ty.abiSize(self.target.*) - payload_ty.abiSize(self.target.*);
1891 switch (operand) {
1892 .stack_offset => |off| {
1893 break :result MCValue{ .stack_offset = off - @intCast(i32, offset) };
1894 },
1895 .register => {
1896 // TODO reuse the operand
1897 const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand);
1898 const shift = @intCast(u8, offset * @sizeOf(usize));
1899 try self.genShiftBinOpMir(.shr, optional_ty, result.register, .{ .immediate = @intCast(u8, shift) });
1900 break :result result;
1901 },
1902 else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}),
1903 }
1888 const pl_mcv = try self.allocRegOrMem(inst, true);
1889 try self.setRegOrMem(pl_ty, pl_mcv, opt_mcv);
1890 break :result pl_mcv;
19041891 };
19051892 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19061893}
19071894
19081895fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
19091896 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1910 const result: MCValue = if (self.liveness.isUnused(inst))
1911 .dead
1912 else
1913 return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
1897 const result: MCValue = result: {
1898 if (self.liveness.isUnused(inst)) break :result .dead;
1899
1900 const dst_ty = self.air.typeOfIndex(inst);
1901 const opt_mcv = try self.resolveInst(ty_op.operand);
1902
1903 break :result if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
1904 opt_mcv
1905 else
1906 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);
1907 };
19141908 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19151909}
19161910
19171911fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
19181912 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1919 const result: MCValue = if (self.liveness.isUnused(inst))
1920 .dead
1921 else
1922 return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
1913 const result = result: {
1914 const dst_ty = self.air.typeOfIndex(inst);
1915 const src_ty = self.air.typeOf(ty_op.operand);
1916 const opt_ty = src_ty.childType();
1917 const src_mcv = try self.resolveInst(ty_op.operand);
1918
1919 if (opt_ty.optionalReprIsPayload()) {
1920 break :result if (self.liveness.isUnused(inst))
1921 .dead
1922 else if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1923 src_mcv
1924 else
1925 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);
1926 }
1927
1928 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1929 src_mcv
1930 else
1931 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);
1932
1933 const pl_ty = dst_ty.childType();
1934 const pl_abi_size = @intCast(i32, pl_ty.abiSize(self.target.*));
1935 try self.asmMemoryImmediate(
1936 .mov,
1937 Memory.sib(.byte, .{ .base = dst_mcv.register, .disp = pl_abi_size }),
1938 Immediate.u(1),
1939 );
1940 break :result if (self.liveness.isUnused(inst)) .dead else dst_mcv;
1941 };
19231942 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19241943}
19251944
......@@ -2150,41 +2169,45 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
21502169
21512170fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
21522171 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2153 if (self.liveness.isUnused(inst)) {
2154 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
2155 }
2156
2157 const payload_ty = self.air.typeOf(ty_op.operand);
21582172 const result: MCValue = result: {
2159 if (!payload_ty.hasRuntimeBits()) {
2160 break :result MCValue{ .immediate = 1 };
2161 }
2173 if (self.liveness.isUnused(inst)) break :result .dead;
21622174
2163 const optional_ty = self.air.typeOfIndex(inst);
2164 const operand = try self.resolveInst(ty_op.operand);
2165 const operand_lock: ?RegisterLock = switch (operand) {
2175 const pl_ty = self.air.typeOf(ty_op.operand);
2176 if (!pl_ty.hasRuntimeBits()) break :result .{ .immediate = 1 };
2177
2178 const opt_ty = self.air.typeOfIndex(inst);
2179 const pl_mcv = try self.resolveInst(ty_op.operand);
2180 const same_repr = opt_ty.optionalReprIsPayload();
2181 if (same_repr and self.reuseOperand(inst, ty_op.operand, 0, pl_mcv)) break :result pl_mcv;
2182
2183 const pl_lock: ?RegisterLock = switch (pl_mcv) {
21662184 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
21672185 else => null,
21682186 };
2169 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
2187 defer if (pl_lock) |lock| self.register_manager.unlockReg(lock);
21702188
2171 if (optional_ty.isPtrLikeOptional()) {
2172 // TODO should we check if we can reuse the operand?
2173 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {
2174 break :result operand;
2175 }
2176 break :result try self.copyToRegisterWithInstTracking(inst, payload_ty, operand);
2177 }
2189 const opt_mcv = try self.allocRegOrMem(inst, true);
2190 try self.setRegOrMem(pl_ty, opt_mcv, pl_mcv);
21782191
2179 const optional_abi_size = @intCast(u32, optional_ty.abiSize(self.target.*));
2180 const optional_abi_align = optional_ty.abiAlignment(self.target.*);
2181 const payload_abi_size = @intCast(u32, payload_ty.abiSize(self.target.*));
2182 const offset = optional_abi_size - payload_abi_size;
2192 if (!same_repr) {
2193 const pl_abi_size = @intCast(i32, pl_ty.abiSize(self.target.*));
2194 switch (opt_mcv) {
2195 else => unreachable,
21832196
2184 const stack_offset = @intCast(i32, try self.allocMem(inst, optional_abi_size, optional_abi_align));
2185 try self.genSetStack(Type.bool, stack_offset, .{ .immediate = 1 }, .{});
2186 try self.genSetStack(payload_ty, stack_offset - @intCast(i32, offset), operand, .{});
2187 break :result MCValue{ .stack_offset = stack_offset };
2197 .register => |opt_reg| try self.asmRegisterImmediate(
2198 .bts,
2199 opt_reg,
2200 Immediate.u(@intCast(u6, pl_abi_size * 8)),
2201 ),
2202
2203 .stack_offset => |off| try self.asmMemoryImmediate(
2204 .mov,
2205 Memory.sib(.byte, .{ .base = .rsp, .disp = pl_abi_size - off }),
2206 Immediate.u(0),
2207 ),
2208 }
2209 }
2210 break :result opt_mcv;
21882211 };
21892212 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
21902213}
......@@ -2619,7 +2642,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
26192642 },
26202643 .register => {
26212644 const shift: u6 = if (layout.tag_align < layout.payload_align)
2622 @intCast(u6, layout.payload_size * @sizeOf(usize))
2645 @intCast(u6, layout.payload_size * 8)
26232646 else
26242647 0;
26252648 const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand);
......@@ -3271,7 +3294,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
32713294 defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);
32723295
32733296 // Shift by struct_field_offset.
3274 const shift = @intCast(u8, struct_field_offset * @sizeOf(usize));
3297 const shift = @intCast(u8, struct_field_offset * 8);
32753298 try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift });
32763299
32773300 // Mask with reg.bitSize() - struct_field_size
......@@ -4928,25 +4951,107 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
49284951 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
49294952}
49304953
4931fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
4954fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MCValue {
49324955 try self.spillEflagsIfOccupied();
49334956 self.eflags_inst = inst;
49344957
4935 const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: {
4936 var buf: Type.Payload.ElemType = undefined;
4937 const payload_ty = ty.optionalChild(&buf);
4938 break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime()) Type.bool else ty;
4939 } else ty;
4958 var pl_buf: Type.Payload.ElemType = undefined;
4959 const pl_ty = opt_ty.optionalChild(&pl_buf);
4960
4961 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
4962 const some_info: struct { off: i32, ty: Type } = if (opt_ty.optionalReprIsPayload())
4963 .{ .off = 0, .ty = if (pl_ty.isSlice()) pl_ty.slicePtrFieldType(&ptr_buf) else pl_ty }
4964 else
4965 .{ .off = @intCast(i32, pl_ty.abiSize(self.target.*)), .ty = Type.bool };
49404966
4941 try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 });
4967 switch (opt_mcv) {
4968 .none,
4969 .unreach,
4970 .dead,
4971 .undef,
4972 .immediate,
4973 .register_overflow,
4974 .ptr_stack_offset,
4975 .eflags,
4976 => unreachable,
4977
4978 .register => |opt_reg| {
4979 if (some_info.off == 0) {
4980 const some_abi_size = @intCast(u32, some_info.ty.abiSize(self.target.*));
4981 const alias_reg = registerAlias(opt_reg, some_abi_size);
4982 assert(some_abi_size * 8 == alias_reg.bitSize());
4983 try self.asmRegisterRegister(.@"test", alias_reg, alias_reg);
4984 return .{ .eflags = .z };
4985 }
4986 assert(some_info.ty.tag() == .bool);
4987 const opt_abi_size = @intCast(u32, opt_ty.abiSize(self.target.*));
4988 try self.asmRegisterImmediate(
4989 .bt,
4990 registerAlias(opt_reg, opt_abi_size),
4991 Immediate.u(@intCast(u6, some_info.off * 8)),
4992 );
4993 return .{ .eflags = .nc };
4994 },
49424995
4943 return MCValue{ .eflags = .e };
4996 .memory, .linker_load => {
4997 const addr_reg = (try self.register_manager.allocReg(null, gp)).to64();
4998 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
4999 defer self.register_manager.unlockReg(addr_reg_lock);
5000
5001 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, opt_mcv);
5002
5003 // To get the actual address of the value we want to modify we have to go through the GOT
5004 try self.asmRegisterMemory(.mov, addr_reg, Memory.sib(.qword, .{
5005 .base = addr_reg,
5006 .disp = 0,
5007 }));
5008
5009 const some_abi_size = @intCast(u32, some_info.ty.abiSize(self.target.*));
5010 try self.asmMemoryImmediate(.cmp, Memory.sib(
5011 Memory.PtrSize.fromSize(some_abi_size),
5012 .{ .base = addr_reg, .disp = some_info.off },
5013 ), Immediate.u(0));
5014 return .{ .eflags = .e };
5015 },
5016
5017 .stack_offset => |off| {
5018 const some_abi_size = @intCast(u32, some_info.ty.abiSize(self.target.*));
5019 try self.asmMemoryImmediate(.cmp, Memory.sib(
5020 Memory.PtrSize.fromSize(some_abi_size),
5021 .{ .base = .rbp, .disp = some_info.off - off },
5022 ), Immediate.u(0));
5023 return .{ .eflags = .e };
5024 },
5025 }
49445026}
49455027
4946fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
4947 const is_null_res = try self.isNull(inst, ty, operand);
4948 assert(is_null_res.eflags == .e);
4949 return MCValue{ .eflags = is_null_res.eflags.negate() };
5028fn isNullPtr(self: *Self, inst: Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) !MCValue {
5029 try self.spillEflagsIfOccupied();
5030 self.eflags_inst = inst;
5031
5032 const opt_ty = ptr_ty.childType();
5033 var pl_buf: Type.Payload.ElemType = undefined;
5034 const pl_ty = opt_ty.optionalChild(&pl_buf);
5035
5036 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
5037 const some_info: struct { off: i32, ty: Type } = if (opt_ty.optionalReprIsPayload())
5038 .{ .off = 0, .ty = if (pl_ty.isSlice()) pl_ty.slicePtrFieldType(&ptr_buf) else pl_ty }
5039 else
5040 .{ .off = @intCast(i32, pl_ty.abiSize(self.target.*)), .ty = Type.bool };
5041
5042 const ptr_reg = switch (ptr_mcv) {
5043 .register => |reg| reg,
5044 else => try self.copyToTmpRegister(ptr_ty, ptr_mcv),
5045 };
5046 const ptr_lock = self.register_manager.lockReg(ptr_reg);
5047 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
5048
5049 const some_abi_size = @intCast(u32, some_info.ty.abiSize(self.target.*));
5050 try self.asmMemoryImmediate(.cmp, Memory.sib(
5051 Memory.PtrSize.fromSize(some_abi_size),
5052 .{ .base = ptr_reg, .disp = some_info.off },
5053 ), Immediate.u(0));
5054 return .{ .eflags = .e };
49505055}
49515056
49525057fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
......@@ -5012,29 +5117,11 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
50125117
50135118fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
50145119 const un_op = self.air.instructions.items(.data)[inst].un_op;
5015
5016 if (self.liveness.isUnused(inst)) {
5017 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
5018 }
5019
5020 const operand_ptr = try self.resolveInst(un_op);
5021 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
5022 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5023 else => null,
5120 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5121 const operand = try self.resolveInst(un_op);
5122 const ty = self.air.typeOf(un_op);
5123 break :result try self.isNullPtr(inst, ty, operand);
50245124 };
5025 defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
5026
5027 const ptr_ty = self.air.typeOf(un_op);
5028 const elem_ty = ptr_ty.childType();
5029 const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr))
5030 // The MCValue that holds the pointer can be re-used as the value.
5031 operand_ptr
5032 else
5033 try self.allocTempRegOrMem(elem_ty, true);
5034 try self.load(operand, operand_ptr, ptr_ty);
5035
5036 const result = try self.isNull(inst, elem_ty, operand);
5037
50385125 return self.finishAir(inst, result, .{ un_op, .none, .none });
50395126}
50405127
......@@ -5043,36 +5130,24 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
50435130 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
50445131 const operand = try self.resolveInst(un_op);
50455132 const ty = self.air.typeOf(un_op);
5046 break :result try self.isNonNull(inst, ty, operand);
5133 break :result switch (try self.isNull(inst, ty, operand)) {
5134 .eflags => |cc| .{ .eflags = cc.negate() },
5135 else => unreachable,
5136 };
50475137 };
50485138 return self.finishAir(inst, result, .{ un_op, .none, .none });
50495139}
50505140
50515141fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
50525142 const un_op = self.air.instructions.items(.data)[inst].un_op;
5053
5054 if (self.liveness.isUnused(inst)) {
5055 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
5056 }
5057
5058 const operand_ptr = try self.resolveInst(un_op);
5059 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
5060 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5061 else => null,
5143 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5144 const operand = try self.resolveInst(un_op);
5145 const ty = self.air.typeOf(un_op);
5146 break :result switch (try self.isNullPtr(inst, ty, operand)) {
5147 .eflags => |cc| .{ .eflags = cc.negate() },
5148 else => unreachable,
5149 };
50625150 };
5063 defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
5064
5065 const ptr_ty = self.air.typeOf(un_op);
5066 const elem_ty = ptr_ty.childType();
5067 const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr))
5068 // The MCValue that holds the pointer can be re-used as the value.
5069 operand_ptr
5070 else
5071 try self.allocTempRegOrMem(elem_ty, true);
5072 try self.load(operand, operand_ptr, ptr_ty);
5073
5074 const result = try self.isNonNull(inst, ptr_ty.elemType(), operand);
5075
50765151 return self.finishAir(inst, result, .{ un_op, .none, .none });
50775152}
50785153
......@@ -6967,7 +7042,10 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
69677042/// Truncates the value in the register in place.
69687043/// Clobbers any remaining bits.
69697044fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
6970 const int_info = ty.intInfo(self.target.*);
7045 const int_info = if (ty.isAbiInt()) ty.intInfo(self.target.*) else std.builtin.Type.Int{
7046 .signedness = .unsigned,
7047 .bits = @intCast(u16, ty.bitSize(self.target.*)),
7048 };
69717049 const max_reg_bit_width = Register.rax.bitSize();
69727050 switch (int_info.signedness) {
69737051 .signed => {
src/arch/x86_64/Emit.zig+4
......@@ -75,6 +75,10 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
7575 .@"and",
7676 .bsf,
7777 .bsr,
78 .bt,
79 .btc,
80 .btr,
81 .bts,
7882 .call,
7983 .cbw,
8084 .cwde,
src/arch/x86_64/Encoding.zig+1-1
......@@ -307,7 +307,7 @@ pub const Mnemonic = enum {
307307 // zig fmt: off
308308 // General-purpose
309309 adc, add, @"and",
310 bsf, bsr,
310 bsf, bsr, bt, btc, btr, bts,
311311 call, cbw, cdq, cdqe,
312312 cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna,
313313 cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno,
src/arch/x86_64/Mir.zig+8
......@@ -42,6 +42,14 @@ pub const Inst = struct {
4242 bsf,
4343 /// Bit scan reverse
4444 bsr,
45 /// Bit test
46 bt,
47 /// Bit test and complement
48 btc,
49 /// Bit test and reset
50 btr,
51 /// Bit test and set
52 bts,
4553 /// Call
4654 call,
4755 /// Convert byte to word
src/arch/x86_64/encodings.zig+28
......@@ -89,6 +89,34 @@ pub const table = &[_]Entry{
8989 .{ .bsr, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0xbd }, 0, .none },
9090 .{ .bsr, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0xbd }, 0, .long },
9191
92 .{ .bt, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xa3 }, 0, .none },
93 .{ .bt, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xa3 }, 0, .none },
94 .{ .bt, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xa3 }, 0, .long },
95 .{ .bt, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 4, .none },
96 .{ .bt, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 4, .none },
97 .{ .bt, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 4, .long },
98
99 .{ .btc, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xbb }, 0, .none },
100 .{ .btc, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xbb }, 0, .none },
101 .{ .btc, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xbb }, 0, .long },
102 .{ .btc, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 7, .none },
103 .{ .btc, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 7, .none },
104 .{ .btc, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 7, .long },
105
106 .{ .btr, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xb3 }, 0, .none },
107 .{ .btr, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xb3 }, 0, .none },
108 .{ .btr, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xb3 }, 0, .long },
109 .{ .btr, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 6, .none },
110 .{ .btr, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 6, .none },
111 .{ .btr, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 6, .long },
112
113 .{ .bts, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xab }, 0, .none },
114 .{ .bts, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xab }, 0, .none },
115 .{ .bts, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xab }, 0, .long },
116 .{ .bts, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 5, .none },
117 .{ .bts, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 5, .none },
118 .{ .bts, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 5, .long },
119
92120 // This is M encoding according to Intel, but D makes more sense here.
93121 .{ .call, .d, .rel32, .none, .none, .none, &.{ 0xe8 }, 0, .none },
94122 .{ .call, .m, .rm64, .none, .none, .none, &.{ 0xff }, 2, .none },
src/codegen.zig+3-2
......@@ -608,7 +608,6 @@ pub fn generateSymbol(
608608 const payload_type = typed_value.ty.optionalChild(&opt_buf);
609609 const is_pl = !typed_value.val.isNull();
610610 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;
611 const offset = abi_size - (math.cast(usize, payload_type.abiSize(target)) orelse return error.Overflow);
612611
613612 if (!payload_type.hasRuntimeBits()) {
614613 try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size);
......@@ -639,8 +638,8 @@ pub fn generateSymbol(
639638 return Result.ok;
640639 }
641640
641 const padding = abi_size - (math.cast(usize, payload_type.abiSize(target)) orelse return error.Overflow) - 1;
642642 const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef);
643 try code.writer().writeByteNTimes(@boolToInt(is_pl), offset);
644643 switch (try generateSymbol(bin_file, src_loc, .{
645644 .ty = payload_type,
646645 .val = value,
......@@ -648,6 +647,8 @@ pub fn generateSymbol(
648647 .ok => {},
649648 .fail => |em| return Result{ .fail = em },
650649 }
650 try code.writer().writeByte(@boolToInt(is_pl));
651 try code.writer().writeByteNTimes(0, padding);
651652
652653 return Result.ok;
653654 },
test/behavior/bugs/12984.zig-1
......@@ -14,7 +14,6 @@ pub const CustomDraw = DeleagateWithContext(fn (?OnConfirm) void);
1414test "simple test" {
1515 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1616 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
17 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1817
1918 var c: CustomDraw = undefined;
2019 _ = c;
test/behavior/bugs/13785.zig-1
......@@ -3,7 +3,6 @@ const std = @import("std");
33
44const S = packed struct { a: u0 = 0 };
55test {
6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/call.zig-1
......@@ -329,7 +329,6 @@ test "inline call preserves tail call" {
329329test "inline call doesn't re-evaluate non generic struct" {
330330 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
331331 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
332 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
333332
334333 const S = struct {
335334 fn foo(f: struct { a: u8, b: u8 }) !void {
test/behavior/cast.zig-1
......@@ -1206,7 +1206,6 @@ fn cast128Float(x: u128) f128 {
12061206test "implicit cast from *[N]T to ?[*]T" {
12071207 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
12081208 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1209 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12101209 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12111210
12121211 var x: ?[*]u16 = null;
test/behavior/error.zig-1
......@@ -451,7 +451,6 @@ test "optional error set is the same size as error set" {
451451}
452452
453453test "nested catch" {
454 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
455454 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
456455 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
457456
test/behavior/if.zig-1
......@@ -130,7 +130,6 @@ test "if peer expressions inferred optional type" {
130130}
131131
132132test "if-else expression with runtime condition result location is inferred optional" {
133 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
134133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
135134 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
136135 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/null.zig-2
......@@ -29,7 +29,6 @@ test "optional type" {
2929}
3030
3131test "test maybe object and get a pointer to the inner value" {
32 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
3332 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3433 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3534 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -138,7 +137,6 @@ test "optional pointer to 0 bit type null value at runtime" {
138137}
139138
140139test "if var maybe pointer" {
141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
142140 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
143141 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
144142 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/optional.zig-7
......@@ -91,7 +91,6 @@ test "address of unwrap optional" {
9191test "nested optional field in struct" {
9292 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9393 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
94 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
9594 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9695
9796 const S2 = struct {
......@@ -109,7 +108,6 @@ test "nested optional field in struct" {
109108test "equality compare optional with non-optional" {
110109 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
111110 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
112 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
113111 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
114112
115113 try test_cmp_optional_non_optional();
......@@ -227,7 +225,6 @@ test "assigning to an unwrapped optional field in an inline loop" {
227225}
228226
229227test "coerce an anon struct literal to optional struct" {
230 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
231228 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
232229 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
233230 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -247,7 +244,6 @@ test "coerce an anon struct literal to optional struct" {
247244}
248245
249246test "0-bit child type coerced to optional return ptr result location" {
250 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
251247 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
252248 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
253249 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -299,7 +295,6 @@ test "0-bit child type coerced to optional" {
299295}
300296
301297test "array of optional unaligned types" {
302 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
303298 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
304299 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
305300 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -336,7 +331,6 @@ test "array of optional unaligned types" {
336331}
337332
338333test "optional pointer to zero bit optional payload" {
339 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
340334 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
341335 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
342336 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -450,7 +444,6 @@ test "Optional slice size is optimized" {
450444test "peer type resolution in nested if expressions" {
451445 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
452446 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
453 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
454447
455448 const Thing = struct { n: i32 };
456449 var a = false;
test/behavior/ptrcast.zig-1
......@@ -18,7 +18,6 @@ fn testReinterpretBytesAsInteger() !void {
1818}
1919
2020test "reinterpret an array over multiple elements, with no well-defined layout" {
21 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2322 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2423 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/struct.zig-3
......@@ -1149,7 +1149,6 @@ test "anon init through error unions and optionals" {
11491149}
11501150
11511151test "anon init through optional" {
1152 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11531152 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11541153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11551154 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -1456,7 +1455,6 @@ test "struct has only one reference" {
14561455test "no dependency loop on pointer to optional struct" {
14571456 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
14581457 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1459 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14601458
14611459 const S = struct {
14621460 const A = struct { b: B };
......@@ -1509,7 +1507,6 @@ test "no dependency loop on optional field wrapped in generic function" {
15091507}
15101508
15111509test "optional field init with tuple" {
1512 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15131510 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15141511 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15151512
test/behavior/tuple.zig-4
......@@ -263,7 +263,6 @@ test "initializing anon struct with mixed comptime-runtime fields" {
263263test "tuple in tuple passed to generic function" {
264264 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
265265 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
266 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
267266 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
268267
269268 const S = struct {
......@@ -283,7 +282,6 @@ test "tuple in tuple passed to generic function" {
283282test "coerce tuple to tuple" {
284283 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
285284 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
286 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
287285 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
288286
289287 const T = std.meta.Tuple(&.{u8});
......@@ -298,7 +296,6 @@ test "coerce tuple to tuple" {
298296test "tuple type with void field" {
299297 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
300298 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
301 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
302299
303300 const T = std.meta.Tuple(&[_]type{void});
304301 const x = T{{}};
......@@ -335,7 +332,6 @@ test "zero sized struct in tuple handled correctly" {
335332test "tuple type with void field and a runtime field" {
336333 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
337334 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
338 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
339335
340336 const T = std.meta.Tuple(&[_]type{ usize, void });
341337 var t: T = .{ 5, {} };
test/behavior/union.zig-1
......@@ -1227,7 +1227,6 @@ test "union tag is set when initiated as a temporary value at runtime" {
12271227}
12281228
12291229test "extern union most-aligned field is smaller" {
1230 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12311230 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12321231 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12331232 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/while.zig-1
......@@ -341,7 +341,6 @@ test "else continue outer while" {
341341}
342342
343343test "try terminating an infinite loop" {
344 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
345344 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
346345 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
347346