| ... | ... | @@ -869,7 +869,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn |
| 869 | 869 | name = "fma"; |
| 870 | 870 | num_args = 3; |
| 871 | 871 | } else if (fn_id == ZigLLVMFnIdFloatOp) { |
| 872 | | name = float_op_to_name(op); |
| 872 | name = float_un_op_to_name(op); |
| 873 | 873 | num_args = 1; |
| 874 | 874 | } else { |
| 875 | 875 | zig_unreachable(); |
| ... | ... | @@ -1604,8 +1604,49 @@ static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType * |
| 1604 | 1604 | return nullptr; |
| 1605 | 1605 | } |
| 1606 | 1606 | |
| 1607 | static const char *get_compiler_rt_type_abbrev(ZigType *type) { |
| 1608 | uint16_t bits; |
| 1609 | if (type->id == ZigTypeIdFloat) { |
| 1610 | bits = type->data.floating.bit_count; |
| 1611 | } else if (type->id == ZigTypeIdInt) { |
| 1612 | bits = type->data.integral.bit_count; |
| 1613 | } else { |
| 1614 | zig_unreachable(); |
| 1615 | } |
| 1616 | switch (bits) { |
| 1617 | case 16: |
| 1618 | return "h"; |
| 1619 | case 32: |
| 1620 | return "s"; |
| 1621 | case 64: |
| 1622 | return "d"; |
| 1623 | case 80: |
| 1624 | return "x"; |
| 1625 | case 128: |
| 1626 | return "t"; |
| 1627 | default: |
| 1628 | zig_unreachable(); |
| 1629 | } |
| 1630 | } |
| 1631 | |
| 1632 | static const char *get_math_h_type_abbrev(CodeGen *g, ZigType *float_type) { |
| 1633 | if (float_type == g->builtin_types.entry_f16) |
| 1634 | return "h"; // Non-standard |
| 1635 | else if (float_type == g->builtin_types.entry_f32) |
| 1636 | return "s"; |
| 1637 | else if (float_type == g->builtin_types.entry_f64) |
| 1638 | return ""; |
| 1639 | else if (float_type == g->builtin_types.entry_f80) |
| 1640 | return "x"; // Non-standard |
| 1641 | else if (float_type == g->builtin_types.entry_c_longdouble) |
| 1642 | return "l"; |
| 1643 | else if (float_type == g->builtin_types.entry_f128) |
| 1644 | return "q"; // Non-standard |
| 1645 | else |
| 1646 | zig_unreachable(); |
| 1647 | } |
| 1607 | 1648 | |
| 1608 | | static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_type, |
| 1649 | static LLVMValueRef gen_soft_float_widen_or_shorten(CodeGen *g, ZigType *actual_type, |
| 1609 | 1650 | ZigType *wanted_type, LLVMValueRef expr_val) |
| 1610 | 1651 | { |
| 1611 | 1652 | ZigType *scalar_actual_type = (actual_type->id == ZigTypeIdVector) ? |
| ... | ... | @@ -1615,87 +1656,47 @@ static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_ty |
| 1615 | 1656 | uint64_t actual_bits = scalar_actual_type->data.floating.bit_count; |
| 1616 | 1657 | uint64_t wanted_bits = scalar_wanted_type->data.floating.bit_count; |
| 1617 | 1658 | |
| 1618 | | |
| 1619 | | LLVMTypeRef param_type; |
| 1620 | | LLVMTypeRef return_type; |
| 1621 | | const char *func_name; |
| 1659 | if (actual_bits == wanted_bits) |
| 1660 | return expr_val; |
| 1622 | 1661 | |
| 1623 | 1662 | LLVMValueRef result; |
| 1624 | 1663 | bool castTruncatedToF16 = false; |
| 1625 | 1664 | |
| 1626 | | if (actual_bits == wanted_bits) { |
| 1627 | | return expr_val; |
| 1628 | | } else if (actual_bits == 80) { |
| 1629 | | param_type = g->builtin_types.entry_f80->llvm_type; |
| 1630 | | switch (wanted_bits) { |
| 1631 | | case 16: |
| 1632 | | // Only Arm has a native f16 type, other platforms soft-implement it |
| 1633 | | // using u16 instead. |
| 1634 | | if (target_is_arm(g->zig_target)) { |
| 1635 | | return_type = g->builtin_types.entry_f16->llvm_type; |
| 1636 | | } else { |
| 1637 | | return_type = g->builtin_types.entry_u16->llvm_type; |
| 1638 | | castTruncatedToF16 = true; |
| 1639 | | } |
| 1640 | | func_name = "__truncxfhf2"; |
| 1641 | | break; |
| 1642 | | case 32: |
| 1643 | | return_type = g->builtin_types.entry_f32->llvm_type; |
| 1644 | | func_name = "__truncxfsf2"; |
| 1645 | | break; |
| 1646 | | case 64: |
| 1647 | | return_type = g->builtin_types.entry_f64->llvm_type; |
| 1648 | | func_name = "__truncxfdf2"; |
| 1649 | | break; |
| 1650 | | case 128: |
| 1651 | | return_type = g->builtin_types.entry_f128->llvm_type; |
| 1652 | | func_name = "__extendxftf2"; |
| 1653 | | break; |
| 1654 | | default: |
| 1655 | | zig_unreachable(); |
| 1665 | char fn_name[64]; |
| 1666 | if (wanted_bits < actual_bits) { |
| 1667 | sprintf(fn_name, "__trunc%sf%sf2", |
| 1668 | get_compiler_rt_type_abbrev(scalar_actual_type), |
| 1669 | get_compiler_rt_type_abbrev(scalar_wanted_type)); |
| 1670 | } else { |
| 1671 | sprintf(fn_name, "__extend%sf%sf2", |
| 1672 | get_compiler_rt_type_abbrev(scalar_actual_type), |
| 1673 | get_compiler_rt_type_abbrev(scalar_wanted_type)); |
| 1674 | } |
| 1675 | |
| 1676 | LLVMTypeRef return_type = scalar_wanted_type->llvm_type; |
| 1677 | LLVMTypeRef param_type = scalar_actual_type->llvm_type; |
| 1678 | |
| 1679 | if (!target_is_arm(g->zig_target)) { |
| 1680 | // Only Arm has a native f16 type, other platforms soft-implement it using u16 instead. |
| 1681 | if (scalar_wanted_type == g->builtin_types.entry_f16) { |
| 1682 | return_type = g->builtin_types.entry_u16->llvm_type; |
| 1683 | castTruncatedToF16 = true; |
| 1656 | 1684 | } |
| 1657 | | } else if (wanted_bits == 80) { |
| 1658 | | return_type = g->builtin_types.entry_f80->llvm_type; |
| 1659 | | switch (actual_bits) { |
| 1660 | | case 16: |
| 1661 | | // Only Arm has a native f16 type, other platforms soft-implement it |
| 1662 | | // using u16 instead. |
| 1663 | | if (target_is_arm(g->zig_target)) { |
| 1664 | | param_type = g->builtin_types.entry_f16->llvm_type; |
| 1665 | | } else { |
| 1666 | | param_type = g->builtin_types.entry_u16->llvm_type; |
| 1667 | | expr_val = LLVMBuildBitCast(g->builder, expr_val, param_type, ""); |
| 1668 | | } |
| 1669 | | func_name = "__extendhfxf2"; |
| 1670 | | break; |
| 1671 | | case 32: |
| 1672 | | param_type = g->builtin_types.entry_f32->llvm_type; |
| 1673 | | func_name = "__extendsfxf2"; |
| 1674 | | break; |
| 1675 | | case 64: |
| 1676 | | param_type = g->builtin_types.entry_f64->llvm_type; |
| 1677 | | func_name = "__extenddfxf2"; |
| 1678 | | break; |
| 1679 | | case 128: |
| 1680 | | param_type = g->builtin_types.entry_f128->llvm_type; |
| 1681 | | func_name = "__trunctfxf2"; |
| 1682 | | break; |
| 1683 | | default: |
| 1684 | | zig_unreachable(); |
| 1685 | if (scalar_actual_type == g->builtin_types.entry_f16) { |
| 1686 | param_type = g->builtin_types.entry_u16->llvm_type; |
| 1687 | expr_val = LLVMBuildBitCast(g->builder, expr_val, param_type, ""); |
| 1685 | 1688 | } |
| 1686 | | } else { |
| 1687 | | zig_unreachable(); |
| 1688 | 1689 | } |
| 1689 | 1690 | |
| 1690 | | LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); |
| 1691 | LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name); |
| 1691 | 1692 | if (func_ref == nullptr) { |
| 1692 | 1693 | LLVMTypeRef fn_type = LLVMFunctionType(return_type, &param_type, 1, false); |
| 1693 | | func_ref = LLVMAddFunction(g->module, func_name, fn_type); |
| 1694 | func_ref = LLVMAddFunction(g->module, fn_name, fn_type); |
| 1694 | 1695 | } |
| 1695 | 1696 | |
| 1696 | 1697 | result = LLVMBuildCall(g->builder, func_ref, &expr_val, 1, ""); |
| 1697 | 1698 | |
| 1698 | | // On non-Arm platforms we need to bitcast __truncxfhf2 result back to f16 |
| 1699 | // On non-Arm platforms we need to bitcast __trunc<>fhf2 result back to f16 |
| 1699 | 1700 | if (castTruncatedToF16) { |
| 1700 | 1701 | result = LLVMBuildBitCast(g->builder, result, g->builtin_types.entry_f16->llvm_type, ""); |
| 1701 | 1702 | } |
| ... | ... | @@ -1721,7 +1722,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z |
| 1721 | 1722 | || scalar_wanted_type == g->builtin_types.entry_f80) |
| 1722 | 1723 | && !target_has_f80(g->zig_target)) |
| 1723 | 1724 | { |
| 1724 | | return gen_soft_f80_widen_or_shorten(g, actual_type, wanted_type, expr_val); |
| 1725 | return gen_soft_float_widen_or_shorten(g, actual_type, wanted_type, expr_val); |
| 1725 | 1726 | } |
| 1726 | 1727 | actual_bits = scalar_actual_type->data.floating.bit_count; |
| 1727 | 1728 | wanted_bits = scalar_wanted_type->data.floating.bit_count; |
| ... | ... | @@ -2978,10 +2979,50 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, |
| 2978 | 2979 | return result; |
| 2979 | 2980 | } |
| 2980 | 2981 | |
| 2981 | | static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { |
| 2982 | | assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); |
| 2983 | | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); |
| 2984 | | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); |
| 2982 | static LLVMValueRef get_soft_float_fn(CodeGen *g, const char *name, int param_count, LLVMTypeRef param_type, LLVMTypeRef return_type) { |
| 2983 | LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name); |
| 2984 | if (existing_llvm_fn != nullptr) return existing_llvm_fn; |
| 2985 | LLVMValueRef existing_llvm_alias = LLVMGetNamedGlobalAlias(g->module, name, strlen(name)); |
| 2986 | if (existing_llvm_alias != nullptr) return LLVMAliasGetAliasee(existing_llvm_alias); |
| 2987 | |
| 2988 | LLVMTypeRef param_types[3] = { param_type, param_type, param_type }; |
| 2989 | LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, param_count, false); |
| 2990 | return LLVMAddFunction(g->module, name, fn_type); |
| 2991 | } |
| 2992 | |
| 2993 | static LLVMValueRef gen_soft_float_un_op(CodeGen *g, LLVMValueRef op, ZigType *operand_type, BuiltinFnId op_id) { |
| 2994 | uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; |
| 2995 | |
| 2996 | char fn_name[64]; |
| 2997 | sprintf(fn_name, "%s%s", float_un_op_to_name(op_id), get_math_h_type_abbrev(g, operand_type)); |
| 2998 | LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, 1, operand_type->llvm_type, operand_type->llvm_type); |
| 2999 | |
| 3000 | LLVMValueRef result; |
| 3001 | if (vector_len == 0) { |
| 3002 | return LLVMBuildCall(g->builder, func_ref, &op, 1, ""); |
| 3003 | } else { |
| 3004 | result = build_alloca(g, operand_type, "", 0); |
| 3005 | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 3006 | for (uint32_t i = 0; i < vector_len; i++) { |
| 3007 | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 3008 | LLVMValueRef param = LLVMBuildExtractElement(g->builder, op, index_value, ""); |
| 3009 | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, &param, 1, ""); |
| 3010 | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 3011 | call_result, index_value, ""); |
| 3012 | } |
| 3013 | return LLVMBuildLoad(g->builder, result, ""); |
| 3014 | } |
| 3015 | } |
| 3016 | |
| 3017 | static LLVMValueRef gen_float_un_op(CodeGen *g, LLVMValueRef operand, ZigType *operand_type, BuiltinFnId op) { |
| 3018 | assert(operand_type->id == ZigTypeIdFloat || operand_type->id == ZigTypeIdVector); |
| 3019 | ZigType *elem_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type; |
| 3020 | if ((elem_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || |
| 3021 | (elem_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { |
| 3022 | return gen_soft_float_un_op(g, operand, operand_type, op); |
| 3023 | } |
| 3024 | LLVMValueRef float_op_fn = get_float_fn(g, operand_type, ZigLLVMFnIdFloatOp, op); |
| 3025 | return LLVMBuildCall(g->builder, float_op_fn, &operand, 1, ""); |
| 2985 | 3026 | } |
| 2986 | 3027 | |
| 2987 | 3028 | enum DivKind { |
| ... | ... | @@ -3088,7 +3129,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 3088 | 3129 | case DivKindExact: |
| 3089 | 3130 | if (want_runtime_safety) { |
| 3090 | 3131 | // Safety check: a / b == floor(a / b) |
| 3091 | | LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); |
| 3132 | LLVMValueRef floored = gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); |
| 3092 | 3133 | |
| 3093 | 3134 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 3094 | 3135 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| ... | ... | @@ -3105,9 +3146,9 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 3105 | 3146 | } |
| 3106 | 3147 | return result; |
| 3107 | 3148 | case DivKindTrunc: |
| 3108 | | return gen_float_op(g, result, operand_type, BuiltinFnIdTrunc); |
| 3149 | return gen_float_un_op(g, result, operand_type, BuiltinFnIdTrunc); |
| 3109 | 3150 | case DivKindFloor: |
| 3110 | | return gen_float_op(g, result, operand_type, BuiltinFnIdFloor); |
| 3151 | return gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); |
| 3111 | 3152 | } |
| 3112 | 3153 | zig_unreachable(); |
| 3113 | 3154 | } |
| ... | ... | @@ -3269,17 +3310,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type |
| 3269 | 3310 | } |
| 3270 | 3311 | } |
| 3271 | 3312 | |
| 3272 | | static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int param_count, LLVMTypeRef return_type) { |
| 3273 | | LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name); |
| 3274 | | if (existing_llvm_fn != nullptr) return existing_llvm_fn; |
| 3275 | | |
| 3276 | | LLVMTypeRef float_type_ref = g->builtin_types.entry_f80->llvm_type; |
| 3277 | | LLVMTypeRef param_types[2] = { float_type_ref, float_type_ref }; |
| 3278 | | LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, param_count, false); |
| 3279 | | return LLVMAddFunction(g->module, name, fn_type); |
| 3280 | | } |
| 3281 | | |
| 3282 | | enum SoftF80Icmp { |
| 3313 | enum Icmp { |
| 3283 | 3314 | NONE, |
| 3284 | 3315 | EQ_ZERO, |
| 3285 | 3316 | NE_ZERO, |
| ... | ... | @@ -3289,7 +3320,7 @@ enum SoftF80Icmp { |
| 3289 | 3320 | EQ_ONE, |
| 3290 | 3321 | }; |
| 3291 | 3322 | |
| 3292 | | static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) { |
| 3323 | static LLVMValueRef add_icmp(CodeGen *g, LLVMValueRef val, Icmp kind) { |
| 3293 | 3324 | switch (kind) { |
| 3294 | 3325 | case NONE: |
| 3295 | 3326 | return val; |
| ... | ... | @@ -3322,22 +3353,123 @@ static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) |
| 3322 | 3353 | } |
| 3323 | 3354 | } |
| 3324 | 3355 | |
| 3325 | | static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, |
| 3326 | | Stage1AirInstBinOp *bin_op_instruction) |
| 3327 | | { |
| 3328 | | IrBinOp op_id = bin_op_instruction->op_id; |
| 3329 | | Stage1AirInst *op1 = bin_op_instruction->op1; |
| 3330 | | Stage1AirInst *op2 = bin_op_instruction->op2; |
| 3331 | | uint32_t vector_len = op1->value->type->id == ZigTypeIdVector ? op1->value->type->data.vector.len : 0; |
| 3356 | static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) { |
| 3357 | uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; |
| 3332 | 3358 | |
| 3333 | | LLVMValueRef op1_value = ir_llvm_value(g, op1); |
| 3334 | | LLVMValueRef op2_value = ir_llvm_value(g, op2); |
| 3359 | // Handle integers of non-pot bitsize by widening them. |
| 3360 | const size_t bitsize = operand_type->data.integral.bit_count; |
| 3361 | const bool is_signed = operand_type->data.integral.is_signed; |
| 3362 | if (bitsize < 32 || !is_power_of_2(bitsize)) { |
| 3363 | const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize); |
| 3364 | ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize); |
| 3365 | value_ref = gen_widen_or_shorten(g, false, operand_type, wider_type, value_ref); |
| 3366 | operand_type = wider_type; |
| 3367 | } |
| 3368 | assert(bitsize <= 128); |
| 3369 | |
| 3370 | const char *int_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type); |
| 3371 | const char *float_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(result_type); |
| 3372 | |
| 3373 | char fn_name[64]; |
| 3374 | if (is_signed) { |
| 3375 | sprintf(fn_name, "__float%si%sf", int_compiler_rt_type_abbrev, float_compiler_rt_type_abbrev); |
| 3376 | } else { |
| 3377 | sprintf(fn_name, "__floatun%si%sf", int_compiler_rt_type_abbrev, float_compiler_rt_type_abbrev); |
| 3378 | } |
| 3379 | |
| 3380 | int param_count = 1; |
| 3381 | LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, result_type->llvm_type); |
| 3382 | |
| 3383 | LLVMValueRef result; |
| 3384 | if (vector_len == 0) { |
| 3385 | LLVMValueRef params[1] = {value_ref}; |
| 3386 | result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3387 | } else { |
| 3388 | ZigType *alloca_ty = operand_type; |
| 3389 | result = build_alloca(g, alloca_ty, "", 0); |
| 3390 | |
| 3391 | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 3392 | for (uint32_t i = 0; i < vector_len; i++) { |
| 3393 | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 3394 | LLVMValueRef params[1] = { |
| 3395 | LLVMBuildExtractElement(g->builder, value_ref, index_value, ""), |
| 3396 | }; |
| 3397 | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3398 | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 3399 | call_result, index_value, ""); |
| 3400 | } |
| 3401 | |
| 3402 | result = LLVMBuildLoad(g->builder, result, ""); |
| 3403 | } |
| 3404 | return result; |
| 3405 | } |
| 3335 | 3406 | |
| 3336 | | bool div_exact_safety_check = false; |
| 3337 | | LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type; |
| 3407 | static LLVMValueRef gen_soft_float_to_int_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) { |
| 3408 | uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; |
| 3409 | |
| 3410 | // Handle integers of non-pot bitsize by truncating a sufficiently wide pot integer |
| 3411 | const size_t bitsize = result_type->data.integral.bit_count; |
| 3412 | const bool is_signed = result_type->data.integral.is_signed; |
| 3413 | ZigType * wider_type = result_type; |
| 3414 | if (bitsize < 32 || !is_power_of_2(bitsize)) { |
| 3415 | const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize); |
| 3416 | wider_type = get_int_type(g, is_signed, wider_bitsize); |
| 3417 | } |
| 3418 | assert(bitsize <= 128); |
| 3419 | |
| 3420 | const char *float_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type); |
| 3421 | const char *int_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(wider_type); |
| 3422 | |
| 3423 | char fn_name[64]; |
| 3424 | if (is_signed) { |
| 3425 | sprintf(fn_name, "__fix%sf%si", float_compiler_rt_type_abbrev, int_compiler_rt_type_abbrev); |
| 3426 | } else { |
| 3427 | sprintf(fn_name, "__fixuns%sf%si", float_compiler_rt_type_abbrev, int_compiler_rt_type_abbrev); |
| 3428 | } |
| 3429 | |
| 3430 | int param_count = 1; |
| 3431 | LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, wider_type->llvm_type); |
| 3432 | |
| 3433 | LLVMValueRef result; |
| 3434 | if (vector_len == 0) { |
| 3435 | LLVMValueRef params[1] = {value_ref}; |
| 3436 | result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3437 | } else { |
| 3438 | ZigType *alloca_ty = operand_type; |
| 3439 | result = build_alloca(g, alloca_ty, "", 0); |
| 3440 | |
| 3441 | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 3442 | for (uint32_t i = 0; i < vector_len; i++) { |
| 3443 | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 3444 | LLVMValueRef params[1] = { |
| 3445 | LLVMBuildExtractElement(g->builder, value_ref, index_value, ""), |
| 3446 | }; |
| 3447 | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3448 | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 3449 | call_result, index_value, ""); |
| 3450 | } |
| 3451 | |
| 3452 | result = LLVMBuildLoad(g->builder, result, ""); |
| 3453 | } |
| 3454 | |
| 3455 | // Handle integers of non-pot bitsize by shortening them on the output |
| 3456 | if (result_type != wider_type) { |
| 3457 | return gen_widen_or_shorten(g, false, wider_type, result_type, result); |
| 3458 | } |
| 3459 | return result; |
| 3460 | } |
| 3461 | |
| 3462 | static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LLVMValueRef op2_value, ZigType *operand_type, IrBinOp op_id) { |
| 3463 | uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; |
| 3464 | |
| 3465 | LLVMTypeRef return_type = operand_type->llvm_type; |
| 3338 | 3466 | int param_count = 2; |
| 3339 | | const char *func_name; |
| 3340 | | SoftF80Icmp res_icmp = NONE; |
| 3467 | |
| 3468 | const char *compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type); |
| 3469 | const char *math_h_type_abbrev = get_math_h_type_abbrev(g, operand_type); |
| 3470 | |
| 3471 | char fn_name[64]; |
| 3472 | Icmp res_icmp = NONE; |
| 3341 | 3473 | switch (op_id) { |
| 3342 | 3474 | case IrBinOpInvalid: |
| 3343 | 3475 | case IrBinOpArrayCat: |
| ... | ... | @@ -3362,152 +3494,129 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, |
| 3362 | 3494 | zig_unreachable(); |
| 3363 | 3495 | case IrBinOpCmpEq: |
| 3364 | 3496 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3365 | | func_name = "__eqxf2"; |
| 3497 | sprintf(fn_name, "__eq%sf2", compiler_rt_type_abbrev); |
| 3366 | 3498 | res_icmp = EQ_ZERO; |
| 3367 | 3499 | break; |
| 3368 | 3500 | case IrBinOpCmpNotEq: |
| 3369 | 3501 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3370 | | func_name = "__nexf2"; |
| 3502 | sprintf(fn_name, "__ne%sf2", compiler_rt_type_abbrev); |
| 3371 | 3503 | res_icmp = NE_ZERO; |
| 3372 | 3504 | break; |
| 3373 | 3505 | case IrBinOpCmpLessOrEq: |
| 3374 | 3506 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3375 | | func_name = "__lexf2"; |
| 3507 | sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev); |
| 3376 | 3508 | res_icmp = LE_ZERO; |
| 3377 | 3509 | break; |
| 3378 | 3510 | case IrBinOpCmpLessThan: |
| 3379 | 3511 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3380 | | func_name = "__lexf2"; |
| 3512 | sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev); |
| 3381 | 3513 | res_icmp = EQ_NEG; |
| 3382 | 3514 | break; |
| 3383 | 3515 | case IrBinOpCmpGreaterOrEq: |
| 3384 | 3516 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3385 | | func_name = "__gexf2"; |
| 3517 | sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev); |
| 3386 | 3518 | res_icmp = GE_ZERO; |
| 3387 | 3519 | break; |
| 3388 | 3520 | case IrBinOpCmpGreaterThan: |
| 3389 | 3521 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3390 | | func_name = "__gexf2"; |
| 3522 | sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev); |
| 3391 | 3523 | res_icmp = EQ_ONE; |
| 3392 | 3524 | break; |
| 3393 | 3525 | case IrBinOpMaximum: |
| 3394 | | func_name = "__fmaxx"; |
| 3526 | sprintf(fn_name, "fmax%s", math_h_type_abbrev); |
| 3395 | 3527 | break; |
| 3396 | 3528 | case IrBinOpMinimum: |
| 3397 | | func_name = "__fminx"; |
| 3529 | sprintf(fn_name, "fmin%s", math_h_type_abbrev); |
| 3398 | 3530 | break; |
| 3399 | 3531 | case IrBinOpMult: |
| 3400 | | func_name = "__mulxf3"; |
| 3532 | sprintf(fn_name, "__mul%sf3", compiler_rt_type_abbrev); |
| 3401 | 3533 | break; |
| 3402 | 3534 | case IrBinOpAdd: |
| 3403 | | func_name = "__addxf3"; |
| 3535 | sprintf(fn_name, "__add%sf3", compiler_rt_type_abbrev); |
| 3404 | 3536 | break; |
| 3405 | 3537 | case IrBinOpSub: |
| 3406 | | func_name = "__subxf3"; |
| 3538 | sprintf(fn_name, "__sub%sf3", compiler_rt_type_abbrev); |
| 3407 | 3539 | break; |
| 3408 | 3540 | case IrBinOpDivUnspecified: |
| 3409 | | func_name = "__divxf3"; |
| 3410 | | break; |
| 3411 | 3541 | case IrBinOpDivExact: |
| 3412 | | func_name = "__divxf3"; |
| 3413 | | div_exact_safety_check = bin_op_instruction->safety_check_on && |
| 3414 | | ir_want_runtime_safety(g, &bin_op_instruction->base); |
| 3415 | | break; |
| 3416 | 3542 | case IrBinOpDivTrunc: |
| 3417 | | param_count = 1; |
| 3418 | | func_name = "__truncx"; |
| 3419 | | break; |
| 3420 | 3543 | case IrBinOpDivFloor: |
| 3421 | | param_count = 1; |
| 3422 | | func_name = "__floorx"; |
| 3544 | sprintf(fn_name, "__div%sf3", compiler_rt_type_abbrev); |
| 3423 | 3545 | break; |
| 3424 | 3546 | case IrBinOpRemRem: |
| 3425 | | param_count = 1; |
| 3426 | | func_name = "__remx"; |
| 3427 | | break; |
| 3428 | 3547 | case IrBinOpRemMod: |
| 3429 | | param_count = 1; |
| 3430 | | func_name = "__modx"; |
| 3548 | sprintf(fn_name, "fmod%s", math_h_type_abbrev); |
| 3431 | 3549 | break; |
| 3432 | 3550 | default: |
| 3433 | 3551 | zig_unreachable(); |
| 3434 | 3552 | } |
| 3435 | 3553 | |
| 3436 | | LLVMValueRef func_ref = get_soft_f80_bin_op_func(g, func_name, param_count, return_type); |
| 3554 | LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, return_type); |
| 3437 | 3555 | |
| 3438 | 3556 | LLVMValueRef result; |
| 3439 | 3557 | if (vector_len == 0) { |
| 3440 | 3558 | LLVMValueRef params[2] = {op1_value, op2_value}; |
| 3441 | 3559 | result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3442 | | result = add_f80_icmp(g, result, res_icmp); |
| 3560 | result = add_icmp(g, result, res_icmp); |
| 3443 | 3561 | } else { |
| 3444 | | ZigType *alloca_ty = op1->value->type; |
| 3562 | ZigType *alloca_ty = operand_type; |
| 3445 | 3563 | if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool); |
| 3446 | 3564 | result = build_alloca(g, alloca_ty, "", 0); |
| 3447 | | } |
| 3448 | | |
| 3449 | | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 3450 | | for (uint32_t i = 0; i < vector_len; i++) { |
| 3451 | | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 3452 | | LLVMValueRef params[2] = { |
| 3453 | | LLVMBuildExtractElement(g->builder, op1_value, index_value, ""), |
| 3454 | | LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), |
| 3455 | | }; |
| 3456 | | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3457 | | call_result = add_f80_icmp(g, call_result, res_icmp); |
| 3458 | | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 3459 | | call_result, index_value, ""); |
| 3460 | | } |
| 3461 | | |
| 3462 | | if (div_exact_safety_check) { |
| 3463 | | // Safety check: a / b == floor(a / b) |
| 3464 | | LLVMValueRef floor_func = get_soft_f80_bin_op_func(g, "__floorx", 1, return_type); |
| 3465 | | LLVMValueRef eq_func = get_soft_f80_bin_op_func(g, "__eqxf2", 2, g->builtin_types.entry_i32->llvm_type); |
| 3466 | | |
| 3467 | | LLVMValueRef ok_bit; |
| 3468 | | if (vector_len == 0) { |
| 3469 | | LLVMValueRef floored = LLVMBuildCall(g->builder, floor_func, &result, 1, ""); |
| 3470 | | |
| 3471 | | LLVMValueRef params[2] = {result, floored}; |
| 3472 | | ok_bit = LLVMBuildCall(g->builder, eq_func, params, 2, ""); |
| 3473 | | } else { |
| 3474 | | ZigType *bool_vec_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool); |
| 3475 | | ok_bit = build_alloca(g, bool_vec_ty, "", 0); |
| 3476 | | } |
| 3477 | 3565 | |
| 3566 | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 3478 | 3567 | for (uint32_t i = 0; i < vector_len; i++) { |
| 3479 | 3568 | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 3480 | | LLVMValueRef div_res = LLVMBuildExtractElement(g->builder, |
| 3481 | | LLVMBuildLoad(g->builder, result, ""), index_value, ""); |
| 3482 | | |
| 3483 | 3569 | LLVMValueRef params[2] = { |
| 3484 | | div_res, |
| 3485 | | LLVMBuildCall(g->builder, floor_func, &div_res, 1, ""), |
| 3570 | LLVMBuildExtractElement(g->builder, op1_value, index_value, ""), |
| 3571 | LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), |
| 3486 | 3572 | }; |
| 3487 | | LLVMValueRef cmp_res = LLVMBuildCall(g->builder, eq_func, params, 2, ""); |
| 3488 | | cmp_res = LLVMBuildTrunc(g->builder, cmp_res, g->builtin_types.entry_bool->llvm_type, ""); |
| 3489 | | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, ok_bit, ""), |
| 3490 | | cmp_res, index_value, ""); |
| 3573 | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3574 | call_result = add_icmp(g, call_result, res_icmp); |
| 3575 | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 3576 | call_result, index_value, ""); |
| 3491 | 3577 | } |
| 3492 | 3578 | |
| 3493 | | if (vector_len != 0) { |
| 3494 | | ok_bit = ZigLLVMBuildAndReduce(g->builder, LLVMBuildLoad(g->builder, ok_bit, "")); |
| 3495 | | } |
| 3496 | | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 3497 | | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 3579 | result = LLVMBuildLoad(g->builder, result, ""); |
| 3580 | } |
| 3498 | 3581 | |
| 3499 | | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 3582 | // Some operations are implemented as compound ops and require us to perform some |
| 3583 | // more operations before we obtain the final result |
| 3584 | switch (op_id) { |
| 3585 | case IrBinOpDivTrunc: |
| 3586 | return gen_float_un_op(g, result, operand_type, BuiltinFnIdTrunc); |
| 3587 | case IrBinOpDivFloor: |
| 3588 | return gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); |
| 3589 | case IrBinOpRemMod: |
| 3590 | { |
| 3591 | LLVMValueRef b = gen_soft_float_bin_op(g, result, op2_value, operand_type, IrBinOpAdd); |
| 3592 | LLVMValueRef wrapped_result = gen_soft_float_bin_op(g, b, op2_value, operand_type, IrBinOpRemRem); |
| 3593 | LLVMValueRef zero = LLVMConstNull(operand_type->llvm_type); |
| 3594 | LLVMValueRef ltz = gen_soft_float_bin_op(g, op1_value, zero, operand_type, IrBinOpCmpLessThan); |
| 3500 | 3595 | |
| 3501 | | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| 3502 | | gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); |
| 3596 | return LLVMBuildSelect(g->builder, ltz, wrapped_result, result, ""); |
| 3597 | } |
| 3598 | case IrBinOpDivExact: |
| 3599 | { |
| 3600 | LLVMValueRef floored = gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); |
| 3601 | LLVMValueRef ok_bit = gen_soft_float_bin_op(g, result, floored, operand_type, IrBinOpCmpEq); |
| 3602 | if (vector_len != 0) { |
| 3603 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 3604 | } |
| 3503 | 3605 | |
| 3504 | | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 3505 | | } |
| 3606 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 3607 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 3608 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 3506 | 3609 | |
| 3507 | | if (vector_len != 0) { |
| 3508 | | result = LLVMBuildLoad(g->builder, result, ""); |
| 3610 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| 3611 | gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); |
| 3612 | |
| 3613 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 3614 | } |
| 3615 | return result; |
| 3616 | default: |
| 3617 | return result; |
| 3509 | 3618 | } |
| 3510 | | return result; |
| 3619 | zig_unreachable(); |
| 3511 | 3620 | } |
| 3512 | 3621 | |
| 3513 | 3622 | static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, |
| ... | ... | @@ -3519,8 +3628,13 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, |
| 3519 | 3628 | |
| 3520 | 3629 | ZigType *operand_type = op1->value->type; |
| 3521 | 3630 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; |
| 3522 | | if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { |
| 3523 | | return ir_render_soft_f80_bin_op(g, executable, bin_op_instruction); |
| 3631 | if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || |
| 3632 | (scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { |
| 3633 | // LLVM incorrectly lowers the soft float calls for f128 as if they operated on `long double`. |
| 3634 | // On some targets this will be incorrect, so we manually lower the call ourselves. |
| 3635 | LLVMValueRef op1_value = ir_llvm_value(g, op1); |
| 3636 | LLVMValueRef op2_value = ir_llvm_value(g, op2); |
| 3637 | return gen_soft_float_bin_op(g, op1_value, op2_value, operand_type, op_id); |
| 3524 | 3638 | } |
| 3525 | 3639 | |
| 3526 | 3640 | |
| ... | ... | @@ -3828,10 +3942,17 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable, |
| 3828 | 3942 | } |
| 3829 | 3943 | case CastOpIntToFloat: |
| 3830 | 3944 | assert(actual_type->id == ZigTypeIdInt); |
| 3831 | | if (actual_type->data.integral.is_signed) { |
| 3832 | | return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3833 | | } else { |
| 3834 | | return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3945 | { |
| 3946 | if ((wanted_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || |
| 3947 | (wanted_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { |
| 3948 | return gen_soft_int_to_float_op(g, expr_val, actual_type, wanted_type); |
| 3949 | } else { |
| 3950 | if (actual_type->data.integral.is_signed) { |
| 3951 | return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3952 | } else { |
| 3953 | return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3954 | } |
| 3955 | } |
| 3835 | 3956 | } |
| 3836 | 3957 | case CastOpFloatToInt: { |
| 3837 | 3958 | assert(wanted_type->id == ZigTypeIdInt); |
| ... | ... | @@ -3840,18 +3961,28 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable, |
| 3840 | 3961 | bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base); |
| 3841 | 3962 | |
| 3842 | 3963 | LLVMValueRef result; |
| 3843 | | if (wanted_type->data.integral.is_signed) { |
| 3844 | | result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3964 | if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || |
| 3965 | (actual_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { |
| 3966 | result = gen_soft_float_to_int_op(g, expr_val, actual_type, wanted_type); |
| 3845 | 3967 | } else { |
| 3846 | | result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3968 | if (wanted_type->data.integral.is_signed) { |
| 3969 | result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3970 | } else { |
| 3971 | result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 3972 | } |
| 3847 | 3973 | } |
| 3848 | 3974 | |
| 3849 | 3975 | if (want_safety) { |
| 3850 | 3976 | LLVMValueRef back_to_float; |
| 3851 | | if (wanted_type->data.integral.is_signed) { |
| 3852 | | back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); |
| 3977 | if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || |
| 3978 | (actual_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { |
| 3979 | back_to_float = gen_soft_int_to_float_op(g, result, wanted_type, actual_type); |
| 3853 | 3980 | } else { |
| 3854 | | back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); |
| 3981 | if (wanted_type->data.integral.is_signed) { |
| 3982 | back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); |
| 3983 | } else { |
| 3984 | back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); |
| 3985 | } |
| 3855 | 3986 | } |
| 3856 | 3987 | LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, ""); |
| 3857 | 3988 | LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f); |
| ... | ... | @@ -4151,42 +4282,46 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable, |
| 4151 | 4282 | return LLVMBuildNot(g->builder, operand, ""); |
| 4152 | 4283 | } |
| 4153 | 4284 | |
| 4154 | | static LLVMValueRef ir_gen_soft_f80_neg(CodeGen *g, ZigType *op_type, LLVMValueRef operand) { |
| 4155 | | uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; |
| 4285 | static LLVMValueRef gen_soft_float_neg(CodeGen *g, ZigType *operand_type, LLVMValueRef operand) { |
| 4286 | uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; |
| 4287 | uint16_t num_bits = operand_type->data.floating.bit_count; |
| 4156 | 4288 | |
| 4157 | | LLVMTypeRef llvm_i80 = LLVMIntType(80); |
| 4158 | | LLVMValueRef sign_mask = LLVMConstInt(llvm_i80, 1, false); |
| 4159 | | sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(llvm_i80, 79, false)); |
| 4289 | ZigType *iX_type = get_int_type(g, true, num_bits); |
| 4290 | LLVMValueRef sign_mask = LLVMConstInt(iX_type->llvm_type, 1, false); |
| 4291 | sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(iX_type->llvm_type, num_bits - 1, false)); |
| 4160 | 4292 | |
| 4161 | | LLVMValueRef result; |
| 4162 | 4293 | if (vector_len == 0) { |
| 4163 | | result = LLVMBuildXor(g->builder, operand, sign_mask, ""); |
| 4294 | LLVMValueRef bitcasted_operand = LLVMBuildBitCast(g->builder, operand, iX_type->llvm_type, ""); |
| 4295 | LLVMValueRef result = LLVMBuildXor(g->builder, bitcasted_operand, sign_mask, ""); |
| 4296 | |
| 4297 | return LLVMBuildBitCast(g->builder, result, operand_type->llvm_type, ""); |
| 4164 | 4298 | } else { |
| 4165 | | result = build_alloca(g, op_type, "", 0); |
| 4166 | | } |
| 4299 | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 4300 | ZigType *iX_vector_type = get_vector_type(g, vector_len, iX_type); |
| 4167 | 4301 | |
| 4168 | | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 4169 | | for (uint32_t i = 0; i < vector_len; i++) { |
| 4170 | | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 4171 | | LLVMValueRef xor_operand = LLVMBuildExtractElement(g->builder, operand, index_value, ""); |
| 4172 | | LLVMValueRef xor_result = LLVMBuildXor(g->builder, xor_operand, sign_mask, ""); |
| 4173 | | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 4174 | | xor_result, index_value, ""); |
| 4175 | | } |
| 4176 | | if (vector_len != 0) { |
| 4177 | | result = LLVMBuildLoad(g->builder, result, ""); |
| 4302 | LLVMValueRef result = build_alloca(g, iX_vector_type, "", 0); |
| 4303 | LLVMValueRef bitcasted_operand = LLVMBuildBitCast(g->builder, operand, iX_vector_type->llvm_type, ""); |
| 4304 | for (uint32_t i = 0; i < vector_len; i++) { |
| 4305 | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 4306 | LLVMValueRef elem = LLVMBuildExtractElement(g->builder, bitcasted_operand, index_value, ""); |
| 4307 | LLVMValueRef result_elem = LLVMBuildXor(g->builder, elem, sign_mask, ""); |
| 4308 | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 4309 | result_elem, index_value, ""); |
| 4310 | } |
| 4311 | return LLVMBuildBitCast(g->builder, LLVMBuildLoad(g->builder, result, ""), operand_type->llvm_type, ""); |
| 4178 | 4312 | } |
| 4179 | | return result; |
| 4180 | 4313 | } |
| 4181 | 4314 | |
| 4182 | | static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) { |
| 4315 | static LLVMValueRef gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) { |
| 4183 | 4316 | LLVMValueRef llvm_operand = ir_llvm_value(g, operand); |
| 4184 | 4317 | ZigType *operand_type = operand->value->type; |
| 4185 | 4318 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? |
| 4186 | 4319 | operand_type->data.vector.elem_type : operand_type; |
| 4187 | 4320 | |
| 4188 | | if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) |
| 4189 | | return ir_gen_soft_f80_neg(g, operand_type, llvm_operand); |
| 4321 | if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || |
| 4322 | (scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { |
| 4323 | return gen_soft_float_neg(g, operand_type, llvm_operand); |
| 4324 | } |
| 4190 | 4325 | |
| 4191 | 4326 | if (scalar_type->id == ZigTypeIdFloat) { |
| 4192 | 4327 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst)); |
| ... | ... | @@ -4210,7 +4345,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirIn |
| 4210 | 4345 | static LLVMValueRef ir_render_negation(CodeGen *g, Stage1Air *executable, |
| 4211 | 4346 | Stage1AirInstNegation *inst) |
| 4212 | 4347 | { |
| 4213 | | return ir_gen_negation(g, &inst->base, inst->operand, inst->wrapping); |
| 4348 | return gen_negation(g, &inst->base, inst->operand, inst->wrapping); |
| 4214 | 4349 | } |
| 4215 | 4350 | |
| 4216 | 4351 | static LLVMValueRef ir_render_bool_not(CodeGen *g, Stage1Air *executable, Stage1AirInstBoolNot *instruction) { |
| ... | ... | @@ -7024,110 +7159,34 @@ static LLVMValueRef ir_render_atomic_store(CodeGen *g, Stage1Air *executable, |
| 7024 | 7159 | return nullptr; |
| 7025 | 7160 | } |
| 7026 | 7161 | |
| 7027 | | static LLVMValueRef ir_render_soft_f80_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) { |
| 7028 | | ZigType *op_type = instruction->operand->value->type; |
| 7029 | | uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; |
| 7030 | | |
| 7031 | | const char *func_name; |
| 7032 | | switch (instruction->fn_id) { |
| 7033 | | case BuiltinFnIdSqrt: |
| 7034 | | func_name = "__sqrtx"; |
| 7035 | | break; |
| 7036 | | case BuiltinFnIdSin: |
| 7037 | | func_name = "__sinx"; |
| 7038 | | break; |
| 7039 | | case BuiltinFnIdCos: |
| 7040 | | func_name = "__cosx"; |
| 7041 | | break; |
| 7042 | | case BuiltinFnIdExp: |
| 7043 | | func_name = "__expx"; |
| 7044 | | break; |
| 7045 | | case BuiltinFnIdExp2: |
| 7046 | | func_name = "__exp2x"; |
| 7047 | | break; |
| 7048 | | case BuiltinFnIdLog: |
| 7049 | | func_name = "__logx"; |
| 7050 | | break; |
| 7051 | | case BuiltinFnIdLog2: |
| 7052 | | func_name = "__log2x"; |
| 7053 | | break; |
| 7054 | | case BuiltinFnIdLog10: |
| 7055 | | func_name = "__log10x"; |
| 7056 | | break; |
| 7057 | | case BuiltinFnIdFabs: |
| 7058 | | func_name = "__fabsx"; |
| 7059 | | break; |
| 7060 | | case BuiltinFnIdFloor: |
| 7061 | | func_name = "__floorx"; |
| 7062 | | break; |
| 7063 | | case BuiltinFnIdCeil: |
| 7064 | | func_name = "__ceilx"; |
| 7065 | | break; |
| 7066 | | case BuiltinFnIdTrunc: |
| 7067 | | func_name = "__truncx"; |
| 7068 | | break; |
| 7069 | | case BuiltinFnIdNearbyInt: |
| 7070 | | func_name = "__nearbyintx"; |
| 7071 | | break; |
| 7072 | | case BuiltinFnIdRound: |
| 7073 | | func_name = "__roundx"; |
| 7074 | | break; |
| 7075 | | default: |
| 7076 | | zig_unreachable(); |
| 7077 | | } |
| 7078 | | |
| 7079 | | |
| 7080 | | LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); |
| 7081 | | if (func_ref == nullptr) { |
| 7082 | | LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type; |
| 7083 | | LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, &f80_ref, 1, false); |
| 7084 | | func_ref = LLVMAddFunction(g->module, func_name, fn_type); |
| 7085 | | } |
| 7086 | | |
| 7087 | | LLVMValueRef operand = ir_llvm_value(g, instruction->operand); |
| 7088 | | LLVMValueRef result; |
| 7089 | | if (vector_len == 0) { |
| 7090 | | result = LLVMBuildCall(g->builder, func_ref, &operand, 1, ""); |
| 7091 | | } else { |
| 7092 | | result = build_alloca(g, instruction->operand->value->type, "", 0); |
| 7093 | | } |
| 7094 | | |
| 7095 | | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; |
| 7096 | | for (uint32_t i = 0; i < vector_len; i++) { |
| 7097 | | LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); |
| 7098 | | LLVMValueRef param = LLVMBuildExtractElement(g->builder, operand, index_value, ""); |
| 7099 | | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, &param, 1, ""); |
| 7100 | | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 7101 | | call_result, index_value, ""); |
| 7102 | | } |
| 7103 | | if (vector_len != 0) { |
| 7104 | | result = LLVMBuildLoad(g->builder, result, ""); |
| 7105 | | } |
| 7106 | | return result; |
| 7107 | | } |
| 7108 | | |
| 7109 | 7162 | static LLVMValueRef ir_render_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) { |
| 7110 | | ZigType *op_type = instruction->operand->value->type; |
| 7111 | | op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type; |
| 7112 | | if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { |
| 7113 | | return ir_render_soft_f80_float_op(g, executable, instruction); |
| 7114 | | } |
| 7115 | 7163 | LLVMValueRef operand = ir_llvm_value(g, instruction->operand); |
| 7116 | | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id); |
| 7117 | | return LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); |
| 7164 | ZigType *operand_type = instruction->operand->value->type; |
| 7165 | return gen_float_un_op(g, operand, operand_type, instruction->fn_id); |
| 7118 | 7166 | } |
| 7119 | 7167 | |
| 7120 | | static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) { |
| 7121 | | ZigType *op_type = instruction->op1->value->type; |
| 7122 | | uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; |
| 7168 | static LLVMValueRef ir_render_soft_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction, ZigType *float_type) { |
| 7169 | ZigType *operand_type = instruction->op1->value->type; |
| 7170 | uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; |
| 7171 | |
| 7172 | const char *fn_name; |
| 7173 | if (float_type == g->builtin_types.entry_f32) |
| 7174 | fn_name = "fmaf"; |
| 7175 | else if (float_type == g->builtin_types.entry_f64) |
| 7176 | fn_name = "fma"; |
| 7177 | else if (float_type == g->builtin_types.entry_f80) |
| 7178 | fn_name = "__fmax"; |
| 7179 | else if (float_type == g->builtin_types.entry_f128) |
| 7180 | fn_name = "fmaq"; |
| 7181 | else |
| 7182 | zig_unreachable(); |
| 7123 | 7183 | |
| 7124 | | const char *func_name = "__fmax"; |
| 7125 | | LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); |
| 7184 | LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name); |
| 7126 | 7185 | if (func_ref == nullptr) { |
| 7127 | | LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type; |
| 7128 | | LLVMTypeRef params[3] = { f80_ref, f80_ref, f80_ref }; |
| 7129 | | LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, params, 3, false); |
| 7130 | | func_ref = LLVMAddFunction(g->module, func_name, fn_type); |
| 7186 | LLVMTypeRef float_type_ref = float_type->llvm_type; |
| 7187 | LLVMTypeRef params[3] = { float_type_ref, float_type_ref, float_type_ref }; |
| 7188 | LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, params, 3, false); |
| 7189 | func_ref = LLVMAddFunction(g->module, fn_name, fn_type); |
| 7131 | 7190 | } |
| 7132 | 7191 | |
| 7133 | 7192 | LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); |
| ... | ... | @@ -7161,10 +7220,11 @@ static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable |
| 7161 | 7220 | } |
| 7162 | 7221 | |
| 7163 | 7222 | static LLVMValueRef ir_render_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) { |
| 7164 | | ZigType *op_type = instruction->op1->value->type; |
| 7165 | | op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type; |
| 7166 | | if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { |
| 7167 | | return ir_render_soft_f80_mul_add(g, executable, instruction); |
| 7223 | ZigType *operand_type = instruction->op1->value->type; |
| 7224 | operand_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type; |
| 7225 | if ((operand_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || |
| 7226 | (operand_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { |
| 7227 | return ir_render_soft_mul_add(g, executable, instruction, operand_type); |
| 7168 | 7228 | } |
| 7169 | 7229 | LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); |
| 7170 | 7230 | LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); |