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(
928928 .sub => .sub,
929929 .cmp_eq => .cmp_eq,
930930 .cmp_gt => .cmp_gt,
931 .shl => .sllw,
932 .shr => .srlw,
931933 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
932934 };
933935
......@@ -947,6 +949,84 @@ fn binOpRegister(
947949 return MCValue{ .register = dest_reg };
948950}
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
9501030/// For all your binary operation needs, this function will generate
9511031/// the corresponding Mir instruction(s). Returns the location of the
9521032/// result.
......@@ -989,8 +1069,10 @@ fn binOp(
9891069 assert(lhs_ty.eql(rhs_ty, mod));
9901070 const int_info = lhs_ty.intInfo(mod);
9911071 if (int_info.bits <= 64) {
992 // TODO immediate operands
993 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1072 if (rhs == .immediate) {
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);
9941076 } else {
9951077 return self.fail("TODO binary operations on int with bits > 64", .{});
9961078 }
......@@ -1025,6 +1107,28 @@ fn binOp(
10251107 else => unreachable,
10261108 }
10271109 },
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 },
10281132 else => unreachable,
10291133 }
10301134}
......@@ -1163,7 +1267,13 @@ fn airXor(self: *Self, inst: Air.Inst.Index) !void {
11631267
11641268fn airShl(self: *Self, inst: Air.Inst.Index) !void {
11651269 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 };
11671277 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11681278}
11691279
......@@ -1426,7 +1536,11 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
14261536
14271537fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
14281538 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 };
14301544 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
14311545}
14321546
src/arch/riscv64/Emit.zig+16-5
......@@ -98,7 +98,11 @@ pub fn emitMir(
9898 .sh => try emit.mirIType(inst),
9999 .sb => try emit.mirIType(inst),
100100
101 .srlw => try emit.mirRType(inst),
102 .sllw => try emit.mirRType(inst),
103
101104 .srli => try emit.mirIType(inst),
105 .slli => try emit.mirIType(inst),
102106
103107 .ldr_ptr_stack => try emit.mirIType(inst),
104108
......@@ -173,14 +177,20 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
173177 const tag = emit.mir.instructions.items(.tag)[inst];
174178 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
176184 switch (tag) {
177 .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),
178 .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)),
179 .cmp_gt => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),
185 .add => try emit.writeInstruction(Instruction.add(rd, rs1, rs2)),
186 .sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)),
187 .cmp_gt => try emit.writeInstruction(Instruction.slt(rd, rs1, rs2)),
180188 .cmp_eq => {
181 try emit.writeInstruction(Instruction.xor(r_type.rd, r_type.rs1, r_type.rs2));
182 try emit.writeInstruction(Instruction.sltiu(r_type.rd, r_type.rd, 1));
189 try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));
190 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
183191 },
192 .sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)),
193 .srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)),
184194 else => unreachable,
185195 }
186196}
......@@ -231,6 +241,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
231241 },
232242
233243 .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
235246 else => unreachable,
236247 }
src/arch/riscv64/Mir.zig+7-1
......@@ -41,8 +41,14 @@ pub const Inst = struct {
4141 /// Absolute Value, uses i_type payload.
4242 abs,
4343
44 /// Logical Right Shift, uses i_type payload
44 /// Immediate Logical Right Shift, uses i_type payload
4545 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
4753 jal,
4854 /// Jumps. Uses `inst` payload.