| author | |
| committer | |
| log | da596b7e4febc95ec5249c9f489166944fbe69b9 |
| tree | 7aa66aea2c4c0894348c2cb50729c4ddecb735d9 |
| parent | 6aa668e0206da8b9233bfb90a2ffe1f692bf18bd |
| parent | fb58fb2d8dfa49b01594f341f262cb45958c5e23 |
| signature |
stage2 ARM: more instructions, return values, parameters4 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 | 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 | 606 | else => { |
| 574 | 607 | try self.dbgSetPrologueEnd(); |
| 575 | 608 | try self.genBody(self.mod_fn.analysis.success); |
| ... | ... | @@ -1461,7 +1494,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1461 | 1494 | } |
| 1462 | 1495 | }, |
| 1463 | 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 | } | |
| 1465 | 1526 | |
| 1466 | 1527 | if (inst.func.cast(ir.Inst.Constant)) |func_inst| { |
| 1467 | 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 | 1537 | else |
| 1477 | 1538 | unreachable; |
| 1478 | 1539 | |
| 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 | 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 | 1550 | } else { |
| 1487 | 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 | 1666 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32()); |
| 1603 | 1667 | }, |
| 1604 | 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 | 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 | 2283 | // least amount of necessary instructions (use |
| 2215 | 2284 | // more intelligent rotating) |
| 2216 | 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 | 2287 | return; |
| 2219 | 2288 | } else if (x <= math.maxInt(u16)) { |
| 2220 | 2289 | // TODO Use movw Note: Not supported on |
| 2221 | 2290 | // all ARM targets! |
| 2222 | 2291 | |
| 2223 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, 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()); | |
| 2292 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).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 | 2294 | } else if (x <= math.maxInt(u32)) { |
| 2226 | 2295 | // TODO Use movw and movt Note: Not |
| 2227 | 2296 | // supported on all ARM targets! Also TODO |
| ... | ... | @@ -2233,20 +2302,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2233 | 2302 | // orr reg, reg, #0xbb, 24 |
| 2234 | 2303 | // orr reg, reg, #0xcc, 16 |
| 2235 | 2304 | // 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()); | |
| 2237 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, 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()); | |
| 2239 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32()); | |
| 2305 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | |
| 2306 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); | |
| 2307 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).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 | 2309 | return; |
| 2241 | 2310 | } else { |
| 2242 | 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 | 2322 | .memory => |addr| { |
| 2246 | 2323 | // The value is in memory at a hard-coded address. |
| 2247 | 2324 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 2248 | 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 | 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 | 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 | 2831 | else => if (param_types.len != 0) |
| 2706 | 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 | 2846 | }, |
| 2721 | 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 | 2861 | else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}), |
| 2724 | 2862 | } |
| 2725 | 2863 | return result; |
src/codegen/arm.zig+342-55| ... | ... | @@ -113,6 +113,13 @@ test "Register.id" { |
| 113 | 113 | testing.expectEqual(@as(u4, 15), Register.pc.id()); |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | /// Program status registers containing flags, mode bits and other | |
| 117 | /// vital information | |
| 118 | pub const Psr = enum { | |
| 119 | cpsr, | |
| 120 | spsr, | |
| 121 | }; | |
| 122 | ||
| 116 | 123 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; |
| 117 | 124 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; |
| 118 | 125 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; |
| ... | ... | @@ -135,15 +142,26 @@ pub const Instruction = union(enum) { |
| 135 | 142 | offset: u12, |
| 136 | 143 | rd: u4, |
| 137 | 144 | rn: u4, |
| 138 | l: u1, | |
| 139 | w: u1, | |
| 140 | b: u1, | |
| 141 | u: u1, | |
| 142 | p: u1, | |
| 143 | i: u1, | |
| 145 | load_store: u1, | |
| 146 | write_back: u1, | |
| 147 | byte_word: u1, | |
| 148 | up_down: u1, | |
| 149 | pre_post: u1, | |
| 150 | imm: u1, | |
| 144 | 151 | fixed: u2 = 0b01, |
| 145 | 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 | 165 | Branch: packed struct { |
| 148 | 166 | offset: u24, |
| 149 | 167 | link: u1, |
| ... | ... | @@ -235,14 +253,14 @@ pub const Instruction = union(enum) { |
| 235 | 253 | rs: u4, |
| 236 | 254 | }, |
| 237 | 255 | |
| 238 | const Type = enum(u2) { | |
| 239 | LogicalLeft, | |
| 240 | LogicalRight, | |
| 241 | ArithmeticRight, | |
| 242 | RotateRight, | |
| 256 | pub const Type = enum(u2) { | |
| 257 | logical_left, | |
| 258 | logical_right, | |
| 259 | arithmetic_right, | |
| 260 | rotate_right, | |
| 243 | 261 | }; |
| 244 | 262 | |
| 245 | const none = Shift{ | |
| 263 | pub const none = Shift{ | |
| 246 | 264 | .Immediate = .{ |
| 247 | 265 | .amount = 0, |
| 248 | 266 | .typ = 0, |
| ... | ... | @@ -338,10 +356,32 @@ pub const Instruction = union(enum) { |
| 338 | 356 | } |
| 339 | 357 | }; |
| 340 | 358 | |
| 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 | 380 | pub fn toU32(self: Instruction) u32 { |
| 342 | 381 | return switch (self) { |
| 343 | 382 | .DataProcessing => |v| @bitCast(u32, v), |
| 344 | 383 | .SingleDataTransfer => |v| @bitCast(u32, v), |
| 384 | .BlockDataTransfer => |v| @bitCast(u32, v), | |
| 345 | 385 | .Branch => |v| @bitCast(u32, v), |
| 346 | 386 | .BranchExchange => |v| @bitCast(u32, v), |
| 347 | 387 | .SupervisorCall => |v| @bitCast(u32, v), |
| ... | ... | @@ -362,7 +402,7 @@ pub const Instruction = union(enum) { |
| 362 | 402 | return Instruction{ |
| 363 | 403 | .DataProcessing = .{ |
| 364 | 404 | .cond = @enumToInt(cond), |
| 365 | .i = if (op2 == .Immediate) 1 else 0, | |
| 405 | .i = @boolToInt(op2 == .Immediate), | |
| 366 | 406 | .opcode = @enumToInt(opcode), |
| 367 | 407 | .s = s, |
| 368 | 408 | .rn = rn.id(), |
| ... | ... | @@ -377,10 +417,10 @@ pub const Instruction = union(enum) { |
| 377 | 417 | rd: Register, |
| 378 | 418 | rn: Register, |
| 379 | 419 | offset: Offset, |
| 380 | pre_post: u1, | |
| 381 | up_down: u1, | |
| 420 | pre_index: bool, | |
| 421 | positive: bool, | |
| 382 | 422 | byte_word: u1, |
| 383 | writeback: u1, | |
| 423 | write_back: bool, | |
| 384 | 424 | load_store: u1, |
| 385 | 425 | ) Instruction { |
| 386 | 426 | return Instruction{ |
| ... | ... | @@ -389,12 +429,36 @@ pub const Instruction = union(enum) { |
| 389 | 429 | .rn = rn.id(), |
| 390 | 430 | .rd = rd.id(), |
| 391 | 431 | .offset = offset.toU12(), |
| 392 | .l = load_store, | |
| 393 | .w = writeback, | |
| 394 | .b = byte_word, | |
| 395 | .u = up_down, | |
| 396 | .p = pre_post, | |
| 397 | .i = if (offset == .Immediate) 0 else 1, | |
| 432 | .load_store = load_store, | |
| 433 | .write_back = @boolToInt(write_back), | |
| 434 | .byte_word = byte_word, | |
| 435 | .up_down = @boolToInt(positive), | |
| 436 | .pre_post = @boolToInt(pre_index), | |
| 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 | 506 | |
| 443 | 507 | // Data processing |
| 444 | 508 | |
| 445 | pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 446 | return dataProcessing(cond, .@"and", s, rd, rn, op2); | |
| 509 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 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 | } |
| 448 | 544 | |
| 449 | pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 450 | return dataProcessing(cond, .eor, s, rd, rn, op2); | |
| 545 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 546 | return dataProcessing(cond, .add, 1, rd, rn, op2); | |
| 451 | 547 | } |
| 452 | 548 | |
| 453 | pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 454 | return dataProcessing(cond, .sub, s, rd, rn, op2); | |
| 549 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 550 | return dataProcessing(cond, .adc, 0, rd, rn, op2); | |
| 455 | 551 | } |
| 456 | 552 | |
| 457 | pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 458 | return dataProcessing(cond, .rsb, s, rd, rn, op2); | |
| 553 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 554 | return dataProcessing(cond, .adc, 1, rd, rn, op2); | |
| 459 | 555 | } |
| 460 | 556 | |
| 461 | pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 462 | return dataProcessing(cond, .add, s, rd, rn, op2); | |
| 557 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 558 | return dataProcessing(cond, .sbc, 0, rd, rn, op2); | |
| 463 | 559 | } |
| 464 | 560 | |
| 465 | pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 466 | return dataProcessing(cond, .adc, s, rd, rn, op2); | |
| 561 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 562 | return dataProcessing(cond, .sbc, 1, rd, rn, op2); | |
| 467 | 563 | } |
| 468 | 564 | |
| 469 | pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 470 | return dataProcessing(cond, .sbc, s, rd, rn, op2); | |
| 565 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 566 | return dataProcessing(cond, .rsc, 0, rd, rn, op2); | |
| 471 | 567 | } |
| 472 | 568 | |
| 473 | pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 474 | return dataProcessing(cond, .rsc, s, rd, rn, op2); | |
| 569 | pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 570 | return dataProcessing(cond, .rsc, 1, rd, rn, op2); | |
| 475 | 571 | } |
| 476 | 572 | |
| 477 | 573 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { |
| ... | ... | @@ -490,32 +586,115 @@ pub const Instruction = union(enum) { |
| 490 | 586 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); |
| 491 | 587 | } |
| 492 | 588 | |
| 493 | pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 494 | return dataProcessing(cond, .orr, s, rd, rn, op2); | |
| 589 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 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 | } |
| 496 | 600 | |
| 497 | pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | |
| 498 | return dataProcessing(cond, .mov, s, rd, .r0, op2); | |
| 601 | pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 602 | return dataProcessing(cond, .mov, 1, rd, .r0, op2); | |
| 499 | 603 | } |
| 500 | 604 | |
| 501 | pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | |
| 502 | return dataProcessing(cond, .bic, s, rd, rn, op2); | |
| 605 | pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 606 | return dataProcessing(cond, .bic, 0, rd, rn, op2); | |
| 503 | 607 | } |
| 504 | 608 | |
| 505 | pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | |
| 506 | return dataProcessing(cond, .mvn, s, rd, .r0, op2); | |
| 609 | pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 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 | } |
| 508 | 626 | |
| 509 | 627 | // Single data transfer |
| 510 | 628 | |
| 511 | pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | |
| 512 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); | |
| 629 | pub const OffsetArgs = struct { | |
| 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 | } |
| 514 | 661 | |
| 515 | pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | |
| 516 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); | |
| 662 | pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 663 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); | |
| 517 | 664 | } |
| 518 | 665 | |
| 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 | 698 | // Branch |
| 520 | 699 | |
| 521 | 700 | pub fn b(cond: Condition, offset: i24) Instruction { |
| ... | ... | @@ -549,6 +728,58 @@ pub const Instruction = union(enum) { |
| 549 | 728 | pub fn bkpt(imm: u16) Instruction { |
| 550 | 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 | }; |
| 553 | 784 | |
| 554 | 785 | test "serialize instructions" { |
| ... | ... | @@ -559,23 +790,31 @@ test "serialize instructions" { |
| 559 | 790 | |
| 560 | 791 | const testcases = [_]Testcase{ |
| 561 | 792 | .{ // 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 | 794 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, |
| 564 | 795 | }, |
| 565 | 796 | .{ // 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 | 798 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, |
| 568 | 799 | }, |
| 569 | 800 | .{ // 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 | 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 | 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 | 812 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, |
| 576 | 813 | }, |
| 577 | 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 | 818 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, |
| 580 | 819 | }, |
| 581 | 820 | .{ // b #12 |
| ... | ... | @@ -598,6 +837,14 @@ test "serialize instructions" { |
| 598 | 837 | .inst = Instruction.bkpt(42), |
| 599 | 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 | }; |
| 602 | 849 | |
| 603 | 850 | for (testcases) |case| { |
| ... | ... | @@ -605,3 +852,43 @@ test "serialize instructions" { |
| 605 | 852 | testing.expectEqual(case.expected, actual); |
| 606 | 853 | } |
| 607 | 854 | } |
| 855 | ||
| 856 | test "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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | |
| 3 | ||
| 4 | const linux_arm = std.zig.CrossTarget{ | |
| 5 | .cpu_arch = .arm, | |
| 6 | .os_tag = .linux, | |
| 7 | }; | |
| 8 | ||
| 9 | pub 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 | 21 | .os_tag = .linux, |
| 22 | 22 | }; |
| 23 | 23 | |
| 24 | const linux_arm = std.zig.CrossTarget{ | |
| 25 | .cpu_arch = .arm, | |
| 26 | .os_tag = .linux, | |
| 27 | }; | |
| 28 | ||
| 29 | 24 | const wasi = std.zig.CrossTarget{ |
| 30 | 25 | .cpu_arch = .wasm32, |
| 31 | 26 | .os_tag = .wasi, |
| ... | ... | @@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 35 | 30 | try @import("zir.zig").addCases(ctx); |
| 36 | 31 | try @import("cbe.zig").addCases(ctx); |
| 37 | 32 | try @import("spu-ii.zig").addCases(ctx); |
| 33 | try @import("arm.zig").addCases(ctx); | |
| 38 | 34 | |
| 39 | 35 | { |
| 40 | 36 | var case = ctx.exe("hello world with updates", linux_x64); |
| ... | ... | @@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 76 | 72 | \\ ); |
| 77 | 73 | \\ unreachable; |
| 78 | 74 | \\} |
| 79 | , | |
| 75 | , | |
| 80 | 76 | "Hello, World!\n", |
| 81 | 77 | ); |
| 82 | 78 | // Now change the message only |
| ... | ... | @@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 108 | 104 | \\ ); |
| 109 | 105 | \\ unreachable; |
| 110 | 106 | \\} |
| 111 | , | |
| 107 | , | |
| 112 | 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 | 110 | // Now we print it twice. |
| ... | ... | @@ -223,42 +219,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 223 | 219 | \\ ); |
| 224 | 220 | \\ unreachable; |
| 225 | 221 | \\} |
| 226 | , | |
| 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 | , | |
| 222 | , | |
| 262 | 223 | "Hello, World!\n", |
| 263 | 224 | ); |
| 264 | 225 | } |
| ... | ... | @@ -283,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 283 | 244 | \\ ); |
| 284 | 245 | \\ unreachable; |
| 285 | 246 | \\} |
| 286 | , | |
| 247 | , | |
| 287 | 248 | "Hello, World!\n", |
| 288 | 249 | ); |
| 289 | 250 | } |
| ... | ... | @@ -310,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 310 | 271 | \\ ); |
| 311 | 272 | \\ unreachable; |
| 312 | 273 | \\} |
| 313 | , | |
| 274 | , | |
| 314 | 275 | "", |
| 315 | 276 | ); |
| 316 | 277 | } |
| ... | ... | @@ -337,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 337 | 298 | \\ ); |
| 338 | 299 | \\ unreachable; |
| 339 | 300 | \\} |
| 340 | , | |
| 301 | , | |
| 341 | 302 | "", |
| 342 | 303 | ); |
| 343 | 304 | } |
| ... | ... | @@ -368,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 368 | 329 | \\ ); |
| 369 | 330 | \\ unreachable; |
| 370 | 331 | \\} |
| 371 | , | |
| 332 | , | |
| 372 | 333 | "", |
| 373 | 334 | ); |
| 374 | 335 | |
| ... | ... | @@ -401,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 401 | 362 | \\ ); |
| 402 | 363 | \\ unreachable; |
| 403 | 364 | \\} |
| 404 | , | |
| 365 | , | |
| 405 | 366 | "", |
| 406 | 367 | ); |
| 407 | 368 | |
| ... | ... | @@ -437,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 437 | 398 | \\ ); |
| 438 | 399 | \\ unreachable; |
| 439 | 400 | \\} |
| 440 | , | |
| 401 | , | |
| 441 | 402 | "", |
| 442 | 403 | ); |
| 443 | 404 | |
| ... | ... | @@ -474,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 474 | 435 | \\ ); |
| 475 | 436 | \\ unreachable; |
| 476 | 437 | \\} |
| 477 | , | |
| 438 | , | |
| 478 | 439 | "", |
| 479 | 440 | ); |
| 480 | 441 | |
| ... | ... | @@ -504,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 504 | 465 | \\ ); |
| 505 | 466 | \\ unreachable; |
| 506 | 467 | \\} |
| 507 | , | |
| 468 | , | |
| 508 | 469 | "", |
| 509 | 470 | ); |
| 510 | 471 | |
| ... | ... | @@ -538,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 538 | 499 | \\ ); |
| 539 | 500 | \\ unreachable; |
| 540 | 501 | \\} |
| 541 | , | |
| 502 | , | |
| 542 | 503 | "", |
| 543 | 504 | ); |
| 544 | 505 | |
| ... | ... | @@ -562,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 562 | 523 | \\ ); |
| 563 | 524 | \\ unreachable; |
| 564 | 525 | \\} |
| 565 | , | |
| 526 | , | |
| 566 | 527 | "", |
| 567 | 528 | ); |
| 568 | 529 | |
| ... | ... | @@ -601,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 601 | 562 | \\ ); |
| 602 | 563 | \\ unreachable; |
| 603 | 564 | \\} |
| 604 | , | |
| 565 | , | |
| 605 | 566 | "hello\nhello\nhello\nhello\n", |
| 606 | 567 | ); |
| 607 | 568 | |
| ... | ... | @@ -638,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 638 | 599 | \\ ); |
| 639 | 600 | \\ unreachable; |
| 640 | 601 | \\} |
| 641 | , | |
| 602 | , | |
| 642 | 603 | "", |
| 643 | 604 | ); |
| 644 | 605 | |
| ... | ... | @@ -680,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 680 | 641 | \\ ); |
| 681 | 642 | \\ unreachable; |
| 682 | 643 | \\} |
| 683 | , | |
| 644 | , | |
| 684 | 645 | "", |
| 685 | 646 | ); |
| 686 | 647 | |
| ... | ... | @@ -732,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 732 | 693 | \\ ); |
| 733 | 694 | \\ unreachable; |
| 734 | 695 | \\} |
| 735 | , | |
| 696 | , | |
| 736 | 697 | "", |
| 737 | 698 | ); |
| 738 | 699 | |
| ... | ... | @@ -794,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 794 | 755 | \\ ); |
| 795 | 756 | \\ unreachable; |
| 796 | 757 | \\} |
| 797 | , | |
| 758 | , | |
| 798 | 759 | "", |
| 799 | 760 | ); |
| 800 | 761 | |
| ... | ... | @@ -827,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 827 | 788 | \\ ); |
| 828 | 789 | \\ unreachable; |
| 829 | 790 | \\} |
| 830 | , | |
| 791 | , | |
| 831 | 792 | "", |
| 832 | 793 | ); |
| 833 | 794 | |
| ... | ... | @@ -859,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 859 | 820 | \\ ); |
| 860 | 821 | \\ unreachable; |
| 861 | 822 | \\} |
| 862 | , | |
| 823 | , | |
| 863 | 824 | "", |
| 864 | 825 | ); |
| 865 | 826 | |
| ... | ... | @@ -884,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 884 | 845 | \\ ); |
| 885 | 846 | \\ unreachable; |
| 886 | 847 | \\} |
| 887 | , | |
| 848 | , | |
| 888 | 849 | "", |
| 889 | 850 | ); |
| 890 | 851 | |
| ... | ... | @@ -910,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 910 | 871 | \\ ); |
| 911 | 872 | \\ unreachable; |
| 912 | 873 | \\} |
| 913 | , | |
| 874 | , | |
| 914 | 875 | "", |
| 915 | 876 | ); |
| 916 | 877 | |
| ... | ... | @@ -943,7 +904,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 943 | 904 | \\ ); |
| 944 | 905 | \\ unreachable; |
| 945 | 906 | \\} |
| 946 | , | |
| 907 | , | |
| 947 | 908 | "hello\nhello\nhello\nhello\nhello\n", |
| 948 | 909 | ); |
| 949 | 910 | } |
| ... | ... | @@ -1000,7 +961,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1000 | 961 | \\ bar(); |
| 1001 | 962 | \\} |
| 1002 | 963 | \\fn bar() void {} |
| 1003 | , | |
| 964 | , | |
| 1004 | 965 | "42\n", |
| 1005 | 966 | ); |
| 1006 | 967 | |
| ... | ... | @@ -1018,7 +979,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1018 | 979 | \\ bar(); |
| 1019 | 980 | \\} |
| 1020 | 981 | \\fn bar() void {} |
| 1021 | , | |
| 982 | , | |
| 1022 | 983 | "42\n", |
| 1023 | 984 | ); |
| 1024 | 985 | |
| ... | ... | @@ -1034,10 +995,10 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1034 | 995 | \\ bar(); |
| 1035 | 996 | \\} |
| 1036 | 997 | \\fn bar() void {} |
| 1037 | , | |
| 1038 | // This is what you get when you take the bits of the IEE-754 | |
| 1039 | // representation of 42.0 and reinterpret them as an unsigned | |
| 1040 | // integer. Guess that's a bug in wasmtime. | |
| 998 | , | |
| 999 | // This is what you get when you take the bits of the IEE-754 | |
| 1000 | // representation of 42.0 and reinterpret them as an unsigned | |
| 1001 | // integer. Guess that's a bug in wasmtime. | |
| 1041 | 1002 | "1109917696\n", |
| 1042 | 1003 | ); |
| 1043 | 1004 | } |