aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjoachimschmidt557 <joachim.schmidt557@outlook.com>2022-03-19 20:44:57 +0100
committerjoachimschmidt557 <joachim.schmidt557@outlook.com>2022-03-19 20:44:57 +0100
commita153732d5a86f7d8c5ca5bc91cfad1ccf0f8f573 (patch)
tree97f065f4df0b8b6f94ed3a2dc2ebed01152757af /src
parent7cdc47a4e08cd32e1f68ca4aad3ca22b11e4e7b2 (diff)
downloadzig-a153732d5a86f7d8c5ca5bc91cfad1ccf0f8f573.tar.gz
zig-a153732d5a86f7d8c5ca5bc91cfad1ccf0f8f573.zip
stage2 RISCV64: implement add, sub for registers
Diffstat (limited to 'src')
-rw-r--r--src/arch/riscv64/CodeGen.zig206
-rw-r--r--src/arch/riscv64/Emit.zig14
-rw-r--r--src/arch/riscv64/Mir.zig10
3 files changed, 212 insertions, 18 deletions
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
index 36c1752e5d..344c3de1a7 100644
--- a/src/arch/riscv64/CodeGen.zig
+++ b/src/arch/riscv64/CodeGen.zig
@@ -409,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", .{});
}
@@ -489,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),
@@ -916,9 +919,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 });
}
@@ -934,12 +1110,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});
diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig
index 10eace940e..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),
@@ -133,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;
diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig
index 60e8af2117..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,
@@ -40,6 +41,7 @@ pub const Inst = struct {
nop,
ret,
sd,
+ sub,
};
/// The position of an MIR instruction within the `Mir` instructions array.
@@ -84,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