| 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,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 | } | ||
| 1465 | 1526 | ||
| 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 | else | 1537 | else |
| 1477 | unreachable; | 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 | 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 (use | 2283 | // 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 on | 2289 | // TODO Use movw Note: Not supported on |
| 2221 | // all ARM targets! | 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()); | 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: Not | 2295 | // TODO Use movw and movt Note: Not |
| 2227 | // supported on all ARM targets! Also TODO | 2296 | // 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, 24 | 2302 | // orr reg, reg, #0xbb, 24 |
| 2234 | // orr reg, reg, #0xcc, 16 | 2303 | // orr reg, reg, #0xcc, 16 |
| 2235 | // orr reg, reg, #0xdd, 8 | 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()); | 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 | } |
| 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 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; | 123 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; |
| 117 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | 124 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; |
| 118 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | 125 | pub 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 | }, |
| 237 | 255 | ||
| 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 | }; |
| 244 | 262 | ||
| 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 | }; |
| 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 | 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) { |
| 442 | 506 | ||
| 443 | // Data processing | 507 | // Data processing |
| 444 | 508 | ||
| 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 | } |
| 448 | 544 | ||
| 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 | } |
| 452 | 548 | ||
| 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 | } |
| 456 | 552 | ||
| 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 | } |
| 460 | 556 | ||
| 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 | } |
| 464 | 560 | ||
| 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 | } |
| 468 | 564 | ||
| 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 | } |
| 472 | 568 | ||
| 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 | } |
| 476 | 572 | ||
| 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 | } |
| 492 | 588 | ||
| 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 | } |
| 496 | 600 | ||
| 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 | } |
| 500 | 604 | ||
| 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 | } |
| 504 | 608 | ||
| 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 | } |
| 508 | 626 | ||
| 509 | // Single data transfer | 627 | // Single data transfer |
| 510 | 628 | ||
| 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 | } |
| 514 | 661 | ||
| 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 | } |
| 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 | // Branch | 698 | // Branch |
| 520 | 699 | ||
| 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 | }; |
| 553 | 784 | ||
| 554 | test "serialize instructions" { | 785 | test "serialize instructions" { |
| ... | @@ -559,23 +790,31 @@ test "serialize instructions" { | ... | @@ -559,23 +790,31 @@ test "serialize instructions" { |
| 559 | 790 | ||
| 560 | const testcases = [_]Testcase{ | 791 | const testcases = [_]Testcase{ |
| 561 | .{ // add r0, r0, r0 | 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 | .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, r2 | 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 | .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, #42 | 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 | .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 #12 | 820 | .{ // 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 | }; |
| 602 | 849 | ||
| 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 | |||
| 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,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{ |
| 21 | .os_tag = .linux, | 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 | const wasi = std.zig.CrossTarget{ | 24 | const 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); | ||
| 38 | 34 | ||
| 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 only | 78 | // 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 | ); |
| 374 | 335 | ||
| ... | @@ -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 | ); |
| 407 | 368 | ||
| ... | @@ -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 | ); |
| 443 | 404 | ||
| ... | @@ -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 | ); |
| 480 | 441 | ||
| ... | @@ -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 | ); |
| 510 | 471 | ||
| ... | @@ -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 | ); |
| 544 | 505 | ||
| ... | @@ -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 | ); |
| 568 | 529 | ||
| ... | @@ -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 | ); |
| 607 | 568 | ||
| ... | @@ -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 | ); |
| 644 | 605 | ||
| ... | @@ -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 | ); |
| 686 | 647 | ||
| ... | @@ -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 | ); |
| 738 | 699 | ||
| ... | @@ -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 | ); |
| 800 | 761 | ||
| ... | @@ -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 | ); |
| 833 | 794 | ||
| ... | @@ -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 | ); |
| 865 | 826 | ||
| ... | @@ -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 | ); |
| 890 | 851 | ||
| ... | @@ -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 | ); |
| 916 | 877 | ||
| ... | @@ -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 | ); |
| 1006 | 967 | ||
| ... | @@ -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 | ); |
| 1024 | 985 | ||
| ... | @@ -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-754 | 999 | // 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 | 1000 | // 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 | } |