| author | |
| committer | |
| log | 15f111a085dcd2da7b91da8e29b89def24cb3c6a |
| tree | b06957a599f66d333a4b91e1e489bacaee5f3a15 |
| parent | b1873f20744dfa9b5402b936ccc0858d7ef01529 |
LLVM 14 makes it so that a RHS of saturating shift left produces a
poison value if the value is greater than the number of bits of the LHS.
Zig now emits code that will check if this is the case and select a
saturated LHS value in such case, matching Zig semantics.2 files changed, 52 insertions(+), 13 deletions(-)
src/codegen/llvm.zig+27-5| ... | @@ -6817,15 +6817,37 @@ pub const FuncGen = struct { | ... | @@ -6817,15 +6817,37 @@ pub const FuncGen = struct { |
| 6817 | const rhs_ty = self.air.typeOf(bin_op.rhs); | 6817 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 6818 | const lhs_scalar_ty = lhs_ty.scalarType(); | 6818 | const lhs_scalar_ty = lhs_ty.scalarType(); |
| 6819 | const rhs_scalar_ty = rhs_ty.scalarType(); | 6819 | const rhs_scalar_ty = rhs_ty.scalarType(); |
| 6820 | |||
| 6821 | const tg = self.dg.module.getTarget(); | 6820 | const tg = self.dg.module.getTarget(); |
| 6821 | const lhs_bits = lhs_scalar_ty.bitSize(tg); | ||
| 6822 | 6822 | ||
| 6823 | const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) | 6823 | const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_bits) |
| 6824 | self.builder.buildZExt(rhs, try self.dg.lowerType(lhs_ty), "") | 6824 | self.builder.buildZExt(rhs, lhs.typeOf(), "") |
| 6825 | else | 6825 | else |
| 6826 | rhs; | 6826 | rhs; |
| 6827 | if (lhs_scalar_ty.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, ""); | 6827 | |
| 6828 | return self.builder.buildUShlSat(lhs, casted_rhs, ""); | 6828 | const result = if (lhs_scalar_ty.isSignedInt()) |
| 6829 | self.builder.buildSShlSat(lhs, casted_rhs, "") | ||
| 6830 | else | ||
| 6831 | self.builder.buildUShlSat(lhs, casted_rhs, ""); | ||
| 6832 | |||
| 6833 | // LLVM langref says "If b is (statically or dynamically) equal to or | ||
| 6834 | // larger than the integer bit width of the arguments, the result is a | ||
| 6835 | // poison value." | ||
| 6836 | // However Zig semantics says that saturating shift left can never produce | ||
| 6837 | // undefined; instead it saturates. | ||
| 6838 | const lhs_scalar_llvm_ty = try self.dg.lowerType(lhs_scalar_ty); | ||
| 6839 | const bits = lhs_scalar_llvm_ty.constInt(lhs_bits, .False); | ||
| 6840 | const lhs_max = lhs_scalar_llvm_ty.constAllOnes(); | ||
| 6841 | if (rhs_ty.zigTypeTag() == .Vector) { | ||
| 6842 | const vec_len = rhs_ty.vectorLen(); | ||
| 6843 | const bits_vec = self.builder.buildVectorSplat(vec_len, bits, ""); | ||
| 6844 | const lhs_max_vec = self.builder.buildVectorSplat(vec_len, lhs_max, ""); | ||
| 6845 | const in_range = self.builder.buildICmp(.ULT, rhs, bits_vec, ""); | ||
| 6846 | return self.builder.buildSelect(in_range, result, lhs_max_vec, ""); | ||
| 6847 | } else { | ||
| 6848 | const in_range = self.builder.buildICmp(.ULT, rhs, bits, ""); | ||
| 6849 | return self.builder.buildSelect(in_range, result, lhs_max, ""); | ||
| 6850 | } | ||
| 6829 | } | 6851 | } |
| 6830 | 6852 | ||
| 6831 | fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*const llvm.Value { | 6853 | fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*const llvm.Value { |
src/stage1/codegen.cpp+25-8| ... | @@ -3868,16 +3868,33 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, | ... | @@ -3868,16 +3868,33 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, |
| 3868 | } else { | 3868 | } else { |
| 3869 | zig_unreachable(); | 3869 | zig_unreachable(); |
| 3870 | } | 3870 | } |
| 3871 | case IrBinOpShlSat: | 3871 | case IrBinOpShlSat: { |
| 3872 | if (scalar_type->id == ZigTypeIdInt) { | 3872 | if (scalar_type->id != ZigTypeIdInt) { |
| 3873 | if (scalar_type->data.integral.is_signed) { | ||
| 3874 | return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, ""); | ||
| 3875 | } else { | ||
| 3876 | return ZigLLVMBuildUShlSat(g->builder, op1_value, op2_value, ""); | ||
| 3877 | } | ||
| 3878 | } else { | ||
| 3879 | zig_unreachable(); | 3873 | zig_unreachable(); |
| 3880 | } | 3874 | } |
| 3875 | LLVMValueRef result = scalar_type->data.integral.is_signed ? | ||
| 3876 | ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "") : | ||
| 3877 | ZigLLVMBuildUShlSat(g->builder, op1_value, op2_value, ""); | ||
| 3878 | // LLVM langref says "If b is (statically or dynamically) equal to or | ||
| 3879 | // larger than the integer bit width of the arguments, the result is a | ||
| 3880 | // poison value." | ||
| 3881 | // However Zig semantics says that saturating shift left can never produce | ||
| 3882 | // undefined; instead it saturates. | ||
| 3883 | LLVMTypeRef lhs_scalar_llvm_ty = get_llvm_type(g, scalar_type); | ||
| 3884 | LLVMValueRef bits = LLVMConstInt(lhs_scalar_llvm_ty, | ||
| 3885 | scalar_type->data.integral.bit_count, false); | ||
| 3886 | LLVMValueRef lhs_max = LLVMConstAllOnes(lhs_scalar_llvm_ty); | ||
| 3887 | if (operand_type->id == ZigTypeIdVector) { | ||
| 3888 | uint64_t vec_len = operand_type->data.vector.len; | ||
| 3889 | LLVMValueRef bits_vec = LLVMBuildVectorSplat(g->builder, vec_len, bits, ""); | ||
| 3890 | LLVMValueRef lhs_max_vec = LLVMBuildVectorSplat(g->builder, vec_len, lhs_max, ""); | ||
| 3891 | LLVMValueRef in_range = LLVMBuildICmp(g->builder, LLVMIntULT, op2_value, bits_vec, ""); | ||
| 3892 | return LLVMBuildSelect(g->builder, in_range, result, lhs_max_vec, ""); | ||
| 3893 | } else { | ||
| 3894 | LLVMValueRef in_range = LLVMBuildICmp(g->builder, LLVMIntULT, op2_value, bits, ""); | ||
| 3895 | return LLVMBuildSelect(g->builder, in_range, result, lhs_max, ""); | ||
| 3896 | } | ||
| 3897 | } | ||
| 3881 | } | 3898 | } |
| 3882 | zig_unreachable(); | 3899 | zig_unreachable(); |
| 3883 | } | 3900 | } |