Commit a0a7d15142

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-09-04 22:28:59
stage2 ARM: support larger function stacks
This is done by introducing a new Mir pseudo-instruction
1 parent 3794f2c
Changed files (4)
src
test
behavior
src/arch/arm/CodeGen.zig
@@ -488,14 +488,10 @@ fn gen(self: *Self) !void {
         const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
         const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
         self.max_end_stack = stack_size;
-        if (Instruction.Operand.fromU32(stack_size)) |op| {
-            self.mir_instructions.set(sub_reloc, .{
-                .tag = .sub,
-                .data = .{ .rr_op = .{ .rd = .sp, .rn = .sp, .op = op } },
-            });
-        } else {
-            return self.failSymbol("TODO ARM: allow larger stacks", .{});
-        }
+        self.mir_instructions.set(sub_reloc, .{
+            .tag = .sub_sp_scratch_r0,
+            .data = .{ .imm32 = stack_size },
+        });
 
         _ = try self.addInst(.{
             .tag = .dbg_epilogue_begin,
src/arch/arm/Emit.zig
@@ -11,6 +11,7 @@ const link = @import("../../link.zig");
 const Module = @import("../../Module.zig");
 const Type = @import("../../type.zig").Type;
 const ErrorMsg = Module.ErrorMsg;
+const Target = std.Target;
 const assert = std.debug.assert;
 const DW = std.dwarf;
 const leb128 = std.leb;
@@ -93,6 +94,8 @@ pub fn emitMir(
             .sub => try emit.mirDataProcessing(inst),
             .subs => try emit.mirDataProcessing(inst),
 
+            .sub_sp_scratch_r0 => try emit.mirSubStackPointer(inst),
+
             .asr => try emit.mirShift(inst),
             .lsl => try emit.mirShift(inst),
             .lsr => try emit.mirShift(inst),
@@ -190,6 +193,24 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
         .dbg_epilogue_begin,
         .dbg_prologue_end,
         => return 0,
+
+        .sub_sp_scratch_r0 => {
+            const imm32 = emit.mir.instructions.items(.data)[inst].imm32;
+
+            if (imm32 == 0) {
+                return 0 * 4;
+            } else if (Instruction.Operand.fromU32(imm32) != null) {
+                // sub
+                return 1 * 4;
+            } else if (Target.arm.featureSetHas(emit.target.cpu.features, .has_v7)) {
+                // movw; movt; sub
+                return 3 * 4;
+            } else {
+                // mov; orr; orr; orr; sub
+                return 5 * 4;
+            }
+        },
+
         else => return 4,
     }
 }
@@ -427,6 +448,37 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
     }
 }
 
+fn mirSubStackPointer(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const cond = emit.mir.instructions.items(.cond)[inst];
+    const imm32 = emit.mir.instructions.items(.data)[inst].imm32;
+
+    switch (tag) {
+        .sub_sp_scratch_r0 => {
+            if (imm32 == 0) return;
+
+            const operand = Instruction.Operand.fromU32(imm32) orelse blk: {
+                const scratch: Register = .r0;
+
+                if (Target.arm.featureSetHas(emit.target.cpu.features, .has_v7)) {
+                    try emit.writeInstruction(Instruction.movw(cond, scratch, @truncate(u16, imm32)));
+                    try emit.writeInstruction(Instruction.movt(cond, scratch, @truncate(u16, imm32 >> 16)));
+                } else {
+                    try emit.writeInstruction(Instruction.mov(cond, scratch, Instruction.Operand.imm(@truncate(u8, imm32), 0)));
+                    try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 8), 12)));
+                    try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 16), 8)));
+                    try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 24), 4)));
+                }
+
+                break :blk Instruction.Operand.reg(scratch, Instruction.Operand.Shift.none);
+            };
+
+            try emit.writeInstruction(Instruction.sub(cond, .sp, .sp, operand));
+        },
+        else => unreachable,
+    }
+}
+
 fn mirShift(emit: *Emit, inst: Mir.Inst.Index) !void {
     const tag = emit.mir.instructions.items(.tag)[inst];
     const cond = emit.mir.instructions.items(.cond)[inst];
src/arch/arm/Mir.zig
@@ -111,6 +111,11 @@ pub const Inst = struct {
         strh,
         /// Subtract
         sub,
+        /// Pseudo-instruction: Subtract 32-bit immediate from stack
+        ///
+        /// r0 can be used by Emit as a scratch register for loading
+        /// the immediate
+        sub_sp_scratch_r0,
         /// Subtract, update condition flags
         subs,
         /// Supervisor Call
@@ -144,6 +149,10 @@ pub const Inst = struct {
         ///
         /// Used by e.g. svc
         imm24: u24,
+        /// A 32-bit immediate value.
+        ///
+        /// Used by e.g. sub_sp_scratch_r0
+        imm32: u32,
         /// Index into `extra`. Meaning of what can be found there is context-dependent.
         ///
         /// Used by e.g. load_memory
test/behavior/eval.zig
@@ -1333,7 +1333,6 @@ test "lazy sizeof is resolved in division" {
 }
 
 test "lazy value is resolved as slice operand" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
 
     const A = struct { a: u32 };