| ... | @@ -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 | } |
| 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 | static LLVMValueRef ir_render_reduce(CodeGen *g, Stage1Air *executable, Stage1AirInstReduce *instruction) { | 6533 | static 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); |
| 6486 | | 6535 | |
| ... | @@ -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; |
| 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 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &instruction->base)); | 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 | 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 | } |
| 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 | static LLVMValueRef ir_render_fence(CodeGen *g, Stage1Air *executable, Stage1AirInstFence *instruction) { | 6636 | static LLVMValueRef ir_render_fence(CodeGen *g, Stage1Air *executable, Stage1AirInstFence *instruction) { |