authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-03-17 15:07:32-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:11-07:00
logb2150094badd3c14411a811ee0e508183b2142a2
tree6c38bc1abb2b7725c4dc45a69bde1b132f3afa58
parent664e3e16fa8dd49ff97f78dcdbf4579ff7f652aa

riscv: implement basic logical shifting


3 files changed, 141 insertions(+), 10 deletions(-)

src/arch/riscv64/CodeGen.zig+118-4
...@@ -928,6 +928,8 @@ fn binOpRegister(...@@ -928,6 +928,8 @@ fn binOpRegister(
928 .sub => .sub,928 .sub => .sub,
929 .cmp_eq => .cmp_eq,929 .cmp_eq => .cmp_eq,
930 .cmp_gt => .cmp_gt,930 .cmp_gt => .cmp_gt,
931 .shl => .sllw,
932 .shr => .srlw,
931 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),933 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
932 };934 };
933935
...@@ -947,6 +949,84 @@ fn binOpRegister(...@@ -947,6 +949,84 @@ fn binOpRegister(
947 return MCValue{ .register = dest_reg };949 return MCValue{ .register = dest_reg };
948}950}
949951
952/// Don't call this function directly. Use binOp instead.
953///
954/// Call this function if rhs is an immediate. Generates I version of binops.
955///
956/// Asserts that rhs is an immediate MCValue
957fn binOpImm(
958 self: *Self,
959 tag: Air.Inst.Tag,
960 maybe_inst: ?Air.Inst.Index,
961 lhs: MCValue,
962 rhs: MCValue,
963 lhs_ty: Type,
964 rhs_ty: Type,
965) !MCValue {
966 _ = rhs_ty;
967 assert(rhs == .immediate);
968
969 const lhs_is_register = lhs == .register;
970
971 const lhs_lock: ?RegisterLock = if (lhs_is_register)
972 self.register_manager.lockReg(lhs.register)
973 else
974 null;
975 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
976
977 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
978
979 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
980 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
981 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
982 break :inst bin_op.lhs.toIndex().?;
983 } else null;
984
985 const reg = try self.register_manager.allocReg(track_inst, gp);
986
987 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
988
989 break :blk reg;
990 };
991 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
992 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
993
994 const dest_reg = if (maybe_inst) |inst| blk: {
995 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
996
997 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
998 break :blk lhs_reg;
999 } else {
1000 break :blk try self.register_manager.allocReg(inst, gp);
1001 }
1002 } else try self.register_manager.allocReg(null, gp);
1003
1004 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1005
1006 const mir_tag: Mir.Inst.Tag = switch (tag) {
1007 .shl => .slli,
1008 .shr => .srli,
1009 else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}),
1010 };
1011
1012 _ = try self.addInst(.{
1013 .tag = mir_tag,
1014 .data = .{
1015 .i_type = .{
1016 .rd = dest_reg,
1017 .rs1 = lhs_reg,
1018 .imm12 = math.cast(i12, rhs.immediate) orelse {
1019 return self.fail("TODO: binOpImm larger than i12 i_type payload", .{});
1020 },
1021 },
1022 },
1023 });
1024
1025 // generate the struct for OF checks
1026
1027 return MCValue{ .register = dest_reg };
1028}
1029
950/// For all your binary operation needs, this function will generate1030/// For all your binary operation needs, this function will generate
951/// the corresponding Mir instruction(s). Returns the location of the1031/// the corresponding Mir instruction(s). Returns the location of the
952/// result.1032/// result.
...@@ -989,8 +1069,10 @@ fn binOp(...@@ -989,8 +1069,10 @@ fn binOp(
989 assert(lhs_ty.eql(rhs_ty, mod));1069 assert(lhs_ty.eql(rhs_ty, mod));
990 const int_info = lhs_ty.intInfo(mod);1070 const int_info = lhs_ty.intInfo(mod);
991 if (int_info.bits <= 64) {1071 if (int_info.bits <= 64) {
992 // TODO immediate operands1072 if (rhs == .immediate) {
993 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);1073 return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1074 }
1075 return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
994 } else {1076 } else {
995 return self.fail("TODO binary operations on int with bits > 64", .{});1077 return self.fail("TODO binary operations on int with bits > 64", .{});
996 }1078 }
...@@ -1025,6 +1107,28 @@ fn binOp(...@@ -1025,6 +1107,28 @@ fn binOp(
1025 else => unreachable,1107 else => unreachable,
1026 }1108 }
1027 },1109 },
1110
1111 // These instructions have unsymteric bit sizes.
1112 .shr,
1113 .shl,
1114 => {
1115 switch (lhs_ty.zigTypeTag(mod)) {
1116 .Float => return self.fail("TODO binary operations on floats", .{}),
1117 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1118 .Int => {
1119 const int_info = lhs_ty.intInfo(mod);
1120 if (int_info.bits <= 64) {
1121 if (rhs == .immediate) {
1122 return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1123 }
1124 return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1125 } else {
1126 return self.fail("TODO binary operations on int with bits > 64", .{});
1127 }
1128 },
1129 else => unreachable,
1130 }
1131 },
1028 else => unreachable,1132 else => unreachable,
1029 }1133 }
1030}1134}
...@@ -1163,7 +1267,13 @@ fn airXor(self: *Self, inst: Air.Inst.Index) !void {...@@ -1163,7 +1267,13 @@ fn airXor(self: *Self, inst: Air.Inst.Index) !void {
11631267
1164fn airShl(self: *Self, inst: Air.Inst.Index) !void {1268fn airShl(self: *Self, inst: Air.Inst.Index) !void {
1165 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1269 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1166 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch});1270 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1271 const lhs = try self.resolveInst(bin_op.lhs);
1272 const rhs = try self.resolveInst(bin_op.rhs);
1273 const lhs_ty = self.typeOf(bin_op.lhs);
1274 const rhs_ty = self.typeOf(bin_op.rhs);
1275 break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty);
1276 };
1167 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1277 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1168}1278}
11691279
...@@ -1426,7 +1536,11 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {...@@ -1426,7 +1536,11 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
14261536
1427fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {1537fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
1428 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1538 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1429 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});1539 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1540 if (true)
1541 return self.fail("TODO: airByteSwap", .{});
1542 break :result undefined;
1543 };
1430 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1544 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1431}1545}
14321546
src/arch/riscv64/Emit.zig+16-5
...@@ -98,7 +98,11 @@ pub fn emitMir(...@@ -98,7 +98,11 @@ pub fn emitMir(
98 .sh => try emit.mirIType(inst),98 .sh => try emit.mirIType(inst),
99 .sb => try emit.mirIType(inst),99 .sb => try emit.mirIType(inst),
100100
101 .srlw => try emit.mirRType(inst),
102 .sllw => try emit.mirRType(inst),
103
101 .srli => try emit.mirIType(inst),104 .srli => try emit.mirIType(inst),
105 .slli => try emit.mirIType(inst),
102106
103 .ldr_ptr_stack => try emit.mirIType(inst),107 .ldr_ptr_stack => try emit.mirIType(inst),
104108
...@@ -173,14 +177,20 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -173,14 +177,20 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
173 const tag = emit.mir.instructions.items(.tag)[inst];177 const tag = emit.mir.instructions.items(.tag)[inst];
174 const r_type = emit.mir.instructions.items(.data)[inst].r_type;178 const r_type = emit.mir.instructions.items(.data)[inst].r_type;
175179
180 const rd = r_type.rd;
181 const rs1 = r_type.rs1;
182 const rs2 = r_type.rs2;
183
176 switch (tag) {184 switch (tag) {
177 .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),185 .add => try emit.writeInstruction(Instruction.add(rd, rs1, rs2)),
178 .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)),186 .sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)),
179 .cmp_gt => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),187 .cmp_gt => try emit.writeInstruction(Instruction.slt(rd, rs1, rs2)),
180 .cmp_eq => {188 .cmp_eq => {
181 try emit.writeInstruction(Instruction.xor(r_type.rd, r_type.rs1, r_type.rs2));189 try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));
182 try emit.writeInstruction(Instruction.sltiu(r_type.rd, r_type.rd, 1));190 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
183 },191 },
192 .sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)),
193 .srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)),
184 else => unreachable,194 else => unreachable,
185 }195 }
186}196}
...@@ -231,6 +241,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -231,6 +241,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
231 },241 },
232242
233 .srli => try emit.writeInstruction(Instruction.srli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))),243 .srli => try emit.writeInstruction(Instruction.srli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))),
244 .slli => try emit.writeInstruction(Instruction.slli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))),
234245
235 else => unreachable,246 else => unreachable,
236 }247 }
src/arch/riscv64/Mir.zig+7-1
...@@ -41,8 +41,14 @@ pub const Inst = struct {...@@ -41,8 +41,14 @@ pub const Inst = struct {
41 /// Absolute Value, uses i_type payload.41 /// Absolute Value, uses i_type payload.
42 abs,42 abs,
4343
44 /// Logical Right Shift, uses i_type payload44 /// Immediate Logical Right Shift, uses i_type payload
45 srli,45 srli,
46 /// Immediate Logical Left Shift, uses i_type payload
47 slli,
48 /// Register Logical Left Shift, uses r_type payload
49 sllw,
50 /// Register Logical Right Shit, uses r_type payload
51 srlw,
4652
47 jal,53 jal,
48 /// Jumps. Uses `inst` payload.54 /// Jumps. Uses `inst` payload.