authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-11 11:04:29-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-13 12:53:20-07:00
log6e6ae8886e6885a648918cdb006f899b40b378aa
tree510c02a839d8c80a4dab10b13b4afe741af8b66f
parentf0d12dd82bfecf28ffe82ff706e98633d4c6b048

stage1: Add softfloat support for `@reduce`


3 files changed, 123 insertions(+), 19 deletions(-)

src/stage1/analyze.cpp+4-2
......@@ -6358,9 +6358,11 @@ void init_const_float(ZigValue *const_val, ZigType *type, double value) {
63586358 const_val->data.x_f64 = value;
63596359 break;
63606360 case 80:
6361 zig_double_to_extF80M(value, &const_val->data.x_f80);
6362 break;
63616363 case 128:
6362 // if we need this, we should add a function that accepts a float128_t param
6363 zig_unreachable();
6364 zig_double_to_f128M(value, &const_val->data.x_f128);
6365 break;
63646366 default:
63656367 zig_unreachable();
63666368 }
src/stage1/codegen.cpp+105-17
......@@ -6481,6 +6481,55 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, Stage1Air *executable, Stage1A
64816481 return result_loc;
64826482}
64836483
6484static LLVMValueRef ir_render_reduced_call(CodeGen *g, LLVMValueRef llvm_fn, LLVMValueRef operand_vector, size_t vector_len, LLVMValueRef accum_init, ZigType *accum_ty) {
6485 LLVMTypeRef llvm_usize_ty = g->builtin_types.entry_usize->llvm_type;
6486 LLVMValueRef llvm_vector_len = LLVMConstInt(llvm_usize_ty, vector_len, false);
6487 LLVMTypeRef llvm_result_ty = LLVMTypeOf(accum_init);
6488
6489 // Allocate and initialize our mutable variables
6490 LLVMValueRef i_ptr = build_alloca(g, g->builtin_types.entry_usize, "i", 0);
6491 LLVMBuildStore(g->builder, LLVMConstInt(llvm_usize_ty, 0, false), i_ptr);
6492 LLVMValueRef accum_ptr = build_alloca(g, accum_ty, "accum", 0);
6493 LLVMBuildStore(g->builder, accum_init, accum_ptr);
6494
6495 // Setup the loop
6496 LLVMBasicBlockRef loop = LLVMAppendBasicBlock(g->cur_fn_val, "ReduceLoop");
6497 LLVMBasicBlockRef loop_exit = LLVMAppendBasicBlock(g->cur_fn_val, "AfterReduce");
6498 LLVMBuildBr(g->builder, loop);
6499 {
6500 LLVMPositionBuilderAtEnd(g->builder, loop);
6501
6502 // while (i < vec.len)
6503 LLVMValueRef i = LLVMBuildLoad2(g->builder, llvm_usize_ty, i_ptr, "");
6504 LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntULT, i, llvm_vector_len, "");
6505 LLVMBasicBlockRef loop_then = LLVMAppendBasicBlock(g->cur_fn_val, "ReduceLoopThen");
6506
6507 LLVMBuildCondBr(g->builder, cond, loop_then, loop_exit);
6508
6509 {
6510 LLVMPositionBuilderAtEnd(g->builder, loop_then);
6511
6512 // accum = f(accum, vec[i]);
6513 LLVMValueRef accum = LLVMBuildLoad2(g->builder, llvm_result_ty, accum_ptr, "");
6514 LLVMValueRef element = LLVMBuildExtractElement(g->builder, operand_vector, i, "");
6515 LLVMValueRef params[] {
6516 accum,
6517 element
6518 };
6519 LLVMValueRef new_accum = LLVMBuildCall2(g->builder, LLVMGlobalGetValueType(llvm_fn), llvm_fn, params, 2, "");
6520 LLVMBuildStore(g->builder, new_accum, accum_ptr);
6521
6522 // i += 1
6523 LLVMValueRef new_i = LLVMBuildAdd(g->builder, i, LLVMConstInt(llvm_usize_ty, 1, false), "");
6524 LLVMBuildStore(g->builder, new_i, i_ptr);
6525 LLVMBuildBr(g->builder, loop);
6526 }
6527 }
6528
6529 LLVMPositionBuilderAtEnd(g->builder, loop_exit);
6530 return LLVMBuildLoad2(g->builder, llvm_result_ty, accum_ptr, "");
6531}
6532
64846533static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1AirInstReduce *instruction) {
64856534 LLVMValueRef value = ir_llvm_value(g, instruction->value);
64866535
......@@ -6488,61 +6537,100 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1Ai
64886537 assert(value_type->id == ZigTypeIdVector);
64896538 ZigType *scalar_type = value_type->data.vector.elem_type;
64906539
6540 bool float_intrinsics_allowed = true;
6541 const char *compiler_rt_type_abbrev = nullptr;
6542 const char *math_float_prefix = nullptr;
6543 const char *math_float_suffix = nullptr;
6544 if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
6545 (scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target)) ||
6546 (scalar_type == g->builtin_types.entry_f16 && !target_is_arm(g->zig_target))) {
6547 float_intrinsics_allowed = false;
6548 compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(scalar_type);
6549 math_float_prefix = libc_float_prefix(g, scalar_type);
6550 math_float_suffix = libc_float_suffix(g, scalar_type);
6551 }
6552
64916553 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &instruction->base));
64926554
6493 LLVMValueRef result_val;
6555 char fn_name[64];
6556 ZigValue *init_value = nullptr;
64946557 switch (instruction->op) {
64956558 case ReduceOp_and:
64966559 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
6497 result_val = ZigLLVMBuildAndReduce(g->builder, value);
6560 return ZigLLVMBuildAndReduce(g->builder, value);
64986561 break;
64996562 case ReduceOp_or:
65006563 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
6501 result_val = ZigLLVMBuildOrReduce(g->builder, value);
6564 return ZigLLVMBuildOrReduce(g->builder, value);
65026565 break;
65036566 case ReduceOp_xor:
65046567 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
6505 result_val = ZigLLVMBuildXorReduce(g->builder, value);
6568 return ZigLLVMBuildXorReduce(g->builder, value);
65066569 break;
65076570 case ReduceOp_min: {
65086571 if (scalar_type->id == ZigTypeIdInt) {
65096572 const bool is_signed = scalar_type->data.integral.is_signed;
6510 result_val = ZigLLVMBuildIntMinReduce(g->builder, value, is_signed);
6573 return ZigLLVMBuildIntMinReduce(g->builder, value, is_signed);
65116574 } else if (scalar_type->id == ZigTypeIdFloat) {
6512 result_val = ZigLLVMBuildFPMinReduce(g->builder, value);
6575 if (float_intrinsics_allowed) {
6576 return ZigLLVMBuildFPMinReduce(g->builder, value);
6577 } else {
6578 snprintf(fn_name, sizeof(fn_name), "%sfmin%s", math_float_prefix, math_float_suffix);
6579 init_value = create_const_float(g, scalar_type, NAN);
6580 }
65136581 } else zig_unreachable();
65146582 } break;
65156583 case ReduceOp_max: {
65166584 if (scalar_type->id == ZigTypeIdInt) {
65176585 const bool is_signed = scalar_type->data.integral.is_signed;
6518 result_val = ZigLLVMBuildIntMaxReduce(g->builder, value, is_signed);
6586 return ZigLLVMBuildIntMaxReduce(g->builder, value, is_signed);
65196587 } else if (scalar_type->id == ZigTypeIdFloat) {
6520 result_val = ZigLLVMBuildFPMaxReduce(g->builder, value);
6588 if (float_intrinsics_allowed) {
6589 return ZigLLVMBuildFPMaxReduce(g->builder, value);
6590 } else {
6591 snprintf(fn_name, sizeof(fn_name), "%sfmax%s", math_float_prefix, math_float_suffix);
6592 init_value = create_const_float(g, scalar_type, NAN);
6593 }
65216594 } else zig_unreachable();
65226595 } break;
65236596 case ReduceOp_add: {
65246597 if (scalar_type->id == ZigTypeIdInt) {
6525 result_val = ZigLLVMBuildAddReduce(g->builder, value);
6598 return ZigLLVMBuildAddReduce(g->builder, value);
65266599 } else if (scalar_type->id == ZigTypeIdFloat) {
6527 LLVMValueRef neutral_value = LLVMConstReal(
6528 get_llvm_type(g, scalar_type), -0.0);
6529 result_val = ZigLLVMBuildFPAddReduce(g->builder, neutral_value, value);
6600 if (float_intrinsics_allowed) {
6601 LLVMValueRef neutral_value = LLVMConstReal(
6602 get_llvm_type(g, scalar_type), -0.0);
6603 return ZigLLVMBuildFPAddReduce(g->builder, neutral_value, value);
6604 } else {
6605 snprintf(fn_name, sizeof(fn_name), "__add%sf3", compiler_rt_type_abbrev);
6606 init_value = create_const_float(g, scalar_type, 0.0);
6607 }
65306608 } else zig_unreachable();
65316609 } break;
65326610 case ReduceOp_mul: {
65336611 if (scalar_type->id == ZigTypeIdInt) {
6534 result_val = ZigLLVMBuildMulReduce(g->builder, value);
6612 return ZigLLVMBuildMulReduce(g->builder, value);
65356613 } else if (scalar_type->id == ZigTypeIdFloat) {
6536 LLVMValueRef neutral_value = LLVMConstReal(
6537 get_llvm_type(g, scalar_type), 1.0);
6538 result_val = ZigLLVMBuildFPMulReduce(g->builder, neutral_value, value);
6614 if (float_intrinsics_allowed) {
6615 LLVMValueRef neutral_value = LLVMConstReal(
6616 get_llvm_type(g, scalar_type), 1.0);
6617 return ZigLLVMBuildFPMulReduce(g->builder, neutral_value, value);
6618 } else {
6619 snprintf(fn_name, sizeof(fn_name), "__mul%sf3", compiler_rt_type_abbrev);
6620 init_value = create_const_float(g, scalar_type, 1.0);
6621 }
65396622 } else zig_unreachable();
65406623 } break;
65416624 default:
65426625 zig_unreachable();
65436626 }
65446627
6545 return result_val;
6628
6629 LLVMValueRef llvm_init_value = gen_const_val(g, init_value, "");
6630 uint32_t vector_len = value_type->data.vector.len;
6631 LLVMTypeRef llvm_scalar_type = get_llvm_type(g, scalar_type);
6632 const LLVMValueRef llvm_fn = get_soft_float_fn(g, fn_name, 2, llvm_scalar_type, llvm_scalar_type);
6633 return ir_render_reduced_call(g, llvm_fn, value, vector_len, llvm_init_value, scalar_type);
65466634}
65476635
65486636static LLVMValueRef ir_render_fence(CodeGen *g, Stage1Air *executable, Stage1AirInstFence *instruction) {
src/stage1/softfloat.hpp+14
......@@ -21,6 +21,20 @@ static inline float16_t zig_double_to_f16(double x) {
2121 return f64_to_f16(y);
2222}
2323
24static inline void zig_double_to_extF80M(double x, extFloat80_t *result) {
25 float64_t y;
26 static_assert(sizeof(x) == sizeof(y), "");
27 memcpy(&y, &x, sizeof(x));
28 f64_to_extF80M(y, result);
29}
30
31static inline void zig_double_to_f128M(double x, float128_t *result) {
32 float64_t y;
33 static_assert(sizeof(x) == sizeof(y), "");
34 memcpy(&y, &x, sizeof(x));
35 f64_to_f128M(y, result);
36}
37
2438
2539// Return value is safe to coerce to float even when |x| is NaN or Infinity.
2640static inline double zig_f16_to_double(float16_t x) {