authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-05 23:19:58+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-06 09:54:33+01:00
logb215241ef2153bf4b20f176923824e0e50d6da4c
treec4a68eb39e9086c687486205a2efdeda0dfe0789
parentf4aa276a0c665633980ea49bccb29da9d02d2ae1

stage2: fix airSliceElemVal

Refactor codegen and fix a bug in Isel.

4 files changed, 180 insertions(+), 130 deletions(-)

src/arch/x86_64/CodeGen.zig+105-94
......@@ -1318,37 +1318,38 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
13181318 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
13191319 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
13201320
1321 const index_ty = self.air.typeOf(bin_op.rhs);
1322 const index_mcv: MCValue = blk: {
1323 switch (try self.resolveInst(bin_op.rhs)) {
1324 .register => |reg| {
1325 if (reg.to64() != .rcx) {
1326 try self.register_manager.getReg(.rcx, inst);
1327 }
1328 break :blk MCValue{ .register = .rcx };
1329 },
1330 else => return self.fail("TODO move index mcv into a register", .{}),
1331 }
1321 const offset_reg = blk: {
1322 const index_ty = self.air.typeOf(bin_op.rhs);
1323 const index_mcv = try self.resolveInst(bin_op.rhs);
1324 const offset_reg = try self.register_manager.allocReg(null, &.{});
1325 try self.genSetReg(index_ty, offset_reg, index_mcv);
1326 try self.genIMulOpMir(index_ty, .{ .register = offset_reg }, .{ .immediate = elem_size });
1327 break :blk offset_reg;
13321328 };
13331329
1334 try self.genIMulOpMir(index_ty, index_mcv, .{ .immediate = elem_size });
1335
13361330 const dst_mcv = blk: {
13371331 switch (slice_mcv) {
1338 .stack_offset => |unadjusted_off| {
1332 .stack_offset => |off| {
13391333 const dst_mcv = try self.allocRegOrMem(inst, false);
1340 const addr_reg = try self.register_manager.allocReg(null, &.{index_mcv.register});
1341 const slice_ptr_abi_size = @intCast(u32, slice_ptr_field_type.abiSize(self.target.*));
1342 const off = unadjusted_off + elem_size;
1343 // lea reg, [rbp - 8 + rcx*1]
1334 const addr_reg = try self.register_manager.allocReg(null, &.{offset_reg});
1335 // mov reg, [rbp - 8]
13441336 _ = try self.addInst(.{
1345 .tag = .lea,
1337 .tag = .mov,
13461338 .ops = (Mir.Ops{
1347 .reg1 = registerAlias(addr_reg, slice_ptr_abi_size),
1339 .reg1 = addr_reg.to64(),
13481340 .reg2 = .rbp,
1349 .flags = 0b11,
1341 .flags = 0b01,
1342 }).encode(),
1343 .data = .{ .imm = -@intCast(i32, off + 16) },
1344 });
1345 // add addr, offset
1346 _ = try self.addInst(.{
1347 .tag = .add,
1348 .ops = (Mir.Ops{
1349 .reg1 = addr_reg.to64(),
1350 .reg2 = offset_reg.to64(),
13501351 }).encode(),
1351 .data = .{ .imm = -@intCast(i32, off) },
1352 .data = undefined,
13521353 });
13531354 try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type);
13541355 break :blk dst_mcv;
......@@ -1528,7 +1529,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
15281529 &.{ reg, .rax, .rcx },
15291530 );
15301531 const addr_reg = regs[0];
1531 const len_reg = regs[1];
1532 const count_reg = regs[1];
15321533 const tmp_reg = regs[2];
15331534
15341535 _ = try self.addInst(.{
......@@ -1544,12 +1545,12 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
15441545 try self.register_manager.getReg(.rcx, null);
15451546
15461547 // TODO allow for abi size to be u64
1547 try self.genSetReg(Type.initTag(.u32), len_reg, .{ .immediate = @intCast(u32, abi_size) });
1548 try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) });
15481549
15491550 return self.genInlineMemcpy(
15501551 -@intCast(i32, off + abi_size),
15511552 registerAlias(addr_reg, @divExact(reg.size(), 8)),
1552 len_reg.to64(),
1553 count_reg.to64(),
15531554 tmp_reg.to8(),
15541555 );
15551556 },
......@@ -1592,11 +1593,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
15921593 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
15931594}
15941595
1595fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1596 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1597 const ptr = try self.resolveInst(bin_op.lhs);
1598 const value = try self.resolveInst(bin_op.rhs);
1599 const elem_ty = self.air.typeOf(bin_op.rhs);
1596fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
1597 _ = ptr_ty;
16001598 switch (ptr) {
16011599 .none => unreachable,
16021600 .undef => unreachable,
......@@ -1605,19 +1603,58 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
16051603 .compare_flags_unsigned => unreachable,
16061604 .compare_flags_signed => unreachable,
16071605 .immediate => |imm| {
1608 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
1606 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
16091607 },
16101608 .ptr_stack_offset => |off| {
1611 try self.genSetStack(elem_ty, off, value);
1609 try self.genSetStack(value_ty, off, value);
16121610 },
16131611 .ptr_embedded_in_code => |off| {
1614 try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value);
1612 try self.setRegOrMem(value_ty, .{ .embedded_in_code = off }, value);
16151613 },
16161614 .embedded_in_code => {
16171615 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
16181616 },
16191617 .register => |reg| {
1620 try self.genSetPtrReg(elem_ty, reg, value);
1618 switch (value) {
1619 .none => unreachable,
1620 .undef => unreachable,
1621 .dead => unreachable,
1622 .unreach => unreachable,
1623 .compare_flags_unsigned => unreachable,
1624 .compare_flags_signed => unreachable,
1625 .immediate => |imm| {
1626 const abi_size = value_ty.abiSize(self.target.*);
1627 switch (abi_size) {
1628 1, 2, 4 => {
1629 // TODO this is wasteful!
1630 // introduce new MIR tag specifically for mov [reg + 0], imm
1631 const payload = try self.addExtra(Mir.ImmPair{
1632 .dest_off = 0,
1633 .operand = @bitCast(i32, @intCast(u32, imm)),
1634 });
1635 _ = try self.addInst(.{
1636 .tag = .mov_mem_imm,
1637 .ops = (Mir.Ops{
1638 .reg1 = reg.to64(),
1639 .flags = switch (abi_size) {
1640 1 => 0b00,
1641 2 => 0b01,
1642 4 => 0b10,
1643 else => unreachable,
1644 },
1645 }).encode(),
1646 .data = .{ .payload = payload },
1647 });
1648 },
1649 else => {
1650 return self.fail("TODO implement set pointee with immediate of ABI size {d}", .{abi_size});
1651 },
1652 }
1653 },
1654 else => |other| {
1655 return self.fail("TODO implement set pointee with {}", .{other});
1656 },
1657 }
16211658 },
16221659 .memory => {
16231660 return self.fail("TODO implement storing to MCValue.memory", .{});
......@@ -1626,6 +1663,15 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
16261663 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
16271664 },
16281665 }
1666}
1667
1668fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1669 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1670 const ptr = try self.resolveInst(bin_op.lhs);
1671 const ptr_ty = self.air.typeOf(bin_op.lhs);
1672 const value = try self.resolveInst(bin_op.rhs);
1673 const value_ty = self.air.typeOf(bin_op.rhs);
1674 try self.store(ptr, value, ptr_ty, value_ty);
16291675 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
16301676}
16311677
......@@ -1653,9 +1699,8 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
16531699
16541700 switch (mcv) {
16551701 .ptr_stack_offset => |off| {
1656 break :result MCValue{
1657 .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size,
1658 };
1702 const ptr_stack_offset = off - struct_size + struct_field_offset + struct_field_size;
1703 break :result MCValue{ .ptr_stack_offset = ptr_stack_offset };
16591704 },
16601705 else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}),
16611706 }
......@@ -1677,9 +1722,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
16771722
16781723 switch (mcv) {
16791724 .stack_offset => |off| {
1680 break :result MCValue{
1681 .stack_offset = off + struct_size - struct_field_offset - struct_field_size,
1682 };
1725 const stack_offset = off - struct_size + struct_field_offset + struct_field_size;
1726 break :result MCValue{ .stack_offset = stack_offset };
16831727 },
16841728 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
16851729 }
......@@ -1956,7 +2000,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
19562000 .immediate => |imm| {
19572001 // TODO take into account the type's ABI size when selecting the register alias
19582002 // register, immediate
1959 if (imm <= math.maxInt(i32)) {
2003 if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) {
19602004 _ = try self.addInst(.{
19612005 .tag = .imul_complex,
19622006 .ops = (Mir.Ops{
......@@ -3166,7 +3210,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
31663210
31673211 const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx });
31683212 const addr_reg = regs[0];
3169 const len_reg = regs[1];
3213 const count_reg = regs[1];
31703214 const tmp_reg = regs[2];
31713215
31723216 try self.register_manager.getReg(.rax, null);
......@@ -3182,12 +3226,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
31823226 });
31833227
31843228 // TODO allow for abi_size to be u64
3185 try self.genSetReg(Type.initTag(.u32), len_reg, .{ .immediate = @intCast(u32, abi_size) });
3229 try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) });
31863230
31873231 return self.genInlineMemcpy(
31883232 -@intCast(i32, stack_offset + abi_size),
31893233 addr_reg.to64(),
3190 len_reg.to64(),
3234 count_reg.to64(),
31913235 tmp_reg.to8(),
31923236 );
31933237 },
......@@ -3198,7 +3242,7 @@ fn genInlineMemcpy(
31983242 self: *Self,
31993243 stack_offset: i32,
32003244 addr_reg: Register,
3201 len_reg: Register,
3245 count_reg: Register,
32023246 tmp_reg: Register,
32033247) InnerError!void {
32043248 // mov rcx, 0
......@@ -3220,20 +3264,19 @@ fn genInlineMemcpy(
32203264 });
32213265
32223266 // loop:
3223 // cmp rcx, len
3267 // cmp count, 0
32243268 const loop_start = try self.addInst(.{
32253269 .tag = .cmp,
32263270 .ops = (Mir.Ops{
3227 .reg1 = .rcx,
3228 .reg2 = len_reg,
3271 .reg1 = count_reg,
32293272 }).encode(),
3230 .data = undefined,
3273 .data = .{ .imm = 0 },
32313274 });
32323275
3233 // jge end
3276 // je end
32343277 const loop_reloc = try self.addInst(.{
3235 .tag = .cond_jmp_above_below,
3236 .ops = (Mir.Ops{ .flags = 0b00 }).encode(),
3278 .tag = .cond_jmp_eq_ne,
3279 .ops = (Mir.Ops{ .flags = 0b01 }).encode(),
32373280 .data = .{ .inst = undefined },
32383281 });
32393282
......@@ -3275,6 +3318,15 @@ fn genInlineMemcpy(
32753318 .data = .{ .imm = 1 },
32763319 });
32773320
3321 // sub count, 1
3322 _ = try self.addInst(.{
3323 .tag = .sub,
3324 .ops = (Mir.Ops{
3325 .reg1 = count_reg,
3326 }).encode(),
3327 .data = .{ .imm = 1 },
3328 });
3329
32783330 // jmp loop
32793331 _ = try self.addInst(.{
32803332 .tag = .jmp,
......@@ -3286,47 +3338,6 @@ fn genInlineMemcpy(
32863338 try self.performReloc(loop_reloc);
32873339}
32883340
3289/// Set pointee via pointer stored in a register.
3290/// mov [reg], value
3291fn genSetPtrReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3292 switch (mcv) {
3293 .dead => unreachable,
3294 .unreach, .none => return, // Nothing to do.
3295 .immediate => |imm| {
3296 const abi_size = ty.abiSize(self.target.*);
3297 switch (abi_size) {
3298 1, 2, 4 => {
3299 // TODO this is wasteful!
3300 // introduce new MIR tag specifically for mov [reg + 0], imm
3301 const payload = try self.addExtra(Mir.ImmPair{
3302 .dest_off = 0,
3303 .operand = @bitCast(i32, @intCast(u32, imm)),
3304 });
3305 _ = try self.addInst(.{
3306 .tag = .mov_mem_imm,
3307 .ops = (Mir.Ops{
3308 .reg1 = reg.to64(),
3309 .flags = switch (abi_size) {
3310 1 => 0b00,
3311 2 => 0b01,
3312 4 => 0b10,
3313 else => unreachable,
3314 },
3315 }).encode(),
3316 .data = .{ .payload = payload },
3317 });
3318 },
3319 else => {
3320 return self.fail("TODO implement set pointee with immediate of ABI size {d}", .{abi_size});
3321 },
3322 }
3323 },
3324 else => |other| {
3325 return self.fail("TODO implement set pointee with {}", .{other});
3326 },
3327 }
3328}
3329
33303341fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
33313342 switch (mcv) {
33323343 .dead => unreachable,
......@@ -3749,7 +3760,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
37493760 if (typed_value.val.tag() == .int_u64) {
37503761 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
37513762 }
3752 return self.fail("TODO codegen more kinds of const pointers", .{});
3763 return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()});
37533764 },
37543765 },
37553766 .Int => {
src/arch/x86_64/Isel.zig+51-30
......@@ -265,32 +265,43 @@ fn mirPushPopRegsFromCalleePreservedRegs(isel: *Isel, tag: Tag, inst: Mir.Inst.I
265265
266266fn mirJmpCall(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
267267 const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]);
268 const flag = @truncate(u1, ops.flags);
269 if (flag == 0) {
270 const target = isel.mir.instructions.items(.data)[inst].inst;
271 const source = isel.code.items.len;
272 lowerToDEnc(tag, 0, isel.code) catch |err|
273 return isel.failWithLoweringError(err);
274 try isel.relocs.append(isel.bin_file.allocator, .{
275 .source = source,
276 .target = target,
277 .offset = isel.code.items.len - 4,
278 .length = 5,
279 });
280 return;
281 }
282 if (ops.reg1 == .none) {
283 // JMP/CALL [imm]
284 const imm = isel.mir.instructions.items(.data)[inst].imm;
285 const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) {
286 16 => .word_ptr,
287 else => .qword_ptr,
288 };
289 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), isel.code) catch |err|
290 isel.failWithLoweringError(err);
268 switch (ops.flags) {
269 0b00 => {
270 const target = isel.mir.instructions.items(.data)[inst].inst;
271 const source = isel.code.items.len;
272 lowerToDEnc(tag, 0, isel.code) catch |err|
273 return isel.failWithLoweringError(err);
274 try isel.relocs.append(isel.bin_file.allocator, .{
275 .source = source,
276 .target = target,
277 .offset = isel.code.items.len - 4,
278 .length = 5,
279 });
280 },
281 0b01 => {
282 if (ops.reg1 == .none) {
283 // JMP/CALL [imm]
284 const imm = isel.mir.instructions.items(.data)[inst].imm;
285 const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) {
286 16 => .word_ptr,
287 else => .qword_ptr,
288 };
289 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), isel.code) catch |err|
290 isel.failWithLoweringError(err);
291 }
292 // JMP/CALL reg
293 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), isel.code) catch |err| isel.failWithLoweringError(err);
294 },
295 0b10 => {
296 // JMP/CALL r/m64
297 const imm = isel.mir.instructions.items(.data)[inst].imm;
298 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
299 .disp = imm,
300 .base = ops.reg1,
301 }), isel.code) catch |err| isel.failWithLoweringError(err);
302 },
303 0b11 => return isel.fail("TODO unused JMP/CALL variant 0b11", .{}),
291304 }
292 // JMP/CALL reg
293 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), isel.code) catch |err| isel.failWithLoweringError(err);
294305}
295306
296307fn mirCondJmp(isel: *Isel, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
......@@ -658,16 +669,17 @@ fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void {
658669 // lea reg, [rbp + rcx + imm32]
659670 const imm = isel.mir.instructions.items(.data)[inst].imm;
660671 const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2;
672 const scale_index = ScaleIndex{
673 .scale = 0,
674 .index = .rcx,
675 };
661676 return lowerToRmEnc(
662677 .lea,
663678 ops.reg1,
664679 RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
665680 .disp = imm,
666681 .base = src_reg,
667 .scale_index = .{
668 .scale = 0,
669 .index = .rcx,
670 },
682 .scale_index = scale_index,
671683 }),
672684 isel.code,
673685 ) catch |err| isel.failWithLoweringError(err);
......@@ -1248,7 +1260,7 @@ const Memory = struct {
12481260 const dst = base.lowId();
12491261 const src = operand;
12501262 if (dst == 4 or mem_op.scale_index != null) {
1251 if (mem_op.disp == 0) {
1263 if (mem_op.disp == 0 and dst != 5) {
12521264 encoder.modRm_SIBDisp0(src);
12531265 if (mem_op.scale_index) |si| {
12541266 encoder.sib_scaleIndexBase(si.scale, si.index.lowId(), dst);
......@@ -1916,6 +1928,15 @@ test "lower RM encoding" {
19161928 },
19171929 }), isel.code());
19181930 try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", isel.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]");
1931 try lowerToRmEnc(.lea, .rsi, RegisterOrMemory.mem(.qword_ptr, .{
1932 .disp = 0,
1933 .base = .rbp,
1934 .scale_index = .{
1935 .scale = 0,
1936 .index = .rcx,
1937 },
1938 }), isel.code());
1939 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", isel.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]");
19191940}
19201941
19211942test "lower MR encoding" {
src/arch/x86_64/Mir.zig+5-6
......@@ -199,12 +199,11 @@ pub const Inst = struct {
199199 /// TODO handle scaling
200200 movabs,
201201
202 /// ops flags: 0bX0:
203 /// - Uses the `inst` Data tag as the jump target.
204 /// - reg1 and reg2 are ignored.
205 /// ops flags: 0bX1:
206 /// - reg1 is the jump target, reg2 and data are ignored.
207 /// - if reg1 is none, [imm]
202 /// ops flags: form:
203 /// 0b00 inst
204 /// 0b01 reg1
205 /// 0b01 [imm32] if reg1 is none
206 /// 0b10 [reg1 + imm32]
208207 jmp,
209208 call,
210209
test/stage2/x86_64.zig+19
......@@ -1761,6 +1761,25 @@ pub fn addCases(ctx: *TestContext) !void {
17611761 \\}
17621762 , "");
17631763 }
1764
1765 {
1766 var case = ctx.exe("access slice element by index - slice_elem_val", target);
1767 case.addCompareOutput(
1768 \\var array = [_]usize{ 0, 42, 123, 34 };
1769 \\var slice: []const usize = &array;
1770 \\
1771 \\pub fn main() void {
1772 \\ assert(slice[0] == 0);
1773 \\ assert(slice[1] == 42);
1774 \\ assert(slice[2] == 123);
1775 \\ assert(slice[3] == 34);
1776 \\}
1777 \\
1778 \\fn assert(ok: bool) void {
1779 \\ if (!ok) unreachable;
1780 \\}
1781 , "");
1782 }
17641783 }
17651784}
17661785