| ... | ... | @@ -18,7 +18,6 @@ const Compilation = @import("../../Compilation.zig"); |
| 18 | 18 | const link = @import("../../link.zig"); |
| 19 | 19 | const Air = @import("../../Air.zig"); |
| 20 | 20 | const Liveness = @import("../../Liveness.zig"); |
| 21 | | const target_util = @import("../../target.zig"); |
| 22 | 21 | const Mir = @import("Mir.zig"); |
| 23 | 22 | const Emit = @import("Emit.zig"); |
| 24 | 23 | const abi = @import("abi.zig"); |
| ... | ... | @@ -27,6 +26,12 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 27 | 26 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 28 | 27 | const Wasm = link.File.Wasm; |
| 29 | 28 | |
| 29 | const target_util = @import("../../target.zig"); |
| 30 | const libcFloatPrefix = target_util.libcFloatPrefix; |
| 31 | const libcFloatSuffix = target_util.libcFloatSuffix; |
| 32 | const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; |
| 33 | const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; |
| 34 | |
| 30 | 35 | /// Reference to the function declaration the code |
| 31 | 36 | /// section belongs to |
| 32 | 37 | owner_nav: InternPool.Nav.Index, |
| ... | ... | @@ -854,7 +859,6 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void { |
| 854 | 859 | } |
| 855 | 860 | } |
| 856 | 861 | |
| 857 | | /// Appends a MIR instruction and returns its index within the list of instructions |
| 858 | 862 | fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { |
| 859 | 863 | try cg.mir_instructions.append(cg.gpa, inst); |
| 860 | 864 | } |
| ... | ... | @@ -873,14 +877,6 @@ fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void |
| 873 | 877 | try cg.addInst(.{ .tag = tag, .data = .{ .label = label } }); |
| 874 | 878 | } |
| 875 | 879 | |
| 876 | | fn addIpIndex(cg: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Index) Allocator.Error!void { |
| 877 | | try cg.addInst(.{ .tag = tag, .data = .{ .ip_index = i } }); |
| 878 | | } |
| 879 | | |
| 880 | | fn addNav(cg: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Nav.Index) Allocator.Error!void { |
| 881 | | try cg.addInst(.{ .tag = tag, .data = .{ .nav_index = i } }); |
| 882 | | } |
| 883 | | |
| 884 | 880 | /// Accepts an unsigned 32bit integer rather than a signed integer to |
| 885 | 881 | /// prevent us from having to bitcast multiple times as most values |
| 886 | 882 | /// within codegen are represented as unsigned rather than signed. |
| ... | ... | @@ -1887,8 +1883,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1887 | 1883 | .shl_sat => cg.airShlSat(inst), |
| 1888 | 1884 | .shr, .shr_exact => cg.airBinOp(inst, .shr), |
| 1889 | 1885 | .xor => cg.airBinOp(inst, .xor), |
| 1890 | | .max => cg.airMaxMin(inst, .max), |
| 1891 | | .min => cg.airMaxMin(inst, .min), |
| 1886 | .max => cg.airMaxMin(inst, .fmax, .gt), |
| 1887 | .min => cg.airMaxMin(inst, .fmin, .lt), |
| 1892 | 1888 | .mul_add => cg.airMulAdd(inst), |
| 1893 | 1889 | |
| 1894 | 1890 | .sqrt => cg.airUnaryFloatOp(inst, .sqrt), |
| ... | ... | @@ -2263,7 +2259,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie |
| 2263 | 2259 | } |
| 2264 | 2260 | |
| 2265 | 2261 | if (callee) |nav_index| { |
| 2266 | | try cg.addNav(.call_nav, nav_index); |
| 2262 | try cg.addInst(.{ .tag = .call_nav, .data = .{ .nav_index = nav_index } }); |
| 2267 | 2263 | } else { |
| 2268 | 2264 | // in this case we call a function pointer |
| 2269 | 2265 | // so load its value onto the stack |
| ... | ... | @@ -2669,20 +2665,20 @@ fn binOpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr |
| 2669 | 2665 | } |
| 2670 | 2666 | |
| 2671 | 2667 | switch (op) { |
| 2672 | | .mul => return cg.callIntrinsic("__multi3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2668 | .mul => return cg.callIntrinsic(.__multi3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2673 | 2669 | .div => switch (int_info.signedness) { |
| 2674 | | .signed => return cg.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2675 | | .unsigned => return cg.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2670 | .signed => return cg.callIntrinsic(.__divti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2671 | .unsigned => return cg.callIntrinsic(.__udivti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2676 | 2672 | }, |
| 2677 | 2673 | .rem => switch (int_info.signedness) { |
| 2678 | | .signed => return cg.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2679 | | .unsigned => return cg.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2674 | .signed => return cg.callIntrinsic(.__modti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2675 | .unsigned => return cg.callIntrinsic(.__umodti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2680 | 2676 | }, |
| 2681 | 2677 | .shr => switch (int_info.signedness) { |
| 2682 | | .signed => return cg.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2683 | | .unsigned => return cg.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2678 | .signed => return cg.callIntrinsic(.__ashrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2679 | .unsigned => return cg.callIntrinsic(.__lshrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2684 | 2680 | }, |
| 2685 | | .shl => return cg.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2681 | .shl => return cg.callIntrinsic(.__ashlti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2686 | 2682 | .@"and", .@"or", .xor => { |
| 2687 | 2683 | const result = try cg.allocStack(ty); |
| 2688 | 2684 | try cg.emitWValue(result); |
| ... | ... | @@ -2802,6 +2798,46 @@ const FloatOp = enum { |
| 2802 | 2798 | => null, |
| 2803 | 2799 | }; |
| 2804 | 2800 | } |
| 2801 | |
| 2802 | fn intrinsic(op: FloatOp, bits: u16) Mir.Intrinsic { |
| 2803 | return switch (op) { |
| 2804 | inline .add, .sub, .div, .mul => |ct_op| switch (bits) { |
| 2805 | inline 16, 80, 128 => |ct_bits| @field( |
| 2806 | Mir.Intrinsic, |
| 2807 | "__" ++ @tagName(ct_op) ++ compilerRtFloatAbbrev(ct_bits) ++ "f3", |
| 2808 | ), |
| 2809 | else => unreachable, |
| 2810 | }, |
| 2811 | |
| 2812 | inline .ceil, |
| 2813 | .cos, |
| 2814 | .exp, |
| 2815 | .exp2, |
| 2816 | .fabs, |
| 2817 | .floor, |
| 2818 | .fma, |
| 2819 | .fmax, |
| 2820 | .fmin, |
| 2821 | .fmod, |
| 2822 | .log, |
| 2823 | .log10, |
| 2824 | .log2, |
| 2825 | .round, |
| 2826 | .sin, |
| 2827 | .sqrt, |
| 2828 | .tan, |
| 2829 | .trunc, |
| 2830 | => |ct_op| switch (bits) { |
| 2831 | inline 16, 80, 128 => |ct_bits| @field( |
| 2832 | Mir.Intrinsic, |
| 2833 | libcFloatPrefix(ct_bits) ++ @tagName(ct_op) ++ libcFloatSuffix(ct_bits), |
| 2834 | ), |
| 2835 | else => unreachable, |
| 2836 | }, |
| 2837 | |
| 2838 | .neg => unreachable, |
| 2839 | }; |
| 2840 | } |
| 2805 | 2841 | }; |
| 2806 | 2842 | |
| 2807 | 2843 | fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -2919,44 +2955,12 @@ fn floatOp(cg: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) Inne |
| 2919 | 2955 | } |
| 2920 | 2956 | } |
| 2921 | 2957 | |
| 2922 | | var fn_name_buf: [64]u8 = undefined; |
| 2923 | | const fn_name = switch (float_op) { |
| 2924 | | .add, |
| 2925 | | .sub, |
| 2926 | | .div, |
| 2927 | | .mul, |
| 2928 | | => std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f3", .{ |
| 2929 | | @tagName(float_op), target_util.compilerRtFloatAbbrev(float_bits), |
| 2930 | | }) catch unreachable, |
| 2931 | | |
| 2932 | | .ceil, |
| 2933 | | .cos, |
| 2934 | | .exp, |
| 2935 | | .exp2, |
| 2936 | | .fabs, |
| 2937 | | .floor, |
| 2938 | | .fma, |
| 2939 | | .fmax, |
| 2940 | | .fmin, |
| 2941 | | .fmod, |
| 2942 | | .log, |
| 2943 | | .log10, |
| 2944 | | .log2, |
| 2945 | | .round, |
| 2946 | | .sin, |
| 2947 | | .sqrt, |
| 2948 | | .tan, |
| 2949 | | .trunc, |
| 2950 | | => std.fmt.bufPrint(&fn_name_buf, "{s}{s}{s}", .{ |
| 2951 | | target_util.libcFloatPrefix(float_bits), @tagName(float_op), target_util.libcFloatSuffix(float_bits), |
| 2952 | | }) catch unreachable, |
| 2953 | | .neg => unreachable, // handled above |
| 2954 | | }; |
| 2958 | const intrinsic = float_op.intrinsic(float_bits); |
| 2955 | 2959 | |
| 2956 | 2960 | // fma requires three operands |
| 2957 | 2961 | var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index }; |
| 2958 | 2962 | const param_types = param_types_buffer[0..args.len]; |
| 2959 | | return cg.callIntrinsic(fn_name, param_types, ty, args); |
| 2963 | return cg.callIntrinsic(intrinsic, param_types, ty, args); |
| 2960 | 2964 | } |
| 2961 | 2965 | |
| 2962 | 2966 | /// NOTE: The result value remains on top of the stack. |
| ... | ... | @@ -3605,12 +3609,8 @@ fn cmpFloat(cg: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math.C |
| 3605 | 3609 | return .stack; |
| 3606 | 3610 | }, |
| 3607 | 3611 | 80, 128 => { |
| 3608 | | var fn_name_buf: [32]u8 = undefined; |
| 3609 | | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f2", .{ |
| 3610 | | @tagName(op), target_util.compilerRtFloatAbbrev(float_bits), |
| 3611 | | }) catch unreachable; |
| 3612 | | |
| 3613 | | const result = try cg.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs }); |
| 3612 | const intrinsic = floatCmpIntrinsic(cmp_op, float_bits); |
| 3613 | const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs }); |
| 3614 | 3614 | return cg.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op); |
| 3615 | 3615 | }, |
| 3616 | 3616 | else => unreachable, |
| ... | ... | @@ -5001,19 +5001,26 @@ fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5001 | 5001 | } |
| 5002 | 5002 | |
| 5003 | 5003 | if ((op_bits != 32 and op_bits != 64) or dest_info.bits > 64) { |
| 5004 | | const dest_bitsize = if (dest_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits); |
| 5005 | | |
| 5006 | | var fn_name_buf: [16]u8 = undefined; |
| 5007 | | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__fix{s}{s}f{s}i", .{ |
| 5008 | | switch (dest_info.signedness) { |
| 5009 | | .signed => "", |
| 5010 | | .unsigned => "uns", |
| 5004 | const dest_bitsize = if (dest_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits); |
| 5005 | |
| 5006 | const intrinsic = switch (dest_info.signedness) { |
| 5007 | inline .signed, .unsigned => |ct_s| switch (op_bits) { |
| 5008 | inline 16, 32, 64, 80, 128 => |ct_op_bits| switch (dest_bitsize) { |
| 5009 | inline 32, 64, 128 => |ct_dest_bits| @field( |
| 5010 | Mir.Intrinsic, |
| 5011 | "__fix" ++ switch (ct_s) { |
| 5012 | .signed => "", |
| 5013 | .unsigned => "uns", |
| 5014 | } ++ |
| 5015 | compilerRtFloatAbbrev(ct_op_bits) ++ "f" ++ |
| 5016 | compilerRtIntAbbrev(ct_dest_bits) ++ "i", |
| 5017 | ), |
| 5018 | else => unreachable, |
| 5019 | }, |
| 5020 | else => unreachable, |
| 5011 | 5021 | }, |
| 5012 | | target_util.compilerRtFloatAbbrev(op_bits), |
| 5013 | | target_util.compilerRtIntAbbrev(dest_bitsize), |
| 5014 | | }) catch unreachable; |
| 5015 | | |
| 5016 | | const result = try cg.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand}); |
| 5022 | }; |
| 5023 | const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand}); |
| 5017 | 5024 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5018 | 5025 | } |
| 5019 | 5026 | |
| ... | ... | @@ -5046,19 +5053,27 @@ fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5046 | 5053 | } |
| 5047 | 5054 | |
| 5048 | 5055 | if (op_info.bits > 64 or (dest_bits > 64 or dest_bits < 32)) { |
| 5049 | | const op_bitsize = if (op_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits); |
| 5050 | | |
| 5051 | | var fn_name_buf: [16]u8 = undefined; |
| 5052 | | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__float{s}{s}i{s}f", .{ |
| 5053 | | switch (op_info.signedness) { |
| 5054 | | .signed => "", |
| 5055 | | .unsigned => "un", |
| 5056 | const op_bitsize = if (op_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits); |
| 5057 | |
| 5058 | const intrinsic = switch (op_info.signedness) { |
| 5059 | inline .signed, .unsigned => |ct_s| switch (op_bitsize) { |
| 5060 | inline 32, 64, 128 => |ct_int_bits| switch (dest_bits) { |
| 5061 | inline 16, 32, 64, 80, 128 => |ct_float_bits| @field( |
| 5062 | Mir.Intrinsic, |
| 5063 | "__float" ++ switch (ct_s) { |
| 5064 | .signed => "", |
| 5065 | .unsigned => "un", |
| 5066 | } ++ |
| 5067 | compilerRtIntAbbrev(ct_int_bits) ++ "i" ++ |
| 5068 | compilerRtFloatAbbrev(ct_float_bits) ++ "f", |
| 5069 | ), |
| 5070 | else => unreachable, |
| 5071 | }, |
| 5072 | else => unreachable, |
| 5056 | 5073 | }, |
| 5057 | | target_util.compilerRtIntAbbrev(op_bitsize), |
| 5058 | | target_util.compilerRtFloatAbbrev(dest_bits), |
| 5059 | | }) catch unreachable; |
| 5074 | }; |
| 5060 | 5075 | |
| 5061 | | const result = try cg.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand}); |
| 5076 | const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand}); |
| 5062 | 5077 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5063 | 5078 | } |
| 5064 | 5079 | |
| ... | ... | @@ -5577,39 +5592,49 @@ fn airFpext(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5577 | 5592 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5578 | 5593 | } |
| 5579 | 5594 | |
| 5580 | | /// Extends a float from a given `Type` to a larger wanted `Type` |
| 5581 | | /// NOTE: Leaves the result on the stack |
| 5595 | /// Extends a float from a given `Type` to a larger wanted `Type`, leaving the |
| 5596 | /// result on the stack. |
| 5582 | 5597 | fn fpext(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 5583 | 5598 | const given_bits = given.floatBits(cg.target.*); |
| 5584 | 5599 | const wanted_bits = wanted.floatBits(cg.target.*); |
| 5585 | 5600 | |
| 5586 | | if (wanted_bits == 64 and given_bits == 32) { |
| 5587 | | try cg.emitWValue(operand); |
| 5588 | | try cg.addTag(.f64_promote_f32); |
| 5589 | | return .stack; |
| 5590 | | } else if (given_bits == 16 and wanted_bits <= 64) { |
| 5591 | | // call __extendhfsf2(f16) f32 |
| 5592 | | const f32_result = try cg.callIntrinsic( |
| 5593 | | "__extendhfsf2", |
| 5594 | | &.{.f16_type}, |
| 5595 | | Type.f32, |
| 5596 | | &.{operand}, |
| 5597 | | ); |
| 5598 | | assert(f32_result == .stack); |
| 5599 | | |
| 5600 | | if (wanted_bits == 64) { |
| 5601 | | try cg.addTag(.f64_promote_f32); |
| 5602 | | } |
| 5603 | | return .stack; |
| 5604 | | } |
| 5605 | | |
| 5606 | | var fn_name_buf: [13]u8 = undefined; |
| 5607 | | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__extend{s}f{s}f2", .{ |
| 5608 | | target_util.compilerRtFloatAbbrev(given_bits), |
| 5609 | | target_util.compilerRtFloatAbbrev(wanted_bits), |
| 5610 | | }) catch unreachable; |
| 5611 | | |
| 5612 | | return cg.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand}); |
| 5601 | const intrinsic: Mir.Intrinsic = switch (given_bits) { |
| 5602 | 16 => switch (wanted_bits) { |
| 5603 | 32 => { |
| 5604 | assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand})); |
| 5605 | return .stack; |
| 5606 | }, |
| 5607 | 64 => { |
| 5608 | assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand})); |
| 5609 | try cg.addTag(.f64_promote_f32); |
| 5610 | return .stack; |
| 5611 | }, |
| 5612 | 80 => .__extendhfxf2, |
| 5613 | 128 => .__extendhftf2, |
| 5614 | else => unreachable, |
| 5615 | }, |
| 5616 | 32 => switch (wanted_bits) { |
| 5617 | 64 => { |
| 5618 | try cg.emitWValue(operand); |
| 5619 | try cg.addTag(.f64_promote_f32); |
| 5620 | return .stack; |
| 5621 | }, |
| 5622 | 80 => .__extendsfxf2, |
| 5623 | 128 => .__extendsftf2, |
| 5624 | else => unreachable, |
| 5625 | }, |
| 5626 | 64 => switch (wanted_bits) { |
| 5627 | 80 => .__extenddfxf2, |
| 5628 | 128 => .__extenddftf2, |
| 5629 | else => unreachable, |
| 5630 | }, |
| 5631 | 80 => switch (wanted_bits) { |
| 5632 | 128 => .__extendxftf2, |
| 5633 | else => unreachable, |
| 5634 | }, |
| 5635 | else => unreachable, |
| 5636 | }; |
| 5637 | return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand}); |
| 5613 | 5638 | } |
| 5614 | 5639 | |
| 5615 | 5640 | fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -5621,34 +5646,48 @@ fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5621 | 5646 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5622 | 5647 | } |
| 5623 | 5648 | |
| 5624 | | /// Truncates a float from a given `Type` to its wanted `Type` |
| 5625 | | /// NOTE: The result value remains on the stack |
| 5649 | /// Truncates a float from a given `Type` to its wanted `Type`, leaving the |
| 5650 | /// result on the stack. |
| 5626 | 5651 | fn fptrunc(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 5627 | 5652 | const given_bits = given.floatBits(cg.target.*); |
| 5628 | 5653 | const wanted_bits = wanted.floatBits(cg.target.*); |
| 5629 | 5654 | |
| 5630 | | if (wanted_bits == 32 and given_bits == 64) { |
| 5631 | | try cg.emitWValue(operand); |
| 5632 | | try cg.addTag(.f32_demote_f64); |
| 5633 | | return .stack; |
| 5634 | | } else if (wanted_bits == 16 and given_bits <= 64) { |
| 5635 | | const op: WValue = if (given_bits == 64) blk: { |
| 5636 | | try cg.emitWValue(operand); |
| 5637 | | try cg.addTag(.f32_demote_f64); |
| 5638 | | break :blk .stack; |
| 5639 | | } else operand; |
| 5640 | | |
| 5641 | | // call __truncsfhf2(f32) f16 |
| 5642 | | return cg.callIntrinsic("__truncsfhf2", &.{.f32_type}, Type.f16, &.{op}); |
| 5643 | | } |
| 5644 | | |
| 5645 | | var fn_name_buf: [12]u8 = undefined; |
| 5646 | | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__trunc{s}f{s}f2", .{ |
| 5647 | | target_util.compilerRtFloatAbbrev(given_bits), |
| 5648 | | target_util.compilerRtFloatAbbrev(wanted_bits), |
| 5649 | | }) catch unreachable; |
| 5650 | | |
| 5651 | | return cg.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand}); |
| 5655 | const intrinsic: Mir.Intrinsic = switch (given_bits) { |
| 5656 | 32 => switch (wanted_bits) { |
| 5657 | 16 => { |
| 5658 | return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{operand}); |
| 5659 | }, |
| 5660 | else => unreachable, |
| 5661 | }, |
| 5662 | 64 => switch (wanted_bits) { |
| 5663 | 16 => { |
| 5664 | try cg.emitWValue(operand); |
| 5665 | try cg.addTag(.f32_demote_f64); |
| 5666 | return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{.stack}); |
| 5667 | }, |
| 5668 | 32 => { |
| 5669 | try cg.emitWValue(operand); |
| 5670 | try cg.addTag(.f32_demote_f64); |
| 5671 | return .stack; |
| 5672 | }, |
| 5673 | else => unreachable, |
| 5674 | }, |
| 5675 | 80 => switch (wanted_bits) { |
| 5676 | 16 => .__truncxfhf2, |
| 5677 | 32 => .__truncxfsf2, |
| 5678 | 64 => .__truncxfdf2, |
| 5679 | else => unreachable, |
| 5680 | }, |
| 5681 | 128 => switch (wanted_bits) { |
| 5682 | 16 => .__trunctfhf2, |
| 5683 | 32 => .__trunctfsf2, |
| 5684 | 64 => .__trunctfdf2, |
| 5685 | 80 => .__trunctfxf2, |
| 5686 | else => unreachable, |
| 5687 | }, |
| 5688 | else => unreachable, |
| 5689 | }; |
| 5690 | return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand}); |
| 5652 | 5691 | } |
| 5653 | 5692 | |
| 5654 | 5693 | fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -5823,7 +5862,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5823 | 5862 | switch (wasm_bits) { |
| 5824 | 5863 | 32 => { |
| 5825 | 5864 | const intrin_ret = try cg.callIntrinsic( |
| 5826 | | "__bitreversesi2", |
| 5865 | .__bitreversesi2, |
| 5827 | 5866 | &.{.u32_type}, |
| 5828 | 5867 | Type.u32, |
| 5829 | 5868 | &.{operand}, |
| ... | ... | @@ -5836,7 +5875,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5836 | 5875 | }, |
| 5837 | 5876 | 64 => { |
| 5838 | 5877 | const intrin_ret = try cg.callIntrinsic( |
| 5839 | | "__bitreversedi2", |
| 5878 | .__bitreversedi2, |
| 5840 | 5879 | &.{.u64_type}, |
| 5841 | 5880 | Type.u64, |
| 5842 | 5881 | &.{operand}, |
| ... | ... | @@ -5853,7 +5892,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5853 | 5892 | try cg.emitWValue(result); |
| 5854 | 5893 | const first_half = try cg.load(operand, Type.u64, 8); |
| 5855 | 5894 | const intrin_ret_first = try cg.callIntrinsic( |
| 5856 | | "__bitreversedi2", |
| 5895 | .__bitreversedi2, |
| 5857 | 5896 | &.{.u64_type}, |
| 5858 | 5897 | Type.u64, |
| 5859 | 5898 | &.{first_half}, |
| ... | ... | @@ -5866,7 +5905,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5866 | 5905 | try cg.emitWValue(result); |
| 5867 | 5906 | const second_half = try cg.load(operand, Type.u64, 0); |
| 5868 | 5907 | const intrin_ret_second = try cg.callIntrinsic( |
| 5869 | | "__bitreversedi2", |
| 5908 | .__bitreversedi2, |
| 5870 | 5909 | &.{.u64_type}, |
| 5871 | 5910 | Type.u64, |
| 5872 | 5911 | &.{second_half}, |
| ... | ... | @@ -6114,19 +6153,19 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6114 | 6153 | defer rhs_msb.free(cg); |
| 6115 | 6154 | |
| 6116 | 6155 | const cross_1 = try cg.callIntrinsic( |
| 6117 | | "__multi3", |
| 6156 | .__multi3, |
| 6118 | 6157 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6119 | 6158 | Type.i128, |
| 6120 | 6159 | &.{ lhs_msb, zero, rhs_lsb, zero }, |
| 6121 | 6160 | ); |
| 6122 | 6161 | const cross_2 = try cg.callIntrinsic( |
| 6123 | | "__multi3", |
| 6162 | .__multi3, |
| 6124 | 6163 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6125 | 6164 | Type.i128, |
| 6126 | 6165 | &.{ rhs_msb, zero, lhs_lsb, zero }, |
| 6127 | 6166 | ); |
| 6128 | 6167 | const mul_lsb = try cg.callIntrinsic( |
| 6129 | | "__multi3", |
| 6168 | .__multi3, |
| 6130 | 6169 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6131 | 6170 | Type.i128, |
| 6132 | 6171 | &.{ rhs_lsb, zero, lhs_lsb, zero }, |
| ... | ... | @@ -6165,7 +6204,7 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6165 | 6204 | } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: { |
| 6166 | 6205 | const overflow_ret = try cg.allocStack(Type.i32); |
| 6167 | 6206 | const res = try cg.callIntrinsic( |
| 6168 | | "__muloti4", |
| 6207 | .__muloti4, |
| 6169 | 6208 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, |
| 6170 | 6209 | Type.i128, |
| 6171 | 6210 | &.{ lhs, rhs, overflow_ret }, |
| ... | ... | @@ -6185,8 +6224,12 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6185 | 6224 | return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); |
| 6186 | 6225 | } |
| 6187 | 6226 | |
| 6188 | | fn airMaxMin(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 6189 | | assert(op == .max or op == .min); |
| 6227 | fn airMaxMin( |
| 6228 | cg: *CodeGen, |
| 6229 | inst: Air.Inst.Index, |
| 6230 | op: enum { fmax, fmin }, |
| 6231 | cmp_op: std.math.CompareOperator, |
| 6232 | ) InnerError!void { |
| 6190 | 6233 | const pt = cg.pt; |
| 6191 | 6234 | const zcu = pt.zcu; |
| 6192 | 6235 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | ... | @@ -6204,20 +6247,22 @@ fn airMaxMin(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 6204 | 6247 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6205 | 6248 | |
| 6206 | 6249 | if (ty.zigTypeTag(zcu) == .float) { |
| 6207 | | var fn_name_buf: [64]u8 = undefined; |
| 6208 | | const float_bits = ty.floatBits(cg.target.*); |
| 6209 | | const fn_name = std.fmt.bufPrint(&fn_name_buf, "{s}f{s}{s}", .{ |
| 6210 | | target_util.libcFloatPrefix(float_bits), |
| 6211 | | @tagName(op), |
| 6212 | | target_util.libcFloatSuffix(float_bits), |
| 6213 | | }) catch unreachable; |
| 6214 | | const result = try cg.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); |
| 6250 | const intrinsic = switch (op) { |
| 6251 | inline .fmin, .fmax => |ct_op| switch (ty.floatBits(cg.target.*)) { |
| 6252 | inline 16, 32, 64, 80, 128 => |bits| @field( |
| 6253 | Mir.Intrinsic, |
| 6254 | libcFloatPrefix(bits) ++ @tagName(ct_op) ++ libcFloatSuffix(bits), |
| 6255 | ), |
| 6256 | else => unreachable, |
| 6257 | }, |
| 6258 | }; |
| 6259 | const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); |
| 6215 | 6260 | try cg.lowerToStack(result); |
| 6216 | 6261 | } else { |
| 6217 | 6262 | // operands to select from |
| 6218 | 6263 | try cg.lowerToStack(lhs); |
| 6219 | 6264 | try cg.lowerToStack(rhs); |
| 6220 | | _ = try cg.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); |
| 6265 | _ = try cg.cmp(lhs, rhs, ty, cmp_op); |
| 6221 | 6266 | |
| 6222 | 6267 | // based on the result from comparison, return operand 0 or 1. |
| 6223 | 6268 | try cg.addTag(.select); |
| ... | ... | @@ -6247,7 +6292,7 @@ fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6247 | 6292 | const addend_ext = try cg.fpext(addend, ty, Type.f32); |
| 6248 | 6293 | // call to compiler-rt `fn fmaf(f32, f32, f32) f32` |
| 6249 | 6294 | const result = try cg.callIntrinsic( |
| 6250 | | "fmaf", |
| 6295 | .fmaf, |
| 6251 | 6296 | &.{ .f32_type, .f32_type, .f32_type }, |
| 6252 | 6297 | Type.f32, |
| 6253 | 6298 | &.{ rhs_ext, lhs_ext, addend_ext }, |
| ... | ... | @@ -6508,7 +6553,7 @@ fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6508 | 6553 | switch (wasm_bits) { |
| 6509 | 6554 | 32 => { |
| 6510 | 6555 | const intrin_ret = try cg.callIntrinsic( |
| 6511 | | "__bswapsi2", |
| 6556 | .__bswapsi2, |
| 6512 | 6557 | &.{.u32_type}, |
| 6513 | 6558 | Type.u32, |
| 6514 | 6559 | &.{operand}, |
| ... | ... | @@ -6520,7 +6565,7 @@ fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6520 | 6565 | }, |
| 6521 | 6566 | 64 => { |
| 6522 | 6567 | const intrin_ret = try cg.callIntrinsic( |
| 6523 | | "__bswapdi2", |
| 6568 | .__bswapdi2, |
| 6524 | 6569 | &.{.u64_type}, |
| 6525 | 6570 | Type.u64, |
| 6526 | 6571 | &.{operand}, |
| ... | ... | @@ -6777,7 +6822,7 @@ fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6777 | 6822 | } |
| 6778 | 6823 | const overflow_ret = try cg.allocStack(Type.i32); |
| 6779 | 6824 | _ = try cg.callIntrinsic( |
| 6780 | | "__mulodi4", |
| 6825 | .__mulodi4, |
| 6781 | 6826 | &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type }, |
| 6782 | 6827 | Type.i64, |
| 6783 | 6828 | &.{ lhs, rhs, overflow_ret }, |
| ... | ... | @@ -6795,7 +6840,7 @@ fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6795 | 6840 | } |
| 6796 | 6841 | const overflow_ret = try cg.allocStack(Type.i32); |
| 6797 | 6842 | const ret = try cg.callIntrinsic( |
| 6798 | | "__muloti4", |
| 6843 | .__muloti4, |
| 6799 | 6844 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, |
| 6800 | 6845 | Type.i128, |
| 6801 | 6846 | &.{ lhs, rhs, overflow_ret }, |
| ... | ... | @@ -7044,17 +7089,14 @@ fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7044 | 7089 | /// May leave the return value on the stack. |
| 7045 | 7090 | fn callIntrinsic( |
| 7046 | 7091 | cg: *CodeGen, |
| 7047 | | name: []const u8, |
| 7092 | intrinsic: Mir.Intrinsic, |
| 7048 | 7093 | param_types: []const InternPool.Index, |
| 7049 | 7094 | return_type: Type, |
| 7050 | 7095 | args: []const WValue, |
| 7051 | 7096 | ) InnerError!WValue { |
| 7052 | 7097 | assert(param_types.len == args.len); |
| 7053 | | const wasm = cg.wasm; |
| 7054 | 7098 | const pt = cg.pt; |
| 7055 | 7099 | const zcu = pt.zcu; |
| 7056 | | const func_type_index = try genFunctype(wasm, .{ .wasm_watc = .{} }, param_types, return_type, pt, cg.target); |
| 7057 | | const func_index = wasm.getOutputFunction(try wasm.internString(name), func_type_index); |
| 7058 | 7100 | |
| 7059 | 7101 | // Always pass over C-ABI |
| 7060 | 7102 | |
| ... | ... | @@ -7074,8 +7116,7 @@ fn callIntrinsic( |
| 7074 | 7116 | try cg.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg); |
| 7075 | 7117 | } |
| 7076 | 7118 | |
| 7077 | | // Actually call our intrinsic |
| 7078 | | try cg.addLabel(.call_func, func_index); |
| 7119 | try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } }); |
| 7079 | 7120 | |
| 7080 | 7121 | if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 7081 | 7122 | return .none; |
| ... | ... | @@ -7097,7 +7138,7 @@ fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7097 | 7138 | const result_ptr = try cg.allocStack(cg.typeOfIndex(inst)); |
| 7098 | 7139 | try cg.lowerToStack(result_ptr); |
| 7099 | 7140 | try cg.emitWValue(operand); |
| 7100 | | try cg.addIpIndex(.call_tag_name, enum_ty.toIntern()); |
| 7141 | try cg.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = enum_ty.toIntern() } }); |
| 7101 | 7142 | |
| 7102 | 7143 | return cg.finishAir(inst, result_ptr, &.{un_op}); |
| 7103 | 7144 | } |
| ... | ... | @@ -7514,3 +7555,38 @@ fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type { |
| 7514 | 7555 | const zcu = pt.zcu; |
| 7515 | 7556 | return cg.air.typeOfIndex(inst, &zcu.intern_pool); |
| 7516 | 7557 | } |
| 7558 | |
| 7559 | fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic { |
| 7560 | return switch (op) { |
| 7561 | .lt => switch (bits) { |
| 7562 | 80 => .__ltxf2, |
| 7563 | 128 => .__lttf2, |
| 7564 | else => unreachable, |
| 7565 | }, |
| 7566 | .lte => switch (bits) { |
| 7567 | 80 => .__lexf2, |
| 7568 | 128 => .__letf2, |
| 7569 | else => unreachable, |
| 7570 | }, |
| 7571 | .eq => switch (bits) { |
| 7572 | 80 => .__eqxf2, |
| 7573 | 128 => .__eqtf2, |
| 7574 | else => unreachable, |
| 7575 | }, |
| 7576 | .neq => switch (bits) { |
| 7577 | 80 => .__nexf2, |
| 7578 | 128 => .__netf2, |
| 7579 | else => unreachable, |
| 7580 | }, |
| 7581 | .gte => switch (bits) { |
| 7582 | 80 => .__gexf2, |
| 7583 | 128 => .__getf2, |
| 7584 | else => unreachable, |
| 7585 | }, |
| 7586 | .gt => switch (bits) { |
| 7587 | 80 => .__gtxf2, |
| 7588 | 128 => .__gttf2, |
| 7589 | else => unreachable, |
| 7590 | }, |
| 7591 | }; |
| 7592 | } |