| ... | ... | @@ -6481,6 +6481,55 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, Stage1Air *executable, Stage1A |
| 6481 | 6481 | return result_loc; |
| 6482 | 6482 | } |
| 6483 | 6483 | |
| 6484 | static 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 | |
| 6484 | 6533 | static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1AirInstReduce *instruction) { |
| 6485 | 6534 | LLVMValueRef value = ir_llvm_value(g, instruction->value); |
| 6486 | 6535 | |
| ... | ... | @@ -6488,61 +6537,100 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1Ai |
| 6488 | 6537 | assert(value_type->id == ZigTypeIdVector); |
| 6489 | 6538 | ZigType *scalar_type = value_type->data.vector.elem_type; |
| 6490 | 6539 | |
| 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 | 6553 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &instruction->base)); |
| 6492 | 6554 | |
| 6493 | | LLVMValueRef result_val; |
| 6555 | char fn_name[64]; |
| 6556 | ZigValue *init_value = nullptr; |
| 6494 | 6557 | switch (instruction->op) { |
| 6495 | 6558 | case ReduceOp_and: |
| 6496 | 6559 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); |
| 6497 | | result_val = ZigLLVMBuildAndReduce(g->builder, value); |
| 6560 | return ZigLLVMBuildAndReduce(g->builder, value); |
| 6498 | 6561 | break; |
| 6499 | 6562 | case ReduceOp_or: |
| 6500 | 6563 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); |
| 6501 | | result_val = ZigLLVMBuildOrReduce(g->builder, value); |
| 6564 | return ZigLLVMBuildOrReduce(g->builder, value); |
| 6502 | 6565 | break; |
| 6503 | 6566 | case ReduceOp_xor: |
| 6504 | 6567 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); |
| 6505 | | result_val = ZigLLVMBuildXorReduce(g->builder, value); |
| 6568 | return ZigLLVMBuildXorReduce(g->builder, value); |
| 6506 | 6569 | break; |
| 6507 | 6570 | case ReduceOp_min: { |
| 6508 | 6571 | if (scalar_type->id == ZigTypeIdInt) { |
| 6509 | 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 | 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 | 6581 | } else zig_unreachable(); |
| 6514 | 6582 | } break; |
| 6515 | 6583 | case ReduceOp_max: { |
| 6516 | 6584 | if (scalar_type->id == ZigTypeIdInt) { |
| 6517 | 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 | 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 | 6594 | } else zig_unreachable(); |
| 6522 | 6595 | } break; |
| 6523 | 6596 | case ReduceOp_add: { |
| 6524 | 6597 | if (scalar_type->id == ZigTypeIdInt) { |
| 6525 | | result_val = ZigLLVMBuildAddReduce(g->builder, value); |
| 6598 | return ZigLLVMBuildAddReduce(g->builder, value); |
| 6526 | 6599 | } 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 | } |
| 6530 | 6608 | } else zig_unreachable(); |
| 6531 | 6609 | } break; |
| 6532 | 6610 | case ReduceOp_mul: { |
| 6533 | 6611 | if (scalar_type->id == ZigTypeIdInt) { |
| 6534 | | result_val = ZigLLVMBuildMulReduce(g->builder, value); |
| 6612 | return ZigLLVMBuildMulReduce(g->builder, value); |
| 6535 | 6613 | } 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 | } |
| 6539 | 6622 | } else zig_unreachable(); |
| 6540 | 6623 | } break; |
| 6541 | 6624 | default: |
| 6542 | 6625 | zig_unreachable(); |
| 6543 | 6626 | } |
| 6544 | 6627 | |
| 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 | } |
| 6547 | 6635 | |
| 6548 | 6636 | static LLVMValueRef ir_render_fence(CodeGen *g, Stage1Air *executable, Stage1AirInstFence *instruction) { |