authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2020-08-23 17:43:00+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2020-08-23 22:33:47+02:00
logb2254023e464027abfcbff809e991ff22cf581e0
treec02ebe998078bc8f4a6729dcc491e7f6f8462628
parent1c53c070535c519cbdc39a2094a24723f5245799

stage2: Implement setReg, call, ret, asm for ARM

These changes enable a Hello World example. However, all implemented codegen is not yet feature-complete. - asm only supports 'svc #0' at the moment - call only supports leaf functions at the moment - setReg uses a naive method at the moment

3 files changed, 127 insertions(+), 14 deletions(-)

src-self-hosted/codegen.zig+110
...@@ -1399,6 +1399,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1399,6 +1399,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1399 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});1399 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1400 }1400 }
1401 },1401 },
1402 .arm => {
1403 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1404
1405 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1406 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1407 const func = func_val.func;
1408 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1409 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1410 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1411 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1412
1413 // TODO only works with leaf functions
1414 // at the moment, which works fine for
1415 // Hello World, but not for real code
1416 // of course. Add pushing lr to stack
1417 // and popping after call
1418 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
1419 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1420 } else {
1421 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1422 }
1423 } else {
1424 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1425 }
1426 },
1402 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),1427 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
1403 }1428 }
1404 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {1429 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
...@@ -1455,6 +1480,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1455,6 +1480,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1455 .riscv64 => {1480 .riscv64 => {
1456 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());1481 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
1457 },1482 },
1483 .arm => {
1484 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1485 },
1458 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1486 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
1459 }1487 }
1460 return .unreach;1488 return .unreach;
...@@ -1705,6 +1733,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1705,6 +1733,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1705 return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});1733 return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});
1706 }1734 }
1707 },1735 },
1736 .arm => {
1737 for (inst.inputs) |input, i| {
1738 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1739 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1740 }
1741 const reg_name = input[1 .. input.len - 1];
1742 const reg = parseRegName(reg_name) orelse
1743 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1744 const arg = try self.resolveInst(inst.args[i]);
1745 try self.genSetReg(inst.base.src, reg, arg);
1746 }
1747
1748 if (mem.eql(u8, inst.asm_source, "svc #0")) {
1749 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
1750 } else {
1751 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
1752 }
1753
1754 if (inst.output) |output| {
1755 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1756 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1757 }
1758 const reg_name = output[2 .. output.len - 1];
1759 const reg = parseRegName(reg_name) orelse
1760 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1761 return MCValue{ .register = reg };
1762 } else {
1763 return MCValue.none;
1764 }
1765 },
1708 .riscv64 => {1766 .riscv64 => {
1709 for (inst.inputs) |input, i| {1767 for (inst.inputs) |input, i| {
1710 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {1768 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 {...@@ -1898,6 +1956,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18981956
1899 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {1957 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1900 switch (arch) {1958 switch (arch) {
1959 .arm => switch (mcv) {
1960 .dead => unreachable,
1961 .ptr_stack_offset => unreachable,
1962 .ptr_embedded_in_code => unreachable,
1963 .unreach, .none => return, // Nothing to do.
1964 .undef => {
1965 if (!self.wantSafety())
1966 return; // The already existing value will do just fine.
1967 // Write the debug undefined value.
1968 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
1969 },
1970 .immediate => |x| {
1971 // TODO better analysis of x to determine the
1972 // least amount of necessary instructions (use
1973 // more intelligent rotating)
1974 if (x <= math.maxInt(u8)) {
1975 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1976 return;
1977 } else if (x <= math.maxInt(u16)) {
1978 // TODO Use movw Note: Not supported on
1979 // all ARM targets!
1980
1981 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1982 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
1983 } else if (x <= math.maxInt(u32)) {
1984 // TODO Use movw and movt Note: Not
1985 // supported on all ARM targets! Also TODO
1986 // write constant to code and load
1987 // relative to pc
1988
1989 // immediate: 0xaabbccdd
1990 // mov reg, #0xaa
1991 // orr reg, reg, #0xbb, 24
1992 // orr reg, reg, #0xcc, 16
1993 // orr reg, reg, #0xdd, 8
1994 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1995 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
1996 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
1997 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
1998 return;
1999 } else {
2000 return self.fail(src, "ARM registers are 32-bit wide", .{});
2001 }
2002 },
2003 .memory => |addr| {
2004 // The value is in memory at a hard-coded address.
2005 // If the type is a pointer, it means the pointer address is at this memory location.
2006 try self.genSetReg(src, reg, .{ .immediate = addr });
2007 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());
2008 },
2009 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
2010 },
1901 .riscv64 => switch (mcv) {2011 .riscv64 => switch (mcv) {
1902 .dead => unreachable,2012 .dead => unreachable,
1903 .ptr_stack_offset => unreachable,2013 .ptr_stack_offset => unreachable,
src-self-hosted/codegen/arm.zig+16-14
...@@ -157,7 +157,7 @@ pub const Instruction = union(enum) {...@@ -157,7 +157,7 @@ pub const Instruction = union(enum) {
157 fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,157 fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
158 cond: u4,158 cond: u4,
159 },159 },
160 SoftwareInterrupt: packed struct {160 SupervisorCall: packed struct {
161 comment: u24,161 comment: u24,
162 fixed: u4 = 0b1111,162 fixed: u4 = 0b1111,
163 cond: u4,163 cond: u4,
...@@ -344,7 +344,7 @@ pub const Instruction = union(enum) {...@@ -344,7 +344,7 @@ pub const Instruction = union(enum) {
344 .SingleDataTransfer => |v| @bitCast(u32, v),344 .SingleDataTransfer => |v| @bitCast(u32, v),
345 .Branch => |v| @bitCast(u32, v),345 .Branch => |v| @bitCast(u32, v),
346 .BranchExchange => |v| @bitCast(u32, v),346 .BranchExchange => |v| @bitCast(u32, v),
347 .SoftwareInterrupt => |v| @bitCast(u32, v),347 .SupervisorCall => |v| @bitCast(u32, v),
348 .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),348 .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),
349 };349 };
350 }350 }
...@@ -355,8 +355,8 @@ pub const Instruction = union(enum) {...@@ -355,8 +355,8 @@ pub const Instruction = union(enum) {
355 cond: Condition,355 cond: Condition,
356 opcode: Opcode,356 opcode: Opcode,
357 s: u1,357 s: u1,
358 rn: Register,
359 rd: Register,358 rd: Register,
359 rn: Register,
360 op2: Operand,360 op2: Operand,
361 ) Instruction {361 ) Instruction {
362 return Instruction{362 return Instruction{
...@@ -394,7 +394,7 @@ pub const Instruction = union(enum) {...@@ -394,7 +394,7 @@ pub const Instruction = union(enum) {
394 .b = byte_word,394 .b = byte_word,
395 .u = up_down,395 .u = up_down,
396 .p = pre_post,396 .p = pre_post,
397 .i = if (offset == .Immediate) 1 else 0,397 .i = if (offset == .Immediate) 0 else 1,
398 },398 },
399 };399 };
400 }400 }
...@@ -419,9 +419,9 @@ pub const Instruction = union(enum) {...@@ -419,9 +419,9 @@ pub const Instruction = union(enum) {
419 };419 };
420 }420 }
421421
422 fn softwareInterrupt(cond: Condition, comment: u24) Instruction {422 fn supervisorCall(cond: Condition, comment: u24) Instruction {
423 return Instruction{423 return Instruction{
424 .SoftwareInterrupt = .{424 .SupervisorCall = .{
425 .cond = @enumToInt(cond),425 .cond = @enumToInt(cond),
426 .comment = comment,426 .comment = comment,
427 },427 },
...@@ -536,10 +536,12 @@ pub const Instruction = union(enum) {...@@ -536,10 +536,12 @@ pub const Instruction = union(enum) {
536 return branchExchange(cond, rn, 1);536 return branchExchange(cond, rn, 1);
537 }537 }
538538
539 // Software interrupt539 // Supervisor Call
540
541 pub const swi = svc;
540542
541 pub fn swi(cond: Condition, comment: u24) Instruction {543 pub fn svc(cond: Condition, comment: u24) Instruction {
542 return softwareInterrupt(cond, comment);544 return supervisorCall(cond, comment);
543 }545 }
544546
545 // Breakpoint547 // Breakpoint
...@@ -562,7 +564,7 @@ test "serialize instructions" {...@@ -562,7 +564,7 @@ test "serialize instructions" {
562 },564 },
563 .{ // mov r4, r2565 .{ // mov r4, r2
564 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),566 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
565 .expected = 0b1110_00_0_1101_0_0100_0000_00000000_0010,567 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
566 },568 },
567 .{ // mov r0, #42569 .{ // mov r0, #42
568 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),570 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
...@@ -570,11 +572,11 @@ test "serialize instructions" {...@@ -570,11 +572,11 @@ test "serialize instructions" {
570 },572 },
571 .{ // ldr r0, [r2, #42]573 .{ // ldr r0, [r2, #42]
572 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),574 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
573 .expected = 0b1110_01_1_1_1_0_0_1_0010_0000_000000101010,575 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
574 },576 },
575 .{ // str r0, [r3]577 .{ // str r0, [r3]
576 .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),578 .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),
577 .expected = 0b1110_01_1_1_1_0_0_0_0011_0000_000000000000,579 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
578 },580 },
579 .{ // b #12581 .{ // b #12
580 .inst = Instruction.b(.al, 12),582 .inst = Instruction.b(.al, 12),
...@@ -588,8 +590,8 @@ test "serialize instructions" {...@@ -588,8 +590,8 @@ test "serialize instructions" {
588 .inst = Instruction.bx(.al, .lr),590 .inst = Instruction.bx(.al, .lr),
589 .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,591 .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
590 },592 },
591 .{ // swi #0593 .{ // svc #0
592 .inst = Instruction.swi(.al, 0),594 .inst = Instruction.svc(.al, 0),
593 .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,595 .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
594 },596 },
595 .{ // bkpt #42597 .{ // bkpt #42
src-self-hosted/type.zig+1
...@@ -756,6 +756,7 @@ pub const Type = extern union {...@@ -756,6 +756,7 @@ pub const Type = extern union {
756 .fn_ccc_void_no_args, // represents machine code; not a pointer756 .fn_ccc_void_no_args, // represents machine code; not a pointer
757 .function, // represents machine code; not a pointer757 .function, // represents machine code; not a pointer
758 => return switch (target.cpu.arch) {758 => return switch (target.cpu.arch) {
759 .arm => 4,
759 .riscv64 => 2,760 .riscv64 => 2,
760 else => 1,761 else => 1,
761 },762 },