Commit 69d03d3a29

joachimschmidt557 <joachim.schmidt557@outlook.com>
2021-12-30 14:39:06
stage2 ARM: implement struct_field_ptr and struct_field_val
1 parent ac7fa95
Changed files (4)
src/arch/arm/CodeGen.zig
@@ -783,7 +783,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
 
 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
     const stack_mcv = try self.allocRegOrMem(inst, false);
-    log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
+    log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
     const reg_mcv = self.getResolvedInstValue(inst);
     assert(reg == reg_mcv.register);
     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
@@ -1591,28 +1591,54 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
-    return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index);
+    const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
+    return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
 }
 
 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
-    return self.structFieldPtr(ty_op.operand, ty_op.ty, index);
+    const result = try self.structFieldPtr(inst, ty_op.operand, index);
+    return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
-fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void {
-    _ = self;
-    _ = operand;
-    _ = ty;
-    _ = index;
-    return self.fail("TODO implement codegen struct_field_ptr", .{});
-    //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
+
+fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
+    return if (self.liveness.isUnused(inst)) .dead else result: {
+        const mcv = try self.resolveInst(operand);
+        const struct_ty = self.air.typeOf(operand).childType();
+        const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*));
+        const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
+        const struct_field_ty = struct_ty.structFieldType(index);
+        const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
+        switch (mcv) {
+            .ptr_stack_offset => |off| {
+                break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size };
+            },
+            else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}),
+        }
+    };
 }
 
 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
-    _ = extra;
-    return self.fail("TODO implement codegen struct_field_val", .{});
-    //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
+    const operand = extra.struct_operand;
+    const index = extra.field_index;
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const mcv = try self.resolveInst(operand);
+        const struct_ty = self.air.typeOf(operand);
+        const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*));
+        const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
+        const struct_field_ty = struct_ty.structFieldType(index);
+        const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
+        switch (mcv) {
+            .stack_offset => |off| {
+                break :result MCValue{ .stack_offset = off + struct_size - struct_field_offset - struct_field_size };
+            },
+            else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
+        }
+    };
+
+    return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
 }
 
 fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
@@ -1985,11 +2011,11 @@ fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_ind
         // Allocate 1 or 2 registers
         if (lhs_is_register) {
             // Move RHS to register
-            dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{mcv.register}) };
+            dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null, &.{mcv.register}) };
             rhs_mcv = dst_mcv;
         } else {
             // Move LHS and RHS to register
-            const regs = try self.register_manager.allocRegs(2, .{ inst, null }, &.{});
+            const regs = try self.register_manager.allocRegs(2, .{ null, null }, &.{});
             lhs_mcv = MCValue{ .register = regs[0] };
             rhs_mcv = MCValue{ .register = regs[1] };
             dst_mcv = lhs_mcv;
@@ -2401,16 +2427,22 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
         if (rhs_should_be_register) {
             if (!lhs_is_register and !rhs_is_register) {
                 const regs = try self.register_manager.allocRegs(2, .{
-                    Air.refToIndex(bin_op.rhs).?, Air.refToIndex(bin_op.lhs).?,
+                    Air.refToIndex(bin_op.lhs).?, Air.refToIndex(bin_op.rhs).?,
                 }, &.{});
                 lhs_mcv = MCValue{ .register = regs[0] };
                 rhs_mcv = MCValue{ .register = regs[1] };
             } else if (!rhs_is_register) {
-                rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.rhs).?, &.{}) };
+                const track_inst = if (self.liveness.operandDies(inst, 1)) null else Air.refToIndex(bin_op.rhs).?;
+                rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst, &.{}) };
+            } else if (!lhs_is_register) {
+                const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?;
+                lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst, &.{}) };
+            }
+        } else {
+            if (!lhs_is_register) {
+                const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?;
+                lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst, &.{}) };
             }
-        }
-        if (!lhs_is_register) {
-            lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?, &.{}) };
         }
 
         // Move the operands to the newly allocated registers
