authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-11-29 14:11:56+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-04 18:16:23-08:00
log2f18c5955ae4fccbbcac312d8b2e0171c704915e
tree0e698c05f9d92b262df9d525ebc9ebc54c2ea939
parent725267f7c20f0ba588b472048a8c1fe1a328c714

stage2 ARM: Implement calling with stack parameters


4 files changed, 241 insertions(+), 40 deletions(-)

src/arch/arm/CodeGen.zig+151-34
......@@ -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`.
8484next_stack_offset: u32 = 0,
8585
86saved_regs_stack_space: u32 = 0,
87
8688/// Debug field, used to find bugs in the compiler.
8789air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
8890
......@@ -123,10 +125,12 @@ const MCValue = union(enum) {
123125 /// The value is in the compare flags assuming a signed operation,
124126 /// with this operator applied on top of it.
125127 compare_flags_signed: math.CompareOperator,
128 /// The value is a function argument passed via the stack.
129 stack_argument_offset: u32,
126130
127131 fn isMemory(mcv: MCValue) bool {
128132 return switch (mcv) {
129 .embedded_in_code, .memory, .stack_offset => true,
133 .embedded_in_code, .memory, .stack_offset, .stack_argument_offset => true,
130134 else => false,
131135 };
132136 }
......@@ -152,6 +156,7 @@ const MCValue = union(enum) {
152156 .ptr_stack_offset,
153157 .ptr_embedded_in_code,
154158 .undef,
159 .stack_argument_offset,
155160 => false,
156161
157162 .register,
......@@ -302,6 +307,7 @@ pub fn generate(
302307 .prev_di_pc = 0,
303308 .prev_di_line = module_fn.lbrace_line,
304309 .prev_di_column = module_fn.lbrace_column,
310 .prologue_stack_space = call_info.stack_byte_count + function.saved_regs_stack_space,
305311 };
306312 defer emit.deinit();
307313
......@@ -387,11 +393,14 @@ fn gen(self: *Self) !void {
387393 .r11 = true, // fp
388394 .r14 = true, // lr
389395 };
396 self.saved_regs_stack_space = 8;
390397 inline for (callee_preserved_regs) |reg| {
391398 if (self.register_manager.isRegAllocated(reg)) {
392399 @field(saved_regs, @tagName(reg)) = true;
400 self.saved_regs_stack_space += 4;
393401 }
394402 }
403
395404 self.mir_instructions.set(push_reloc, .{
396405 .tag = .push,
397406 .cond = .al,
......@@ -399,9 +408,10 @@ fn gen(self: *Self) !void {
399408 });
400409
401410 // Backpatch stack offset
402 const stack_end = self.max_end_stack;
403 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
404 if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| {
411 const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
412 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
413 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
414 if (Instruction.Operand.fromU32(stack_size)) |op| {
405415 self.mir_instructions.set(sub_reloc, .{
406416 .tag = .sub,
407417 .cond = .al,
......@@ -1256,6 +1266,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
12561266 .stack_offset => {
12571267 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
12581268 },
1269 .stack_argument_offset => {
1270 return self.fail("TODO implement loading from MCValue.stack_argument_offset", .{});
1271 },
12591272 }
12601273}
12611274
......@@ -1297,6 +1310,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
12971310 .dead => unreachable,
12981311 .compare_flags_unsigned => unreachable,
12991312 .compare_flags_signed => unreachable,
1313 .stack_argument_offset => unreachable,
13001314 .immediate => |imm| {
13011315 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
13021316 },
......@@ -1367,6 +1381,7 @@ fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
13671381 },
13681382 .register => true,
13691383 .stack_offset,
1384 .stack_argument_offset,
13701385 .embedded_in_code,
13711386 .memory,
13721387 => true,
......@@ -1533,6 +1548,7 @@ fn genArmBinOpCode(
15331548 .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?,
15341549 .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
15351550 .stack_offset,
1551 .stack_argument_offset,
15361552 .embedded_in_code,
15371553 .memory,
15381554 => unreachable,
......@@ -1749,6 +1765,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
17491765 .none => {},
17501766 }
17511767 },
1768 .stack_argument_offset => return self.fail("TODO genArgDbgInfo for stack_argument_offset", .{}),
17521769 else => {},
17531770 }
17541771}
......@@ -1814,6 +1831,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
18141831 var info = try self.resolveCallingConventionValues(fn_ty);
18151832 defer info.deinit(self);
18161833
1834 // Make space for the arguments passed via the stack
1835 self.max_end_stack += info.stack_byte_count;
1836
18171837 // Due to incremental compilation, how function calls are generated depends
18181838 // on linking.
18191839 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 {
18361856 try self.register_manager.getReg(reg, null);
18371857 try self.genSetReg(arg_ty, reg, arg_mcv);
18381858 },
1839 .stack_offset => {
1840 return self.fail("TODO implement calling with parameters in memory", .{});
1841 },
1859 .stack_offset => unreachable,
1860 .stack_argument_offset => |offset| try self.genSetStackArgument(
1861 arg_ty,
1862 info.stack_byte_count - offset,
1863 arg_mcv,
1864 ),
18421865 .ptr_stack_offset => {
18431866 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
18441867 },
......@@ -2616,16 +2639,26 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
26162639 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
26172640 }
26182641 },
2619 .memory => |vaddr| {
2620 _ = vaddr;
2621 return self.fail("TODO implement set stack variable from memory vaddr", .{});
2642 .memory,
2643 .stack_argument_offset,
2644 => {
2645 if (ty.abiSize(self.target.*) <= 4) {
2646 const reg = try self.copyToTmpRegister(ty, mcv);
2647 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
2648 } else {
2649 return self.fail("TODO implement memcpy", .{});
2650 }
26222651 },
26232652 .stack_offset => |off| {
26242653 if (stack_offset == off)
26252654 return; // Copy stack variable to itself; nothing to do.
26262655
2627 const reg = try self.copyToTmpRegister(ty, mcv);
2628 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
2656 if (ty.abiSize(self.target.*) <= 4) {
2657 const reg = try self.copyToTmpRegister(ty, mcv);
2658 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
2659 } else {
2660 return self.fail("TODO implement memcpy", .{});
2661 }
26292662 },
26302663 }
26312664}
......@@ -2878,10 +2911,115 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
28782911 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
28792912 }
28802913 },
2914 .stack_argument_offset => |unadjusted_off| {
2915 // TODO: maybe addressing from sp instead of fp
2916 const abi_size = ty.abiSize(self.target.*);
2917 const adj_off = unadjusted_off + abi_size;
2918
2919 const tag: Mir.Inst.Tag = switch (abi_size) {
2920 1 => .ldrb_stack_argument,
2921 2 => .ldrh_stack_argument,
2922 4 => .ldr_stack_argument,
2923 else => unreachable,
2924 };
2925
2926 _ = try self.addInst(.{
2927 .tag = tag,
2928 .cond = .al,
2929 .data = .{ .r_stack_offset = .{
2930 .rt = reg,
2931 .stack_offset = @intCast(u32, adj_off),
2932 } },
2933 });
2934 },
28812935 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
28822936 }
28832937}
28842938
2939fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
2940 switch (mcv) {
2941 .dead => unreachable,
2942 .none, .unreach => return,
2943 .undef => {
2944 if (!self.wantSafety())
2945 return; // The already existing value will do just fine.
2946 // TODO Upgrade this to a memset call when we have that available.
2947 switch (ty.abiSize(self.target.*)) {
2948 1 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }),
2949 2 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }),
2950 4 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
2951 8 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
2952 else => return self.fail("TODO implement memset", .{}),
2953 }
2954 },
2955 .register => |reg| {
2956 const abi_size = ty.abiSize(self.target.*);
2957 const adj_off = stack_offset - abi_size;
2958
2959 switch (abi_size) {
2960 1, 4 => {
2961 const offset = if (math.cast(u12, adj_off)) |imm| blk: {
2962 break :blk Instruction.Offset.imm(imm);
2963 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
2964
2965 const tag: Mir.Inst.Tag = switch (abi_size) {
2966 1 => .strb,
2967 4 => .str,
2968 else => unreachable,
2969 };
2970
2971 _ = try self.addInst(.{
2972 .tag = tag,
2973 .cond = .al,
2974 .data = .{ .rr_offset = .{
2975 .rt = reg,
2976 .rn = .sp,
2977 .offset = .{ .offset = offset },
2978 } },
2979 });
2980 },
2981 2 => {
2982 const offset = if (adj_off <= math.maxInt(u8)) blk: {
2983 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
2984 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
2985
2986 _ = try self.addInst(.{
2987 .tag = .strh,
2988 .cond = .al,
2989 .data = .{ .rr_extra_offset = .{
2990 .rt = reg,
2991 .rn = .sp,
2992 .offset = .{ .offset = offset },
2993 } },
2994 });
2995 },
2996 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
2997 }
2998 },
2999 .immediate,
3000 .compare_flags_signed,
3001 .compare_flags_unsigned,
3002 .stack_offset,
3003 .memory,
3004 .stack_argument_offset,
3005 .embedded_in_code,
3006 => {
3007 if (ty.abiSize(self.target.*) <= 4) {
3008 const reg = try self.copyToTmpRegister(ty, mcv);
3009 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
3010 } else {
3011 return self.fail("TODO implement memcpy", .{});
3012 }
3013 },
3014 .ptr_stack_offset => {
3015 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
3016 },
3017 .ptr_embedded_in_code => {
3018 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
3019 },
3020 }
3021}
3022
28853023fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
28863024 const un_op = self.air.instructions.items(.data)[inst].un_op;
28873025 const result = try self.resolveInst(un_op);
......@@ -3002,27 +3140,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
30023140 }
30033141}
30043142
3005/// If the MCValue is an immediate, and it does not fit within this type,
3006/// we put it in a register.
3007/// A potential opportunity for future optimization here would be keeping track
3008/// of the fact that the instruction is available both as an immediate
3009/// and as a register.
3010fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
3011 const mcv = try self.resolveInst(operand);
3012 const ti = @typeInfo(T).Int;
3013 switch (mcv) {
3014 .immediate => |imm| {
3015 // This immediate is unsigned.
3016 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
3017 if (imm >= math.maxInt(U)) {
3018 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
3019 }
3020 },
3021 else => {},
3022 }
3023 return mcv;
3024}
3025
30263143fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
30273144 if (typed_value.val.isUndef())
30283145 return MCValue{ .undef = {} };
......@@ -3214,7 +3331,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
32143331 if (ty.abiAlignment(self.target.*) == 8)
32153332 nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8);
32163333
3217 result.args[i] = .{ .stack_offset = nsaa };
3334 result.args[i] = .{ .stack_argument_offset = nsaa };
32183335 nsaa += param_size;
32193336 }
32203337 }
src/arch/arm/Emit.zig+55
......@@ -30,6 +30,10 @@ prev_di_column: u32,
3030/// Relative to the beginning of `code`.
3131prev_di_pc: usize,
3232
33/// The amount of stack space consumed by all stack arguments as well
34/// as the saved callee-saved registers
35prologue_stack_space: u32,
36
3337/// The branch type of every branch
3438branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{},
3539/// For every forward branch, maps the target instruction to a list of
......@@ -102,6 +106,10 @@ pub fn emitMir(
102106 .str => try emit.mirLoadStore(inst),
103107 .strb => try emit.mirLoadStore(inst),
104108
109 .ldr_stack_argument => try emit.mirLoadStack(inst),
110 .ldrb_stack_argument => try emit.mirLoadStack(inst),
111 .ldrh_stack_argument => try emit.mirLoadStack(inst),
112
105113 .ldrh => try emit.mirLoadStoreExtra(inst),
106114 .strh => try emit.mirLoadStoreExtra(inst),
107115
......@@ -468,6 +476,53 @@ fn mirLoadStore(emit: *Emit, inst: Mir.Inst.Index) !void {
468476 }
469477}
470478
479fn mirLoadStack(emit: *Emit, inst: Mir.Inst.Index) !void {
480 const tag = emit.mir.instructions.items(.tag)[inst];
481 const cond = emit.mir.instructions.items(.cond)[inst];
482 const r_stack_offset = emit.mir.instructions.items(.data)[inst].r_stack_offset;
483
484 const raw_offset = emit.prologue_stack_space - r_stack_offset.stack_offset;
485 switch (tag) {
486 .ldr_stack_argument => {
487 const offset = if (raw_offset <= math.maxInt(u12)) blk: {
488 break :blk Instruction.Offset.imm(@intCast(u12, raw_offset));
489 } else return emit.fail("TODO mirLoadStack larger offsets", .{});
490
491 try emit.writeInstruction(Instruction.ldr(
492 cond,
493 r_stack_offset.rt,
494 .fp,
495 .{ .offset = offset },
496 ));
497 },
498 .ldrb_stack_argument => {
499 const offset = if (raw_offset <= math.maxInt(u12)) blk: {
500 break :blk Instruction.Offset.imm(@intCast(u12, raw_offset));
501 } else return emit.fail("TODO mirLoadStack larger offsets", .{});
502
503 try emit.writeInstruction(Instruction.ldrb(
504 cond,
505 r_stack_offset.rt,
506 .fp,
507 .{ .offset = offset },
508 ));
509 },
510 .ldrh_stack_argument => {
511 const offset = if (raw_offset <= math.maxInt(u8)) blk: {
512 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, raw_offset));
513 } else return emit.fail("TODO mirLoadStack larger offsets", .{});
514
515 try emit.writeInstruction(Instruction.ldrh(
516 cond,
517 r_stack_offset.rt,
518 .fp,
519 .{ .offset = offset },
520 ));
521 },
522 else => unreachable,
523 }
524}
525
471526fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void {
472527 const tag = emit.mir.instructions.items(.tag)[inst];
473528 const cond = emit.mir.instructions.items(.cond)[inst];
src/arch/arm/Mir.zig+13
......@@ -51,10 +51,16 @@ pub const Inst = struct {
5151 eor,
5252 /// Load Register
5353 ldr,
54 /// Load Register
55 ldr_stack_argument,
5456 /// Load Register Byte
5557 ldrb,
58 /// Load Register Byte
59 ldrb_stack_argument,
5660 /// Load Register Halfword
5761 ldrh,
62 /// Load Register Halfword
63 ldrh_stack_argument,
5864 /// Logical Shift Left
5965 lsl,
6066 /// Logical Shift Right
......@@ -124,6 +130,13 @@ pub const Inst = struct {
124130 ///
125131 /// Used by e.g. blx
126132 reg: Register,
133 /// A register and a stack offset
134 ///
135 /// Used by e.g. ldr_stack_argument
136 r_stack_offset: struct {
137 rt: Register,
138 stack_offset: u32,
139 },
127140 /// A register and a 16-bit unsigned immediate
128141 ///
129142 /// Used by e.g. movw
test/stage2/arm.zig+22-6
......@@ -72,6 +72,22 @@ pub fn addCases(ctx: *TestContext) !void {
7272 ,
7373 "Hello, World!\n",
7474 );
75
76 case.addCompareOutput(
77 \\pub fn main() void {
78 \\ assert(add(1, 2, 3, 4, 5, 6) == 21);
79 \\}
80 \\
81 \\fn add(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32) u32 {
82 \\ return a + b + c + d + e + f;
83 \\}
84 \\
85 \\pub fn assert(ok: bool) void {
86 \\ if (!ok) unreachable; // assertion failure
87 \\}
88 ,
89 "",
90 );
7591 }
7692
7793 {
......@@ -465,12 +481,12 @@ pub fn addCases(ctx: *TestContext) !void {
465481 \\ const j = i + d; // 110
466482 \\ const k = i + j; // 210
467483 \\ const l = k + c; // 217
468 \\ const m = l * d; // 2170
469 \\ const n = m + e; // 2184
470 \\ const o = n * f; // 52416
471 \\ const p = o + g; // 52454
472 \\ const q = p * h; // 3252148
473 \\ const r = q + i; // 3252248
484 \\ const m = l * d; // 2170
485 \\ const n = m + e; // 2184
486 \\ const o = n * f; // 52416
487 \\ const p = o + g; // 52454
488 \\ const q = p * h; // 3252148
489 \\ const r = q + i; // 3252248
474490 \\ const s = r * j; // 357747280
475491 \\ const t = s + k; // 357747490
476492 \\ break :blk t;