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) {...@@ -6358,9 +6358,11 @@ void init_const_float(ZigValue *const_val, ZigType *type, double value) {
6358 const_val->data.x_f64 = value;6358 const_val->data.x_f64 = value;
6359 break;6359 break;
6360 case 80:6360 case 80:
6361 zig_double_to_extF80M(value, &const_val->data.x_f80);
6362 break;
6361 case 128:6363 case 128:
6362 // if we need this, we should add a function that accepts a float128_t param6364 zig_double_to_f128M(value, &const_val->data.x_f128);
6363 zig_unreachable();6365 break;
6364 default:6366 default:
6365 zig_unreachable();6367 zig_unreachable();
6366 }6368 }
src/stage1/codegen.cpp+105-17
...@@ -6481,6 +6481,55 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, Stage1Air *executable, Stage1A...@@ -6481,6 +6481,55 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, Stage1Air *executable, Stage1A
6481 return result_loc;6481 return result_loc;
6482}6482}
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
6484static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1AirInstReduce *instruction) {6533static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1AirInstReduce *instruction) {
6485 LLVMValueRef value = ir_llvm_value(g, instruction->value);6534 LLVMValueRef value = ir_llvm_value(g, instruction->value);
64866535
...@@ -6488,61 +6537,100 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1Ai...@@ -6488,61 +6537,100 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1Ai
6488 assert(value_type->id == ZigTypeIdVector);6537 assert(value_type->id == ZigTypeIdVector);
6489 ZigType *scalar_type = value_type->data.vector.elem_type;6538 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
6491 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &instruction->base));6553 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;
6494 switch (instruction->op) {6557 switch (instruction->op) {
6495 case ReduceOp_and:6558 case ReduceOp_and:
6496 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);6559 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
6497 result_val = ZigLLVMBuildAndReduce(g->builder, value);6560 return ZigLLVMBuildAndReduce(g->builder, value);
6498 break;6561 break;
6499 case ReduceOp_or:6562 case ReduceOp_or:
6500 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);6563 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
6501 result_val = ZigLLVMBuildOrReduce(g->builder, value);6564 return ZigLLVMBuildOrReduce(g->builder, value);
6502 break;6565 break;
6503 case ReduceOp_xor:6566 case ReduceOp_xor:
6504 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);6567 assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
6505 result_val = ZigLLVMBuildXorReduce(g->builder, value);6568 return ZigLLVMBuildXorReduce(g->builder, value);
6506 break;6569 break;
6507 case ReduceOp_min: {6570 case ReduceOp_min: {
6508 if (scalar_type->id == ZigTypeIdInt) {6571 if (scalar_type->id == ZigTypeIdInt) {
6509 const bool is_signed = scalar_type->data.integral.is_signed;6572 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);
6511 } else if (scalar_type->id == ZigTypeIdFloat) {6574 } 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 }
6513 } else zig_unreachable();6581 } else zig_unreachable();
6514 } break;6582 } break;
6515 case ReduceOp_max: {6583 case ReduceOp_max: {
6516 if (scalar_type->id == ZigTypeIdInt) {6584 if (scalar_type->id == ZigTypeIdInt) {
6517 const bool is_signed = scalar_type->data.integral.is_signed;6585 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);
6519 } else if (scalar_type->id == ZigTypeIdFloat) {6587 } 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 }
6521 } else zig_unreachable();6594 } else zig_unreachable();
6522 } break;6595 } break;
6523 case ReduceOp_add: {6596 case ReduceOp_add: {
6524 if (scalar_type->id == ZigTypeIdInt) {6597 if (scalar_type->id == ZigTypeIdInt) {
6525 result_val = ZigLLVMBuildAddReduce(g->builder, value);6598 return ZigLLVMBuildAddReduce(g->builder, value);
6526 } else if (scalar_type->id == ZigTypeIdFloat) {6599 } else if (scalar_type->id == ZigTypeIdFloat) {
6527 LLVMValueRef neutral_value = LLVMConstReal(6600 if (float_intrinsics_allowed) {
6528 get_llvm_type(g, scalar_type), -0.0);6601 LLVMValueRef neutral_value = LLVMConstReal(
6529 result_val = ZigLLVMBuildFPAddReduce(g->builder, neutral_value, value);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 }
6530 } else zig_unreachable();6608 } else zig_unreachable();
6531 } break;6609 } break;
6532 case ReduceOp_mul: {6610 case ReduceOp_mul: {
6533 if (scalar_type->id == ZigTypeIdInt) {6611 if (scalar_type->id == ZigTypeIdInt) {
6534 result_val = ZigLLVMBuildMulReduce(g->builder, value);6612 return ZigLLVMBuildMulReduce(g->builder, value);
6535 } else if (scalar_type->id == ZigTypeIdFloat) {6613 } else if (scalar_type->id == ZigTypeIdFloat) {
6536 LLVMValueRef neutral_value = LLVMConstReal(6614 if (float_intrinsics_allowed) {
6537 get_llvm_type(g, scalar_type), 1.0);6615 LLVMValueRef neutral_value = LLVMConstReal(
6538 result_val = ZigLLVMBuildFPMulReduce(g->builder, neutral_value, value);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 }
6539 } else zig_unreachable();6622 } else zig_unreachable();
6540 } break;6623 } break;
6541 default:6624 default:
6542 zig_unreachable();6625 zig_unreachable();
6543 }6626 }
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);
6546}6634}
65476635
6548static LLVMValueRef ir_render_fence(CodeGen *g, Stage1Air *executable, Stage1AirInstFence *instruction) {6636static 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) {...@@ -21,6 +21,20 @@ static inline float16_t zig_double_to_f16(double x) {
21 return f64_to_f16(y);21 return f64_to_f16(y);
22}22}
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
25// Return value is safe to coerce to float even when |x| is NaN or Infinity.39// Return value is safe to coerce to float even when |x| is NaN or Infinity.
26static inline double zig_f16_to_double(float16_t x) {40static inline double zig_f16_to_double(float16_t x) {