| ... | ... | @@ -20,6 +20,7 @@ const link = @import("../../link.zig"); |
| 20 | 20 | const TypedValue = @import("../../TypedValue.zig"); |
| 21 | 21 | const Air = @import("../../Air.zig"); |
| 22 | 22 | const Liveness = @import("../../Liveness.zig"); |
| 23 | const target_util = @import("../../target.zig"); |
| 23 | 24 | const Mir = @import("Mir.zig"); |
| 24 | 25 | const Emit = @import("Emit.zig"); |
| 25 | 26 | const abi = @import("abi.zig"); |
| ... | ... | @@ -1786,9 +1787,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1786 | 1787 | .div_trunc, |
| 1787 | 1788 | => func.airDiv(inst), |
| 1788 | 1789 | .div_floor => func.airDivFloor(inst), |
| 1789 | | .ceil => func.airCeilFloorTrunc(inst, .ceil), |
| 1790 | | .floor => func.airCeilFloorTrunc(inst, .floor), |
| 1791 | | .trunc_float => func.airCeilFloorTrunc(inst, .trunc), |
| 1792 | 1790 | .bit_and => func.airBinOp(inst, .@"and"), |
| 1793 | 1791 | .bit_or => func.airBinOp(inst, .@"or"), |
| 1794 | 1792 | .bool_and => func.airBinOp(inst, .@"and"), |
| ... | ... | @@ -1803,6 +1801,22 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1803 | 1801 | .min => func.airMaxMin(inst, .min), |
| 1804 | 1802 | .mul_add => func.airMulAdd(inst), |
| 1805 | 1803 | |
| 1804 | .sqrt => func.airUnaryFloatOp(inst, .sqrt), |
| 1805 | .sin => func.airUnaryFloatOp(inst, .sin), |
| 1806 | .cos => func.airUnaryFloatOp(inst, .cos), |
| 1807 | .tan => func.airUnaryFloatOp(inst, .tan), |
| 1808 | .exp => func.airUnaryFloatOp(inst, .exp), |
| 1809 | .exp2 => func.airUnaryFloatOp(inst, .exp2), |
| 1810 | .log => func.airUnaryFloatOp(inst, .log), |
| 1811 | .log2 => func.airUnaryFloatOp(inst, .log2), |
| 1812 | .log10 => func.airUnaryFloatOp(inst, .log10), |
| 1813 | .fabs => func.airUnaryFloatOp(inst, .fabs), |
| 1814 | .floor => func.airUnaryFloatOp(inst, .floor), |
| 1815 | .ceil => func.airUnaryFloatOp(inst, .ceil), |
| 1816 | .round => func.airUnaryFloatOp(inst, .round), |
| 1817 | .trunc_float => func.airUnaryFloatOp(inst, .trunc), |
| 1818 | .neg => func.airUnaryFloatOp(inst, .neg), |
| 1819 | |
| 1806 | 1820 | .add_with_overflow => func.airAddSubWithOverflow(inst, .add), |
| 1807 | 1821 | .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub), |
| 1808 | 1822 | .shl_with_overflow => func.airShlWithOverflow(inst), |
| ... | ... | @@ -1939,19 +1953,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1939 | 1953 | .is_err_ptr, |
| 1940 | 1954 | .is_non_err_ptr, |
| 1941 | 1955 | |
| 1942 | | .sqrt, |
| 1943 | | .sin, |
| 1944 | | .cos, |
| 1945 | | .tan, |
| 1946 | | .exp, |
| 1947 | | .exp2, |
| 1948 | | .log, |
| 1949 | | .log2, |
| 1950 | | .log10, |
| 1951 | | .fabs, |
| 1952 | | .round, |
| 1953 | | .neg, |
| 1954 | | |
| 1955 | 1956 | .cmpxchg_weak, |
| 1956 | 1957 | .cmpxchg_strong, |
| 1957 | 1958 | .fence, |
| ... | ... | @@ -2462,7 +2463,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2462 | 2463 | // When we have an argument that's passed using more than a single parameter, |
| 2463 | 2464 | // we combine them into a single stack value |
| 2464 | 2465 | if (arg_classes[0] == .direct and arg_classes[1] == .direct) { |
| 2465 | | if (arg_ty.zigTypeTag() != .Int) { |
| 2466 | if (arg_ty.zigTypeTag() != .Int and arg_ty.zigTypeTag() != .Float) { |
| 2466 | 2467 | return func.fail( |
| 2467 | 2468 | "TODO: Implement C-ABI argument for type '{}'", |
| 2468 | 2469 | .{arg_ty.fmt(func.bin_file.base.options.module.?)}, |
| ... | ... | @@ -2506,6 +2507,12 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 2506 | 2507 | /// NOTE: THis leaves the value on top of the stack. |
| 2507 | 2508 | fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2508 | 2509 | assert(!(lhs != .stack and rhs == .stack)); |
| 2510 | |
| 2511 | if (ty.isAnyFloat()) { |
| 2512 | const float_op = FloatOp.fromOp(op); |
| 2513 | return func.floatOp(float_op, ty, &.{ lhs, rhs }); |
| 2514 | } |
| 2515 | |
| 2509 | 2516 | if (isByRef(ty, func.target)) { |
| 2510 | 2517 | if (ty.zigTypeTag() == .Int) { |
| 2511 | 2518 | return func.binOpBigInt(lhs, rhs, ty, op); |
| ... | ... | @@ -2517,10 +2524,6 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! |
| 2517 | 2524 | } |
| 2518 | 2525 | } |
| 2519 | 2526 | |
| 2520 | | if (ty.isAnyFloat() and ty.floatBits(func.target) == 16) { |
| 2521 | | return func.binOpFloat16(lhs, rhs, op); |
| 2522 | | } |
| 2523 | | |
| 2524 | 2527 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2525 | 2528 | .op = op, |
| 2526 | 2529 | .valtype1 = typeToValtype(ty, func.target), |
| ... | ... | @@ -2534,17 +2537,6 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! |
| 2534 | 2537 | return WValue{ .stack = {} }; |
| 2535 | 2538 | } |
| 2536 | 2539 | |
| 2537 | | /// Performs a binary operation for 16-bit floats. |
| 2538 | | /// NOTE: Leaves the result value on the stack |
| 2539 | | fn binOpFloat16(func: *CodeGen, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { |
| 2540 | | const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned }); |
| 2541 | | _ = try func.fpext(lhs, Type.f16, Type.f32); |
| 2542 | | _ = try func.fpext(rhs, Type.f16, Type.f32); |
| 2543 | | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2544 | | |
| 2545 | | return func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); |
| 2546 | | } |
| 2547 | | |
| 2548 | 2540 | fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2549 | 2541 | if (ty.intInfo(func.target).bits > 128) { |
| 2550 | 2542 | return func.fail("TODO: Implement binary operation for big integer", .{}); |
| ... | ... | @@ -2580,6 +2572,155 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner |
| 2580 | 2572 | return result; |
| 2581 | 2573 | } |
| 2582 | 2574 | |
| 2575 | const FloatOp = enum { |
| 2576 | add, |
| 2577 | ceil, |
| 2578 | cos, |
| 2579 | div, |
| 2580 | exp, |
| 2581 | exp2, |
| 2582 | fabs, |
| 2583 | floor, |
| 2584 | fma, |
| 2585 | fmax, |
| 2586 | fmin, |
| 2587 | fmod, |
| 2588 | log, |
| 2589 | log10, |
| 2590 | log2, |
| 2591 | mul, |
| 2592 | neg, |
| 2593 | round, |
| 2594 | sin, |
| 2595 | sqrt, |
| 2596 | sub, |
| 2597 | tan, |
| 2598 | trunc, |
| 2599 | |
| 2600 | pub fn fromOp(op: Op) FloatOp { |
| 2601 | return switch (op) { |
| 2602 | .add => .add, |
| 2603 | .ceil => .ceil, |
| 2604 | .div => .div, |
| 2605 | .abs => .fabs, |
| 2606 | .floor => .floor, |
| 2607 | .max => .fmax, |
| 2608 | .min => .fmin, |
| 2609 | .mul => .mul, |
| 2610 | .neg => .neg, |
| 2611 | .nearest => .round, |
| 2612 | .sqrt => .sqrt, |
| 2613 | .sub => .sub, |
| 2614 | .trunc => .trunc, |
| 2615 | else => unreachable, |
| 2616 | }; |
| 2617 | } |
| 2618 | |
| 2619 | pub fn toOp(float_op: FloatOp) ?Op { |
| 2620 | return switch (float_op) { |
| 2621 | .add => .add, |
| 2622 | .ceil => .ceil, |
| 2623 | .div => .div, |
| 2624 | .fabs => .abs, |
| 2625 | .floor => .floor, |
| 2626 | .fmax => .max, |
| 2627 | .fmin => .min, |
| 2628 | .mul => .mul, |
| 2629 | .neg => .neg, |
| 2630 | .round => .nearest, |
| 2631 | .sqrt => .sqrt, |
| 2632 | .sub => .sub, |
| 2633 | .trunc => .trunc, |
| 2634 | |
| 2635 | .cos, |
| 2636 | .exp, |
| 2637 | .exp2, |
| 2638 | .fma, |
| 2639 | .fmod, |
| 2640 | .log, |
| 2641 | .log10, |
| 2642 | .log2, |
| 2643 | .sin, |
| 2644 | .tan, |
| 2645 | => null, |
| 2646 | }; |
| 2647 | } |
| 2648 | }; |
| 2649 | |
| 2650 | fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void { |
| 2651 | const un_op = func.air.instructions.items(.data)[inst].un_op; |
| 2652 | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op}); |
| 2653 | const operand = try func.resolveInst(un_op); |
| 2654 | const ty = func.air.typeOf(un_op); |
| 2655 | |
| 2656 | const result = try (try func.floatOp(op, ty, &.{operand})).toLocal(func, ty); |
| 2657 | func.finishAir(inst, result, &.{un_op}); |
| 2658 | } |
| 2659 | |
| 2660 | fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue { |
| 2661 | if (ty.zigTypeTag() == .Vector) { |
| 2662 | return func.fail("TODO: Implement floatOps for vectors", .{}); |
| 2663 | } |
| 2664 | |
| 2665 | const float_bits = ty.floatBits(func.target); |
| 2666 | if (float_bits == 32 or float_bits == 64) { |
| 2667 | if (float_op.toOp()) |op| { |
| 2668 | for (args) |operand| { |
| 2669 | try func.emitWValue(operand); |
| 2670 | } |
| 2671 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, func.target) }); |
| 2672 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2673 | return .stack; |
| 2674 | } |
| 2675 | } else if (float_bits == 16 and float_op == .neg) { |
| 2676 | try func.emitWValue(args[0]); |
| 2677 | try func.addImm32(std.math.minInt(i16)); |
| 2678 | try func.addTag(Mir.Inst.Tag.fromOpcode(.i32_xor)); |
| 2679 | return .stack; |
| 2680 | } else if (float_bits == 128 and float_op == .neg) { |
| 2681 | return func.fail("TODO: Implement neg for f128", .{}); |
| 2682 | } |
| 2683 | |
| 2684 | var fn_name_buf: [64]u8 = undefined; |
| 2685 | const fn_name = switch (float_op) { |
| 2686 | .add, |
| 2687 | .sub, |
| 2688 | .div, |
| 2689 | .mul, |
| 2690 | => std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f3", .{ |
| 2691 | @tagName(float_op), target_util.compilerRtFloatAbbrev(float_bits), |
| 2692 | }) catch unreachable, |
| 2693 | |
| 2694 | .ceil, |
| 2695 | .cos, |
| 2696 | .exp, |
| 2697 | .exp2, |
| 2698 | .fabs, |
| 2699 | .floor, |
| 2700 | .fma, |
| 2701 | .fmax, |
| 2702 | .fmin, |
| 2703 | .fmod, |
| 2704 | .log, |
| 2705 | .log10, |
| 2706 | .log2, |
| 2707 | .round, |
| 2708 | .sin, |
| 2709 | .sqrt, |
| 2710 | .tan, |
| 2711 | .trunc, |
| 2712 | => std.fmt.bufPrint(&fn_name_buf, "{s}{s}{s}", .{ |
| 2713 | target_util.libcFloatPrefix(float_bits), @tagName(float_op), target_util.libcFloatSuffix(float_bits), |
| 2714 | }) catch unreachable, |
| 2715 | .neg => unreachable, // handled above |
| 2716 | }; |
| 2717 | |
| 2718 | // fma requires three operands |
| 2719 | var param_types_buffer: [3]Type = .{ ty, ty, ty }; |
| 2720 | const param_types = param_types_buffer[0..args.len]; |
| 2721 | return func.callIntrinsic(fn_name, param_types, ty, args); |
| 2722 | } |
| 2723 | |
| 2583 | 2724 | fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 2584 | 2725 | const bin_op = func.air.instructions.items(.data)[inst].bin_op; |
| 2585 | 2726 | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| ... | ... | @@ -3322,6 +3463,8 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3322 | 3463 | fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue { |
| 3323 | 3464 | // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction |
| 3324 | 3465 | if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand; |
| 3466 | if (wanted_ty.tag() == .f16 or given_ty.tag() == .f16) return operand; |
| 3467 | if (wanted_ty.bitSize(func.target) > 64) return operand; |
| 3325 | 3468 | assert((wanted_ty.isInt() and given_ty.isAnyFloat()) or (wanted_ty.isAnyFloat() and given_ty.isInt())); |
| 3326 | 3469 | |
| 3327 | 3470 | const opcode = buildOpcode(.{ |
| ... | ... | @@ -4855,7 +4998,7 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError! |
| 4855 | 4998 | try func.emitWValue(operand); |
| 4856 | 4999 | try func.addTag(.f64_promote_f32); |
| 4857 | 5000 | return WValue{ .stack = {} }; |
| 4858 | | } else if (given_bits == 16) { |
| 5001 | } else if (given_bits == 16 and wanted_bits <= 64) { |
| 4859 | 5002 | // call __extendhfsf2(f16) f32 |
| 4860 | 5003 | const f32_result = try func.callIntrinsic( |
| 4861 | 5004 | "__extendhfsf2", |
| ... | ... | @@ -4863,19 +5006,21 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError! |
| 4863 | 5006 | Type.f32, |
| 4864 | 5007 | &.{operand}, |
| 4865 | 5008 | ); |
| 5009 | std.debug.assert(f32_result == .stack); |
| 4866 | 5010 | |
| 4867 | | if (wanted_bits == 32) { |
| 4868 | | return f32_result; |
| 4869 | | } |
| 4870 | 5011 | if (wanted_bits == 64) { |
| 4871 | 5012 | try func.addTag(.f64_promote_f32); |
| 4872 | | return WValue{ .stack = {} }; |
| 4873 | 5013 | } |
| 4874 | | return func.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); |
| 4875 | | } else { |
| 4876 | | // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2 |
| 4877 | | return func.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); |
| 5014 | return WValue{ .stack = {} }; |
| 4878 | 5015 | } |
| 5016 | |
| 5017 | var fn_name_buf: [13]u8 = undefined; |
| 5018 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__extend{s}f{s}f2", .{ |
| 5019 | target_util.compilerRtFloatAbbrev(given_bits), |
| 5020 | target_util.compilerRtFloatAbbrev(wanted_bits), |
| 5021 | }) catch unreachable; |
| 5022 | |
| 5023 | return func.callIntrinsic(fn_name, &.{given}, wanted, &.{operand}); |
| 4879 | 5024 | } |
| 4880 | 5025 | |
| 4881 | 5026 | fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -4899,7 +5044,7 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4899 | 5044 | try func.emitWValue(operand); |
| 4900 | 5045 | try func.addTag(.f32_demote_f64); |
| 4901 | 5046 | return WValue{ .stack = {} }; |
| 4902 | | } else if (wanted_bits == 16) { |
| 5047 | } else if (wanted_bits == 16 and given_bits <= 64) { |
| 4903 | 5048 | const op: WValue = if (given_bits == 64) blk: { |
| 4904 | 5049 | try func.emitWValue(operand); |
| 4905 | 5050 | try func.addTag(.f32_demote_f64); |
| ... | ... | @@ -4908,10 +5053,15 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4908 | 5053 | |
| 4909 | 5054 | // call __truncsfhf2(f32) f16 |
| 4910 | 5055 | return func.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op}); |
| 4911 | | } else { |
| 4912 | | // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2 |
| 4913 | | return func.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{wanted_bits}); |
| 4914 | 5056 | } |
| 5057 | |
| 5058 | var fn_name_buf: [12]u8 = undefined; |
| 5059 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__trunc{s}f{s}f2", .{ |
| 5060 | target_util.compilerRtFloatAbbrev(given_bits), |
| 5061 | target_util.compilerRtFloatAbbrev(wanted_bits), |
| 5062 | }) catch unreachable; |
| 5063 | |
| 5064 | return func.callIntrinsic(fn_name, &.{given}, wanted, &.{operand}); |
| 4915 | 5065 | } |
| 4916 | 5066 | |
| 4917 | 5067 | fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -5873,38 +6023,6 @@ fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 5873 | 6023 | return WValue{ .stack = {} }; |
| 5874 | 6024 | } |
| 5875 | 6025 | |
| 5876 | | fn airCeilFloorTrunc(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 5877 | | const un_op = func.air.instructions.items(.data)[inst].un_op; |
| 5878 | | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op}); |
| 5879 | | |
| 5880 | | const ty = func.air.typeOfIndex(inst); |
| 5881 | | const float_bits = ty.floatBits(func.target); |
| 5882 | | const is_f16 = float_bits == 16; |
| 5883 | | |
| 5884 | | if (ty.zigTypeTag() == .Vector) { |
| 5885 | | return func.fail("TODO: Implement `@ceil` for vectors", .{}); |
| 5886 | | } |
| 5887 | | if (float_bits > 64) { |
| 5888 | | return func.fail("TODO: implement `@ceil`, `@trunc`, `@floor` for floats larger than 64bits", .{}); |
| 5889 | | } |
| 5890 | | |
| 5891 | | const operand = try func.resolveInst(un_op); |
| 5892 | | const op_to_lower = if (is_f16) blk: { |
| 5893 | | break :blk try func.fpext(operand, Type.f16, Type.f32); |
| 5894 | | } else operand; |
| 5895 | | try func.emitWValue(op_to_lower); |
| 5896 | | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, func.target) }); |
| 5897 | | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 5898 | | |
| 5899 | | if (is_f16) { |
| 5900 | | _ = try func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); |
| 5901 | | } |
| 5902 | | |
| 5903 | | const result = try func.allocLocal(ty); |
| 5904 | | try func.addLabel(.local_set, result.local.value); |
| 5905 | | func.finishAir(inst, result, &.{un_op}); |
| 5906 | | } |
| 5907 | | |
| 5908 | 6026 | fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 5909 | 6027 | assert(op == .add or op == .sub); |
| 5910 | 6028 | const bin_op = func.air.instructions.items(.data)[inst].bin_op; |