| ... | ... | @@ -102,6 +102,7 @@ pub fn generateSymbol( |
| 102 | 102 | //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 103 | 103 | //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 104 | 104 | //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 105 | .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 105 | 106 | //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 106 | 107 | //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 107 | 108 | //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| ... | ... | @@ -1349,6 +1350,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1349 | 1350 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); |
| 1350 | 1351 | } |
| 1351 | 1352 | }, |
| 1353 | .spu_2 => { |
| 1354 | if (inst.func.cast(ir.Inst.Constant)) |func_inst| { |
| 1355 | if (info.args.len != 0) { |
| 1356 | return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{}); |
| 1357 | } |
| 1358 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| 1359 | const func = func_val.func; |
| 1360 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; |
| 1361 | const got_addr = @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2); |
| 1362 | const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType(); |
| 1363 | // First, push the return address, then jump; if noreturn, don't bother with the first step |
| 1364 | // TODO: implement packed struct -> u16 at comptime and move the bitcast here |
| 1365 | var instr = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .jump, .command = .load16 }; |
| 1366 | if (return_type.zigTypeTag() == .NoReturn) { |
| 1367 | try self.code.resize(self.code.items.len + 4); |
| 1368 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr)); |
| 1369 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr); |
| 1370 | return MCValue.unreach; |
| 1371 | } else { |
| 1372 | try self.code.resize(self.code.items.len + 8); |
| 1373 | var push = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .push, .command = .ipget }; |
| 1374 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 8 ..][0..2], @bitCast(u16, push)); |
| 1375 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 6 ..][0..2], @as(u16, 4)); |
| 1376 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr)); |
| 1377 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr); |
| 1378 | switch (return_type.zigTypeTag()) { |
| 1379 | .Void => return MCValue{ .none = {} }, |
| 1380 | .NoReturn => unreachable, |
| 1381 | else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}), |
| 1382 | } |
| 1383 | } |
| 1384 | } else { |
| 1385 | return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); |
| 1386 | } |
| 1387 | } else { |
| 1388 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); |
| 1389 | } |
| 1390 | }, |
| 1352 | 1391 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), |
| 1353 | 1392 | } |
| 1354 | 1393 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| ... | ... | @@ -1647,19 +1686,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1647 | 1686 | return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{}); |
| 1648 | 1687 | } |
| 1649 | 1688 | if (mem.eql(u8, inst.asm_source, "undefined0")) { |
| 1650 | | // Instructions are 16-bits, plus up to two sixteen bit immediates. |
| 1651 | | // Upper three bits of first byte are the execution |
| 1652 | | // condition; for now, only always (0b000) is supported. |
| 1653 | | // Next, there are two two-bit sequences indicating inputs; |
| 1654 | | // we only care to use zero (0b00). |
| 1655 | | // The lowest bit of byte one indicates whether flags |
| 1656 | | // should be updated; TODO: support that somehow. |
| 1657 | | // In all, we use a zero byte for the first half of the |
| 1658 | | // instruction. |
| 1659 | | // The second byte is 0bOOCCCCCR; OO is output behavior (we |
| 1660 | | // use zero, which discards the output), CCCCC is the |
| 1661 | | // command (8 for undefined0), R is reserved. |
| 1662 | | try self.code.appendSlice(&[_]u8{ 0x00, 0b00010000 }); |
| 1689 | try self.code.resize(self.code.items.len + 2); |
| 1690 | var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined0 }; |
| 1691 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr)); |
| 1663 | 1692 | return MCValue.none; |
| 1664 | 1693 | } else { |
| 1665 | 1694 | return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{}); |
| ... | ... | @@ -1742,6 +1771,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1742 | 1771 | /// X => extension to the SIB.index field |
| 1743 | 1772 | /// B => extension to the MODRM.rm field or the SIB.base field |
| 1744 | 1773 | fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void { |
| 1774 | std.debug.assert(arch == .x86_64); |
| 1745 | 1775 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. |
| 1746 | 1776 | var value: u8 = 0x40; |
| 1747 | 1777 | if (arg.b) { |
| ... | ... | @@ -2289,7 +2319,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2289 | 2319 | result.stack_byte_count = next_stack_offset; |
| 2290 | 2320 | result.stack_align = 16; |
| 2291 | 2321 | }, |
| 2292 | | else => return self.fail(src, "TODO implement function parameters for {}", .{cc}), |
| 2322 | else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}), |
| 2293 | 2323 | } |
| 2294 | 2324 | }, |
| 2295 | 2325 | else => if (param_types.len != 0) |
| ... | ... | @@ -2336,6 +2366,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2336 | 2366 | .i386 => @import("codegen/x86.zig"), |
| 2337 | 2367 | .x86_64 => @import("codegen/x86_64.zig"), |
| 2338 | 2368 | .riscv64 => @import("codegen/riscv64.zig"), |
| 2369 | .spu_2 => @import("codegen/spu-mk2.zig"), |
| 2339 | 2370 | else => struct { |
| 2340 | 2371 | pub const Register = enum { |
| 2341 | 2372 | dummy, |