authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-04 19:49:18-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-04 19:49:18-04:00
logda596b7e4febc95ec5249c9f489166944fbe69b9
tree7aa66aea2c4c0894348c2cb50729c4ddecb735d9
parent6aa668e0206da8b9233bfb90a2ffe1f692bf18bd
parentfb58fb2d8dfa49b01594f341f262cb45958c5e23
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6255 from joachimschmidt557/stage2-arm

stage2 ARM: more instructions, return values, parameters

4 files changed, 642 insertions(+), 140 deletions(-)

src/codegen.zig+154-16
...@@ -570,6 +570,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -570,6 +570,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
570 try self.dbgSetEpilogueBegin();570 try self.dbgSetEpilogueBegin();
571 }571 }
572 },572 },
573 .arm => {
574 const cc = self.fn_type.fnCallingConvention();
575 if (cc != .Naked) {
576 // push {fp, lr}
577 // mov fp, sp
578 // sub sp, sp, #reloc
579 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32());
580 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32());
581 // TODO: prepare stack for local variables
582 // const backpatch_reloc = try self.code.addManyAsArray(4);
583
584 try self.dbgSetPrologueEnd();
585
586 try self.genBody(self.mod_fn.analysis.success);
587
588 // Backpatch stack offset
589 // const stack_end = self.max_end_stack;
590 // const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
591 // mem.writeIntLittle(u32, backpatch_reloc, Instruction.sub(.al, .sp, .sp, Instruction.Operand.imm()));
592
593 try self.dbgSetEpilogueBegin();
594
595 // mov sp, fp
596 // pop {fp, pc}
597 // TODO: return by jumping to this code, use relocations
598 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
599 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
600 } else {
601 try self.dbgSetPrologueEnd();
602 try self.genBody(self.mod_fn.analysis.success);
603 try self.dbgSetEpilogueBegin();
604 }
605 },
573 else => {606 else => {
574 try self.dbgSetPrologueEnd();607 try self.dbgSetPrologueEnd();
575 try self.genBody(self.mod_fn.analysis.success);608 try self.genBody(self.mod_fn.analysis.success);
...@@ -1461,7 +1494,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1461,7 +1494,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1461 }1494 }
1462 },1495 },
1463 .arm => {1496 .arm => {
1464 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});1497 for (info.args) |mc_arg, arg_i| {
1498 const arg = inst.args[arg_i];
1499 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1500
1501 switch (mc_arg) {
1502 .none => continue,
1503 .undef => unreachable,
1504 .immediate => unreachable,
1505 .unreach => unreachable,
1506 .dead => unreachable,
1507 .embedded_in_code => unreachable,
1508 .memory => unreachable,
1509 .compare_flags_signed => unreachable,
1510 .compare_flags_unsigned => unreachable,
1511 .register => |reg| {
1512 try self.genSetReg(arg.src, reg, arg_mcv);
1513 // TODO interact with the register allocator to mark the instruction as moved.
1514 },
1515 .stack_offset => {
1516 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
1517 },
1518 .ptr_stack_offset => {
1519 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1520 },
1521 .ptr_embedded_in_code => {
1522 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1523 },
1524 }
1525 }
14651526
1466 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1527 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1467 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1528 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
...@@ -1476,13 +1537,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1476,13 +1537,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1476 else1537 else
1477 unreachable;1538 unreachable;
14781539
1479 // TODO only works with leaf functions
1480 // at the moment, which works fine for
1481 // Hello World, but not for real code
1482 // of course. Add pushing lr to stack
1483 // and popping after call
1484 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });1540 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
1485 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());1541
1542 // TODO: add Instruction.supportedOn
1543 // function for ARM
1544 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
1545 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1546 } else {
1547 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32());
1548 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1549 }
1486 } else {1550 } else {
1487 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});1551 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1488 }1552 }
...@@ -1602,7 +1666,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1602,7 +1666,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1602 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());1666 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
1603 },1667 },
1604 .arm => {1668 .arm => {
1605 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());1669 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
1670 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
1671 // TODO: jump to the end with relocation
1672 // // Just add space for an instruction, patch this later
1673 // try self.code.resize(self.code.items.len + 4);
1674 // try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1606 },1675 },
1607 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1676 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
1608 }1677 }
...@@ -2214,14 +2283,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2214,14 +2283,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2214 // least amount of necessary instructions (use2283 // least amount of necessary instructions (use
2215 // more intelligent rotating)2284 // more intelligent rotating)
2216 if (x <= math.maxInt(u8)) {2285 if (x <= math.maxInt(u8)) {
2217 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());2286 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2218 return;2287 return;
2219 } else if (x <= math.maxInt(u16)) {2288 } else if (x <= math.maxInt(u16)) {
2220 // TODO Use movw Note: Not supported on2289 // TODO Use movw Note: Not supported on
2221 // all ARM targets!2290 // all ARM targets!
22222291
2223 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());2292 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2224 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());2293 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2225 } else if (x <= math.maxInt(u32)) {2294 } else if (x <= math.maxInt(u32)) {
2226 // TODO Use movw and movt Note: Not2295 // TODO Use movw and movt Note: Not
2227 // supported on all ARM targets! Also TODO2296 // supported on all ARM targets! Also TODO
...@@ -2233,20 +2302,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2233,20 +2302,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2233 // orr reg, reg, #0xbb, 242302 // orr reg, reg, #0xbb, 24
2234 // orr reg, reg, #0xcc, 162303 // orr reg, reg, #0xcc, 16
2235 // orr reg, reg, #0xdd, 82304 // orr reg, reg, #0xdd, 8
2236 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());2305 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2237 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());2306 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2238 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());2307 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2239 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());2308 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
2240 return;2309 return;
2241 } else {2310 } else {
2242 return self.fail(src, "ARM registers are 32-bit wide", .{});2311 return self.fail(src, "ARM registers are 32-bit wide", .{});
2243 }2312 }
2244 },2313 },
2314 .register => |src_reg| {
2315 // If the registers are the same, nothing to do.
2316 if (src_reg.id() == reg.id())
2317 return;
2318
2319 // mov reg, src_reg
2320 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none)).toU32());
2321 },
2245 .memory => |addr| {2322 .memory => |addr| {
2246 // The value is in memory at a hard-coded address.2323 // The value is in memory at a hard-coded address.
2247 // If the type is a pointer, it means the pointer address is at this memory location.2324 // If the type is a pointer, it means the pointer address is at this memory location.
2248 try self.genSetReg(src, reg, .{ .immediate = addr });2325 try self.genSetReg(src, reg, .{ .immediate = addr });
2249 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());2326 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
2250 },2327 },
2251 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),2328 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
2252 },2329 },
...@@ -2702,6 +2779,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2702,6 +2779,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2702 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),2779 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
2703 }2780 }
2704 },2781 },
2782 .arm => {
2783 switch (cc) {
2784 .Naked => {
2785 assert(result.args.len == 0);
2786 result.return_value = .{ .unreach = {} };
2787 result.stack_byte_count = 0;
2788 result.stack_align = 1;
2789 return result;
2790 },
2791 .Unspecified, .C => {
2792 // ARM Procedure Call Standard, Chapter 6.5
2793 var ncrn: usize = 0; // Next Core Register Number
2794 var nsaa: u32 = 0; // Next stacked argument address
2795
2796 for (param_types) |ty, i| {
2797 if (ty.abiAlignment(self.target.*) == 8) {
2798 // Round up NCRN to the next even number
2799 ncrn += ncrn % 2;
2800 }
2801
2802 const param_size = @intCast(u32, ty.abiSize(self.target.*));
2803 if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) {
2804 if (param_size <= 4) {
2805 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
2806 ncrn += 1;
2807 } else {
2808 return self.fail(src, "TODO MCValues with multiple registers", .{});
2809 }
2810 } else if (ncrn < 4 and nsaa == 0) {
2811 return self.fail(src, "TODO MCValues split between registers and stack", .{});
2812 } else {
2813 ncrn = 4;
2814 if (ty.abiAlignment(self.target.*) == 8) {
2815 if (nsaa % 8 != 0) {
2816 nsaa += 8 - (nsaa % 8);
2817 }
2818 }
2819
2820 result.args[i] = .{ .stack_offset = nsaa };
2821 nsaa += param_size;
2822 }
2823 }
2824
2825 result.stack_byte_count = nsaa;
2826 result.stack_align = 4;
2827 },
2828 else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}),
2829 }
2830 },
2705 else => if (param_types.len != 0)2831 else => if (param_types.len != 0)
2706 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),2832 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
2707 }2833 }
...@@ -2720,6 +2846,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2720,6 +2846,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2720 },2846 },
2721 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),2847 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
2722 },2848 },
2849 .arm => switch (cc) {
2850 .Naked => unreachable,
2851 .Unspecified, .C => {
2852 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2853 if (ret_ty_size <= 4) {
2854 result.return_value = .{ .register = c_abi_int_return_regs[0] };
2855 } else {
2856 return self.fail(src, "TODO support more return types for ARM backend", .{});
2857 }
2858 },
2859 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
2860 },
2723 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),2861 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
2724 }2862 }
2725 return result;2863 return result;
src/codegen/arm.zig+342-55
...@@ -113,6 +113,13 @@ test "Register.id" {...@@ -113,6 +113,13 @@ test "Register.id" {
113 testing.expectEqual(@as(u4, 15), Register.pc.id());113 testing.expectEqual(@as(u4, 15), Register.pc.id());
114}114}
115115
116/// Program status registers containing flags, mode bits and other
117/// vital information
118pub const Psr = enum {
119 cpsr,
120 spsr,
121};
122
116pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };123pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
117pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };124pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
118pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };125pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
...@@ -135,15 +142,26 @@ pub const Instruction = union(enum) {...@@ -135,15 +142,26 @@ pub const Instruction = union(enum) {
135 offset: u12,142 offset: u12,
136 rd: u4,143 rd: u4,
137 rn: u4,144 rn: u4,
138 l: u1,145 load_store: u1,
139 w: u1,146 write_back: u1,
140 b: u1,147 byte_word: u1,
141 u: u1,148 up_down: u1,
142 p: u1,149 pre_post: u1,
143 i: u1,150 imm: u1,
144 fixed: u2 = 0b01,151 fixed: u2 = 0b01,
145 cond: u4,152 cond: u4,
146 },153 },
154 BlockDataTransfer: packed struct {
155 register_list: u16,
156 rn: u4,
157 load_store: u1,
158 write_back: u1,
159 psr_or_user: u1,
160 up_down: u1,
161 pre_post: u1,
162 fixed: u3 = 0b100,
163 cond: u4,
164 },
147 Branch: packed struct {165 Branch: packed struct {
148 offset: u24,166 offset: u24,
149 link: u1,167 link: u1,
...@@ -235,14 +253,14 @@ pub const Instruction = union(enum) {...@@ -235,14 +253,14 @@ pub const Instruction = union(enum) {
235 rs: u4,253 rs: u4,
236 },254 },
237255
238 const Type = enum(u2) {256 pub const Type = enum(u2) {
239 LogicalLeft,257 logical_left,
240 LogicalRight,258 logical_right,
241 ArithmeticRight,259 arithmetic_right,
242 RotateRight,260 rotate_right,
243 };261 };
244262
245 const none = Shift{263 pub const none = Shift{
246 .Immediate = .{264 .Immediate = .{
247 .amount = 0,265 .amount = 0,
248 .typ = 0,266 .typ = 0,
...@@ -338,10 +356,32 @@ pub const Instruction = union(enum) {...@@ -338,10 +356,32 @@ pub const Instruction = union(enum) {
338 }356 }
339 };357 };
340358
359 /// Represents the register list operand to a block data transfer
360 /// instruction
361 pub const RegisterList = packed struct {
362 r0: bool = false,
363 r1: bool = false,
364 r2: bool = false,
365 r3: bool = false,
366 r4: bool = false,
367 r5: bool = false,
368 r6: bool = false,
369 r7: bool = false,
370 r8: bool = false,
371 r9: bool = false,
372 r10: bool = false,
373 r11: bool = false,
374 r12: bool = false,
375 r13: bool = false,
376 r14: bool = false,
377 r15: bool = false,
378 };
379
341 pub fn toU32(self: Instruction) u32 {380 pub fn toU32(self: Instruction) u32 {
342 return switch (self) {381 return switch (self) {
343 .DataProcessing => |v| @bitCast(u32, v),382 .DataProcessing => |v| @bitCast(u32, v),
344 .SingleDataTransfer => |v| @bitCast(u32, v),383 .SingleDataTransfer => |v| @bitCast(u32, v),
384 .BlockDataTransfer => |v| @bitCast(u32, v),
345 .Branch => |v| @bitCast(u32, v),385 .Branch => |v| @bitCast(u32, v),
346 .BranchExchange => |v| @bitCast(u32, v),386 .BranchExchange => |v| @bitCast(u32, v),
347 .SupervisorCall => |v| @bitCast(u32, v),387 .SupervisorCall => |v| @bitCast(u32, v),
...@@ -362,7 +402,7 @@ pub const Instruction = union(enum) {...@@ -362,7 +402,7 @@ pub const Instruction = union(enum) {
362 return Instruction{402 return Instruction{
363 .DataProcessing = .{403 .DataProcessing = .{
364 .cond = @enumToInt(cond),404 .cond = @enumToInt(cond),
365 .i = if (op2 == .Immediate) 1 else 0,405 .i = @boolToInt(op2 == .Immediate),
366 .opcode = @enumToInt(opcode),406 .opcode = @enumToInt(opcode),
367 .s = s,407 .s = s,
368 .rn = rn.id(),408 .rn = rn.id(),
...@@ -377,10 +417,10 @@ pub const Instruction = union(enum) {...@@ -377,10 +417,10 @@ pub const Instruction = union(enum) {
377 rd: Register,417 rd: Register,
378 rn: Register,418 rn: Register,
379 offset: Offset,419 offset: Offset,
380 pre_post: u1,420 pre_index: bool,
381 up_down: u1,421 positive: bool,
382 byte_word: u1,422 byte_word: u1,
383 writeback: u1,423 write_back: bool,
384 load_store: u1,424 load_store: u1,
385 ) Instruction {425 ) Instruction {
386 return Instruction{426 return Instruction{
...@@ -389,12 +429,36 @@ pub const Instruction = union(enum) {...@@ -389,12 +429,36 @@ pub const Instruction = union(enum) {
389 .rn = rn.id(),429 .rn = rn.id(),
390 .rd = rd.id(),430 .rd = rd.id(),
391 .offset = offset.toU12(),431 .offset = offset.toU12(),
392 .l = load_store,432 .load_store = load_store,
393 .w = writeback,433 .write_back = @boolToInt(write_back),
394 .b = byte_word,434 .byte_word = byte_word,
395 .u = up_down,435 .up_down = @boolToInt(positive),
396 .p = pre_post,436 .pre_post = @boolToInt(pre_index),
397 .i = if (offset == .Immediate) 0 else 1,437 .imm = @boolToInt(offset != .Immediate),
438 },
439 };
440 }
441
442 fn blockDataTransfer(
443 cond: Condition,
444 rn: Register,
445 reg_list: RegisterList,
446 pre_post: u1,
447 up_down: u1,
448 psr_or_user: u1,
449 write_back: bool,
450 load_store: u1,
451 ) Instruction {
452 return Instruction{
453 .BlockDataTransfer = .{
454 .register_list = @bitCast(u16, reg_list),
455 .rn = rn.id(),
456 .load_store = load_store,
457 .write_back = @boolToInt(write_back),
458 .psr_or_user = psr_or_user,
459 .up_down = up_down,
460 .pre_post = pre_post,
461 .cond = @enumToInt(cond),
398 },462 },
399 };463 };
400 }464 }
...@@ -442,36 +506,68 @@ pub const Instruction = union(enum) {...@@ -442,36 +506,68 @@ pub const Instruction = union(enum) {
442506
443 // Data processing507 // Data processing
444508
445 pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {509 pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
446 return dataProcessing(cond, .@"and", s, rd, rn, op2);510 return dataProcessing(cond, .@"and", 0, rd, rn, op2);
511 }
512
513 pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
514 return dataProcessing(cond, .@"and", 1, rd, rn, op2);
515 }
516
517 pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
518 return dataProcessing(cond, .eor, 0, rd, rn, op2);
519 }
520
521 pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
522 return dataProcessing(cond, .eor, 1, rd, rn, op2);
523 }
524
525 pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
526 return dataProcessing(cond, .sub, 0, rd, rn, op2);
527 }
528
529 pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
530 return dataProcessing(cond, .sub, 1, rd, rn, op2);
531 }
532
533 pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
534 return dataProcessing(cond, .rsb, 0, rd, rn, op2);
535 }
536
537 pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
538 return dataProcessing(cond, .rsb, 1, rd, rn, op2);
539 }
540
541 pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
542 return dataProcessing(cond, .add, 0, rd, rn, op2);
447 }543 }
448544
449 pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {545 pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
450 return dataProcessing(cond, .eor, s, rd, rn, op2);546 return dataProcessing(cond, .add, 1, rd, rn, op2);
451 }547 }
452548
453 pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {549 pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
454 return dataProcessing(cond, .sub, s, rd, rn, op2);550 return dataProcessing(cond, .adc, 0, rd, rn, op2);
455 }551 }
456552
457 pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {553 pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
458 return dataProcessing(cond, .rsb, s, rd, rn, op2);554 return dataProcessing(cond, .adc, 1, rd, rn, op2);
459 }555 }
460556
461 pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {557 pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
462 return dataProcessing(cond, .add, s, rd, rn, op2);558 return dataProcessing(cond, .sbc, 0, rd, rn, op2);
463 }559 }
464560
465 pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {561 pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
466 return dataProcessing(cond, .adc, s, rd, rn, op2);562 return dataProcessing(cond, .sbc, 1, rd, rn, op2);
467 }563 }
468564
469 pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {565 pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
470 return dataProcessing(cond, .sbc, s, rd, rn, op2);566 return dataProcessing(cond, .rsc, 0, rd, rn, op2);
471 }567 }
472568
473 pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {569 pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
474 return dataProcessing(cond, .rsc, s, rd, rn, op2);570 return dataProcessing(cond, .rsc, 1, rd, rn, op2);
475 }571 }
476572
477 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {573 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
...@@ -490,32 +586,115 @@ pub const Instruction = union(enum) {...@@ -490,32 +586,115 @@ pub const Instruction = union(enum) {
490 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);586 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
491 }587 }
492588
493 pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {589 pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
494 return dataProcessing(cond, .orr, s, rd, rn, op2);590 return dataProcessing(cond, .orr, 0, rd, rn, op2);
591 }
592
593 pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
594 return dataProcessing(cond, .orr, 1, rd, rn, op2);
595 }
596
597 pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
598 return dataProcessing(cond, .mov, 0, rd, .r0, op2);
495 }599 }
496600
497 pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {601 pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
498 return dataProcessing(cond, .mov, s, rd, .r0, op2);602 return dataProcessing(cond, .mov, 1, rd, .r0, op2);
499 }603 }
500604
501 pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {605 pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction {
502 return dataProcessing(cond, .bic, s, rd, rn, op2);606 return dataProcessing(cond, .bic, 0, rd, rn, op2);
503 }607 }
504608
505 pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {609 pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction {
506 return dataProcessing(cond, .mvn, s, rd, .r0, op2);610 return dataProcessing(cond, .bic, 1, rd, rn, op2);
611 }
612
613 pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
614 return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
615 }
616
617 pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
618 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
619 }
620
621 // PSR transfer
622
623 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
624 return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none));
507 }625 }
508626
509 // Single data transfer627 // Single data transfer
510628
511 pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {629 pub const OffsetArgs = struct {
512 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);630 pre_index: bool = true,
631 positive: bool = true,
632 offset: Offset,
633 write_back: bool = false,
634 };
635
636 pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
637 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1);
638 }
639
640 pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
641 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1);
642 }
643
644 pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
645 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0);
646 }
647
648 pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
649 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
650 }
651
652 // Block data transfer
653
654 pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
655 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1);
656 }
657
658 pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
659 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1);
513 }660 }
514661
515 pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {662 pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
516 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);663 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1);
517 }664 }
518665
666 pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
667 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1);
668 }
669
670 pub const ldmfa = ldmda;
671 pub const ldmea = ldmdb;
672 pub const ldmed = ldmib;
673 pub const ldmfd = ldmia;
674 pub const ldm = ldmia;
675
676 pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
677 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0);
678 }
679
680 pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
681 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0);
682 }
683
684 pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
685 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0);
686 }
687
688 pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
689 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0);
690 }
691
692 pub const stmed = stmda;
693 pub const stmfd = stmdb;
694 pub const stmfa = stmib;
695 pub const stmea = stmia;
696 pub const stm = stmia;
697
519 // Branch698 // Branch
520699
521 pub fn b(cond: Condition, offset: i24) Instruction {700 pub fn b(cond: Condition, offset: i24) Instruction {
...@@ -549,6 +728,58 @@ pub const Instruction = union(enum) {...@@ -549,6 +728,58 @@ pub const Instruction = union(enum) {
549 pub fn bkpt(imm: u16) Instruction {728 pub fn bkpt(imm: u16) Instruction {
550 return breakpoint(imm);729 return breakpoint(imm);
551 }730 }
731
732 // Aliases
733
734 pub fn pop(cond: Condition, args: anytype) Instruction {
735 if (@typeInfo(@TypeOf(args)) != .Struct) {
736 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
737 }
738
739 if (args.len < 1) {
740 @compileError("Expected at least one register");
741 } else if (args.len == 1) {
742 const reg = args[0];
743 return ldr(cond, reg, .sp, .{
744 .pre_index = false,
745 .positive = true,
746 .offset = Offset.imm(4),
747 .write_back = false,
748 });
749 } else {
750 var register_list: u16 = 0;
751 inline for (args) |arg| {
752 const reg = @as(Register, arg);
753 register_list |= @as(u16, 1) << reg.id();
754 }
755 return ldm(cond, .sp, true, @bitCast(RegisterList, register_list));
756 }
757 }
758
759 pub fn push(cond: Condition, args: anytype) Instruction {
760 if (@typeInfo(@TypeOf(args)) != .Struct) {
761 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
762 }
763
764 if (args.len < 1) {
765 @compileError("Expected at least one register");
766 } else if (args.len == 1) {
767 const reg = args[0];
768 return str(cond, reg, .sp, .{
769 .pre_index = true,
770 .positive = false,
771 .offset = Offset.imm(4),
772 .write_back = true,
773 });
774 } else {
775 var register_list: u16 = 0;
776 inline for (args) |arg| {
777 const reg = @as(Register, arg);
778 register_list |= @as(u16, 1) << reg.id();
779 }
780 return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
781 }
782 }
552};783};
553784
554test "serialize instructions" {785test "serialize instructions" {
...@@ -559,23 +790,31 @@ test "serialize instructions" {...@@ -559,23 +790,31 @@ test "serialize instructions" {
559790
560 const testcases = [_]Testcase{791 const testcases = [_]Testcase{
561 .{ // add r0, r0, r0792 .{ // add r0, r0, r0
562 .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),793 .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
563 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,794 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
564 },795 },
565 .{ // mov r4, r2796 .{ // mov r4, r2
566 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),797 .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
567 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,798 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
568 },799 },
569 .{ // mov r0, #42800 .{ // mov r0, #42
570 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),801 .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
571 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,802 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
572 },803 },
804 .{ // mrs r5, cpsr
805 .inst = Instruction.mrs(.al, .r5, .cpsr),
806 .expected = 0b1110_00010_0_001111_0101_000000000000,
807 },
573 .{ // ldr r0, [r2, #42]808 .{ // ldr r0, [r2, #42]
574 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),809 .inst = Instruction.ldr(.al, .r0, .r2, .{
810 .offset = Instruction.Offset.imm(42),
811 }),
575 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,812 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
576 },813 },
577 .{ // str r0, [r3]814 .{ // str r0, [r3]
578 .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),815 .inst = Instruction.str(.al, .r0, .r3, .{
816 .offset = Instruction.Offset.none,
817 }),
579 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,818 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
580 },819 },
581 .{ // b #12820 .{ // b #12
...@@ -598,6 +837,14 @@ test "serialize instructions" {...@@ -598,6 +837,14 @@ test "serialize instructions" {
598 .inst = Instruction.bkpt(42),837 .inst = Instruction.bkpt(42),
599 .expected = 0b1110_0001_0010_000000000010_0111_1010,838 .expected = 0b1110_0001_0010_000000000010_0111_1010,
600 },839 },
840 .{ // stmdb r9, {r0}
841 .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }),
842 .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
843 },
844 .{ // ldmea r4!, {r2, r5}
845 .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
846 .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
847 },
601 };848 };
602849
603 for (testcases) |case| {850 for (testcases) |case| {
...@@ -605,3 +852,43 @@ test "serialize instructions" {...@@ -605,3 +852,43 @@ test "serialize instructions" {
605 testing.expectEqual(case.expected, actual);852 testing.expectEqual(case.expected, actual);
606 }853 }
607}854}
855
856test "aliases" {
857 const Testcase = struct {
858 expected: Instruction,
859 actual: Instruction,
860 };
861
862 const testcases = [_]Testcase{
863 .{ // pop { r6 }
864 .actual = Instruction.pop(.al, .{.r6}),
865 .expected = Instruction.ldr(.al, .r6, .sp, .{
866 .pre_index = false,
867 .positive = true,
868 .offset = Instruction.Offset.imm(4),
869 .write_back = false,
870 }),
871 },
872 .{ // pop { r1, r5 }
873 .actual = Instruction.pop(.al, .{ .r1, .r5 }),
874 .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }),
875 },
876 .{ // push { r3 }
877 .actual = Instruction.push(.al, .{.r3}),
878 .expected = Instruction.str(.al, .r3, .sp, .{
879 .pre_index = true,
880 .positive = false,
881 .offset = Instruction.Offset.imm(4),
882 .write_back = true,
883 }),
884 },
885 .{ // push { r0, r2 }
886 .actual = Instruction.push(.al, .{ .r0, .r2 }),
887 .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
888 },
889 };
890
891 for (testcases) |case| {
892 testing.expectEqual(case.expected.toU32(), case.actual.toU32());
893 }
894}
test/stage2/arm.zig created+116
...@@ -0,0 +1,116 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4const linux_arm = std.zig.CrossTarget{
5 .cpu_arch = .arm,
6 .os_tag = .linux,
7};
8
9pub fn addCases(ctx: *TestContext) !void {
10 {
11 var case = ctx.exe("hello world", linux_arm);
12 // Regular old hello world
13 case.addCompareOutput(
14 \\export fn _start() noreturn {
15 \\ print();
16 \\ exit();
17 \\}
18 \\
19 \\fn print() void {
20 \\ asm volatile ("svc #0"
21 \\ :
22 \\ : [number] "{r7}" (4),
23 \\ [arg1] "{r0}" (1),
24 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
25 \\ [arg3] "{r2}" (14)
26 \\ : "memory"
27 \\ );
28 \\ return;
29 \\}
30 \\
31 \\fn exit() noreturn {
32 \\ asm volatile ("svc #0"
33 \\ :
34 \\ : [number] "{r7}" (1),
35 \\ [arg1] "{r0}" (0)
36 \\ : "memory"
37 \\ );
38 \\ unreachable;
39 \\}
40 ,
41 "Hello, World!\n",
42 );
43 }
44
45 {
46 var case = ctx.exe("parameters and return values", linux_arm);
47 // Testing simple parameters and return values
48 //
49 // TODO: The parameters to the asm statement in print() had to
50 // be in a specific order because otherwise the write to r0
51 // would overwrite the len parameter which resides in r0
52 case.addCompareOutput(
53 \\export fn _start() noreturn {
54 \\ print(id(14));
55 \\ exit();
56 \\}
57 \\
58 \\fn id(x: u32) u32 {
59 \\ return x;
60 \\}
61 \\
62 \\fn print(len: u32) void {
63 \\ asm volatile ("svc #0"
64 \\ :
65 \\ : [number] "{r7}" (4),
66 \\ [arg3] "{r2}" (len),
67 \\ [arg1] "{r0}" (1),
68 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n"))
69 \\ : "memory"
70 \\ );
71 \\ return;
72 \\}
73 \\
74 \\fn exit() noreturn {
75 \\ asm volatile ("svc #0"
76 \\ :
77 \\ : [number] "{r7}" (1),
78 \\ [arg1] "{r0}" (0)
79 \\ : "memory"
80 \\ );
81 \\ unreachable;
82 \\}
83 ,
84 "Hello, World!\n",
85 );
86 }
87
88 {
89 var case = ctx.exe("non-leaf functions", linux_arm);
90 // Testing non-leaf functions
91 case.addCompareOutput(
92 \\export fn _start() noreturn {
93 \\ foo();
94 \\ exit();
95 \\}
96 \\
97 \\fn foo() void {
98 \\ bar();
99 \\}
100 \\
101 \\fn bar() void {}
102 \\
103 \\fn exit() noreturn {
104 \\ asm volatile ("svc #0"
105 \\ :
106 \\ : [number] "{r7}" (1),
107 \\ [arg1] "{r0}" (0)
108 \\ : "memory"
109 \\ );
110 \\ unreachable;
111 \\}
112 ,
113 "",
114 );
115 }
116}
test/stage2/test.zig+30-69
...@@ -21,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{...@@ -21,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{
21 .os_tag = .linux,21 .os_tag = .linux,
22};22};
2323
24const linux_arm = std.zig.CrossTarget{
25 .cpu_arch = .arm,
26 .os_tag = .linux,
27};
28
29const wasi = std.zig.CrossTarget{24const wasi = std.zig.CrossTarget{
30 .cpu_arch = .wasm32,25 .cpu_arch = .wasm32,
31 .os_tag = .wasi,26 .os_tag = .wasi,
...@@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void {
35 try @import("zir.zig").addCases(ctx);30 try @import("zir.zig").addCases(ctx);
36 try @import("cbe.zig").addCases(ctx);31 try @import("cbe.zig").addCases(ctx);
37 try @import("spu-ii.zig").addCases(ctx);32 try @import("spu-ii.zig").addCases(ctx);
33 try @import("arm.zig").addCases(ctx);
3834
39 {35 {
40 var case = ctx.exe("hello world with updates", linux_x64);36 var case = ctx.exe("hello world with updates", linux_x64);
...@@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void {
76 \\ );72 \\ );
77 \\ unreachable;73 \\ unreachable;
78 \\}74 \\}
79 ,75 ,
80 "Hello, World!\n",76 "Hello, World!\n",
81 );77 );
82 // Now change the message only78 // Now change the message only
...@@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void {
108 \\ );104 \\ );
109 \\ unreachable;105 \\ unreachable;
110 \\}106 \\}
111 ,107 ,
112 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",108 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
113 );109 );
114 // Now we print it twice.110 // Now we print it twice.
...@@ -223,42 +219,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -223,42 +219,7 @@ pub fn addCases(ctx: *TestContext) !void {
223 \\ );219 \\ );
224 \\ unreachable;220 \\ unreachable;
225 \\}221 \\}
226 ,222 ,
227 "Hello, World!\n",
228 );
229 }
230
231 {
232 var case = ctx.exe("hello world", linux_arm);
233 // Regular old hello world
234 case.addCompareOutput(
235 \\export fn _start() noreturn {
236 \\ print();
237 \\ exit();
238 \\}
239 \\
240 \\fn print() void {
241 \\ asm volatile ("svc #0"
242 \\ :
243 \\ : [number] "{r7}" (4),
244 \\ [arg1] "{r0}" (1),
245 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
246 \\ [arg3] "{r2}" (14)
247 \\ : "memory"
248 \\ );
249 \\ return;
250 \\}
251 \\
252 \\fn exit() noreturn {
253 \\ asm volatile ("svc #0"
254 \\ :
255 \\ : [number] "{r7}" (1),
256 \\ [arg1] "{r0}" (0)
257 \\ : "memory"
258 \\ );
259 \\ unreachable;
260 \\}
261 ,
262 "Hello, World!\n",223 "Hello, World!\n",
263 );224 );
264 }225 }
...@@ -283,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -283,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void {
283 \\ );244 \\ );
284 \\ unreachable;245 \\ unreachable;
285 \\}246 \\}
286 ,247 ,
287 "Hello, World!\n",248 "Hello, World!\n",
288 );249 );
289 }250 }
...@@ -310,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -310,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void {
310 \\ );271 \\ );
311 \\ unreachable;272 \\ unreachable;
312 \\}273 \\}
313 ,274 ,
314 "",275 "",
315 );276 );
316 }277 }
...@@ -337,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -337,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void {
337 \\ );298 \\ );
338 \\ unreachable;299 \\ unreachable;
339 \\}300 \\}
340 ,301 ,
341 "",302 "",
342 );303 );
343 }304 }
...@@ -368,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -368,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void {
368 \\ );329 \\ );
369 \\ unreachable;330 \\ unreachable;
370 \\}331 \\}
371 ,332 ,
372 "",333 "",
373 );334 );
374335
...@@ -401,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -401,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void {
401 \\ );362 \\ );
402 \\ unreachable;363 \\ unreachable;
403 \\}364 \\}
404 ,365 ,
405 "",366 "",
406 );367 );
407368
...@@ -437,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -437,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void {
437 \\ );398 \\ );
438 \\ unreachable;399 \\ unreachable;
439 \\}400 \\}
440 ,401 ,
441 "",402 "",
442 );403 );
443404
...@@ -474,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -474,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void {
474 \\ );435 \\ );
475 \\ unreachable;436 \\ unreachable;
476 \\}437 \\}
477 ,438 ,
478 "",439 "",
479 );440 );
480441
...@@ -504,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -504,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void {
504 \\ );465 \\ );
505 \\ unreachable;466 \\ unreachable;
506 \\}467 \\}
507 ,468 ,
508 "",469 "",
509 );470 );
510471
...@@ -538,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -538,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void {
538 \\ );499 \\ );
539 \\ unreachable;500 \\ unreachable;
540 \\}501 \\}
541 ,502 ,
542 "",503 "",
543 );504 );
544505
...@@ -562,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -562,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void {
562 \\ );523 \\ );
563 \\ unreachable;524 \\ unreachable;
564 \\}525 \\}
565 ,526 ,
566 "",527 "",
567 );528 );
568529
...@@ -601,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -601,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void {
601 \\ );562 \\ );
602 \\ unreachable;563 \\ unreachable;
603 \\}564 \\}
604 ,565 ,
605 "hello\nhello\nhello\nhello\n",566 "hello\nhello\nhello\nhello\n",
606 );567 );
607568
...@@ -638,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -638,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void {
638 \\ );599 \\ );
639 \\ unreachable;600 \\ unreachable;
640 \\}601 \\}
641 ,602 ,
642 "",603 "",
643 );604 );
644605
...@@ -680,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -680,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void {
680 \\ );641 \\ );
681 \\ unreachable;642 \\ unreachable;
682 \\}643 \\}
683 ,644 ,
684 "",645 "",
685 );646 );
686647
...@@ -732,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -732,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void {
732 \\ );693 \\ );
733 \\ unreachable;694 \\ unreachable;
734 \\}695 \\}
735 ,696 ,
736 "",697 "",
737 );698 );
738699
...@@ -794,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -794,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void {
794 \\ );755 \\ );
795 \\ unreachable;756 \\ unreachable;
796 \\}757 \\}
797 ,758 ,
798 "",759 "",
799 );760 );
800761
...@@ -827,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -827,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void {
827 \\ );788 \\ );
828 \\ unreachable;789 \\ unreachable;
829 \\}790 \\}
830 ,791 ,
831 "",792 "",
832 );793 );
833794
...@@ -859,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -859,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void {
859 \\ );820 \\ );
860 \\ unreachable;821 \\ unreachable;
861 \\}822 \\}
862 ,823 ,
863 "",824 "",
864 );825 );
865826
...@@ -884,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -884,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void {
884 \\ );845 \\ );
885 \\ unreachable;846 \\ unreachable;
886 \\}847 \\}
887 ,848 ,
888 "",849 "",
889 );850 );
890851
...@@ -910,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -910,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void {
910 \\ );871 \\ );
911 \\ unreachable;872 \\ unreachable;
912 \\}873 \\}
913 ,874 ,
914 "",875 "",
915 );876 );
916877
...@@ -943,7 +904,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -943,7 +904,7 @@ pub fn addCases(ctx: *TestContext) !void {
943 \\ );904 \\ );
944 \\ unreachable;905 \\ unreachable;
945 \\}906 \\}
946 ,907 ,
947 "hello\nhello\nhello\nhello\nhello\n",908 "hello\nhello\nhello\nhello\nhello\n",
948 );909 );
949 }910 }
...@@ -1000,7 +961,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1000,7 +961,7 @@ pub fn addCases(ctx: *TestContext) !void {
1000 \\ bar();961 \\ bar();
1001 \\}962 \\}
1002 \\fn bar() void {}963 \\fn bar() void {}
1003 ,964 ,
1004 "42\n",965 "42\n",
1005 );966 );
1006967
...@@ -1018,7 +979,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1018,7 +979,7 @@ pub fn addCases(ctx: *TestContext) !void {
1018 \\ bar();979 \\ bar();
1019 \\}980 \\}
1020 \\fn bar() void {}981 \\fn bar() void {}
1021 ,982 ,
1022 "42\n",983 "42\n",
1023 );984 );
1024985
...@@ -1034,10 +995,10 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1034,10 +995,10 @@ pub fn addCases(ctx: *TestContext) !void {
1034 \\ bar();995 \\ bar();
1035 \\}996 \\}
1036 \\fn bar() void {}997 \\fn bar() void {}
1037 ,998 ,
1038 // This is what you get when you take the bits of the IEE-754999 // This is what you get when you take the bits of the IEE-754
1039 // representation of 42.0 and reinterpret them as an unsigned1000 // representation of 42.0 and reinterpret them as an unsigned
1040 // integer. Guess that's a bug in wasmtime.1001 // integer. Guess that's a bug in wasmtime.
1041 "1109917696\n",1002 "1109917696\n",
1042 );1003 );
1043 }1004 }