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,...@@ -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
...@@ -123,10 +125,12 @@ const MCValue = union(enum) {...@@ -123,10 +125,12 @@ const MCValue = union(enum) {
123 /// The value is in the compare flags assuming a signed operation,125 /// The value is in the compare flags assuming a signed operation,
124 /// with this operator applied on top of it.126 /// with this operator applied on top of it.
125 compare_flags_signed: math.CompareOperator,127 compare_flags_signed: math.CompareOperator,
128 /// The value is a function argument passed via the stack.
129 stack_argument_offset: u32,
126130
127 fn isMemory(mcv: MCValue) bool {131 fn isMemory(mcv: MCValue) bool {
128 return switch (mcv) {132 return switch (mcv) {
129 .embedded_in_code, .memory, .stack_offset => true,133 .embedded_in_code, .memory, .stack_offset, .stack_argument_offset => true,
130 else => false,134 else => false,
131 };135 };
132 }136 }
...@@ -152,6 +156,7 @@ const MCValue = union(enum) {...@@ -152,6 +156,7 @@ const MCValue = union(enum) {
152 .ptr_stack_offset,156 .ptr_stack_offset,
153 .ptr_embedded_in_code,157 .ptr_embedded_in_code,
154 .undef,158 .undef,
159 .stack_argument_offset,
155 => false,160 => false,
156161
157 .register,162 .register,
...@@ -302,6 +307,7 @@ pub fn generate(...@@ -302,6 +307,7 @@ pub fn generate(
302 .prev_di_pc = 0,307 .prev_di_pc = 0,
303 .prev_di_line = module_fn.lbrace_line,308 .prev_di_line = module_fn.lbrace_line,
304 .prev_di_column = module_fn.lbrace_column,309 .prev_di_column = module_fn.lbrace_column,
310 .prologue_stack_space = call_info.stack_byte_count + function.saved_regs_stack_space,
305 };311 };
306 defer emit.deinit();312 defer emit.deinit();
307313
...@@ -387,11 +393,14 @@ fn gen(self: *Self) !void {...@@ -387,11 +393,14 @@ fn gen(self: *Self) !void {
387 .r11 = true, // fp393 .r11 = true, // fp
388 .r14 = true, // lr394 .r14 = true, // lr
389 };395 };
396 self.saved_regs_stack_space = 8;
390 inline for (callee_preserved_regs) |reg| {397 inline for (callee_preserved_regs) |reg| {
391 if (self.register_manager.isRegAllocated(reg)) {398 if (self.register_manager.isRegAllocated(reg)) {
392 @field(saved_regs, @tagName(reg)) = true;399 @field(saved_regs, @tagName(reg)) = true;
400 self.saved_regs_stack_space += 4;
393 }401 }
394 }402 }
403
395 self.mir_instructions.set(push_reloc, .{404 self.mir_instructions.set(push_reloc, .{
396 .tag = .push,405 .tag = .push,
397 .cond = .al,406 .cond = .al,
...@@ -399,9 +408,10 @@ fn gen(self: *Self) !void {...@@ -399,9 +408,10 @@ fn gen(self: *Self) !void {
399 });408 });
400409
401 // Backpatch stack offset410 // Backpatch stack offset
402 const stack_end = self.max_end_stack;411 const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
403 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);412 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
404 if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| {413 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
414 if (Instruction.Operand.fromU32(stack_size)) |op| {
405 self.mir_instructions.set(sub_reloc, .{415 self.mir_instructions.set(sub_reloc, .{
406 .tag = .sub,416 .tag = .sub,
407 .cond = .al,417 .cond = .al,
...@@ -1256,6 +1266,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1256,6 +1266,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1256 .stack_offset => {1266 .stack_offset => {
1257 return self.fail("TODO implement loading from MCValue.stack_offset", .{});1267 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1258 },1268 },
1269 .stack_argument_offset => {
1270 return self.fail("TODO implement loading from MCValue.stack_argument_offset", .{});
1271 },
1259 }1272 }
1260}1273}
12611274
...@@ -1297,6 +1310,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {...@@ -1297,6 +1310,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1297 .dead => unreachable,1310 .dead => unreachable,
1298 .compare_flags_unsigned => unreachable,1311 .compare_flags_unsigned => unreachable,
1299 .compare_flags_signed => unreachable,1312 .compare_flags_signed => unreachable,
1313 .stack_argument_offset => unreachable,
1300 .immediate => |imm| {1314 .immediate => |imm| {
1301 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);1315 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
1302 },1316 },
...@@ -1367,6 +1381,7 @@ fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {...@@ -1367,6 +1381,7 @@ fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
1367 },1381 },
1368 .register => true,1382 .register => true,
1369 .stack_offset,1383 .stack_offset,
1384 .stack_argument_offset,
1370 .embedded_in_code,1385 .embedded_in_code,
1371 .memory,1386 .memory,
1372 => true,1387 => true,
...@@ -1533,6 +1548,7 @@ fn genArmBinOpCode(...@@ -1533,6 +1548,7 @@ fn genArmBinOpCode(
1533 .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?,1548 .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?,
1534 .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),1549 .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
1535 .stack_offset,1550 .stack_offset,
1551 .stack_argument_offset,
1536 .embedded_in_code,1552 .embedded_in_code,
1537 .memory,1553 .memory,
1538 => unreachable,1554 => unreachable,
...@@ -1749,6 +1765,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {...@@ -1749,6 +1765,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
1749 .none => {},1765 .none => {},
1750 }1766 }
1751 },1767 },
1768 .stack_argument_offset => return self.fail("TODO genArgDbgInfo for stack_argument_offset", .{}),
1752 else => {},1769 else => {},
1753 }1770 }
1754}1771}
...@@ -1814,6 +1831,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1814,6 +1831,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1814 var info = try self.resolveCallingConventionValues(fn_ty);1831 var info = try self.resolveCallingConventionValues(fn_ty);
1815 defer info.deinit(self);1832 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
1817 // Due to incremental compilation, how function calls are generated depends1837 // Due to incremental compilation, how function calls are generated depends
1818 // on linking.1838 // on linking.
1819 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {1839 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 {...@@ -1836,9 +1856,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1836 try self.register_manager.getReg(reg, null);1856 try self.register_manager.getReg(reg, null);
1837 try self.genSetReg(arg_ty, reg, arg_mcv);1857 try self.genSetReg(arg_ty, reg, arg_mcv);
1838 },1858 },
1839 .stack_offset => {1859 .stack_offset => unreachable,
1840 return self.fail("TODO implement calling with parameters in memory", .{});1860 .stack_argument_offset => |offset| try self.genSetStackArgument(
1841 },1861 arg_ty,
1862 info.stack_byte_count - offset,
1863 arg_mcv,
1864 ),
1842 .ptr_stack_offset => {1865 .ptr_stack_offset => {
1843 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});1866 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1844 },1867 },
...@@ -2616,16 +2639,26 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -2616,16 +2639,26 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2616 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),2639 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
2617 }2640 }
2618 },2641 },
2619 .memory => |vaddr| {2642 .memory,
2620 _ = vaddr;2643 .stack_argument_offset,
2621 return self.fail("TODO implement set stack variable from memory vaddr", .{});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 }
2622 },2651 },
2623 .stack_offset => |off| {2652 .stack_offset => |off| {
2624 if (stack_offset == off)2653 if (stack_offset == off)
2625 return; // Copy stack variable to itself; nothing to do.2654 return; // Copy stack variable to itself; nothing to do.
26262655
2627 const reg = try self.copyToTmpRegister(ty, mcv);2656 if (ty.abiSize(self.target.*) <= 4) {
2628 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });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 }
2629 },2662 },
2630 }2663 }
2631}2664}
...@@ -2878,10 +2911,115 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2878,10 +2911,115 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2878 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),2911 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
2879 }2912 }
2880 },2913 },
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 },
2881 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),2935 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
2882 }2936 }
2883}2937}
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
2885fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {3023fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
2886 const un_op = self.air.instructions.items(.data)[inst].un_op;3024 const un_op = self.air.instructions.items(.data)[inst].un_op;
2887 const result = try self.resolveInst(un_op);3025 const result = try self.resolveInst(un_op);
...@@ -3002,27 +3140,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {...@@ -3002,27 +3140,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
3002 }3140 }
3003}3141}
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
3026fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {3143fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
3027 if (typed_value.val.isUndef())3144 if (typed_value.val.isUndef())
3028 return MCValue{ .undef = {} };3145 return MCValue{ .undef = {} };
...@@ -3214,7 +3331,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -3214,7 +3331,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
3214 if (ty.abiAlignment(self.target.*) == 8)3331 if (ty.abiAlignment(self.target.*) == 8)
3215 nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8);3332 nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8);
32163333
3217 result.args[i] = .{ .stack_offset = nsaa };3334 result.args[i] = .{ .stack_argument_offset = nsaa };
3218 nsaa += param_size;3335 nsaa += param_size;
3219 }3336 }
3220 }3337 }
src/arch/arm/Emit.zig+55
...@@ -30,6 +30,10 @@ prev_di_column: u32,...@@ -30,6 +30,10 @@ prev_di_column: u32,
30/// Relative to the beginning of `code`.30/// Relative to the beginning of `code`.
31prev_di_pc: usize,31prev_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
33/// The branch type of every branch37/// The branch type of every branch
34branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{},38branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{},
35/// For every forward branch, maps the target instruction to a list of39/// For every forward branch, maps the target instruction to a list of
...@@ -102,6 +106,10 @@ pub fn emitMir(...@@ -102,6 +106,10 @@ pub fn emitMir(
102 .str => try emit.mirLoadStore(inst),106 .str => try emit.mirLoadStore(inst),
103 .strb => try emit.mirLoadStore(inst),107 .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
105 .ldrh => try emit.mirLoadStoreExtra(inst),113 .ldrh => try emit.mirLoadStoreExtra(inst),
106 .strh => try emit.mirLoadStoreExtra(inst),114 .strh => try emit.mirLoadStoreExtra(inst),
107115
...@@ -468,6 +476,53 @@ fn mirLoadStore(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -468,6 +476,53 @@ fn mirLoadStore(emit: *Emit, inst: Mir.Inst.Index) !void {
468 }476 }
469}477}
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
471fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void {526fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void {
472 const tag = emit.mir.instructions.items(.tag)[inst];527 const tag = emit.mir.instructions.items(.tag)[inst];
473 const cond = emit.mir.instructions.items(.cond)[inst];528 const cond = emit.mir.instructions.items(.cond)[inst];
src/arch/arm/Mir.zig+13
...@@ -51,10 +51,16 @@ pub const Inst = struct {...@@ -51,10 +51,16 @@ pub const Inst = struct {
51 eor,51 eor,
52 /// Load Register52 /// Load Register
53 ldr,53 ldr,
54 /// Load Register
55 ldr_stack_argument,
54 /// Load Register Byte56 /// Load Register Byte
55 ldrb,57 ldrb,
58 /// Load Register Byte
59 ldrb_stack_argument,
56 /// Load Register Halfword60 /// Load Register Halfword
57 ldrh,61 ldrh,
62 /// Load Register Halfword
63 ldrh_stack_argument,
58 /// Logical Shift Left64 /// Logical Shift Left
59 lsl,65 lsl,
60 /// Logical Shift Right66 /// Logical Shift Right
...@@ -124,6 +130,13 @@ pub const Inst = struct {...@@ -124,6 +130,13 @@ pub const Inst = struct {
124 ///130 ///
125 /// Used by e.g. blx131 /// Used by e.g. blx
126 reg: Register,132 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 },
127 /// A register and a 16-bit unsigned immediate140 /// A register and a 16-bit unsigned immediate
128 ///141 ///
129 /// Used by e.g. movw142 /// Used by e.g. movw
test/stage2/arm.zig+22-6
...@@ -72,6 +72,22 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -72,6 +72,22 @@ pub fn addCases(ctx: *TestContext) !void {
72 ,72 ,
73 "Hello, World!\n",73 "Hello, World!\n",
74 );74 );
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 );
75 }91 }
7692
77 {93 {
...@@ -465,12 +481,12 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -465,12 +481,12 @@ pub fn addCases(ctx: *TestContext) !void {
465 \\ const j = i + d; // 110481 \\ const j = i + d; // 110
466 \\ const k = i + j; // 210482 \\ const k = i + j; // 210
467 \\ const l = k + c; // 217483 \\ const l = k + c; // 217
468 \\ const m = l * d; // 2170 484 \\ const m = l * d; // 2170
469 \\ const n = m + e; // 2184 485 \\ const n = m + e; // 2184
470 \\ const o = n * f; // 52416 486 \\ const o = n * f; // 52416
471 \\ const p = o + g; // 52454 487 \\ const p = o + g; // 52454
472 \\ const q = p * h; // 3252148 488 \\ const q = p * h; // 3252148
473 \\ const r = q + i; // 3252248 489 \\ const r = q + i; // 3252248
474 \\ const s = r * j; // 357747280490 \\ const s = r * j; // 357747280
475 \\ const t = s + k; // 357747490491 \\ const t = s + k; // 357747490
476 \\ break :blk t;492 \\ break :blk t;