authorgravatar for pfg@pfg.pwpfg <pfg@pfg.pw> 2020-08-03 22:38:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-04 14:38:33-07:00
log52ae2b10aaba858185c1f3a224a4042f6b632a29
tree22f6f8c1578d4ebaed0b70a2935a94174caa75a1
parent952a397b0e006444e770e51d32cce93186959bdb

stage2: starting on a riscv64 backend


3 files changed, 175 insertions(+), 30 deletions(-)

src-self-hosted/codegen.zig+102-30
...@@ -78,7 +78,7 @@ pub fn generateSymbol(...@@ -78,7 +78,7 @@ pub fn generateSymbol(
78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
81 //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),81 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
...@@ -1023,6 +1023,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1023,6 +1023,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1023 .i386, .x86_64 => {1023 .i386, .x86_64 => {
1024 try self.code.append(0xcc); // int31024 try self.code.append(0xcc); // int3
1025 },1025 },
1026 .riscv64 => {
1027 const full = @bitCast(u32, Instructions.CallBreak{
1028 .mode = @enumToInt(Instructions.CallBreak.Mode.ebreak),
1029 });
1030
1031 try self.code.resize(self.code.items.len + 4);
1032 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full);
1033 },
1026 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),1034 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
1027 }1035 }
1028 return .none;1036 return .none;
...@@ -1325,36 +1333,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1325,36 +1333,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1325 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {1333 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
1326 if (!inst.is_volatile and inst.base.isUnused())1334 if (!inst.is_volatile and inst.base.isUnused())
1327 return MCValue.dead;1335 return MCValue.dead;
1328 if (arch != .x86_64 and arch != .i386) {1336 switch (arch) {
1329 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});1337 .riscv64 => {
1330 }1338 for (inst.inputs) |input, i| {
1331 for (inst.inputs) |input, i| {1339 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1332 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {1340 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1333 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});1341 }
1334 }1342 const reg_name = input[1 .. input.len - 1];
1335 const reg_name = input[1 .. input.len - 1];1343 const reg = parseRegName(reg_name) orelse
1336 const reg = parseRegName(reg_name) orelse1344 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1337 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});1345 const arg = try self.resolveInst(inst.args[i]);
1338 const arg = try self.resolveInst(inst.args[i]);1346 try self.genSetReg(inst.base.src, reg, arg);
1339 try self.genSetReg(inst.base.src, reg, arg);1347 }
1340 }
13411348
1342 if (mem.eql(u8, inst.asm_source, "syscall")) {1349 if (mem.eql(u8, inst.asm_source, "ecall")) {
1343 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });1350 const full = @bitCast(u32, Instructions.CallBreak{
1344 } else {1351 .mode = @enumToInt(Instructions.CallBreak.Mode.ecall),
1345 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});1352 });
1346 }
13471353
1348 if (inst.output) |output| {1354 try self.code.resize(self.code.items.len + 4);
1349 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {1355 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full);
1350 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});1356 } else {
1351 }1357 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
1352 const reg_name = output[2 .. output.len - 1];1358 }
1353 const reg = parseRegName(reg_name) orelse1359
1354 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});1360 if (inst.output) |output| {
1355 return MCValue{ .register = reg };1361 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1356 } else {1362 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1357 return MCValue.none;1363 }
1364 const reg_name = output[2 .. output.len - 1];
1365 const reg = parseRegName(reg_name) orelse
1366 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1367 return MCValue{ .register = reg };
1368 } else {
1369 return MCValue.none;
1370 }
1371 },
1372 .x86_64, .i386 => {
1373 for (inst.inputs) |input, i| {
1374 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1375 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1376 }
1377 const reg_name = input[1 .. input.len - 1];
1378 const reg = parseRegName(reg_name) orelse
1379 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1380 const arg = try self.resolveInst(inst.args[i]);
1381 try self.genSetReg(inst.base.src, reg, arg);
1382 }
1383
1384 if (mem.eql(u8, inst.asm_source, "syscall")) {
1385 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
1386 } else {
1387 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
1388 }
1389
1390 if (inst.output) |output| {
1391 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1392 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1393 }
1394 const reg_name = output[2 .. output.len - 1];
1395 const reg = parseRegName(reg_name) orelse
1396 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1397 return MCValue{ .register = reg };
1398 } else {
1399 return MCValue.none;
1400 }
1401 },
1402 else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),
1358 }1403 }
1359 }1404 }
13601405
...@@ -1500,6 +1545,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1500,6 +1545,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15001545
1501 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {1546 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1502 switch (arch) {1547 switch (arch) {
1548 .riscv64 => switch (mcv) {
1549 .immediate => |x| {
1550 if (x > math.maxInt(u11)) {
1551 return self.fail(src, "TODO genSetReg 12+ bit immediates for riscv64", .{});
1552 }
1553 const Instruction = packed struct {
1554 opcode: u7,
1555 rd: u5,
1556 mode: u3,
1557 rsi1: u5,
1558 imm: u11,
1559 signextend: u1 = 0,
1560 };
1561 const full = @bitCast(u32, Instructions.Addi{
1562 .imm = @intCast(u11, x),
1563 .rsi1 = Register.zero.id(),
1564 .mode = @enumToInt(Instructions.Addi.Mode.addi),
1565 .rd = reg.id(),
1566 });
1567
1568 try self.code.resize(self.code.items.len + 4);
1569 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full);
1570 },
1571 else => return self.fail(src, "TODO implement getSetReg for riscv64 MCValue {}", .{mcv}),
1572 },
1503 .x86_64 => switch (mcv) {1573 .x86_64 => switch (mcv) {
1504 .dead => unreachable,1574 .dead => unreachable,
1505 .ptr_stack_offset => unreachable,1575 .ptr_stack_offset => unreachable,
...@@ -1873,7 +1943,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1873,7 +1943,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1873 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),1943 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1874 }1944 }
1875 },1945 },
1876 else => return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),1946 else => if (param_types.len != 0)
1947 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
1877 }1948 }
18781949
1879 if (ret_ty.zigTypeTag() == .NoReturn) {1950 if (ret_ty.zigTypeTag() == .NoReturn) {
...@@ -1915,6 +1986,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1915,6 +1986,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1915 usingnamespace switch (arch) {1986 usingnamespace switch (arch) {
1916 .i386 => @import("codegen/x86.zig"),1987 .i386 => @import("codegen/x86.zig"),
1917 .x86_64 => @import("codegen/x86_64.zig"),1988 .x86_64 => @import("codegen/x86_64.zig"),
1989 .riscv64 => @import("codegen/riscv64.zig"),
1918 else => struct {1990 else => struct {
1919 pub const Register = enum {1991 pub const Register = enum {
1920 dummy,1992 dummy,
src-self-hosted/codegen/riscv64.zig created+68
...@@ -0,0 +1,68 @@
1pub const Instructions = struct {
2 pub const CallBreak = packed struct {
3 pub const Mode = packed enum(u12) { ecall, ebreak };
4 opcode: u7 = 0b1110011,
5 unused1: u5 = 0,
6 unused2: u3 = 0,
7 unused3: u5 = 0,
8 mode: u12,
9 };
10 pub const Addi = packed struct {
11 pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 };
12 opcode: u7 = 0b0010011,
13 rd: u5,
14 mode: u3,
15 rsi1: u5,
16 imm: u11,
17 signextend: u1 = 0,
18 };
19};
20
21// zig fmt: off
22pub const Register = enum(u8) {
23 // 64 bit registers
24 zero = 0, // zero
25 ra = 1, // return address. caller saved
26 sp = 2, // stack pointer. callee saved.
27 gp = 3, // global pointer
28 tp = 4, // thread pointer
29 t0 = 5, t1 = 6, t2 = 7, // temporaries. caller saved.
30 s0 = 8, // s0/fp, callee saved.
31 s1, // callee saved.
32 a0, a1, // fn args/return values. caller saved.
33 a2, a3, a4, a5, a6, a7, // fn args. caller saved.
34 s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
35 t3, t4, t5, t6, // caller saved
36
37 /// Returns the bit-width of the register.
38 pub fn size(self: @This()) u7 {
39 return switch (@enumToInt(self)) {
40 0...31 => 64,
41 else => unreachable,
42 };
43 }
44
45 pub fn to64(self: @This()) Register {
46 return self;
47 }
48
49 /// Returns the register's id. This is used in practically every opcode the
50 /// riscv64 has.
51 pub fn id(self: @This()) u5 {
52 return @truncate(u5, @enumToInt(self));
53 }
54
55 /// Returns the index into `callee_preserved_regs`.
56 pub fn allocIndex(self: Register) ?u4 {
57 inline for(callee_preserved_regs) |cpreg, i| {
58 if(self == cpreg) return i;
59 }
60 return null;
61 }
62};
63
64// zig fmt: on
65
66pub const callee_preserved_regs = [_]Register{
67 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
68};
test/stage2/compare_output.zig+5
...@@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{...@@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{
7 .os_tag = .linux,7 .os_tag = .linux,
8};8};
99
10const riscv64 = std.zig.CrossTarget{
11 .cpu_arch = .riscv64,
12 .os_tag = .linux,
13};
14
10pub fn addCases(ctx: *TestContext) !void {15pub fn addCases(ctx: *TestContext) !void {
11 if (std.Target.current.os.tag != .linux or16 if (std.Target.current.os.tag != .linux or
12 std.Target.current.cpu.arch != .x86_64)17 std.Target.current.cpu.arch != .x86_64)