From 0a54f04dbc4ce9a0a1d1b5fe9d1a5a502030b47e Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 31 Aug 2020 12:01:06 +0200 Subject: stage2 ARM: start adding more instructions, return values, parameters --- src/codegen/arm.zig | 236 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 190 insertions(+), 46 deletions(-) (limited to 'src/codegen/arm.zig') diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 05178ea7d3..096012a1e8 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -113,6 +113,13 @@ test "Register.id" { testing.expectEqual(@as(u4, 15), Register.pc.id()); } +/// Program status registers containing flags, mode bits and other +/// vital information +pub const Psr = enum { + cpsr, + spsr, +}; + pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; @@ -135,15 +142,26 @@ pub const Instruction = union(enum) { offset: u12, rd: u4, rn: u4, - l: u1, - w: u1, - b: u1, - u: u1, - p: u1, - i: u1, + load_store: u1, + write_back: u1, + byte_word: u1, + up_down: u1, + pre_post: u1, + imm: u1, fixed: u2 = 0b01, cond: u4, }, + BlockDataTransfer: packed struct { + register_list: u16, + rn: u4, + load_store: u1, + write_back: u1, + psr_or_user: u1, + up_down: u1, + pre_post: u1, + fixed: u3 = 0b100, + cond: u4, + }, Branch: packed struct { offset: u24, link: u1, @@ -235,14 +253,14 @@ pub const Instruction = union(enum) { rs: u4, }, - const Type = enum(u2) { - LogicalLeft, - LogicalRight, - ArithmeticRight, - RotateRight, + pub const Type = enum(u2) { + logical_left, + logical_right, + arithmetic_right, + rotate_right, }; - const none = Shift{ + pub const none = Shift{ .Immediate = .{ .amount = 0, .typ = 0, @@ -338,10 +356,32 @@ pub const Instruction = union(enum) { } }; + /// Represents the register list operand to a block data transfer + /// instruction + pub const RegisterList = packed struct { + r0: bool = false, + r1: bool = false, + r2: bool = false, + r3: bool = false, + r4: bool = false, + r5: bool = false, + r6: bool = false, + r7: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + r12: bool = false, + r13: bool = false, + r14: bool = false, + r15: bool = false, + }; + pub fn toU32(self: Instruction) u32 { return switch (self) { .DataProcessing => |v| @bitCast(u32, v), .SingleDataTransfer => |v| @bitCast(u32, v), + .BlockDataTransfer => |v| @bitCast(u32, v), .Branch => |v| @bitCast(u32, v), .BranchExchange => |v| @bitCast(u32, v), .SupervisorCall => |v| @bitCast(u32, v), @@ -380,7 +420,7 @@ pub const Instruction = union(enum) { pre_post: u1, up_down: u1, byte_word: u1, - writeback: u1, + write_back: u1, load_store: u1, ) Instruction { return Instruction{ @@ -389,12 +429,36 @@ pub const Instruction = union(enum) { .rn = rn.id(), .rd = rd.id(), .offset = offset.toU12(), - .l = load_store, - .w = writeback, - .b = byte_word, - .u = up_down, - .p = pre_post, - .i = if (offset == .Immediate) 0 else 1, + .load_store = load_store, + .write_back = write_back, + .byte_word = byte_word, + .up_down = up_down, + .pre_post = pre_post, + .imm = if (offset == .Immediate) 0 else 1, + }, + }; + } + + fn blockDataTransfer( + cond: Condition, + rn: Register, + reg_list: RegisterList, + pre_post: u1, + up_down: u1, + psr_or_user: u1, + write_back: u1, + load_store: u1, + ) Instruction { + return Instruction{ + .BlockDataTransfer = .{ + .register_list = @bitCast(u16, reg_list), + .rn = rn.id(), + .load_store = load_store, + .write_back = write_back, + .psr_or_user = psr_or_user, + .up_down = up_down, + .pre_post = pre_post, + .cond = @enumToInt(cond), }, }; } @@ -442,36 +506,68 @@ pub const Instruction = union(enum) { // Data processing - pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .@"and", s, rd, rn, op2); + pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .@"and", 0, rd, rn, op2); + } + + pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .@"and", 1, rd, rn, op2); + } + + pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .eor, 0, rd, rn, op2); + } + + pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .eor, 1, rd, rn, op2); + } + + pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .sub, 0, rd, rn, op2); + } + + pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .sub, 1, rd, rn, op2); + } + + pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .rsb, 0, rd, rn, op2); + } + + pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .rsb, 1, rd, rn, op2); } - pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .eor, s, rd, rn, op2); + pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .add, 0, rd, rn, op2); } - pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sub, s, rd, rn, op2); + pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .add, 1, rd, rn, op2); } - pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsb, s, rd, rn, op2); + pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .adc, 0, rd, rn, op2); } - pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .add, s, rd, rn, op2); + pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .adc, 1, rd, rn, op2); } - pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .adc, s, rd, rn, op2); + pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .sbc, 0, rd, rn, op2); } - pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sbc, s, rd, rn, op2); + pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .sbc, 1, rd, rn, op2); } - pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsc, s, rd, rn, op2); + pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .rsc, 0, rd, rn, op2); + } + + pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .rsc, 1, rd, rn, op2); } pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { @@ -490,20 +586,42 @@ pub const Instruction = union(enum) { return dataProcessing(cond, .cmn, 1, .r0, rn, op2); } - pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .orr, s, rd, rn, op2); + pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .orr, 0, rd, rn, op2); + } + + pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { + return dataProcessing(cond, .orr, 1, rd, rn, op2); + } + + pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { + return dataProcessing(cond, .mov, 0, rd, .r0, op2); + } + + pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { + return dataProcessing(cond, .mov, 1, rd, .r0, op2); } - pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mov, s, rd, .r0, op2); + pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction { + return dataProcessing(cond, .bic, 0, rd, rn, op2); } - pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .bic, s, rd, rn, op2); + pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction { + return dataProcessing(cond, .bic, 1, rd, rn, op2); } - pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mvn, s, rd, .r0, op2); + pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { + return dataProcessing(cond, .mvn, 0, rd, .r0, op2); + } + + pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { + return dataProcessing(cond, .mvn, 1, rd, .r0, op2); + } + + // PSR transfer + + pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { + return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none)); } // Single data transfer @@ -512,10 +630,28 @@ pub const Instruction = union(enum) { return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); } + pub fn ldrb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { + return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 1); + } + pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); } + pub fn strb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { + return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 0); + } + + // Block data transfer + + pub fn ldm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 1); + } + + pub fn stm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 0); + } + // Branch pub fn b(cond: Condition, offset: i24) Instruction { @@ -559,17 +695,21 @@ test "serialize instructions" { const testcases = [_]Testcase{ .{ // add r0, r0, r0 - .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), + .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, }, .{ // mov r4, r2 - .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), + .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, }, .{ // mov r0, #42 - .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)), + .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, }, + .{ // mrs r5, cpsr + .inst = Instruction.mrs(.al, .r5, .cpsr), + .expected = 0b1110_00010_0_001111_0101_000000000000, + }, .{ // ldr r0, [r2, #42] .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, @@ -598,6 +738,10 @@ test "serialize instructions" { .inst = Instruction.bkpt(42), .expected = 0b1110_0001_0010_000000000010_0111_1010, }, + .{ // stmfd r9, {r0} + .inst = Instruction.stm(.al, .r9, .{ .r0 = true }), + .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, + }, }; for (testcases) |case| { -- cgit v1.2.3 From 35b228630cf1972868fe820baeb41a09801f2fbb Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 24 Sep 2020 19:10:12 +0200 Subject: stage2 ARM: Add stm, ldm variants and misc. additions --- src/codegen.zig | 41 ++++++++++++++++++++++++++++++++++----- src/codegen/arm.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 84 insertions(+), 13 deletions(-) (limited to 'src/codegen/arm.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 9bb53c5cdc..541dced068 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -570,6 +570,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.dbgSetEpilogueBegin(); } }, + .arm => { + const cc = self.fn_type.fnCallingConvention(); + if (cc != .Naked) { + // push {fp, lr} + // mov fp, sp + // sub sp, sp, #reloc + // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32()); + // const backpatch_reloc = try self.code.addManyAsArray(4); + + try self.dbgSetPrologueEnd(); + + try self.genBody(self.mod_fn.analysis.success); + + // Backpatch stack offset + // const stack_end = self.max_end_stack; + // const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); + // mem.writeIntLittle(u32, backpatch_reloc, Instruction.sub(.al, .sp, .sp, Instruction.Operand.imm())); + + try self.dbgSetEpilogueBegin(); + + // mov sp, fp + // pop {fp, pc} + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32()); + } else { + try self.dbgSetPrologueEnd(); + try self.genBody(self.mod_fn.analysis.success); + try self.dbgSetEpilogueBegin(); + } + }, else => { try self.dbgSetPrologueEnd(); try self.genBody(self.mod_fn.analysis.success); @@ -1504,13 +1533,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else unreachable; - // TODO only works with leaf functions - // at the moment, which works fine for - // Hello World, but not for real code - // of course. Add pushing lr to stack - // and popping after call try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr }); + // TODO: add Instruction.supportedOn + // function for ARM if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32()); } else { @@ -1636,6 +1662,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .arm => { mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32()); + // // Just add space for an instruction, patch this later + // try self.code.resize(self.code.items.len + 4); + // try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); }, else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), } @@ -2771,6 +2800,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { return self.fail(src, "TODO MCValues with multiple registers", .{}); } + } else if (ncrn < 4 and nsaa == 0) { + return self.fail(src, "TODO MCValues split between registers and stack", .{}); } else { ncrn = 4; if (ty.abiAlignment(self.target.*) == 8) { diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 096012a1e8..5acc2ab431 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -446,7 +446,7 @@ pub const Instruction = union(enum) { pre_post: u1, up_down: u1, psr_or_user: u1, - write_back: u1, + write_back: bool, load_store: u1, ) Instruction { return Instruction{ @@ -454,7 +454,7 @@ pub const Instruction = union(enum) { .register_list = @bitCast(u16, reg_list), .rn = rn.id(), .load_store = load_store, - .write_back = write_back, + .write_back = if (write_back) 1 else 0, .psr_or_user = psr_or_user, .up_down = up_down, .pre_post = pre_post, @@ -644,14 +644,50 @@ pub const Instruction = union(enum) { // Block data transfer - pub fn ldm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 1); + pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1); } - pub fn stm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 0); + pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1); + } + + pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); + } + + pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1); + } + + pub const ldmfa = ldmda; + pub const ldmea = ldmdb; + pub const ldmed = ldmib; + pub const ldmfd = ldmia; + pub const ldm = ldmia; + + pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0); } + pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0); + } + + pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0); + } + + pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { + return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0); + } + + pub const stmed = stmda; + pub const stmfd = stmdb; + pub const stmfa = stmib; + pub const stmea = stmia; + pub const stm = stmia; + // Branch pub fn b(cond: Condition, offset: i24) Instruction { @@ -738,10 +774,14 @@ test "serialize instructions" { .inst = Instruction.bkpt(42), .expected = 0b1110_0001_0010_000000000010_0111_1010, }, - .{ // stmfd r9, {r0} - .inst = Instruction.stm(.al, .r9, .{ .r0 = true }), + .{ // stmdb r9, {r0} + .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }), .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, }, + .{ // ldmea r4!, {r2, r5} + .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), + .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, + }, }; for (testcases) |case| { -- cgit v1.2.3 From cfe486e3887ee5ea622234439fc7bd41fb2774bc Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 25 Sep 2020 22:19:40 +0200 Subject: stage2 ARM: Add push, pop alias instructions; non-leaf functions Non-leaf functions now work. Combined with simple parameters and return types, this allows more complicated programs than Hello World to be correctly compiled. --- src/codegen.zig | 14 ++++-- src/codegen/arm.zig | 135 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 129 insertions(+), 20 deletions(-) (limited to 'src/codegen/arm.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 541dced068..20da342db7 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -576,7 +576,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // push {fp, lr} // mov fp, sp // sub sp, sp, #reloc - // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32()); + // TODO: prepare stack for local variables // const backpatch_reloc = try self.code.addManyAsArray(4); try self.dbgSetPrologueEnd(); @@ -592,7 +594,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // mov sp, fp // pop {fp, pc} - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32()); + // TODO: return by jumping to this code, use relocations + // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32()); + // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32()); } else { try self.dbgSetPrologueEnd(); try self.genBody(self.mod_fn.analysis.success); @@ -1661,7 +1665,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32()); }, .arm => { - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32()); + // TODO: jump to the end with relocation // // Just add space for an instruction, patch this later // try self.code.resize(self.code.items.len + 4); // try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); @@ -2316,7 +2322,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. try self.genSetReg(src, reg, .{ .immediate = addr }); - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); }, else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}), }, diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 5acc2ab431..bf13244f4f 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -417,10 +417,10 @@ pub const Instruction = union(enum) { rd: Register, rn: Register, offset: Offset, - pre_post: u1, - up_down: u1, + pre_index: bool, + positive: bool, byte_word: u1, - write_back: u1, + write_back: bool, load_store: u1, ) Instruction { return Instruction{ @@ -430,10 +430,10 @@ pub const Instruction = union(enum) { .rd = rd.id(), .offset = offset.toU12(), .load_store = load_store, - .write_back = write_back, + .write_back = if (write_back) 1 else 0, .byte_word = byte_word, - .up_down = up_down, - .pre_post = pre_post, + .up_down = if (positive) 1 else 0, + .pre_post = if (pre_index) 1 else 0, .imm = if (offset == .Immediate) 0 else 1, }, }; @@ -626,20 +626,27 @@ pub const Instruction = union(enum) { // Single data transfer - pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { - return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); + pub const OffsetArgs = struct { + pre_index: bool = true, + positive: bool = true, + offset: Offset, + write_back: bool = false, + }; + + pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { + return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); } - pub fn ldrb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { - return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 1); + pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { + return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); } - pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { - return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); + pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { + return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); } - pub fn strb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { - return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 0); + pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { + return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); } // Block data transfer @@ -721,6 +728,58 @@ pub const Instruction = union(enum) { pub fn bkpt(imm: u16) Instruction { return breakpoint(imm); } + + // Aliases + + pub fn pop(cond: Condition, args: anytype) Instruction { + if (@typeInfo(@TypeOf(args)) != .Struct) { + @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); + } + + if (args.len < 1) { + @compileError("Expected at least one register"); + } else if (args.len == 1) { + const reg = args[0]; + return ldr(cond, reg, .sp, .{ + .pre_index = false, + .positive = true, + .offset = Offset.imm(4), + .write_back = false, + }); + } else { + var register_list: u16 = 0; + inline for (args) |arg| { + const reg = @as(Register, arg); + register_list |= @as(u16, 1) << reg.id(); + } + return ldm(cond, .sp, true, @bitCast(RegisterList, register_list)); + } + } + + pub fn push(cond: Condition, args: anytype) Instruction { + if (@typeInfo(@TypeOf(args)) != .Struct) { + @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); + } + + if (args.len < 1) { + @compileError("Expected at least one register"); + } else if (args.len == 1) { + const reg = args[0]; + return str(cond, reg, .sp, .{ + .pre_index = true, + .positive = false, + .offset = Offset.imm(4), + .write_back = true, + }); + } else { + var register_list: u16 = 0; + inline for (args) |arg| { + const reg = @as(Register, arg); + register_list |= @as(u16, 1) << reg.id(); + } + return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); + } + } }; test "serialize instructions" { @@ -747,11 +806,15 @@ test "serialize instructions" { .expected = 0b1110_00010_0_001111_0101_000000000000, }, .{ // ldr r0, [r2, #42] - .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), + .inst = Instruction.ldr(.al, .r0, .r2, .{ + .offset = Instruction.Offset.imm(42), + }), .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, }, .{ // str r0, [r3] - .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none), + .inst = Instruction.str(.al, .r0, .r3, .{ + .offset = Instruction.Offset.none, + }), .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, }, .{ // b #12 @@ -789,3 +852,43 @@ test "serialize instructions" { testing.expectEqual(case.expected, actual); } } + +test "aliases" { + const Testcase = struct { + expected: Instruction, + actual: Instruction, + }; + + const testcases = [_]Testcase{ + .{ // pop { r6 } + .actual = Instruction.pop(.al, .{.r6}), + .expected = Instruction.ldr(.al, .r6, .sp, .{ + .pre_index = false, + .positive = true, + .offset = Instruction.Offset.imm(4), + .write_back = false, + }), + }, + .{ // pop { r1, r5 } + .actual = Instruction.pop(.al, .{ .r1, .r5 }), + .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), + }, + .{ // push { r3 } + .actual = Instruction.push(.al, .{.r3}), + .expected = Instruction.str(.al, .r3, .sp, .{ + .pre_index = true, + .positive = false, + .offset = Instruction.Offset.imm(4), + .write_back = true, + }), + }, + .{ // push { r0, r2 } + .actual = Instruction.push(.al, .{ .r0, .r2 }), + .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), + }, + }; + + for (testcases) |case| { + testing.expectEqual(case.expected.toU32(), case.actual.toU32()); + } +} -- cgit v1.2.3 From fb58fb2d8dfa49b01594f341f262cb45958c5e23 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 27 Sep 2020 11:27:40 +0200 Subject: stage2 ARM: add testcases for non-leaf fns, parameters, return values --- src/codegen/arm.zig | 12 +++--- test/stage2/arm.zig | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++ test/stage2/test.zig | 99 +++++++++++++------------------------------ 3 files changed, 152 insertions(+), 75 deletions(-) create mode 100644 test/stage2/arm.zig (limited to 'src/codegen/arm.zig') diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index bf13244f4f..36fb858022 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -402,7 +402,7 @@ pub const Instruction = union(enum) { return Instruction{ .DataProcessing = .{ .cond = @enumToInt(cond), - .i = if (op2 == .Immediate) 1 else 0, + .i = @boolToInt(op2 == .Immediate), .opcode = @enumToInt(opcode), .s = s, .rn = rn.id(), @@ -430,11 +430,11 @@ pub const Instruction = union(enum) { .rd = rd.id(), .offset = offset.toU12(), .load_store = load_store, - .write_back = if (write_back) 1 else 0, + .write_back = @boolToInt(write_back), .byte_word = byte_word, - .up_down = if (positive) 1 else 0, - .pre_post = if (pre_index) 1 else 0, - .imm = if (offset == .Immediate) 0 else 1, + .up_down = @boolToInt(positive), + .pre_post = @boolToInt(pre_index), + .imm = @boolToInt(offset != .Immediate), }, }; } @@ -454,7 +454,7 @@ pub const Instruction = union(enum) { .register_list = @bitCast(u16, reg_list), .rn = rn.id(), .load_store = load_store, - .write_back = if (write_back) 1 else 0, + .write_back = @boolToInt(write_back), .psr_or_user = psr_or_user, .up_down = up_down, .pre_post = pre_post, diff --git a/test/stage2/arm.zig b/test/stage2/arm.zig new file mode 100644 index 0000000000..8b15c88552 --- /dev/null +++ b/test/stage2/arm.zig @@ -0,0 +1,116 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; + +const linux_arm = std.zig.CrossTarget{ + .cpu_arch = .arm, + .os_tag = .linux, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exe("hello world", linux_arm); + // Regular old hello world + case.addCompareOutput( + \\export fn _start() noreturn { + \\ print(); + \\ exit(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{r7}" (4), + \\ [arg1] "{r0}" (1), + \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{r2}" (14) + \\ : "memory" + \\ ); + \\ return; + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{r7}" (1), + \\ [arg1] "{r0}" (0) + \\ : "memory" + \\ ); + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } + + { + var case = ctx.exe("parameters and return values", linux_arm); + // Testing simple parameters and return values + // + // TODO: The parameters to the asm statement in print() had to + // be in a specific order because otherwise the write to r0 + // would overwrite the len parameter which resides in r0 + case.addCompareOutput( + \\export fn _start() noreturn { + \\ print(id(14)); + \\ exit(); + \\} + \\ + \\fn id(x: u32) u32 { + \\ return x; + \\} + \\ + \\fn print(len: u32) void { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{r7}" (4), + \\ [arg3] "{r2}" (len), + \\ [arg1] "{r0}" (1), + \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")) + \\ : "memory" + \\ ); + \\ return; + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{r7}" (1), + \\ [arg1] "{r0}" (0) + \\ : "memory" + \\ ); + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } + + { + var case = ctx.exe("non-leaf functions", linux_arm); + // Testing non-leaf functions + case.addCompareOutput( + \\export fn _start() noreturn { + \\ foo(); + \\ exit(); + \\} + \\ + \\fn foo() void { + \\ bar(); + \\} + \\ + \\fn bar() void {} + \\ + \\fn exit() noreturn { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{r7}" (1), + \\ [arg1] "{r0}" (0) + \\ : "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } +} diff --git a/test/stage2/test.zig b/test/stage2/test.zig index a22dc23d36..4f413daf8e 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -21,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{ .os_tag = .linux, }; -const linux_arm = std.zig.CrossTarget{ - .cpu_arch = .arm, - .os_tag = .linux, -}; - const wasi = std.zig.CrossTarget{ .cpu_arch = .wasm32, .os_tag = .wasi, @@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void { try @import("zir.zig").addCases(ctx); try @import("cbe.zig").addCases(ctx); try @import("spu-ii.zig").addCases(ctx); + try @import("arm.zig").addCases(ctx); { var case = ctx.exe("hello world with updates", linux_x64); @@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); // Now change the message only @@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", ); // Now we print it twice. @@ -184,42 +180,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , - "Hello, World!\n", - ); - } - - { - var case = ctx.exe("hello world", linux_arm); - // Regular old hello world - case.addCompareOutput( - \\export fn _start() noreturn { - \\ print(); - \\ exit(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{r2}" (14) - \\ : "memory" - \\ ); - \\ return; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (1), - \\ [arg1] "{r0}" (0) - \\ : "memory" - \\ ); - \\ unreachable; - \\} - , + , "Hello, World!\n", ); } @@ -244,7 +205,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); } @@ -271,7 +232,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); } @@ -298,7 +259,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); } @@ -329,7 +290,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -362,7 +323,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -398,7 +359,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -435,7 +396,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -465,7 +426,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -499,7 +460,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -523,7 +484,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -562,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "hello\nhello\nhello\nhello\n", ); @@ -599,7 +560,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -641,7 +602,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -693,7 +654,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -755,7 +716,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -788,7 +749,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -820,7 +781,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -845,7 +806,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -871,7 +832,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -904,7 +865,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "hello\nhello\nhello\nhello\nhello\n", ); } @@ -923,7 +884,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , "42\n", ); @@ -941,7 +902,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , "42\n", ); @@ -957,10 +918,10 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , - // This is what you get when you take the bits of the IEE-754 - // representation of 42.0 and reinterpret them as an unsigned - // integer. Guess that's a bug in wasmtime. + , + // This is what you get when you take the bits of the IEE-754 + // representation of 42.0 and reinterpret them as an unsigned + // integer. Guess that's a bug in wasmtime. "1109917696\n", ); } -- cgit v1.2.3