From e41661168b49f64a6edfd6cab931a5a88528312b Mon Sep 17 00:00:00 2001 From: pfg Date: Mon, 3 Aug 2020 22:38:01 -0700 Subject: [PATCH 1/5] stage2: starting on a riscv64 backend --- src-self-hosted/codegen.zig | 132 +++++++++++++++++++++------- src-self-hosted/codegen/riscv64.zig | 68 ++++++++++++++ test/stage2/compare_output.zig | 5 ++ 3 files changed, 175 insertions(+), 30 deletions(-) create mode 100644 src-self-hosted/codegen/riscv64.zig diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 2ea255bf7fb7..1bbabaf9fea2 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -72,7 +72,7 @@ pub fn generateSymbol( //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code), //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code), //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code), - //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code), + .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code), //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code), //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code), //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code), @@ -949,6 +949,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .i386, .x86_64 => { try self.code.append(0xcc); // int3 }, + .riscv64 => { + const full = @bitCast(u32, Instructions.CallBreak{ + .mode = @enumToInt(Instructions.CallBreak.Mode.ebreak), + }); + + try self.code.resize(self.code.items.len + 4); + mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full); + }, else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}), } return .none; @@ -1246,36 +1254,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue { if (!inst.is_volatile and inst.base.isUnused()) return MCValue.dead; - if (arch != .x86_64 and arch != .i386) { - return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}); - } - for (inst.inputs) |input, i| { - if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); - } - const reg_name = input[1 .. input.len - 1]; - const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); - const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); - } + switch (arch) { + .riscv64 => { + for (inst.inputs) |input, i| { + if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { + return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + } + const reg_name = input[1 .. input.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + const arg = try self.resolveInst(inst.args[i]); + try self.genSetReg(inst.base.src, reg, arg); + } - if (mem.eql(u8, inst.asm_source, "syscall")) { - try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); - } else { - return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); - } + if (mem.eql(u8, inst.asm_source, "ecall")) { + const full = @bitCast(u32, Instructions.CallBreak{ + .mode = @enumToInt(Instructions.CallBreak.Mode.ecall), + }); - if (inst.output) |output| { - if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); - } - const reg_name = output[2 .. output.len - 1]; - const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); - return MCValue{ .register = reg }; - } else { - return MCValue.none; + try self.code.resize(self.code.items.len + 4); + mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full); + } else { + return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{}); + } + + if (inst.output) |output| { + if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { + return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + } + const reg_name = output[2 .. output.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return MCValue{ .register = reg }; + } else { + return MCValue.none; + } + }, + .x86_64, .i386 => { + for (inst.inputs) |input, i| { + if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { + return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + } + const reg_name = input[1 .. input.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + const arg = try self.resolveInst(inst.args[i]); + try self.genSetReg(inst.base.src, reg, arg); + } + + if (mem.eql(u8, inst.asm_source, "syscall")) { + try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); + } else { + return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); + } + + if (inst.output) |output| { + if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { + return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + } + const reg_name = output[2 .. output.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return MCValue{ .register = reg }; + } else { + return MCValue.none; + } + }, + else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}), } } @@ -1421,6 +1466,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { switch (arch) { + .riscv64 => switch (mcv) { + .immediate => |x| { + if (x > math.maxInt(u11)) { + return self.fail(src, "TODO genSetReg 12+ bit immediates for riscv64", .{}); + } + const Instruction = packed struct { + opcode: u7, + rd: u5, + mode: u3, + rsi1: u5, + imm: u11, + signextend: u1 = 0, + }; + const full = @bitCast(u32, Instructions.Addi{ + .imm = @intCast(u11, x), + .rsi1 = Register.zero.id(), + .mode = @enumToInt(Instructions.Addi.Mode.addi), + .rd = reg.id(), + }); + + try self.code.resize(self.code.items.len + 4); + mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full); + }, + else => return self.fail(src, "TODO implement getSetReg for riscv64 MCValue {}", .{mcv}), + }, .x86_64 => switch (mcv) { .dead => unreachable, .ptr_stack_offset => unreachable, @@ -1794,7 +1864,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else => return self.fail(src, "TODO implement function parameters for {}", .{cc}), } }, - else => return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}), + else => if (param_types.len != 0) + return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}), } if (ret_ty.zigTypeTag() == .NoReturn) { @@ -1836,6 +1907,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { usingnamespace switch (arch) { .i386 => @import("codegen/x86.zig"), .x86_64 => @import("codegen/x86_64.zig"), + .riscv64 => @import("codegen/riscv64.zig"), else => struct { pub const Register = enum { dummy, diff --git a/src-self-hosted/codegen/riscv64.zig b/src-self-hosted/codegen/riscv64.zig new file mode 100644 index 000000000000..b6b4e099fc38 --- /dev/null +++ b/src-self-hosted/codegen/riscv64.zig @@ -0,0 +1,68 @@ +pub const Instructions = struct { + pub const CallBreak = packed struct { + pub const Mode = packed enum(u12) { ecall, ebreak }; + opcode: u7 = 0b1110011, + unused1: u5 = 0, + unused2: u3 = 0, + unused3: u5 = 0, + mode: u12, + }; + pub const Addi = packed struct { + pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 }; + opcode: u7 = 0b0010011, + rd: u5, + mode: u3, + rsi1: u5, + imm: u11, + signextend: u1 = 0, + }; +}; + +// zig fmt: off +pub const Register = enum(u8) { + // 64 bit registers + zero = 0, // zero + ra = 1, // return address. caller saved + sp = 2, // stack pointer. callee saved. + gp = 3, // global pointer + tp = 4, // thread pointer + t0 = 5, t1 = 6, t2 = 7, // temporaries. caller saved. + s0 = 8, // s0/fp, callee saved. + s1, // callee saved. + a0, a1, // fn args/return values. caller saved. + a2, a3, a4, a5, a6, a7, // fn args. caller saved. + s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved. + t3, t4, t5, t6, // caller saved + + /// Returns the bit-width of the register. + pub fn size(self: @This()) u7 { + return switch (@enumToInt(self)) { + 0...31 => 64, + else => unreachable, + }; + } + + pub fn to64(self: @This()) Register { + return self; + } + + /// Returns the register's id. This is used in practically every opcode the + /// riscv64 has. + pub fn id(self: @This()) u5 { + return @truncate(u5, @enumToInt(self)); + } + + /// Returns the index into `callee_preserved_regs`. + pub fn allocIndex(self: Register) ?u4 { + inline for(callee_preserved_regs) |cpreg, i| { + if(self == cpreg) return i; + } + return null; + } +}; + +// zig fmt: on + +pub const callee_preserved_regs = [_]Register{ + .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, +}; diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index bf6a01f483a5..b461e2114451 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{ .os_tag = .linux, }; +const riscv64 = std.zig.CrossTarget{ + .cpu_arch = .riscv64, + .os_tag = .linux, +}; + pub fn addCases(ctx: *TestContext) !void { if (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64) From a8b7b6e31d6c6978fd18c5d0ad59b52d8ad589db Mon Sep 17 00:00:00 2001 From: pfg Date: Tue, 4 Aug 2020 01:17:08 -0700 Subject: [PATCH 2/5] stage2: riscv hello world --- src-self-hosted/codegen.zig | 87 +++++++++++++++++++++-------- src-self-hosted/codegen/riscv64.zig | 36 ++++++++---- 2 files changed, 89 insertions(+), 34 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 1bbabaf9fea2..7df202c9a671 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -954,8 +954,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .mode = @enumToInt(Instructions.CallBreak.Mode.ebreak), }); - try self.code.resize(self.code.items.len + 4); - mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full); }, else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}), } @@ -1272,8 +1271,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .mode = @enumToInt(Instructions.CallBreak.Mode.ecall), }); - try self.code.resize(self.code.items.len + 4); - mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full); } else { return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{}); } @@ -1467,29 +1465,74 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { switch (arch) { .riscv64 => switch (mcv) { - .immediate => |x| { - if (x > math.maxInt(u11)) { - return self.fail(src, "TODO genSetReg 12+ bit immediates for riscv64", .{}); + .dead => unreachable, + .ptr_stack_offset => unreachable, + .ptr_embedded_in_code => unreachable, + .unreach, .none => return, // Nothing to do. + .undef => { + if (!self.wantSafety()) + return; // The already existing value will do just fine. + // Write the debug undefined value. + switch (reg.size()) { + 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), + else => unreachable, } - const Instruction = packed struct { - opcode: u7, - rd: u5, - mode: u3, - rsi1: u5, - imm: u11, - signextend: u1 = 0, - }; - const full = @bitCast(u32, Instructions.Addi{ - .imm = @intCast(u11, x), - .rsi1 = Register.zero.id(), - .mode = @enumToInt(Instructions.Addi.Mode.addi), + }, + .immediate => |unsigned_x| { + const x = @bitCast(i64, unsigned_x); + if (math.minInt(i12) <= x and x <= math.maxInt(i12)) { + const instruction = @bitCast(u32, Instructions.Addi{ + .mode = @enumToInt(Instructions.Addi.Mode.addi), + .imm = @truncate(i12, x), + .rs1 = Register.zero.id(), + .rd = reg.id(), + }); + + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), instruction); + return; + } + if (math.minInt(i32) <= x and x <= math.maxInt(i32)) { + const split = @bitCast(packed struct { + low12: i12, + up20: i20, + }, @truncate(i32, x)); + if (split.low12 < 0) return self.fail(src, "TODO support riscv64 genSetReg i32 immediates with 12th bit set to 1", .{}); + + const lui = @bitCast(u32, Instructions.Lui{ + .imm = split.up20, + .rd = reg.id(), + }); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), lui); + + const addi = @bitCast(u32, Instructions.Addi{ + .mode = @enumToInt(Instructions.Addi.Mode.addi), + .imm = @truncate(i12, split.low12), + .rs1 = reg.id(), + .rd = reg.id(), + }); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi); + return; + } + return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf + }, + .memory => |addr| { + // The value is in memory at a hard-coded address. + // If the type is a pointer, it means the pointer address is at this memory location. + try self.genSetReg(src, reg, .{ .immediate = addr }); + + const ld = @bitCast(u32, Instructions.Load{ + .mode = @enumToInt(Instructions.Load.Mode.ld), + .rs1 = reg.id(), .rd = reg.id(), + .offset = 0, }); - try self.code.resize(self.code.items.len + 4); - mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], full); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ld); + // LOAD imm=[i12 offset = 0], rs1 = + + // return self.fail("TODO implement genSetReg memory for riscv64"); }, - else => return self.fail(src, "TODO implement getSetReg for riscv64 MCValue {}", .{mcv}), + else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}), }, .x86_64 => switch (mcv) { .dead => unreachable, diff --git a/src-self-hosted/codegen/riscv64.zig b/src-self-hosted/codegen/riscv64.zig index b6b4e099fc38..1dab28901069 100644 --- a/src-self-hosted/codegen/riscv64.zig +++ b/src-self-hosted/codegen/riscv64.zig @@ -5,29 +5,41 @@ pub const Instructions = struct { unused1: u5 = 0, unused2: u3 = 0, unused3: u5 = 0, - mode: u12, + mode: u12, //: Mode }; pub const Addi = packed struct { pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 }; opcode: u7 = 0b0010011, rd: u5, - mode: u3, - rsi1: u5, - imm: u11, - signextend: u1 = 0, + mode: u3, //: Mode + rs1: u5, + imm: i12, + }; + pub const Lui = packed struct { + opcode: u7 = 0b0110111, + rd: u5, + imm: i20, + }; + pub const Load = packed struct { + pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 }; + opcode: u7 = 0b0000011, + rd: u5, + mode: u3, //: Mode + rs1: u5, + offset: i12, }; }; // zig fmt: off pub const Register = enum(u8) { // 64 bit registers - zero = 0, // zero - ra = 1, // return address. caller saved - sp = 2, // stack pointer. callee saved. - gp = 3, // global pointer - tp = 4, // thread pointer - t0 = 5, t1 = 6, t2 = 7, // temporaries. caller saved. - s0 = 8, // s0/fp, callee saved. + zero, // zero + ra, // return address. caller saved + sp, // stack pointer. callee saved. + gp, // global pointer + tp, // thread pointer + t0, t1, t2, // temporaries. caller saved. + s0, // s0/fp, callee saved. s1, // callee saved. a0, a1, // fn args/return values. caller saved. a2, a3, a4, a5, a6, a7, // fn args. caller saved. From 9e7a474a4112ce58d672d1dab903f7d7b2bab4b0 Mon Sep 17 00:00:00 2001 From: pfg Date: Tue, 4 Aug 2020 02:33:40 -0700 Subject: [PATCH 3/5] stage2: riscv 0 argument non-nested function calls --- src-self-hosted/codegen.zig | 60 +++++++++++++++++++++++------ src-self-hosted/codegen/riscv64.zig | 30 +++++++++++++-- 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 7df202c9a671..9523f390673e 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -950,8 +950,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.code.append(0xcc); // int3 }, .riscv64 => { - const full = @bitCast(u32, Instructions.CallBreak{ - .mode = @enumToInt(Instructions.CallBreak.Mode.ebreak), + const full = @bitCast(u32, instructions.CallBreak{ + .mode = @enumToInt(instructions.CallBreak.Mode.ebreak), }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full); @@ -1018,6 +1018,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } }, + .riscv64 => { + if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch}); + + if (inst.func.cast(ir.Inst.Constant)) |func_inst| { + if (func_inst.val.cast(Value.Payload.Function)) |func_val| { + const func = func_val.func; + const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?]; + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes); + + try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr }); + const jalr = instructions.Jalr{ + .rd = Register.ra.id(), + .rs1 = Register.ra.id(), + .offset = 0, + }; + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr)); + } else { + return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); + } + } else { + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + } + }, else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), } @@ -1066,6 +1091,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); }, + .riscv64 => { + const jalr = instructions.Jalr{ + .rd = Register.zero.id(), + .rs1 = Register.ra.id(), + .offset = 0, + }; + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr)); + }, else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), } return .unreach; @@ -1267,8 +1300,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } if (mem.eql(u8, inst.asm_source, "ecall")) { - const full = @bitCast(u32, Instructions.CallBreak{ - .mode = @enumToInt(Instructions.CallBreak.Mode.ecall), + const full = @bitCast(u32, instructions.CallBreak{ + .mode = @enumToInt(instructions.CallBreak.Mode.ecall), }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full); @@ -1481,8 +1514,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .immediate => |unsigned_x| { const x = @bitCast(i64, unsigned_x); if (math.minInt(i12) <= x and x <= math.maxInt(i12)) { - const instruction = @bitCast(u32, Instructions.Addi{ - .mode = @enumToInt(Instructions.Addi.Mode.addi), + const instruction = @bitCast(u32, instructions.Addi{ + .mode = @enumToInt(instructions.Addi.Mode.addi), .imm = @truncate(i12, x), .rs1 = Register.zero.id(), .rd = reg.id(), @@ -1498,14 +1531,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, @truncate(i32, x)); if (split.low12 < 0) return self.fail(src, "TODO support riscv64 genSetReg i32 immediates with 12th bit set to 1", .{}); - const lui = @bitCast(u32, Instructions.Lui{ + const lui = @bitCast(u32, instructions.Lui{ .imm = split.up20, .rd = reg.id(), }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), lui); - const addi = @bitCast(u32, Instructions.Addi{ - .mode = @enumToInt(Instructions.Addi.Mode.addi), + const addi = @bitCast(u32, instructions.Addi{ + .mode = @enumToInt(instructions.Addi.Mode.addi), .imm = @truncate(i12, split.low12), .rs1 = reg.id(), .rd = reg.id(), @@ -1513,6 +1546,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi); return; } + // li rd, immediate + // "Myriad sequences" return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf }, .memory => |addr| { @@ -1520,8 +1555,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // If the type is a pointer, it means the pointer address is at this memory location. try self.genSetReg(src, reg, .{ .immediate = addr }); - const ld = @bitCast(u32, Instructions.Load{ - .mode = @enumToInt(Instructions.Load.Mode.ld), + const ld = @bitCast(u32, instructions.Load{ + .mode = @enumToInt(instructions.Load.Mode.ld), .rs1 = reg.id(), .rd = reg.id(), .offset = 0, @@ -1967,6 +2002,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } }); fn parseRegName(name: []const u8) ?Register { + if (@hasDecl(Register, "parseRegName")) { + return Register.parseRegName(name); + } return std.meta.stringToEnum(Register, name); } diff --git a/src-self-hosted/codegen/riscv64.zig b/src-self-hosted/codegen/riscv64.zig index 1dab28901069..4b311feaf9a5 100644 --- a/src-self-hosted/codegen/riscv64.zig +++ b/src-self-hosted/codegen/riscv64.zig @@ -1,4 +1,6 @@ -pub const Instructions = struct { +const std = @import("std"); + +pub const instructions = struct { pub const CallBreak = packed struct { pub const Mode = packed enum(u12) { ecall, ebreak }; opcode: u7 = 0b1110011, @@ -7,6 +9,7 @@ pub const Instructions = struct { unused3: u5 = 0, mode: u12, //: Mode }; + // I-type pub const Addi = packed struct { pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 }; opcode: u7 = 0b0010011, @@ -20,6 +23,7 @@ pub const Instructions = struct { rd: u5, imm: i20, }; + // I_type pub const Load = packed struct { pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 }; opcode: u7 = 0b0000011, @@ -28,9 +32,24 @@ pub const Instructions = struct { rs1: u5, offset: i12, }; + // I-type + pub const Jalr = packed struct { + opcode: u7 = 0b1100111, + rd: u5, + mode: u3 = 0, + rs1: u5, + offset: i12, + }; }; // zig fmt: off +pub const RawRegister = enum(u8) { + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, + x24, x25, x26, x27, x28, x29, x30, x31, +}; + pub const Register = enum(u8) { // 64 bit registers zero, // zero @@ -45,6 +64,12 @@ pub const Register = enum(u8) { a2, a3, a4, a5, a6, a7, // fn args. caller saved. s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved. t3, t4, t5, t6, // caller saved + + pub fn parseRegName(name: []const u8) ?Register { + if(std.meta.stringToEnum(Register, name)) |reg| return reg; + if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg)); + return null; + } /// Returns the bit-width of the register. pub fn size(self: @This()) u7 { @@ -58,8 +83,7 @@ pub const Register = enum(u8) { return self; } - /// Returns the register's id. This is used in practically every opcode the - /// riscv64 has. + /// Returns the register's id. pub fn id(self: @This()) u5 { return @truncate(u5, @enumToInt(self)); } From d896f2abef5efdb30fe1897b999108c50678a384 Mon Sep 17 00:00:00 2001 From: pfg Date: Tue, 4 Aug 2020 03:30:55 -0700 Subject: [PATCH 4/5] stage2: riscv correctly aligned functions --- src-self-hosted/type.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 457a69ac6d7a..ca46caeaa8c0 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -510,13 +510,18 @@ pub const Type = extern union { .u8, .i8, .bool, + .array_u8_sentinel_0, + => return 1, + .fn_noreturn_no_args, // represents machine code; not a pointer .fn_void_no_args, // represents machine code; not a pointer .fn_naked_noreturn_no_args, // represents machine code; not a pointer .fn_ccc_void_no_args, // represents machine code; not a pointer .function, // represents machine code; not a pointer - .array_u8_sentinel_0, - => return 1, + => return switch (target.cpu.arch) { + .riscv64 => 2, + else => 1, + }, .i16, .u16 => return 2, .i32, .u32 => return 4, From 4f07264637a06085bab7cc46afc25153f57996bd Mon Sep 17 00:00:00 2001 From: pfg Date: Tue, 4 Aug 2020 14:32:09 -0700 Subject: [PATCH 5/5] stage2: riscv test case --- src-self-hosted/codegen.zig | 7 ++---- src-self-hosted/codegen/riscv64.zig | 12 ---------- test/stage2/compare_output.zig | 36 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 9523f390673e..2475d3884bc6 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -515,7 +515,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { entry.value = .dead; switch (prev_value) { .register => |reg| { - const reg64 = reg.to64(); + const reg64 = if (arch == .x86_64) reg.to64() else reg; _ = branch.registers.remove(reg64); branch.markRegFree(reg64); }, @@ -1506,10 +1506,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (!self.wantSafety()) return; // The already existing value will do just fine. // Write the debug undefined value. - switch (reg.size()) { - 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), - else => unreachable, - } + return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .immediate => |unsigned_x| { const x = @bitCast(i64, unsigned_x); diff --git a/src-self-hosted/codegen/riscv64.zig b/src-self-hosted/codegen/riscv64.zig index 4b311feaf9a5..c5c762709a69 100644 --- a/src-self-hosted/codegen/riscv64.zig +++ b/src-self-hosted/codegen/riscv64.zig @@ -71,18 +71,6 @@ pub const Register = enum(u8) { return null; } - /// Returns the bit-width of the register. - pub fn size(self: @This()) u7 { - return switch (@enumToInt(self)) { - 0...31 => 64, - else => unreachable, - }; - } - - pub fn to64(self: @This()) Register { - return self; - } - /// Returns the register's id. pub fn id(self: @This()) u5 { return @truncate(u5, @enumToInt(self)); diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index b461e2114451..b124a4ef2db6 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -403,4 +403,40 @@ pub fn addCases(ctx: *TestContext) !void { "", ); } + + { + var case = ctx.exe("hello world with updates", riscv64); + // Regular old hello world + case.addCompareOutput( + \\export fn _start() noreturn { + \\ print(); + \\ + \\ exit(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("ecall" + \\ : + \\ : [number] "{a7}" (64), + \\ [arg1] "{a0}" (1), + \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{a2}" ("Hello, World!\n".len) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("ecall" + \\ : + \\ : [number] "{a7}" (94), + \\ [arg1] "{a0}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } }