Commit 2f18c5955a
Changed files (4)
src
arch
test
stage2
src/arch/arm/CodeGen.zig
@@ -83,6 +83,8 @@ max_end_stack: u32 = 0,
/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
next_stack_offset: u32 = 0,
+saved_regs_stack_space: u32 = 0,
+
/// Debug field, used to find bugs in the compiler.
air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
@@ -123,10 +125,12 @@ const MCValue = union(enum) {
/// The value is in the compare flags assuming a signed operation,
/// with this operator applied on top of it.
compare_flags_signed: math.CompareOperator,
+ /// The value is a function argument passed via the stack.
+ stack_argument_offset: u32,
fn isMemory(mcv: MCValue) bool {
return switch (mcv) {
- .embedded_in_code, .memory, .stack_offset => true,
+ .embedded_in_code, .memory, .stack_offset, .stack_argument_offset => true,
else => false,
};
}
@@ -152,6 +156,7 @@ const MCValue = union(enum) {
.ptr_stack_offset,
.ptr_embedded_in_code,
.undef,
+ .stack_argument_offset,
=> false,
.register,
@@ -302,6 +307,7 @@ pub fn generate(
.prev_di_pc = 0,
.prev_di_line = module_fn.lbrace_line,
.prev_di_column = module_fn.lbrace_column,
+ .prologue_stack_space = call_info.stack_byte_count + function.saved_regs_stack_space,
};
defer emit.deinit();
@@ -387,11 +393,14 @@ fn gen(self: *Self) !void {
.r11 = true, // fp
.r14 = true, // lr
};
+ self.saved_regs_stack_space = 8;
inline for (callee_preserved_regs) |reg| {
if (self.register_manager.isRegAllocated(reg)) {
@field(saved_regs, @tagName(reg)) = true;
+ self.saved_regs_stack_space += 4;
}
}
+
self.mir_instructions.set(push_reloc, .{
.tag = .push,
.cond = .al,
@@ -399,9 +408,10 @@ fn gen(self: *Self) !void {
});
// Backpatch stack offset
- const stack_end = self.max_end_stack;
- const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
- if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| {
+ const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
+ 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;
+ if (Instruction.Operand.fromU32(stack_size)) |op| {
self.mir_instructions.set(sub_reloc, .{
.tag = .sub,
.cond = .al,
@@ -1256,6 +1266,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.stack_offset => {
return self.fail("TODO implement loading from MCValue.stack_offset", .{});
},
+ .stack_argument_offset => {
+ return self.fail("TODO implement loading from MCValue.stack_argument_offset", .{});
+ },
}
}
@@ -1297,6 +1310,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
.dead => unreachable,
.compare_flags_unsigned => unreachable,
.compare_flags_signed => unreachable,
+ .stack_argument_offset => unreachable,
.immediate => |imm| {
try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
},
@@ -1367,6 +1381,7 @@ fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
},
.register => true,
.stack_offset,
+ .stack_argument_offset,
.embedded_in_code,
.memory,
=> true,
@@ -1533,6 +1548,7 @@ fn genArmBinOpCode(
.immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?,
.register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
.stack_offset,
+ .stack_argument_offset,
.embedded_in_code,
.memory,
=> unreachable,
@@ -1749,6 +1765,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
.none => {},
}
},
+ .stack_argument_offset => return self.fail("TODO genArgDbgInfo for stack_argument_offset", .{}),
else => {},
}
}
@@ -1814,6 +1831,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
var info = try self.resolveCallingConventionValues(fn_ty);
defer info.deinit(self);
+ // Make space for the arguments passed via the stack
+ self.max_end_stack += info.stack_byte_count;
+
// Due to incremental compilation, how function calls are generated depends
// on linking.
if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
@@ -1836,9 +1856,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
try self.register_manager.getReg(reg, null);
try self.genSetReg(arg_ty, reg, arg_mcv);
},
- .stack_offset => {
- return self.fail("TODO implement calling with parameters in memory", .{});
- },
+ .stack_offset => unreachable,
+ .stack_argument_offset => |offset| try self.genSetStackArgument(
+ arg_ty,
+ info.stack_byte_count - offset,
+ arg_mcv,
+ ),
.ptr_stack_offset => {
return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
},
@@ -2616,16 +2639,26 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
}
},
- .memory => |vaddr| {
- _ = vaddr;
- return self.fail("TODO implement set stack variable from memory vaddr", .{});
+ .memory,
+ .stack_argument_offset,
+ => {
+ if (ty.abiSize(self.target.*) <= 4) {
+ const reg = try self.copyToTmpRegister(ty, mcv);
+ return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
+ } else {
+ return self.fail("TODO implement memcpy", .{});
+ }
},
.stack_offset => |off| {
if (stack_offset == off)
return; // Copy stack variable to itself; nothing to do.
- const reg = try self.copyToTmpRegister(ty, mcv);
- return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
+ if (ty.abiSize(self.target.*) <= 4) {
+ const reg = try self.copyToTmpRegister(ty, mcv);
+ return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
+ } else {
+ return self.fail("TODO implement memcpy", .{});
+ }
},
}
}
@@ -2878,10 +2911,115 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
}
},
+ .stack_argument_offset => |unadjusted_off| {
+ // TODO: maybe addressing from sp instead of fp
+ const abi_size = ty.abiSize(self.target.*);
+ const adj_off = unadjusted_off + abi_size;
+
+ const tag: Mir.Inst.Tag = switch (abi_size) {
+ 1 => .ldrb_stack_argument,
+ 2 => .ldrh_stack_argument,
+ 4 => .ldr_stack_argument,
+ else => unreachable,
+ };
+
+ _ = try self.addInst(.{
+ .tag = tag,
+ .cond = .al,
+ .data = .{ .r_stack_offset = .{
+ .rt = reg,
+ .stack_offset = @intCast(u32, adj_off),
+ } },
+ });
+ },
else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
}
}
+fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
+ switch (mcv) {
+ .dead => unreachable,
+ .none, .unreach => return,
+ .undef => {
+ if (!self.wantSafety())
+ return; // The already existing value will do just fine.
+ // TODO Upgrade this to a memset call when we have that available.
+ switch (ty.abiSize(self.target.*)) {
+ 1 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }),
+ 2 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }),
+ 4 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
+ 8 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
+ else => return self.fail("TODO implement memset", .{}),
+ }
+ },
+ .register => |reg| {
+ const abi_size = ty.abiSize(self.target.*);
+ const adj_off = stack_offset - abi_size;
+
+ switch (abi_size) {
+ 1, 4 => {
+ const offset = if (math.cast(u12, adj_off)) |imm| blk: {
+ break :blk Instruction.Offset.imm(imm);
+ } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
+
+ const tag: Mir.Inst.Tag = switch (abi_size) {
+ 1 => .strb,
+ 4 => .str,
+ else => unreachable,
+ };
+
+ _ = try self.addInst(.{
+ .tag = tag,
+ .cond = .al,
+ .data = .{ .rr_offset = .{
+ .rt = reg,
+ .rn = .sp,
+ .offset = .{ .offset = offset },
+ } },
+ });
+ },
+ 2 => {
+ const offset = if (adj_off <= math.maxInt(u8)) blk: {
+ break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
+ } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
+
+ _ = try self.addInst(.{
+ .tag = .strh,
+ .cond = .al,
+ .data = .{ .rr_extra_offset = .{
+ .rt = reg,
+ .rn = .sp,
+ .offset = .{ .offset = offset },
+ } },
+ });
+ },
+ else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
+ }
+ },
+ .immediate,
+ .compare_flags_signed,
+ .compare_flags_unsigned,
+ .stack_offset,
+ .memory,
+ .stack_argument_offset,
+ .embedded_in_code,
+ => {
+ if (ty.abiSize(self.target.*) <= 4) {
+ const reg = try self.copyToTmpRegister(ty, mcv);
+ return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
+ } else {
+ return self.fail("TODO implement memcpy", .{});
+ }
+ },
+ .ptr_stack_offset => {
+ return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
+ },
+ .ptr_embedded_in_code => {
+ return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
+ },
+ }
+}
+
fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[inst].un_op;
const result = try self.resolveInst(un_op);
@@ -3002,27 +3140,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
}
}
-/// If the MCValue is an immediate, and it does not fit within this type,
-/// we put it in a register.
-/// A potential opportunity for future optimization here would be keeping track
-/// of the fact that the instruction is available both as an immediate
-/// and as a register.
-fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
- const mcv = try self.resolveInst(operand);
- const ti = @typeInfo(T).Int;
- switch (mcv) {
- .immediate => |imm| {
- // This immediate is unsigned.
- const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
- if (imm >= math.maxInt(U)) {
- return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
- }
- },
- else => {},
- }
- return mcv;
-}
-
fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
if (typed_value.val.isUndef())
return MCValue{ .undef = {} };
@@ -3214,7 +3331,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
if (ty.abiAlignment(self.target.*) == 8)
nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8);
- result.args[i] = .{ .stack_offset = nsaa };
+ result.args[i] = .{ .stack_argument_offset = nsaa };
nsaa += param_size;
}
}
src/arch/arm/Emit.zig
@@ -30,6 +30,10 @@ prev_di_column: u32,
/// Relative to the beginning of `code`.
prev_di_pc: usize,
+/// The amount of stack space consumed by all stack arguments as well
+/// as the saved callee-saved registers
+prologue_stack_space: u32,
+
/// The branch type of every branch
branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{},
/// For every forward branch, maps the target instruction to a list of
@@ -102,6 +106,10 @@ pub fn emitMir(
.str => try emit.mirLoadStore(inst),
.strb => try emit.mirLoadStore(inst),
+ .ldr_stack_argument => try emit.mirLoadStack(inst),
+ .ldrb_stack_argument => try emit.mirLoadStack(inst),
+ .ldrh_stack_argument => try emit.mirLoadStack(inst),
+
.ldrh => try emit.mirLoadStoreExtra(inst),
.strh => try emit.mirLoadStoreExtra(inst),
@@ -468,6 +476,53 @@ fn mirLoadStore(emit: *Emit, inst: Mir.Inst.Index) !void {
}
}
+fn mirLoadStack(emit: *Emit, inst: Mir.Inst.Index) !void {
+ const tag = emit.mir.instructions.items(.tag)[inst];
+ const cond = emit.mir.instructions.items(.cond)[inst];
+ const r_stack_offset = emit.mir.instructions.items(.data)[inst].r_stack_offset;
+
+ const raw_offset = emit.prologue_stack_space - r_stack_offset.stack_offset;
+ switch (tag) {
+ .ldr_stack_argument => {
+ const offset = if (raw_offset <= math.maxInt(u12)) blk: {
+ break :blk Instruction.Offset.imm(@intCast(u12, raw_offset));
+ } else return emit.fail("TODO mirLoadStack larger offsets", .{});
+
+ try emit.writeInstruction(Instruction.ldr(
+ cond,
+ r_stack_offset.rt,
+ .fp,
+ .{ .offset = offset },
+ ));
+ },
+ .ldrb_stack_argument => {
+ const offset = if (raw_offset <= math.maxInt(u12)) blk: {
+ break :blk Instruction.Offset.imm(@intCast(u12, raw_offset));
+ } else return emit.fail("TODO mirLoadStack larger offsets", .{});
+
+ try emit.writeInstruction(Instruction.ldrb(
+ cond,
+ r_stack_offset.rt,
+ .fp,
+ .{ .offset = offset },
+ ));
+ },
+ .ldrh_stack_argument => {
+ const offset = if (raw_offset <= math.maxInt(u8)) blk: {
+ break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, raw_offset));
+ } else return emit.fail("TODO mirLoadStack larger offsets", .{});
+
+ try emit.writeInstruction(Instruction.ldrh(
+ cond,
+ r_stack_offset.rt,
+ .fp,
+ .{ .offset = offset },
+ ));
+ },
+ else => unreachable,
+ }
+}
+
fn mirLoadStoreExtra(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
@@ -51,10 +51,16 @@ pub const Inst = struct {
eor,
/// Load Register
ldr,
+ /// Load Register
+ ldr_stack_argument,
/// Load Register Byte
ldrb,
+ /// Load Register Byte
+ ldrb_stack_argument,
/// Load Register Halfword
ldrh,
+ /// Load Register Halfword
+ ldrh_stack_argument,
/// Logical Shift Left
lsl,
/// Logical Shift Right
@@ -124,6 +130,13 @@ pub const Inst = struct {
///
/// Used by e.g. blx
reg: Register,
+ /// A register and a stack offset
+ ///
+ /// Used by e.g. ldr_stack_argument
+ r_stack_offset: struct {
+ rt: Register,
+ stack_offset: u32,
+ },
/// A register and a 16-bit unsigned immediate
///
/// Used by e.g. movw
test/stage2/arm.zig
@@ -72,6 +72,22 @@ pub fn addCases(ctx: *TestContext) !void {
,
"Hello, World!\n",
);
+
+ case.addCompareOutput(
+ \\pub fn main() void {
+ \\ assert(add(1, 2, 3, 4, 5, 6) == 21);
+ \\}
+ \\
+ \\fn add(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32) u32 {
+ \\ return a + b + c + d + e + f;
+ \\}
+ \\
+ \\pub fn assert(ok: bool) void {
+ \\ if (!ok) unreachable; // assertion failure
+ \\}
+ ,
+ "",
+ );
}
{
@@ -465,12 +481,12 @@ pub fn addCases(ctx: *TestContext) !void {
\\ const j = i + d; // 110
\\ const k = i + j; // 210
\\ const l = k + c; // 217
- \\ const m = l * d; // 2170
- \\ const n = m + e; // 2184
- \\ const o = n * f; // 52416
- \\ const p = o + g; // 52454
- \\ const q = p * h; // 3252148
- \\ const r = q + i; // 3252248
+ \\ const m = l * d; // 2170
+ \\ const n = m + e; // 2184
+ \\ const o = n * f; // 52416
+ \\ const p = o + g; // 52454
+ \\ const q = p * h; // 3252148
+ \\ const r = q + i; // 3252248
\\ const s = r * j; // 357747280
\\ const t = s + k; // 357747490
\\ break :blk t;