diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 1e26f4ccf2..4d6075e2e9 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -1399,6 +1399,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } }, + .arm => { + if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch}); + + if (inst.func.cast(ir.Inst.Constant)) |func_inst| { + if (func_inst.val.cast(Value.Payload.Function)) |func_val| { + const func = func_val.func; + const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + + // 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 }); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32()); + } else { + return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); + } + } else { + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + } + }, else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), } } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { @@ -1455,6 +1480,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .riscv64 => { 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()); + }, else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), } return .unreach; @@ -1705,6 +1733,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{}); } }, + .arm => { + for (inst.inputs) |input, i| { + if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { + return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + } + const reg_name = input[1 .. input.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + const arg = try self.resolveInst(inst.args[i]); + try self.genSetReg(inst.base.src, reg, arg); + } + + if (mem.eql(u8, inst.asm_source, "svc #0")) { + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32()); + } else { + return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{}); + } + + if (inst.output) |output| { + if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { + return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + } + const reg_name = output[2 .. output.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return MCValue{ .register = reg }; + } else { + return MCValue.none; + } + }, .riscv64 => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { @@ -1898,6 +1956,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { switch (arch) { + .arm => switch (mcv) { + .dead => unreachable, + .ptr_stack_offset => unreachable, + .ptr_embedded_in_code => unreachable, + .unreach, .none => return, // Nothing to do. + .undef => { + if (!self.wantSafety()) + return; // The already existing value will do just fine. + // Write the debug undefined value. + return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }); + }, + .immediate => |x| { + // TODO better analysis of x to determine the + // least amount of necessary instructions (use + // more intelligent rotating) + if (x <= math.maxInt(u8)) { + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); + return; + } else if (x <= math.maxInt(u16)) { + // TODO Use movw Note: Not supported on + // all ARM targets! + + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); + } else if (x <= math.maxInt(u32)) { + // TODO Use movw and movt Note: Not + // supported on all ARM targets! Also TODO + // write constant to code and load + // relative to pc + + // immediate: 0xaabbccdd + // mov reg, #0xaa + // orr reg, reg, #0xbb, 24 + // orr reg, reg, #0xcc, 16 + // orr reg, reg, #0xdd, 8 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32()); + return; + } else { + return self.fail(src, "ARM registers are 32-bit wide", .{}); + } + }, + .memory => |addr| { + // 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()); + }, + else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}), + }, .riscv64 => switch (mcv) { .dead => unreachable, .ptr_stack_offset => unreachable, diff --git a/src-self-hosted/codegen/arm.zig b/src-self-hosted/codegen/arm.zig index f509d4b6e4..05178ea7d3 100644 --- a/src-self-hosted/codegen/arm.zig +++ b/src-self-hosted/codegen/arm.zig @@ -157,7 +157,7 @@ pub const Instruction = union(enum) { fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, cond: u4, }, - SoftwareInterrupt: packed struct { + SupervisorCall: packed struct { comment: u24, fixed: u4 = 0b1111, cond: u4, @@ -344,7 +344,7 @@ pub const Instruction = union(enum) { .SingleDataTransfer => |v| @bitCast(u32, v), .Branch => |v| @bitCast(u32, v), .BranchExchange => |v| @bitCast(u32, v), - .SoftwareInterrupt => |v| @bitCast(u32, v), + .SupervisorCall => |v| @bitCast(u32, v), .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), }; } @@ -355,8 +355,8 @@ pub const Instruction = union(enum) { cond: Condition, opcode: Opcode, s: u1, - rn: Register, rd: Register, + rn: Register, op2: Operand, ) Instruction { return Instruction{ @@ -394,7 +394,7 @@ pub const Instruction = union(enum) { .b = byte_word, .u = up_down, .p = pre_post, - .i = if (offset == .Immediate) 1 else 0, + .i = if (offset == .Immediate) 0 else 1, }, }; } @@ -419,9 +419,9 @@ pub const Instruction = union(enum) { }; } - fn softwareInterrupt(cond: Condition, comment: u24) Instruction { + fn supervisorCall(cond: Condition, comment: u24) Instruction { return Instruction{ - .SoftwareInterrupt = .{ + .SupervisorCall = .{ .cond = @enumToInt(cond), .comment = comment, }, @@ -536,10 +536,12 @@ pub const Instruction = union(enum) { return branchExchange(cond, rn, 1); } - // Software interrupt + // Supervisor Call - pub fn swi(cond: Condition, comment: u24) Instruction { - return softwareInterrupt(cond, comment); + pub const swi = svc; + + pub fn svc(cond: Condition, comment: u24) Instruction { + return supervisorCall(cond, comment); } // Breakpoint @@ -562,7 +564,7 @@ test "serialize instructions" { }, .{ // mov r4, r2 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), - .expected = 0b1110_00_0_1101_0_0100_0000_00000000_0010, + .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, }, .{ // mov r0, #42 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)), @@ -570,11 +572,11 @@ test "serialize instructions" { }, .{ // ldr r0, [r2, #42] .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), - .expected = 0b1110_01_1_1_1_0_0_1_0010_0000_000000101010, + .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, }, .{ // str r0, [r3] .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none), - .expected = 0b1110_01_1_1_1_0_0_0_0011_0000_000000000000, + .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, }, .{ // b #12 .inst = Instruction.b(.al, 12), @@ -588,8 +590,8 @@ test "serialize instructions" { .inst = Instruction.bx(.al, .lr), .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, }, - .{ // swi #0 - .inst = Instruction.swi(.al, 0), + .{ // svc #0 + .inst = Instruction.svc(.al, 0), .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, }, .{ // bkpt #42 diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 2218c49200..37b13c1d15 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -756,6 +756,7 @@ pub const Type = extern union { .fn_ccc_void_no_args, // represents machine code; not a pointer .function, // represents machine code; not a pointer => return switch (target.cpu.arch) { + .arm => 4, .riscv64 => 2, else => 1, },