Commit d288f202a4

Jakub Konka <kubkon@jakubkonka.com>
2022-01-03 19:32:52
stage2: implement slice_elem_val
1 parent e9f069f
Changed files (3)
src/arch/x86_64/CodeGen.zig
@@ -1274,7 +1274,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
         const operand = try self.resolveInst(ty_op.operand);
         const dst_mcv: MCValue = switch (operand) {
-            .stack_offset => |off| MCValue{ .stack_offset = off + 4 },
+            .stack_offset => |off| MCValue{ .stack_offset = off + 8 },
             else => return self.fail("TODO implement slice_len for {}", .{operand}),
         };
         break :result dst_mcv;
@@ -1303,10 +1303,57 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
     const is_volatile = false; // TODO
     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
-    const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst))
-        .dead
-    else
-        return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch});
+    const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
+        const slice_mcv = try self.resolveInst(bin_op.lhs);
+        const slice_ty = self.air.typeOf(bin_op.lhs);
+
+        const elem_ty = slice_ty.childType();
+        const elem_size = elem_ty.abiSize(self.target.*);
+
+        var buf: Type.SlicePtrFieldTypeBuffer = undefined;
+        const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
+
+        const index_ty = self.air.typeOf(bin_op.rhs);
+        const index_mcv: MCValue = blk: {
+            switch (try self.resolveInst(bin_op.rhs)) {
+                .register => |reg| {
+                    if (reg.to64() != .rcx) {
+                        try self.register_manager.getReg(.rcx, inst);
+                    }
+                    break :blk MCValue{ .register = .rcx };
+                },
+                else => return self.fail("TODO move index mcv into a register", .{}),
+            }
+        };
+
+        try self.genIMulOpMir(index_ty, index_mcv, .{ .immediate = elem_size });
+
+        const dst_mcv = blk: {
+            switch (slice_mcv) {
+                .stack_offset => |unadjusted_off| {
+                    const dst_mcv = try self.allocRegOrMem(inst, false);
+                    const addr_reg = try self.register_manager.allocReg(null, &.{index_mcv.register});
+                    const slice_ptr_abi_size = @intCast(u32, slice_ptr_field_type.abiSize(self.target.*));
+                    const off = unadjusted_off + elem_size;
+                    // lea reg, [rbp - 8 + rcx*1]
+                    _ = try self.addInst(.{
+                        .tag = .lea,
+                        .ops = (Mir.Ops{
+                            .reg1 = registerAlias(addr_reg, slice_ptr_abi_size),
+                            .reg2 = .rbp,
+                            .flags = 0b11,
+                        }).encode(),
+                        .data = .{ .imm = -@intCast(i32, off) },
+                    });
+                    try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type);
+                    break :blk dst_mcv;
+                },
+                else => return self.fail("TODO implement slice_elem_val when slice is {}", .{slice_mcv}),
+            }
+        };
+
+        break :result dst_mcv;
+    };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
@@ -1791,6 +1838,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
                             .data = .{ .imm = @intCast(i32, imm) },
                         });
                     } else {
+                        // TODO verify we don't spill and assign to the same register as dst_mcv
                         const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);
                         return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });
                     }
@@ -2906,12 +2954,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                 },
             }
         },
-        .embedded_in_code, .memory => {
-            // TODO this and `.stack_offset` below need to get improved to support types greater than
-            // register size, and do general memcpy
-            const reg = try self.copyToTmpRegister(ty, mcv);
-            return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
-        },
         .register => |reg| {
             if (stack_offset > math.maxInt(i32)) {
                 return self.fail("stack offset too large", .{});
@@ -2928,15 +2970,23 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                 .data = .{ .imm = -@intCast(i32, adj_off) },
             });
         },
-        .stack_offset => |off| {
-            // TODO this and `.embedded_in_code` above need to get improved to support types greater than
+        .stack_offset,
+        .embedded_in_code,
+        .memory,
+        => {
+            // TODO this needs to get improved to support types greater than
             // register size, and do general memcpy
+            if (mcv == .stack_offset and mcv.stack_offset == stack_offset) {
+                // Copy stack variable to itself; nothing to do.
+                return;
+            }
 
-            if (stack_offset == off)
-                return; // Copy stack variable to itself; nothing to do.
+            if (ty.abiSize(self.target.*) <= 8) {
+                const reg = try self.copyToTmpRegister(ty, mcv);
+                return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
+            }
 
-            const reg = try self.copyToTmpRegister(ty, mcv);
-            return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
+            return self.fail("TODO implement memcpy for ABI size {} > 8", .{ty.abiSize(self.target.*)});
         },
     }
 }
src/arch/x86_64/Isel.zig
@@ -654,7 +654,24 @@ fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void {
                 );
             }
         },
-        0b11 => return isel.fail("TODO unused variant lea reg1, reg2, 0b11", .{}),
+        0b11 => {
+            // lea reg, [rbp + rcx + imm32]
+            const imm = isel.mir.instructions.items(.data)[inst].imm;
+            const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2;
+            return lowerToRmEnc(
+                .lea,
+                ops.reg1,
+                RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{
+                    .disp = imm,
+                    .base = src_reg,
+                    .scale_index = .{
+                        .scale = 0,
+                        .index = .rcx,
+                    },
+                }),
+                isel.code,
+            ) catch |err| isel.failWithLoweringError(err);
+        },
     }
 }
 
src/arch/x86_64/Mir.zig
@@ -167,6 +167,7 @@ pub const Inst = struct {
         ///      0b00  reg1, [ds:imm32]
         ///      0b01  reg1, [rip + imm32]
         ///      0b10  reg1, [rip + reloc]
+        ///      0b11  reg1, [reg2 + rcx + imm32]
         /// Notes:
         /// * if flags are 0b10, `Data` contains `got_entry` for the linker to generate
         /// a valid relocation for.