implement vector math safety with ext and trunc
This commit is contained in:
@@ -1773,25 +1773,46 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
|
||||
}
|
||||
}
|
||||
|
||||
typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
|
||||
// These are lookup table using the AddSubMul enum as the lookup.
|
||||
// If AddSubMul ever changes, then these tables will be out of
|
||||
// date.
|
||||
static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
|
||||
static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
|
||||
static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
|
||||
static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
|
||||
|
||||
static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,
|
||||
LLVMValueRef val1, LLVMValueRef val2)
|
||||
{
|
||||
LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
|
||||
LLVMValueRef params[] = {
|
||||
val1,
|
||||
val2,
|
||||
};
|
||||
LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
|
||||
LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
|
||||
|
||||
LLVMValueRef overflow_bit;
|
||||
LLVMValueRef result;
|
||||
|
||||
if (operand_type->id == ZigTypeIdVector) {
|
||||
LLVMValueRef overflow_vector = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
|
||||
LLVMTypeRef bigger_int_type_ref = LLVMIntType(operand_type->data.vector.len);
|
||||
LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bigger_int_type_ref, "");
|
||||
LLVMValueRef zero = LLVMConstNull(bigger_int_type_ref);
|
||||
ZigType *int_type = operand_type->data.vector.elem_type;
|
||||
assert(int_type->id == ZigTypeIdInt);
|
||||
LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1);
|
||||
LLVMTypeRef one_more_bit_int_vector = LLVMVectorType(one_more_bit_int, operand_type->data.vector.len);
|
||||
const auto buildExtFn = int_type->data.integral.is_signed ? LLVMBuildSExt : LLVMBuildZExt;
|
||||
LLVMValueRef extended1 = buildExtFn(g->builder, val1, one_more_bit_int_vector, "");
|
||||
LLVMValueRef extended2 = buildExtFn(g->builder, val2, one_more_bit_int_vector, "");
|
||||
LLVMValueRef extended_result = wrap_op[op](g->builder, extended1, extended2, "");
|
||||
result = LLVMBuildTrunc(g->builder, extended_result, operand_type->type_ref, "");
|
||||
|
||||
LLVMValueRef re_extended_result = buildExtFn(g->builder, result, one_more_bit_int_vector, "");
|
||||
LLVMValueRef overflow_vector = LLVMBuildICmp(g->builder, LLVMIntNE, extended_result, re_extended_result, "");
|
||||
LLVMTypeRef bitcast_int_type = LLVMIntType(operand_type->data.vector.len);
|
||||
LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bitcast_int_type, "");
|
||||
LLVMValueRef zero = LLVMConstNull(bitcast_int_type);
|
||||
overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, "");
|
||||
} else {
|
||||
LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
|
||||
LLVMValueRef params[] = {
|
||||
val1,
|
||||
val2,
|
||||
};
|
||||
LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
|
||||
result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
|
||||
overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
|
||||
}
|
||||
|
||||
@@ -2623,8 +2644,6 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
|
||||
|
||||
}
|
||||
|
||||
typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
|
||||
|
||||
static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionBinOp *bin_op_instruction)
|
||||
{
|
||||
@@ -2690,14 +2709,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
case IrBinOpAddWrap:
|
||||
case IrBinOpSub:
|
||||
case IrBinOpSubWrap: {
|
||||
// These are lookup table using the AddSubMul enum as the lookup.
|
||||
// If AddSubMul ever changes, then these tables will be out of
|
||||
// date.
|
||||
static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
|
||||
static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
|
||||
static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
|
||||
static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
|
||||
|
||||
bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
|
||||
AddSubMul add_sub_mul =
|
||||
op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
|
||||
|
||||
Reference in New Issue
Block a user