src/codegen.zig
@@ -372,11 +372,32 @@ pub fn generateSymbol(
             return Result{ .appended = {} };
         },
         .Struct => {
+            // TODO debug info
+            // TODO padding of struct members
             const field_vals = typed_value.val.castTag(.@"struct").?.data;
-            _ = field_vals; // TODO write the fields for real
+            for (field_vals) |field_val, index| {
+                const field_ty = typed_value.ty.structFieldType(index);
+                if (!field_ty.hasCodeGenBits()) continue;
+                switch (try generateSymbol(bin_file, src_loc, .{
+                    .ty = field_ty,
+                    .val = field_val,
+                }, code, debug_output)) {
+                    .appended => {},
+                    .externally_managed => |external_slice| {
+                        code.appendSliceAssumeCapacity(external_slice);
+                    },
+                    .fail => |em| return Result{ .fail = em },
+                }
+            }
+
+            return Result{ .appended = {} };
+        },
+        .Union => {
+            // TODO generateSymbol for unions
             const target = bin_file.options.target;
             const abi_size = try math.cast(usize, typed_value.ty.abiSize(target));
             try code.writer().writeByteNTimes(0xaa, abi_size);
+
             return Result{ .appended = {} };
         },
         else => |t| {
src/register_manager.zig
@@ -129,7 +129,7 @@ pub fn RegisterManager(
             comptime assert(count > 0 and count <= callee_preserved_regs.len);
             assert(count + exceptions.len <= callee_preserved_regs.len);
 
-            return self.tryAllocRegs(count, insts, exceptions) orelse blk: {
+            const result = self.tryAllocRegs(count, insts, exceptions) orelse blk: {
                 // We'll take over the first count registers. Spill
                 // the instructions that were previously there to a
                 // stack allocations.
@@ -164,6 +164,9 @@ pub fn RegisterManager(
 
                 break :blk regs;
             };
+
+            log.debug("allocated registers {any} for insts {any}", .{ result, insts });
+            return result;
         }
 
         /// Allocates a register and optionally tracks it with a
@@ -213,6 +216,7 @@ pub fn RegisterManager(
         /// Marks the specified register as free
         pub fn freeReg(self: *Self, reg: Register) void {
             const index = reg.allocIndex() orelse return;
+            log.debug("freeing register {}", .{reg});
 
             self.registers[index] = null;
             self.markRegFree(reg);
test/stage2/arm.zig
@@ -658,4 +658,50 @@ pub fn addCases(ctx: *TestContext) !void {
             "",
         );
     }
+
+    {
+        var case = ctx.exe("structs", linux_arm);
+        case.addCompareOutput(
+            \\var array = [_]SomeStruct{
+            \\    .{ .a = 0, .b = 42, .c = 69 },
+            \\    .{ .a = 1, .b = 2, .c = 3 },
+            \\    .{ .a = 123, .b = 456, .c = 789 },
+            \\};
+            \\var s: []const SomeStruct = &array;
+            \\
+            \\var some_struct: SomeStruct = .{
+            \\    .a = 0,
+            \\    .b = 42,
+            \\    .c = 69,
+            \\};
+            \\
+            \\const SomeStruct = struct {
+            \\    a: u32,
+            \\    b: u32,
+            \\    c: u32,
+            \\};
+            \\
+            \\pub fn main() void {
+            \\    assert(some_struct.a == 0);
+            \\    assert(some_struct.b == 42);
+            \\    assert(some_struct.c == 69);
+            \\
+            \\    assert(s[0].a == 0);
+            \\    assert(s[0].b == 42);
+            \\    assert(s[0].c == 69);
+            \\    assert(s[1].a == 1);
+            \\    assert(s[1].b == 2);
+            \\    assert(s[1].c == 3);
+            \\    assert(s[2].a == 123);
+            \\    assert(s[2].b == 456);
+            \\    assert(s[2].c == 789);
+            \\}
+            \\
+            \\fn assert(ok: bool) void {
+            \\    if (!ok) unreachable;
+            \\}
+        ,
+            "",
+        );
+    }
 }