authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-03-25 10:33:34-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:11-07:00
log3c0015c82889eff557cb937b655eccaaa7ecd01b
tree1308bdd01e13736c836e060a1b4111d33121e245
parent685f8282180016ac8d82ecf2fe7facc1a2d6b9f7

riscv: implement a basic `@intCast`

the truncation panic logic is generated in Sema, so I don't need to roll anything of my own. I add all of the boilerplate for that detecting the truncation and it works in basic test cases!

4 files changed, 91 insertions(+), 17 deletions(-)

src/Sema.zig+4-3
...@@ -10499,9 +10499,10 @@ fn intCast(...@@ -10499,9 +10499,10 @@ fn intCast(
10499 const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod, operand_scalar_ty);10499 const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod, operand_scalar_ty);
10500 const dest_max_val = try sema.splat(operand_ty, dest_max_val_scalar);10500 const dest_max_val = try sema.splat(operand_ty, dest_max_val_scalar);
10501 const dest_max = Air.internedToRef(dest_max_val.toIntern());10501 const dest_max = Air.internedToRef(dest_max_val.toIntern());
10502 const diff = try block.addBinOp(.sub_wrap, dest_max, operand);
1050310502
10504 if (actual_info.signedness == .signed) {10503 if (actual_info.signedness == .signed) {
10504 const diff = try block.addBinOp(.sub_wrap, dest_max, operand);
10505
10505 // Reinterpret the sign-bit as part of the value. This will make10506 // Reinterpret the sign-bit as part of the value. This will make
10506 // negative differences (`operand` > `dest_max`) appear too big.10507 // negative differences (`operand` > `dest_max`) appear too big.
10507 const unsigned_scalar_operand_ty = try mod.intType(.unsigned, actual_bits);10508 const unsigned_scalar_operand_ty = try mod.intType(.unsigned, actual_bits);
...@@ -10542,7 +10543,7 @@ fn intCast(...@@ -10542,7 +10543,7 @@ fn intCast(
10542 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);10543 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
10543 } else {10544 } else {
10544 const ok = if (is_vector) ok: {10545 const ok = if (is_vector) ok: {
10545 const is_in_range = try block.addCmpVector(diff, dest_max, .lte);10546 const is_in_range = try block.addCmpVector(operand, dest_max, .lte);
10546 const all_in_range = try block.addInst(.{10547 const all_in_range = try block.addInst(.{
10547 .tag = if (block.float_mode == .optimized) .reduce_optimized else .reduce,10548 .tag = if (block.float_mode == .optimized) .reduce_optimized else .reduce,
10548 .data = .{ .reduce = .{10549 .data = .{ .reduce = .{
...@@ -10552,7 +10553,7 @@ fn intCast(...@@ -10552,7 +10553,7 @@ fn intCast(
10552 });10553 });
10553 break :ok all_in_range;10554 break :ok all_in_range;
10554 } else ok: {10555 } else ok: {
10555 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);10556 const is_in_range = try block.addBinOp(.cmp_lte, operand, dest_max);
10556 break :ok is_in_range;10557 break :ok is_in_range;
10557 };10558 };
10558 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);10559 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
src/arch/riscv64/CodeGen.zig+76-14
...@@ -797,23 +797,57 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {...@@ -797,23 +797,57 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
797}797}
798798
799fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {799fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
800 const mod = self.bin_file.comp.module.?;
800 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;801 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
801 if (self.liveness.isUnused(inst))802 const src_ty = self.typeOf(ty_op.operand);
802 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });803 const dst_ty = self.typeOfIndex(inst);
803804
804 const mod = self.bin_file.comp.module.?;805 const result: MCValue = result: {
805 const operand_ty = self.typeOf(ty_op.operand);806 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(mod));
806 const operand = try self.resolveInst(ty_op.operand);
807 const info_a = operand_ty.intInfo(mod);
808 const info_b = self.typeOfIndex(inst).intInfo(mod);
809 if (info_a.signedness != info_b.signedness)
810 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
811807
812 if (info_a.bits == info_b.bits)808 const src_int_info = src_ty.intInfo(mod);
813 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });809 const dst_int_info = dst_ty.intInfo(mod);
810 const extend = switch (src_int_info.signedness) {
811 .signed => dst_int_info,
812 .unsigned => src_int_info,
813 }.signedness;
814814
815 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});815 _ = dst_abi_size;
816 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });816 _ = extend;
817
818 const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty;
819
820 const src_mcv = try self.resolveInst(ty_op.operand);
821
822 const src_storage_bits: u16 = switch (src_mcv) {
823 .register => 64,
824 .stack_offset => src_int_info.bits,
825 else => return self.fail("airIntCast from {s}", .{@tagName(src_mcv)}),
826 };
827
828 const dst_mcv = if (dst_int_info.bits <= src_storage_bits and
829 math.divCeil(u16, dst_int_info.bits, 64) catch unreachable ==
830 math.divCeil(u32, src_storage_bits, 64) catch unreachable and
831 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
832 const dst_mcv = try self.allocRegOrMem(inst, true);
833 try self.setValue(min_ty, dst_mcv, src_mcv);
834 break :dst dst_mcv;
835 };
836
837 if (dst_int_info.bits <= src_int_info.bits) {
838 break :result dst_mcv;
839 }
840
841 if (dst_int_info.bits > 64 or src_int_info.bits > 64) {
842 break :result null; // TODO
843 }
844
845 break :result dst_mcv;
846 } orelse return self.fail("TODO implement airIntCast from {} to {}", .{
847 src_ty.fmt(mod), dst_ty.fmt(mod),
848 });
849
850 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
817}851}
818852
819fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {853fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1080,7 +1114,9 @@ fn binOpImm(...@@ -1080,7 +1114,9 @@ fn binOpImm(
1080 .shr => .srli,1114 .shr => .srli,
1081 .cmp_gte => .cmp_imm_gte,1115 .cmp_gte => .cmp_imm_gte,
1082 .cmp_eq => .cmp_imm_eq,1116 .cmp_eq => .cmp_imm_eq,
1117 .cmp_lte => .cmp_imm_lte,
1083 .add => .addi,1118 .add => .addi,
1119 .sub => .addiw,
1084 else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}),1120 else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}),
1085 };1121 };
10861122
...@@ -1090,6 +1126,7 @@ fn binOpImm(...@@ -1090,6 +1126,7 @@ fn binOpImm(
1090 .srli,1126 .srli,
1091 .addi,1127 .addi,
1092 .cmp_imm_eq,1128 .cmp_imm_eq,
1129 .cmp_imm_lte,
1093 => {1130 => {
1094 _ = try self.addInst(.{1131 _ = try self.addInst(.{
1095 .tag = mir_tag,1132 .tag = mir_tag,
...@@ -1102,6 +1139,18 @@ fn binOpImm(...@@ -1102,6 +1139,18 @@ fn binOpImm(
1102 } },1139 } },
1103 });1140 });
1104 },1141 },
1142 .addiw => {
1143 _ = try self.addInst(.{
1144 .tag = mir_tag,
1145 .data = .{ .i_type = .{
1146 .rd = dest_reg,
1147 .rs1 = lhs_reg,
1148 .imm12 = -(math.cast(i12, rhs.immediate) orelse {
1149 return self.fail("TODO: binOpImm larger than i12 i_type payload", .{});
1150 }),
1151 } },
1152 });
1153 },
1105 .cmp_imm_gte => {1154 .cmp_imm_gte => {
1106 const imm_reg = try self.copyToTmpRegister(rhs_ty, .{ .immediate = rhs.immediate - 1 });1155 const imm_reg = try self.copyToTmpRegister(rhs_ty, .{ .immediate = rhs.immediate - 1 });
11071156
...@@ -1146,7 +1195,16 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1146,7 +1195,16 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
11461195
1147fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {1196fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
1148 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1197 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1149 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});1198 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1199 // RISCV arthemtic instructions already wrap, so this is simply a sub binOp with
1200 // no overflow checks.
1201 const lhs = try self.resolveInst(bin_op.lhs);
1202 const rhs = try self.resolveInst(bin_op.rhs);
1203 const lhs_ty = self.typeOf(bin_op.lhs);
1204 const rhs_ty = self.typeOf(bin_op.rhs);
1205
1206 break :result try self.binOp(.sub, inst, lhs, rhs, lhs_ty, rhs_ty);
1207 };
1150 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1208 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1151}1209}
11521210
...@@ -3441,3 +3499,7 @@ fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type {...@@ -3441,3 +3499,7 @@ fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type {
3441 const mod = self.bin_file.comp.module.?;3499 const mod = self.bin_file.comp.module.?;
3442 return self.air.typeOfIndex(inst, &mod.intern_pool);3500 return self.air.typeOfIndex(inst, &mod.intern_pool);
3443}3501}
3502
3503fn hasFeature(self: *Self, feature: Target.riscv.Feature) bool {
3504 return Target.riscv.featureSetHas(self.target.cpu.features, feature);
3505}
src/arch/riscv64/Emit.zig+8
...@@ -64,11 +64,13 @@ pub fn emitMir(...@@ -64,11 +64,13 @@ pub fn emitMir(
64 .cmp_lt => try emit.mirRType(inst),64 .cmp_lt => try emit.mirRType(inst),
65 .cmp_imm_gte => try emit.mirRType(inst),65 .cmp_imm_gte => try emit.mirRType(inst),
66 .cmp_imm_eq => try emit.mirIType(inst),66 .cmp_imm_eq => try emit.mirIType(inst),
67 .cmp_imm_lte => try emit.mirIType(inst),
6768
68 .beq => try emit.mirBType(inst),69 .beq => try emit.mirBType(inst),
69 .bne => try emit.mirBType(inst),70 .bne => try emit.mirBType(inst),
7071
71 .addi => try emit.mirIType(inst),72 .addi => try emit.mirIType(inst),
73 .addiw => try emit.mirIType(inst),
72 .andi => try emit.mirIType(inst),74 .andi => try emit.mirIType(inst),
73 .jalr => try emit.mirIType(inst),75 .jalr => try emit.mirIType(inst),
74 .abs => try emit.mirIType(inst),76 .abs => try emit.mirIType(inst),
...@@ -259,6 +261,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -259,6 +261,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
259261
260 switch (tag) {262 switch (tag) {
261 .addi => try emit.writeInstruction(Instruction.addi(rd, rs1, imm12)),263 .addi => try emit.writeInstruction(Instruction.addi(rd, rs1, imm12)),
264 .addiw => try emit.writeInstruction(Instruction.addiw(rd, rs1, imm12)),
262 .jalr => try emit.writeInstruction(Instruction.jalr(rd, imm12, rs1)),265 .jalr => try emit.writeInstruction(Instruction.jalr(rd, imm12, rs1)),
263266
264 .andi => try emit.writeInstruction(Instruction.andi(rd, rs1, imm12)),267 .andi => try emit.writeInstruction(Instruction.andi(rd, rs1, imm12)),
...@@ -288,6 +291,11 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -288,6 +291,11 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
288 try emit.writeInstruction(Instruction.xori(rd, rs1, imm12));291 try emit.writeInstruction(Instruction.xori(rd, rs1, imm12));
289 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));292 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
290 },293 },
294
295 .cmp_imm_lte => {
296 try emit.writeInstruction(Instruction.sltiu(rd, rs1, @bitCast(imm12)));
297 },
298
291 else => unreachable,299 else => unreachable,
292 }300 }
293}301}
src/arch/riscv64/Mir.zig+3
...@@ -25,6 +25,7 @@ pub const Inst = struct {...@@ -25,6 +25,7 @@ pub const Inst = struct {
2525
26 pub const Tag = enum(u16) {26 pub const Tag = enum(u16) {
27 addi,27 addi,
28 addiw,
28 jalr,29 jalr,
29 lui,30 lui,
30 mv,31 mv,
...@@ -83,6 +84,8 @@ pub const Inst = struct {...@@ -83,6 +84,8 @@ pub const Inst = struct {
8384
84 /// Immediate `==`, uses i_type85 /// Immediate `==`, uses i_type
85 cmp_imm_eq,86 cmp_imm_eq,
87 /// Immediate `<=`, uses i_typei
88 cmp_imm_lte,
8689
87 /// Branch if equal, Uses b_type90 /// Branch if equal, Uses b_type
88 beq,91 beq,