authorgravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-07-21 21:33:53-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-22 12:45:29-07:00
logcdefc6acbaf7f582c6689d1e003405819f797181
tree649f12ad8cd798afe6f568de9d29141b4a8c3d25
parentd005ff16c6fe1a0ddb3997552d6cccf314e6d94f

SPU-II: Implement function calls


2 files changed, 212 insertions(+), 14 deletions(-)

src-self-hosted/codegen.zig+45-14
...@@ -102,6 +102,7 @@ pub fn generateSymbol(...@@ -102,6 +102,7 @@ pub fn generateSymbol(
102 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),102 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
103 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),103 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
104 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),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 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),106 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
106 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),107 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
107 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),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,6 +1350,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1349 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});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 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),1391 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
1353 }1392 }
1354 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {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,19 +1686,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1647 return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{});1686 return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{});
1648 }1687 }
1649 if (mem.eql(u8, inst.asm_source, "undefined0")) {1688 if (mem.eql(u8, inst.asm_source, "undefined0")) {
1650 // Instructions are 16-bits, plus up to two sixteen bit immediates.1689 try self.code.resize(self.code.items.len + 2);
1651 // Upper three bits of first byte are the execution1690 var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined0 };
1652 // condition; for now, only always (0b000) is supported.1691 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
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 });
1663 return MCValue.none;1692 return MCValue.none;
1664 } else {1693 } else {
1665 return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});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,6 +1771,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1742 /// X => extension to the SIB.index field1771 /// X => extension to the SIB.index field
1743 /// B => extension to the MODRM.rm field or the SIB.base field1772 /// B => extension to the MODRM.rm field or the SIB.base field
1744 fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {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 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.1775 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
1746 var value: u8 = 0x40;1776 var value: u8 = 0x40;
1747 if (arg.b) {1777 if (arg.b) {
...@@ -2289,7 +2319,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2289,7 +2319,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2289 result.stack_byte_count = next_stack_offset;2319 result.stack_byte_count = next_stack_offset;
2290 result.stack_align = 16;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 else => if (param_types.len != 0)2325 else => if (param_types.len != 0)
...@@ -2336,6 +2366,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2336,6 +2366,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2336 .i386 => @import("codegen/x86.zig"),2366 .i386 => @import("codegen/x86.zig"),
2337 .x86_64 => @import("codegen/x86_64.zig"),2367 .x86_64 => @import("codegen/x86_64.zig"),
2338 .riscv64 => @import("codegen/riscv64.zig"),2368 .riscv64 => @import("codegen/riscv64.zig"),
2369 .spu_2 => @import("codegen/spu-mk2.zig"),
2339 else => struct {2370 else => struct {
2340 pub const Register = enum {2371 pub const Register = enum {
2341 dummy,2372 dummy,
src-self-hosted/codegen/spu-mk2.zig created+167
...@@ -0,0 +1,167 @@
1const std = @import("std");
2
3pub const ExecutionCondition = enum(u3) {
4 always = 0,
5 when_zero = 1,
6 not_zero = 2,
7 greater_zero = 3,
8 less_than_zero = 4,
9 greater_or_equal_zero = 5,
10 less_or_equal_zero = 6,
11 overflow = 7,
12};
13
14pub const InputBehaviour = enum(u2) {
15 zero = 0,
16 immediate = 1,
17 peek = 2,
18 pop = 3,
19};
20
21pub const OutputBehaviour = enum(u2) {
22 discard = 0,
23 push = 1,
24 jump = 2,
25 jump_relative = 3,
26};
27
28pub const Command = enum(u5) {
29 copy = 0,
30 ipget = 1,
31 get = 2,
32 set = 3,
33 store8 = 4,
34 store16 = 5,
35 load8 = 6,
36 load16 = 7,
37 undefined0 = 8,
38 undefined1 = 9,
39 frget = 10,
40 frset = 11,
41 bpget = 12,
42 bpset = 13,
43 spget = 14,
44 spset = 15,
45 add = 16,
46 sub = 17,
47 mul = 18,
48 div = 19,
49 mod = 20,
50 @"and" = 21,
51 @"or" = 22,
52 xor = 23,
53 not = 24,
54 signext = 25,
55 rol = 26,
56 ror = 27,
57 bswap = 28,
58 asr = 29,
59 lsl = 30,
60 lsr = 31,
61};
62
63pub const Instruction = packed struct {
64 condition: ExecutionCondition,
65 input0: InputBehaviour,
66 input1: InputBehaviour,
67 modify_flags: bool,
68 output: OutputBehaviour,
69 command: Command,
70 reserved: u1 = 0,
71
72 pub fn format(instr: Instruction, comptime fmt: []const u8, options: std.fmt.FormatOptions, out: anytype) !void {
73 try std.fmt.format(out, "0x{x:0<4} ", .{@bitCast(u16, instr)});
74 try out.writeAll(switch (instr.condition) {
75 .always => " ",
76 .when_zero => "== 0",
77 .not_zero => "!= 0",
78 .greater_zero => " > 0",
79 .less_than_zero => " < 0",
80 .greater_or_equal_zero => ">= 0",
81 .less_or_equal_zero => "<= 0",
82 .overflow => "ovfl",
83 });
84 try out.writeAll(" ");
85 try out.writeAll(switch (instr.input0) {
86 .zero => "zero",
87 .immediate => "imm ",
88 .peek => "peek",
89 .pop => "pop ",
90 });
91 try out.writeAll(" ");
92 try out.writeAll(switch (instr.input1) {
93 .zero => "zero",
94 .immediate => "imm ",
95 .peek => "peek",
96 .pop => "pop ",
97 });
98 try out.writeAll(" ");
99 try out.writeAll(switch (instr.command) {
100 .copy => "copy ",
101 .ipget => "ipget ",
102 .get => "get ",
103 .set => "set ",
104 .store8 => "store8 ",
105 .store16 => "store16 ",
106 .load8 => "load8 ",
107 .load16 => "load16 ",
108 .undefined0 => "undefined",
109 .undefined1 => "undefined",
110 .frget => "frget ",
111 .frset => "frset ",
112 .bpget => "bpget ",
113 .bpset => "bpset ",
114 .spget => "spget ",
115 .spset => "spset ",
116 .add => "add ",
117 .sub => "sub ",
118 .mul => "mul ",
119 .div => "div ",
120 .mod => "mod ",
121 .@"and" => "and ",
122 .@"or" => "or ",
123 .xor => "xor ",
124 .not => "not ",
125 .signext => "signext ",
126 .rol => "rol ",
127 .ror => "ror ",
128 .bswap => "bswap ",
129 .asr => "asr ",
130 .lsl => "lsl ",
131 .lsr => "lsr ",
132 });
133 try out.writeAll(" ");
134 try out.writeAll(switch (instr.output) {
135 .discard => "discard",
136 .push => "push ",
137 .jump => "jmp ",
138 .jump_relative => "rjmp ",
139 });
140 try out.writeAll(" ");
141 try out.writeAll(if (instr.modify_flags)
142 "+ flags"
143 else
144 " ");
145 }
146};
147
148pub const FlagRegister = packed struct {
149 zero: bool,
150 negative: bool,
151 carry: bool,
152 carry_enabled: bool,
153 interrupt0_enabled: bool,
154 interrupt1_enabled: bool,
155 interrupt2_enabled: bool,
156 interrupt3_enabled: bool,
157 reserved: u8 = 0,
158};
159
160pub const Register = enum {
161 dummy,
162
163 pub fn allocIndex(self: Register) ?u4 {
164 return null;
165 }
166};
167pub const callee_preserved_regs = [_]Register{};