authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-12-12 20:29:28+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-19 23:24:44-08:00
logecf0050a92f2b11f4ad912cc7f856a82666dc7d1
tree1546f2a688870547c7929289d744cbaad359a46e
parent5c7f2ab011f9c1cd522ab76a674d0acc5f397041

stage2 AArch64: Implement saving callee-saved registers


3 files changed, 131 insertions(+), 10 deletions(-)

src/arch/aarch64/CodeGen.zig+44-10
...@@ -83,6 +83,8 @@ max_end_stack: u32 = 0,...@@ -83,6 +83,8 @@ max_end_stack: u32 = 0,
83/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.83/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
84next_stack_offset: u32 = 0,84next_stack_offset: u32 = 0,
8585
86saved_regs_stack_space: u32 = 0,
87
86/// Debug field, used to find bugs in the compiler.88/// Debug field, used to find bugs in the compiler.
87air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,89air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
8890
...@@ -350,12 +352,7 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {...@@ -350,12 +352,7 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
350fn gen(self: *Self) !void {352fn gen(self: *Self) !void {
351 const cc = self.fn_type.fnCallingConvention();353 const cc = self.fn_type.fnCallingConvention();
352 if (cc != .Naked) {354 if (cc != .Naked) {
353 // TODO Finish function prologue and epilogue for aarch64.
354
355 // stp fp, lr, [sp, #-16]!355 // stp fp, lr, [sp, #-16]!
356 // mov fp, sp
357 // sub sp, sp, #reloc
358
359 _ = try self.addInst(.{356 _ = try self.addInst(.{
360 .tag = .stp,357 .tag = .stp,
361 .data = .{ .load_store_register_pair = .{358 .data = .{ .load_store_register_pair = .{
...@@ -366,11 +363,19 @@ fn gen(self: *Self) !void {...@@ -366,11 +363,19 @@ fn gen(self: *Self) !void {
366 } },363 } },
367 });364 });
368365
366 // <store other registers>
367 const backpatch_save_registers = try self.addInst(.{
368 .tag = .nop,
369 .data = .{ .nop = {} },
370 });
371
372 // mov fp, sp
369 _ = try self.addInst(.{373 _ = try self.addInst(.{
370 .tag = .mov_to_from_sp,374 .tag = .mov_to_from_sp,
371 .data = .{ .rr = .{ .rd = .x29, .rn = .xzr } },375 .data = .{ .rr = .{ .rd = .x29, .rn = .xzr } },
372 });376 });
373377
378 // sub sp, sp, #reloc
374 const backpatch_reloc = try self.addInst(.{379 const backpatch_reloc = try self.addInst(.{
375 .tag = .nop,380 .tag = .nop,
376 .data = .{ .nop = {} },381 .data = .{ .nop = {} },
...@@ -383,10 +388,33 @@ fn gen(self: *Self) !void {...@@ -383,10 +388,33 @@ fn gen(self: *Self) !void {
383388
384 try self.genBody(self.air.getMainBody());389 try self.genBody(self.air.getMainBody());
385390
391 // Backpatch push callee saved regs
392 var saved_regs: u32 = 0;
393 self.saved_regs_stack_space = 16;
394 inline for (callee_preserved_regs) |reg| {
395 if (self.register_manager.isRegAllocated(reg)) {
396 saved_regs |= @as(u32, 1) << reg.id();
397 self.saved_regs_stack_space += 8;
398 }
399 }
400
401 // Emit.mirPopPushRegs automatically adds extra empty space so
402 // that sp is always aligned to 16
403 if (!std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)) {
404 self.saved_regs_stack_space += 8;
405 }
406 assert(std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16));
407
408 self.mir_instructions.set(backpatch_save_registers, .{
409 .tag = .push_regs,
410 .data = .{ .reg_list = saved_regs },
411 });
412
386 // Backpatch stack offset413 // Backpatch stack offset
387 const stack_end = self.max_end_stack;414 const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
388 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);415 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
389 if (math.cast(u12, aligned_stack_end)) |size| {416 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
417 if (math.cast(u12, stack_size)) |size| {
390 self.mir_instructions.set(backpatch_reloc, .{418 self.mir_instructions.set(backpatch_reloc, .{
391 .tag = .sub_immediate,419 .tag = .sub_immediate,
392 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } },420 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } },
...@@ -418,7 +446,13 @@ fn gen(self: *Self) !void {...@@ -418,7 +446,13 @@ fn gen(self: *Self) !void {
418 // add sp, sp, #stack_size446 // add sp, sp, #stack_size
419 _ = try self.addInst(.{447 _ = try self.addInst(.{
420 .tag = .add_immediate,448 .tag = .add_immediate,
421 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, aligned_stack_end) } },449 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, stack_size) } },
450 });
451
452 // <load other registers>
453 _ = try self.addInst(.{
454 .tag = .pop_regs,
455 .data = .{ .reg_list = saved_regs },
422 });456 });
423457
424 // ldp fp, lr, [sp], #16458 // ldp fp, lr, [sp], #16
...@@ -1754,7 +1788,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1754,7 +1788,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
1754 },1788 },
1755 },1789 },
1756 }),1790 }),
1757 else => return self.fail("TODO implement condr when condition is {s}", .{@tagName(cond)}),1791 else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(cond)}),
1758 };1792 };
17591793
1760 // Capture the state of register and stack allocation state so that we can revert to it.1794 // Capture the state of register and stack allocation state so that we can revert to it.
src/arch/aarch64/Emit.zig+79
...@@ -126,6 +126,9 @@ pub fn emitMir(...@@ -126,6 +126,9 @@ pub fn emitMir(
126 .movz => try emit.mirMoveWideImmediate(inst),126 .movz => try emit.mirMoveWideImmediate(inst),
127127
128 .nop => try emit.mirNop(),128 .nop => try emit.mirNop(),
129
130 .push_regs => try emit.mirPushPopRegs(inst),
131 .pop_regs => try emit.mirPushPopRegs(inst),
129 }132 }
130 }133 }
131}134}
...@@ -798,3 +801,79 @@ fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -798,3 +801,79 @@ fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
798fn mirNop(emit: *Emit) !void {801fn mirNop(emit: *Emit) !void {
799 try emit.writeInstruction(Instruction.nop());802 try emit.writeInstruction(Instruction.nop());
800}803}
804
805fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
806 const tag = emit.mir.instructions.items(.tag)[inst];
807 const reg_list = emit.mir.instructions.items(.data)[inst].reg_list;
808
809 if (reg_list & @as(u32, 1) << 31 != 0) return emit.fail("xzr is not a valid register for {}", .{tag});
810
811 // sp must be aligned at all times, so we only use stp and ldp
812 // instructions for minimal instruction count. However, if we do
813 // not have an even number of registers, we use str and ldr
814 const number_of_regs = @popCount(u32, reg_list);
815
816 switch (tag) {
817 .pop_regs => {
818 var i: u6 = 32;
819 var count: u6 = 0;
820 var other_reg: Register = undefined;
821 while (i > 0) : (i -= 1) {
822 const reg = @intToEnum(Register, i - 1);
823 if (reg_list & @as(u32, 1) << reg.id() != 0) {
824 if (count % 2 == 0) {
825 if (count == number_of_regs - 1) {
826 try emit.writeInstruction(Instruction.ldr(
827 reg,
828 Register.sp,
829 Instruction.LoadStoreOffset.imm_post_index(16),
830 ));
831 } else {
832 other_reg = reg;
833 }
834 } else {
835 try emit.writeInstruction(Instruction.ldp(
836 reg,
837 other_reg,
838 Register.sp,
839 Instruction.LoadStorePairOffset.post_index(16),
840 ));
841 }
842 count += 1;
843 }
844 }
845 assert(count == number_of_regs);
846 },
847 .push_regs => {
848 var i: u6 = 0;
849 var count: u6 = 0;
850 var other_reg: Register = undefined;
851 while (i < 32) : (i += 1) {
852 const reg = @intToEnum(Register, i);
853 if (reg_list & @as(u32, 1) << reg.id() != 0) {
854 if (count % 2 == 0) {
855 if (count == number_of_regs - 1) {
856 try emit.writeInstruction(Instruction.str(
857 reg,
858 Register.sp,
859 Instruction.LoadStoreOffset.imm_pre_index(-16),
860 ));
861 } else {
862 other_reg = reg;
863 }
864 } else {
865 try emit.writeInstruction(Instruction.stp(
866 other_reg,
867 reg,
868 Register.sp,
869 Instruction.LoadStorePairOffset.pre_index(-16),
870 ));
871 }
872 count += 1;
873 }
874 }
875 assert(count == number_of_regs);
876 },
877 else => unreachable,
878 }
879}
src/arch/aarch64/Mir.zig+8
...@@ -81,6 +81,10 @@ pub const Inst = struct {...@@ -81,6 +81,10 @@ pub const Inst = struct {
81 movz,81 movz,
82 /// No Operation82 /// No Operation
83 nop,83 nop,
84 /// Pseudo-instruction: Pop multiple registers
85 pop_regs,
86 /// Psuedo-instruction: Push multiple registers
87 push_regs,
84 /// Return from subroutine88 /// Return from subroutine
85 ret,89 ret,
86 /// Store Pair of Registers90 /// Store Pair of Registers
...@@ -137,6 +141,10 @@ pub const Inst = struct {...@@ -137,6 +141,10 @@ pub const Inst = struct {
137 ///141 ///
138 /// Used by e.g. blr142 /// Used by e.g. blr
139 reg: Register,143 reg: Register,
144 /// Multiple registers
145 ///
146 /// Used by e.g. pop_regs
147 reg_list: u32,
140 /// Another instruction and a condition148 /// Another instruction and a condition
141 ///149 ///
142 /// Used by e.g. b_cond150 /// Used by e.g. b_cond