riscv: implement basic logical shifting

This commit is contained in:
David Rubin 2024-03-17 15:07:32 -07:00
parent 664e3e16fa
commit b2150094ba
3 changed files with 141 additions and 10 deletions

View File

@ -928,6 +928,8 @@ fn binOpRegister(
.sub => .sub,
.cmp_eq => .cmp_eq,
.cmp_gt => .cmp_gt,
.shl => .sllw,
.shr => .srlw,
else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
};
@ -947,6 +949,84 @@ fn binOpRegister(
return MCValue{ .register = dest_reg };
}
/// Don't call this function directly. Use binOp instead.
///
/// Call this function if rhs is an immediate. Generates I version of binops.
///
/// Asserts that rhs is an immediate MCValue
fn binOpImm(
self: *Self,
tag: Air.Inst.Tag,
maybe_inst: ?Air.Inst.Index,
lhs: MCValue,
rhs: MCValue,
lhs_ty: Type,
rhs_ty: Type,
) !MCValue {
_ = rhs_ty;
assert(rhs == .immediate);
const lhs_is_register = lhs == .register;
const lhs_lock: ?RegisterLock = if (lhs_is_register)
self.register_manager.lockReg(lhs.register)
else
null;
defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
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)[@intFromEnum(inst)].bin_op;
break :inst bin_op.lhs.toIndex().?;
} else null;
const reg = try self.register_manager.allocReg(track_inst, gp);
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = if (maybe_inst) |inst| blk: {
const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
break :blk lhs_reg;
} else {
break :blk try self.register_manager.allocReg(inst, gp);
}
} else try self.register_manager.allocReg(null, gp);
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
const mir_tag: Mir.Inst.Tag = switch (tag) {
.shl => .slli,
.shr => .srli,
else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}),
};
_ = try self.addInst(.{
.tag = mir_tag,
.data = .{
.i_type = .{
.rd = dest_reg,
.rs1 = lhs_reg,
.imm12 = math.cast(i12, rhs.immediate) orelse {
return self.fail("TODO: binOpImm larger than i12 i_type payload", .{});
},
},
},
});
// generate the struct for OF checks
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.
@ -989,8 +1069,10 @@ fn binOp(
assert(lhs_ty.eql(rhs_ty, mod));
const int_info = lhs_ty.intInfo(mod);
if (int_info.bits <= 64) {
// TODO immediate operands
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
if (rhs == .immediate) {
return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
}
return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
} else {
return self.fail("TODO binary operations on int with bits > 64", .{});
}
@ -1025,6 +1107,28 @@ fn binOp(
else => unreachable,
}
},
// These instructions have unsymteric bit sizes.
.shr,
.shl,
=> {
switch (lhs_ty.zigTypeTag(mod)) {
.Float => return self.fail("TODO binary operations on floats", .{}),
.Vector => return self.fail("TODO binary operations on vectors", .{}),
.Int => {
const int_info = lhs_ty.intInfo(mod);
if (int_info.bits <= 64) {
if (rhs == .immediate) {
return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
}
return 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,
}
},
else => unreachable,
}
}
@ -1163,7 +1267,13 @@ fn airXor(self: *Self, inst: Air.Inst.Index) !void {
fn airShl(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch});
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
const lhs_ty = self.typeOf(bin_op.lhs);
const rhs_ty = self.typeOf(bin_op.rhs);
break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty);
};
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
@ -1426,7 +1536,11 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
if (true)
return self.fail("TODO: airByteSwap", .{});
break :result undefined;
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}

View File

@ -98,7 +98,11 @@ pub fn emitMir(
.sh => try emit.mirIType(inst),
.sb => try emit.mirIType(inst),
.srlw => try emit.mirRType(inst),
.sllw => try emit.mirRType(inst),
.srli => try emit.mirIType(inst),
.slli => try emit.mirIType(inst),
.ldr_ptr_stack => try emit.mirIType(inst),
@ -173,14 +177,20 @@ 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;
const rd = r_type.rd;
const rs1 = r_type.rs1;
const rs2 = r_type.rs2;
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)),
.cmp_gt => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),
.add => try emit.writeInstruction(Instruction.add(rd, rs1, rs2)),
.sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)),
.cmp_gt => try emit.writeInstruction(Instruction.slt(rd, rs1, rs2)),
.cmp_eq => {
try emit.writeInstruction(Instruction.xor(r_type.rd, r_type.rs1, r_type.rs2));
try emit.writeInstruction(Instruction.sltiu(r_type.rd, r_type.rd, 1));
try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));
try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
},
.sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)),
.srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)),
else => unreachable,
}
}
@ -231,6 +241,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
},
.srli => try emit.writeInstruction(Instruction.srli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))),
.slli => try emit.writeInstruction(Instruction.slli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))),
else => unreachable,
}

View File

@ -41,8 +41,14 @@ pub const Inst = struct {
/// Absolute Value, uses i_type payload.
abs,
/// Logical Right Shift, uses i_type payload
/// Immediate Logical Right Shift, uses i_type payload
srli,
/// Immediate Logical Left Shift, uses i_type payload
slli,
/// Register Logical Left Shift, uses r_type payload
sllw,
/// Register Logical Right Shit, uses r_type payload
srlw,
jal,
/// Jumps. Uses `inst` payload.