Skip to content

Commit ecf0050

Browse files
joachimschmidt557andrewrk
authored andcommitted
stage2 AArch64: Implement saving callee-saved registers
1 parent 5c7f2ab commit ecf0050

File tree

3 files changed

+131
-10
lines changed

3 files changed

+131
-10
lines changed

src/arch/aarch64/CodeGen.zig

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ max_end_stack: u32 = 0,
8383
/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
8484
next_stack_offset: u32 = 0,
8585

86+
saved_regs_stack_space: u32 = 0,
87+
8688
/// Debug field, used to find bugs in the compiler.
8789
air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
8890

@@ -350,12 +352,7 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
350352
fn gen(self: *Self) !void {
351353
const cc = self.fn_type.fnCallingConvention();
352354
if (cc != .Naked) {
353-
// TODO Finish function prologue and epilogue for aarch64.
354-
355355
// stp fp, lr, [sp, #-16]!
356-
// mov fp, sp
357-
// sub sp, sp, #reloc
358-
359356
_ = try self.addInst(.{
360357
.tag = .stp,
361358
.data = .{ .load_store_register_pair = .{
@@ -366,11 +363,19 @@ fn gen(self: *Self) !void {
366363
} },
367364
});
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
369373
_ = try self.addInst(.{
370374
.tag = .mov_to_from_sp,
371375
.data = .{ .rr = .{ .rd = .x29, .rn = .xzr } },
372376
});
373377

378+
// sub sp, sp, #reloc
374379
const backpatch_reloc = try self.addInst(.{
375380
.tag = .nop,
376381
.data = .{ .nop = {} },
@@ -383,10 +388,33 @@ fn gen(self: *Self) !void {
383388

384389
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+
386413
// Backpatch stack offset
387-
const stack_end = self.max_end_stack;
388-
const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
389-
if (math.cast(u12, aligned_stack_end)) |size| {
414+
const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
415+
const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
416+
const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
417+
if (math.cast(u12, stack_size)) |size| {
390418
self.mir_instructions.set(backpatch_reloc, .{
391419
.tag = .sub_immediate,
392420
.data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } },
@@ -418,7 +446,13 @@ fn gen(self: *Self) !void {
418446
// add sp, sp, #stack_size
419447
_ = try self.addInst(.{
420448
.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 },
422456
});
423457

424458
// ldp fp, lr, [sp], #16
@@ -1754,7 +1788,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
17541788
},
17551789
},
17561790
}),
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)}),
17581792
};
17591793

17601794
// Capture the state of register and stack allocation state so that we can revert to it.

src/arch/aarch64/Emit.zig

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ pub fn emitMir(
126126
.movz => try emit.mirMoveWideImmediate(inst),
127127

128128
.nop => try emit.mirNop(),
129+
130+
.push_regs => try emit.mirPushPopRegs(inst),
131+
.pop_regs => try emit.mirPushPopRegs(inst),
129132
}
130133
}
131134
}
@@ -798,3 +801,79 @@ fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
798801
fn mirNop(emit: *Emit) !void {
799802
try emit.writeInstruction(Instruction.nop());
800803
}
804+
805+
fn 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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ pub const Inst = struct {
8181
movz,
8282
/// No Operation
8383
nop,
84+
/// Pseudo-instruction: Pop multiple registers
85+
pop_regs,
86+
/// Psuedo-instruction: Push multiple registers
87+
push_regs,
8488
/// Return from subroutine
8589
ret,
8690
/// Store Pair of Registers
@@ -137,6 +141,10 @@ pub const Inst = struct {
137141
///
138142
/// Used by e.g. blr
139143
reg: Register,
144+
/// Multiple registers
145+
///
146+
/// Used by e.g. pop_regs
147+
reg_list: u32,
140148
/// Another instruction and a condition
141149
///
142150
/// Used by e.g. b_cond

0 commit comments

Comments
 (0)