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(
7878 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
7979 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
8080 //.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),
8282 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
8383 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
8484 //.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 {
10231023 .i386, .x86_64 => {
10241024 try self.code.append(0xcc); // int3
10251025 },
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 },
10261034 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
10271035 }
10281036 return .none;
......@@ -1325,36 +1333,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13251333 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
13261334 if (!inst.is_volatile and inst.base.isUnused())
13271335 return MCValue.dead;
1328 if (arch != .x86_64 and arch != .i386) {
1329 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
1330 }
1331 for (inst.inputs) |input, i| {
1332 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1333 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1334 }
1335 const reg_name = input[1 .. input.len - 1];
1336 const reg = parseRegName(reg_name) orelse
1337 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1338 const arg = try self.resolveInst(inst.args[i]);
1339 try self.genSetReg(inst.base.src, reg, arg);
1340 }
1336 switch (arch) {
1337 .riscv64 => {
1338 for (inst.inputs) |input, i| {
1339 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1340 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1341 }
1342 const reg_name = input[1 .. input.len - 1];
1343 const reg = parseRegName(reg_name) orelse
1344 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1345 const arg = try self.resolveInst(inst.args[i]);
1346 try self.genSetReg(inst.base.src, reg, arg);
1347 }
13411348
1342 if (mem.eql(u8, inst.asm_source, "syscall")) {
1343 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
1344 } else {
1345 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
1346 }
1349 if (mem.eql(u8, inst.asm_source, "ecall")) {
1350 const full = @bitCast(u32, Instructions.CallBreak{
1351 .mode = @enumToInt(Instructions.CallBreak.Mode.ecall),
1352 });
13471353
1348 if (inst.output) |output| {
1349 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1350 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1351 }
1352 const reg_name = output[2 .. output.len - 1];
1353 const reg = parseRegName(reg_name) orelse
1354 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1355 return MCValue{ .register = reg };
1356 } else {
1357 return MCValue.none;
1354 try self.code.resize(self.code.items.len + 4);
1355 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full);
1356 } else {
1357 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
1358 }
1359
1360 if (inst.output) |output| {
1361 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1362 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
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", .{}),
13581403 }
13591404 }
13601405
......@@ -1500,6 +1545,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15001545
15011546 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
15021547 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 },
15031573 .x86_64 => switch (mcv) {
15041574 .dead => unreachable,
15051575 .ptr_stack_offset => unreachable,
......@@ -1873,7 +1943,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18731943 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
18741944 }
18751945 },
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}),
18771948 }
18781949
18791950 if (ret_ty.zigTypeTag() == .NoReturn) {
......@@ -1915,6 +1986,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19151986 usingnamespace switch (arch) {
19161987 .i386 => @import("codegen/x86.zig"),
19171988 .x86_64 => @import("codegen/x86_64.zig"),
1989 .riscv64 => @import("codegen/riscv64.zig"),
19181990 else => struct {
19191991 pub const Register = enum {
19201992 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{
77 .os_tag = .linux,
88};
99
10const riscv64 = std.zig.CrossTarget{
11 .cpu_arch = .riscv64,
12 .os_tag = .linux,
13};
14
1015pub fn addCases(ctx: *TestContext) !void {
1116 if (std.Target.current.os.tag != .linux or
1217 std.Target.current.cpu.arch != .x86_64)