| ... | ... | @@ -1399,6 +1399,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1399 | 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 | 1427 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), |
| 1403 | 1428 | } |
| 1404 | 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 | 1480 | .riscv64 => { |
| 1456 | 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 | 1486 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), |
| 1459 | 1487 | } |
| 1460 | 1488 | return .unreach; |
| ... | ... | @@ -1705,6 +1733,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1705 | 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 | 1766 | .riscv64 => { |
| 1709 | 1767 | for (inst.inputs) |input, i| { |
| 1710 | 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 | 1956 | |
| 1899 | 1957 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { |
| 1900 | 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 | 2011 | .riscv64 => switch (mcv) { |
| 1902 | 2012 | .dead => unreachable, |
| 1903 | 2013 | .ptr_stack_offset => unreachable, |