stage1: Add softfloat support for @reduce

This commit is contained in:
Cody Tapscott 2022-10-11 11:04:29 -07:00
parent f0d12dd82b
commit 6e6ae8886e
3 changed files with 123 additions and 19 deletions

View File

@ -6358,9 +6358,11 @@ void init_const_float(ZigValue *const_val, ZigType *type, double value) {
const_val->data.x_f64 = value;
break;
case 80:
zig_double_to_extF80M(value, &const_val->data.x_f80);
break;
case 128:
// if we need this, we should add a function that accepts a float128_t param
zig_unreachable();
zig_double_to_f128M(value, &const_val->data.x_f128);
break;
default:
zig_unreachable();
}

View File

@ -6481,6 +6481,55 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, Stage1Air *executable, Stage1A
return result_loc;
}
static LLVMValueRef ir_render_reduced_call(CodeGen *g, LLVMValueRef llvm_fn, LLVMValueRef operand_vector, size_t vector_len, LLVMValueRef accum_init, ZigType *accum_ty) {
LLVMTypeRef llvm_usize_ty = g->builtin_types.entry_usize->llvm_type;
LLVMValueRef llvm_vector_len = LLVMConstInt(llvm_usize_ty, vector_len, false);
LLVMTypeRef llvm_result_ty = LLVMTypeOf(accum_init);
// Allocate and initialize our mutable variables
LLVMValueRef i_ptr = build_alloca(g, g->builtin_types.entry_usize, "i", 0);
LLVMBuildStore(g->builder, LLVMConstInt(llvm_usize_ty, 0, false), i_ptr);
LLVMValueRef accum_ptr = build_alloca(g, accum_ty, "accum", 0);
LLVMBuildStore(g->builder, accum_init, accum_ptr);
// Setup the loop
LLVMBasicBlockRef loop = LLVMAppendBasicBlock(g->cur_fn_val, "ReduceLoop");
LLVMBasicBlockRef loop_exit = LLVMAppendBasicBlock(g->cur_fn_val, "AfterReduce");
LLVMBuildBr(g->builder, loop);
{
LLVMPositionBuilderAtEnd(g->builder, loop);
// while (i < vec.len)
LLVMValueRef i = LLVMBuildLoad2(g->builder, llvm_usize_ty, i_ptr, "");
LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntULT, i, llvm_vector_len, "");
LLVMBasicBlockRef loop_then = LLVMAppendBasicBlock(g->cur_fn_val, "ReduceLoopThen");
LLVMBuildCondBr(g->builder, cond, loop_then, loop_exit);
{
LLVMPositionBuilderAtEnd(g->builder, loop_then);
// accum = f(accum, vec[i]);
LLVMValueRef accum = LLVMBuildLoad2(g->builder, llvm_result_ty, accum_ptr, "");
LLVMValueRef element = LLVMBuildExtractElement(g->builder, operand_vector, i, "");
LLVMValueRef params[] {
accum,
element
};
LLVMValueRef new_accum = LLVMBuildCall2(g->builder, LLVMGlobalGetValueType(llvm_fn), llvm_fn, params, 2, "");
LLVMBuildStore(g->builder, new_accum, accum_ptr);
// i += 1
LLVMValueRef new_i = LLVMBuildAdd(g->builder, i, LLVMConstInt(llvm_usize_ty, 1, false), "");
LLVMBuildStore(g->builder, new_i, i_ptr);
LLVMBuildBr(g->builder, loop);
}
}
LLVMPositionBuilderAtEnd(g->builder, loop_exit);
return LLVMBuildLoad2(g->builder, llvm_result_ty, accum_ptr, "");
}
static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1AirInstReduce *instruction) {
LLVMValueRef value = ir_llvm_value(g, instruction->value);
@ -6488,61 +6537,100 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1Ai
assert(value_type->id == ZigTypeIdVector);
ZigType *scalar_type = value_type->data.vector.elem_type;
bool float_intrinsics_allowed = true;
const char *compiler_rt_type_abbrev = nullptr;
const char *math_float_prefix = nullptr;
const char *math_float_suffix = nullptr;
if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
(scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target)) ||
(scalar_type == g->builtin_types.entry_f16 && !target_is_arm(g->zig_target))) {
float_intrinsics_allowed = false;
compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(scalar_type);
math_float_prefix = libc_float_prefix(g, scalar_type);
math_float_suffix = libc_float_suffix(g, scalar_type);
}
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &instruction->base));
LLVMValueRef result_val;
char fn_name[64];
ZigValue *init_value = nullptr;
switch (instruction->op) {
case ReduceOp_and:
assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
result_val = ZigLLVMBuildAndReduce(g->builder, value);
return ZigLLVMBuildAndReduce(g->builder, value);
break;
case ReduceOp_or:
assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
result_val = ZigLLVMBuildOrReduce(g->builder, value);
return ZigLLVMBuildOrReduce(g->builder, value);
break;
case ReduceOp_xor:
assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
result_val = ZigLLVMBuildXorReduce(g->builder, value);
return ZigLLVMBuildXorReduce(g->builder, value);
break;
case ReduceOp_min: {
if (scalar_type->id == ZigTypeIdInt) {
const bool is_signed = scalar_type->data.integral.is_signed;
result_val = ZigLLVMBuildIntMinReduce(g->builder, value, is_signed);
return ZigLLVMBuildIntMinReduce(g->builder, value, is_signed);
} else if (scalar_type->id == ZigTypeIdFloat) {
result_val = ZigLLVMBuildFPMinReduce(g->builder, value);
if (float_intrinsics_allowed) {
return ZigLLVMBuildFPMinReduce(g->builder, value);
} else {
snprintf(fn_name, sizeof(fn_name), "%sfmin%s", math_float_prefix, math_float_suffix);
init_value = create_const_float(g, scalar_type, NAN);
}
} else zig_unreachable();
} break;
case ReduceOp_max: {
if (scalar_type->id == ZigTypeIdInt) {
const bool is_signed = scalar_type->data.integral.is_signed;
result_val = ZigLLVMBuildIntMaxReduce(g->builder, value, is_signed);
return ZigLLVMBuildIntMaxReduce(g->builder, value, is_signed);
} else if (scalar_type->id == ZigTypeIdFloat) {
result_val = ZigLLVMBuildFPMaxReduce(g->builder, value);
if (float_intrinsics_allowed) {
return ZigLLVMBuildFPMaxReduce(g->builder, value);
} else {
snprintf(fn_name, sizeof(fn_name), "%sfmax%s", math_float_prefix, math_float_suffix);
init_value = create_const_float(g, scalar_type, NAN);
}
} else zig_unreachable();
} break;
case ReduceOp_add: {
if (scalar_type->id == ZigTypeIdInt) {
result_val = ZigLLVMBuildAddReduce(g->builder, value);
return ZigLLVMBuildAddReduce(g->builder, value);
} else if (scalar_type->id == ZigTypeIdFloat) {
LLVMValueRef neutral_value = LLVMConstReal(
get_llvm_type(g, scalar_type), -0.0);
result_val = ZigLLVMBuildFPAddReduce(g->builder, neutral_value, value);
if (float_intrinsics_allowed) {
LLVMValueRef neutral_value = LLVMConstReal(
get_llvm_type(g, scalar_type), -0.0);
return ZigLLVMBuildFPAddReduce(g->builder, neutral_value, value);
} else {
snprintf(fn_name, sizeof(fn_name), "__add%sf3", compiler_rt_type_abbrev);
init_value = create_const_float(g, scalar_type, 0.0);
}
} else zig_unreachable();
} break;
case ReduceOp_mul: {
if (scalar_type->id == ZigTypeIdInt) {
result_val = ZigLLVMBuildMulReduce(g->builder, value);
return ZigLLVMBuildMulReduce(g->builder, value);
} else if (scalar_type->id == ZigTypeIdFloat) {
LLVMValueRef neutral_value = LLVMConstReal(
get_llvm_type(g, scalar_type), 1.0);
result_val = ZigLLVMBuildFPMulReduce(g->builder, neutral_value, value);
if (float_intrinsics_allowed) {
LLVMValueRef neutral_value = LLVMConstReal(
get_llvm_type(g, scalar_type), 1.0);
return ZigLLVMBuildFPMulReduce(g->builder, neutral_value, value);
} else {
snprintf(fn_name, sizeof(fn_name), "__mul%sf3", compiler_rt_type_abbrev);
init_value = create_const_float(g, scalar_type, 1.0);
}
} else zig_unreachable();
} break;
default:
zig_unreachable();
}
return result_val;
LLVMValueRef llvm_init_value = gen_const_val(g, init_value, "");
uint32_t vector_len = value_type->data.vector.len;
LLVMTypeRef llvm_scalar_type = get_llvm_type(g, scalar_type);
const LLVMValueRef llvm_fn = get_soft_float_fn(g, fn_name, 2, llvm_scalar_type, llvm_scalar_type);
return ir_render_reduced_call(g, llvm_fn, value, vector_len, llvm_init_value, scalar_type);
}
static LLVMValueRef ir_render_fence(CodeGen *g, Stage1Air *executable, Stage1AirInstFence *instruction) {

View File

@ -21,6 +21,20 @@ static inline float16_t zig_double_to_f16(double x) {
return f64_to_f16(y);
}
static inline void zig_double_to_extF80M(double x, extFloat80_t *result) {
float64_t y;
static_assert(sizeof(x) == sizeof(y), "");
memcpy(&y, &x, sizeof(x));
f64_to_extF80M(y, result);
}
static inline void zig_double_to_f128M(double x, float128_t *result) {
float64_t y;
static_assert(sizeof(x) == sizeof(y), "");
memcpy(&y, &x, sizeof(x));
f64_to_f128M(y, result);
}
// Return value is safe to coerce to float even when |x| is NaN or Infinity.
static inline double zig_f16_to_double(float16_t x) {