diff options
| author | Joachim Schmidt <joachim.schmidt557@outlook.com> | 2022-03-21 12:36:47 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-03-21 12:36:47 +0100 |
| commit | b48d8cce52369e7625f9dab8a37efe9c3a33186b (patch) | |
| tree | 1ff1d7af0c7a98a74f651dab3505be75e499438b /src | |
| parent | 3d8d6c0a6d7a29396725467672023b5ec3adbce6 (diff) | |
| parent | a153732d5a86f7d8c5ca5bc91cfad1ccf0f8f573 (diff) | |
| download | zig-b48d8cce52369e7625f9dab8a37efe9c3a33186b.tar.gz zig-b48d8cce52369e7625f9dab8a37efe9c3a33186b.zip | |
Merge pull request #11235 from joachimschmidt557/stage2-riscv
stage2 RISCV64: remove MCValue.embedded_in_code
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/riscv64/CodeGen.zig | 290 | ||||
| -rw-r--r-- | src/arch/riscv64/Emit.zig | 25 | ||||
| -rw-r--r-- | src/arch/riscv64/Mir.zig | 18 |
3 files changed, 262 insertions, 71 deletions
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 4565322a5c..14beedd2a9 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -111,11 +111,6 @@ const MCValue = union(enum) { /// A pointer-sized integer that fits in a register. /// If the type is a pointer, this is the pointer address in virtual address space. immediate: u64, - /// The constant was emitted into the code, at this offset. - /// If the type is a pointer, it means the pointer address is embedded in the code. - embedded_in_code: usize, - /// The value is a pointer to a constant which was emitted into the code, at this offset. - ptr_embedded_in_code: usize, /// The value is in a target-specific register. register: Register, /// The value is in memory at a hard-coded address. @@ -129,7 +124,7 @@ const MCValue = union(enum) { fn isMemory(mcv: MCValue) bool { return switch (mcv) { - .embedded_in_code, .memory, .stack_offset => true, + .memory, .stack_offset => true, else => false, }; } @@ -148,10 +143,8 @@ const MCValue = union(enum) { .dead => unreachable, .immediate, - .embedded_in_code, .memory, .ptr_stack_offset, - .ptr_embedded_in_code, .undef, => false, @@ -416,14 +409,17 @@ fn gen(self: *Self) !void { }); // exitlude jumps - if (self.exitlude_jump_relocs.items.len == 1) { - // There is only one relocation. Hence, - // this relocation must be at the end of - // the code. Therefore, we can just delete - // the space initially reserved for the - // jump - self.mir_instructions.len -= 1; - } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { + if (self.exitlude_jump_relocs.items.len > 0 and + self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) + { + // If the last Mir instruction (apart from the + // dbg_epilogue_begin) is the last exitlude jump + // relocation (which would just jump one instruction + // further), it can be safely removed + self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); + } + + for (self.exitlude_jump_relocs.items) |jmp_reloc| { _ = jmp_reloc; return self.fail("TODO add branches in RISCV64", .{}); } @@ -496,10 +492,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airAdd(inst), + .add, .ptr_add => try self.airBinOp(inst), .addwrap => try self.airAddWrap(inst), .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airSub(inst), + .sub, .ptr_sub => try self.airBinOp(inst), .subwrap => try self.airSubWrap(inst), .sub_sat => try self.airSubSat(inst), .mul => try self.airMul(inst), @@ -927,9 +923,182 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAdd(self: *Self, inst: Air.Inst.Index) !void { +/// Don't call this function directly. Use binOp instead. +/// +/// Calling this function signals an intention to generate a Mir +/// instruction of the form +/// +/// op dest, lhs, rhs +/// +/// Asserts that generating an instruction of that form is possible. +fn binOpRegister( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; + + if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex(bin_op.lhs).?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst); + self.register_manager.freezeRegs(&.{reg}); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + + const rhs_reg = if (rhs_is_register) rhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex(bin_op.rhs).?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst); + self.register_manager.freezeRegs(&.{reg}); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + + const dest_reg = if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst); + } + } else try self.register_manager.allocReg(null); + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add => .add, + .sub => .sub, + else => unreachable, + }; + const mir_data: Mir.Inst.Data = switch (tag) { + .add, + .sub, + => .{ .r_type = .{ + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2 = rhs_reg, + } }, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = mir_tag, + .data = mir_data, + }); + + return MCValue{ .register = dest_reg }; +} + +/// For all your binary operation needs, this function will generate +/// the corresponding Mir instruction(s). Returns the location of the +/// result. +/// +/// If the binary operation itself happens to be an Air instruction, +/// pass the corresponding index in the inst parameter. That helps +/// this function do stuff like reusing operands. +/// +/// This function does not do any lowering to Mir itself, but instead +/// looks at the lhs and rhs and determines which kind of lowering +/// would be best suitable and then delegates the lowering to other +/// functions. +fn binOp( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) InnerError!MCValue { + switch (tag) { + // Arithmetic operations on integers and floats + .add, + .sub, + => { + switch (lhs_ty.zigTypeTag()) { + .Float => return self.fail("TODO binary operations on floats", .{}), + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + // TODO immediate operands + return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, + .ptr_add, + .ptr_sub, + => { + switch (lhs_ty.zigTypeTag()) { + .Pointer => { + const ptr_ty = lhs_ty; + const elem_ty = switch (ptr_ty.ptrSize()) { + .One => ptr_ty.childType().childType(), // ptr to array, so get array element type + else => ptr_ty.childType(), + }; + const elem_size = elem_ty.abiSize(self.target.*); + + if (elem_size == 1) { + const base_tag: Air.Inst.Tag = switch (tag) { + .ptr_add => .add, + .ptr_sub => .sub, + else => unreachable, + }; + + return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } else { + return self.fail("TODO ptr_add with elem_size > 1", .{}); + } + }, + else => unreachable, + } + }, + else => unreachable, + } +} + +fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch}); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -945,12 +1114,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airSub(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); @@ -1283,12 +1446,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .dead => unreachable, .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), - .ptr_embedded_in_code => |off| { - try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); - }, - .embedded_in_code => { - return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); - }, .register => { return self.fail("TODO implement loading from MCValue.register", .{}); }, @@ -1331,27 +1488,19 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airStore(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const ptr = try self.resolveInst(bin_op.lhs); - const value = try self.resolveInst(bin_op.rhs); - const elem_ty = self.air.typeOf(bin_op.rhs); +fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) !void { + _ = ptr_ty; + switch (ptr) { .none => unreachable, .undef => unreachable, .unreach => unreachable, .dead => unreachable, .immediate => |imm| { - try self.setRegOrMem(elem_ty, .{ .memory = imm }, value); + try self.setRegOrMem(value_ty, .{ .memory = imm }, value); }, .ptr_stack_offset => |off| { - try self.genSetStack(elem_ty, off, value); - }, - .ptr_embedded_in_code => |off| { - try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value); - }, - .embedded_in_code => { - return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); + try self.genSetStack(value_ty, off, value); }, .register => { return self.fail("TODO implement storing to MCValue.register", .{}); @@ -1363,6 +1512,17 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement storing to MCValue.stack_offset", .{}); }, } +} + +fn airStore(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ptr = try self.resolveInst(bin_op.lhs); + const value = try self.resolveInst(bin_op.rhs); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const value_ty = self.air.typeOf(bin_op.rhs); + + try self.store(ptr, value, ptr_ty, value_ty); + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1508,7 +1668,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .immediate => unreachable, .unreach => unreachable, .dead => unreachable, - .embedded_in_code => unreachable, .memory => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); @@ -1520,9 +1679,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, } } @@ -2052,7 +2208,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (mcv) { .dead => unreachable, .ptr_stack_offset => unreachable, - .ptr_embedded_in_code => unreachable, .unreach, .none => return, // Nothing to do. .undef => { if (!self.wantSafety()) @@ -2098,6 +2253,20 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf } }, + .register => |src_reg| { + // If the registers are the same, nothing to do. + if (src_reg.id() == reg.id()) + return; + + // mov reg, src_reg + _ = try self.addInst(.{ + .tag = .mv, + .data = .{ .rr = .{ + .rd = reg, + .rs = src_reg, + } }, + }); + }, .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. @@ -2321,27 +2490,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -/// If the MCValue is an immediate, and it does not fit within this type, -/// we put it in a register. -/// A potential opportunity for future optimization here would be keeping track -/// of the fact that the instruction is available both as an immediate -/// and as a register. -fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue { - const mcv = try self.resolveInst(operand); - const ti = @typeInfo(T).Int; - switch (mcv) { - .immediate => |imm| { - // This immediate is unsigned. - const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); - if (imm >= math.maxInt(U)) { - return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) }; - } - }, - else => {}, - } - return mcv; -} - fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 840247cf80..c34d2f4ff4 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -43,6 +43,9 @@ pub fn emitMir( for (mir_tags) |tag, index| { const inst = @intCast(u32, index); switch (tag) { + .add => try emit.mirRType(inst), + .sub => try emit.mirRType(inst), + .addi => try emit.mirIType(inst), .jalr => try emit.mirIType(inst), .ld => try emit.mirIType(inst), @@ -56,6 +59,8 @@ pub fn emitMir( .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), + .mv => try emit.mirRR(inst), + .nop => try emit.mirNop(inst), .ret => try emit.mirNop(inst), @@ -131,6 +136,17 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { } } +fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const r_type = emit.mir.instructions.items(.data)[inst].r_type; + + switch (tag) { + .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)), + .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)), + else => unreachable, + } +} + fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const i_type = emit.mir.instructions.items(.data)[inst].i_type; @@ -186,6 +202,15 @@ fn mirDebugEpilogueBegin(self: *Emit) !void { } } +fn mirRR(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const rr = emit.mir.instructions.items(.data)[inst].rr; + + switch (tag) { + .mv => try emit.writeInstruction(Instruction.addi(rr.rd, rr.rs, 0)), + else => unreachable, + } +} fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const u_type = emit.mir.instructions.items(.data)[inst].u_type; diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 70546e0803..5df3a86229 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -24,6 +24,7 @@ pub const Inst = struct { data: Data, pub const Tag = enum(u16) { + add, addi, /// Pseudo-instruction: End of prologue dbg_prologue_end, @@ -36,9 +37,11 @@ pub const Inst = struct { jalr, ld, lui, + mv, nop, ret, sd, + sub, }; /// The position of an MIR instruction within the `Mir` instructions array. @@ -68,6 +71,13 @@ pub const Inst = struct { /// /// Used by e.g. blr reg: Register, + /// Two registers + /// + /// Used by e.g. mv + rr: struct { + rd: Register, + rs: Register, + }, /// I-Type /// /// Used by e.g. jalr @@ -76,6 +86,14 @@ pub const Inst = struct { rs1: Register, imm12: i12, }, + /// R-Type + /// + /// Used by e.g. add + r_type: struct { + rd: Register, + rs1: Register, + rs2: Register, + }, /// U-Type /// /// Used by e.g. lui |
