| ... | ... | @@ -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), |
| ... | ... | @@ -1938,19 +1952,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1938 | 1952 | .is_err_ptr, |
| 1939 | 1953 | .is_non_err_ptr, |
| 1940 | 1954 | |
| 1941 | | .sqrt, |
| 1942 | | .sin, |
| 1943 | | .cos, |
| 1944 | | .tan, |
| 1945 | | .exp, |
| 1946 | | .exp2, |
| 1947 | | .log, |
| 1948 | | .log2, |
| 1949 | | .log10, |
| 1950 | | .fabs, |
| 1951 | | .round, |
| 1952 | | .neg, |
| 1953 | | |
| 1954 | 1955 | .cmpxchg_weak, |
| 1955 | 1956 | .cmpxchg_strong, |
| 1956 | 1957 | .fence, |
| ... | ... | @@ -2456,7 +2457,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2456 | 2457 | // When we have an argument that's passed using more than a single parameter, |
| 2457 | 2458 | // we combine them into a single stack value |
| 2458 | 2459 | if (arg_classes[0] == .direct and arg_classes[1] == .direct) { |
| 2459 | | if (arg_ty.zigTypeTag() != .Int) { |
| 2460 | if (arg_ty.zigTypeTag() != .Int and arg_ty.zigTypeTag() != .Float) { |
| 2460 | 2461 | return func.fail( |
| 2461 | 2462 | "TODO: Implement C-ABI argument for type '{}'", |
| 2462 | 2463 | .{arg_ty.fmt(func.bin_file.base.options.module.?)}, |
| ... | ... | @@ -2500,6 +2501,12 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 2500 | 2501 | /// NOTE: THis leaves the value on top of the stack. |
| 2501 | 2502 | fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2502 | 2503 | assert(!(lhs != .stack and rhs == .stack)); |
| 2504 | |
| 2505 | if (ty.isAnyFloat()) { |
| 2506 | const float_op = FloatOp.fromOp(op); |
| 2507 | return func.floatOp(float_op, ty, &.{ lhs, rhs }); |
| 2508 | } |
| 2509 | |
| 2503 | 2510 | if (isByRef(ty, func.target)) { |
| 2504 | 2511 | if (ty.zigTypeTag() == .Int) { |
| 2505 | 2512 | return func.binOpBigInt(lhs, rhs, ty, op); |
| ... | ... | @@ -2511,10 +2518,6 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! |
| 2511 | 2518 | } |
| 2512 | 2519 | } |
| 2513 | 2520 | |
| 2514 | | if (ty.isAnyFloat() and ty.floatBits(func.target) == 16) { |
| 2515 | | return func.binOpFloat16(lhs, rhs, op); |
| 2516 | | } |
| 2517 | | |
| 2518 | 2521 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2519 | 2522 | .op = op, |
| 2520 | 2523 | .valtype1 = typeToValtype(ty, func.target), |
| ... | ... | @@ -2528,17 +2531,6 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! |
| 2528 | 2531 | return WValue{ .stack = {} }; |
| 2529 | 2532 | } |
| 2530 | 2533 | |
| 2531 | | /// Performs a binary operation for 16-bit floats. |
| 2532 | | /// NOTE: Leaves the result value on the stack |
| 2533 | | fn binOpFloat16(func: *CodeGen, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { |
| 2534 | | const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned }); |
| 2535 | | _ = try func.fpext(lhs, Type.f16, Type.f32); |
| 2536 | | _ = try func.fpext(rhs, Type.f16, Type.f32); |
| 2537 | | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2538 | | |
| 2539 | | return func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); |
| 2540 | | } |
| 2541 | | |
| 2542 | 2534 | fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2543 | 2535 | if (ty.intInfo(func.target).bits > 128) { |
| 2544 | 2536 | return func.fail("TODO: Implement binary operation for big integer", .{}); |
| ... | ... | @@ -2574,6 +2566,155 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner |
| 2574 | 2566 | return result; |
| 2575 | 2567 | } |
| 2576 | 2568 | |
| 2569 | const FloatOp = enum { |
| 2570 | add, |
| 2571 | ceil, |
| 2572 | cos, |
| 2573 | div, |
| 2574 | exp, |
| 2575 | exp2, |
| 2576 | fabs, |
| 2577 | floor, |
| 2578 | fma, |
| 2579 | fmax, |
| 2580 | fmin, |
| 2581 | fmod, |
| 2582 | log, |
| 2583 | log10, |
| 2584 | log2, |
| 2585 | mul, |
| 2586 | neg, |
| 2587 | round, |
| 2588 | sin, |
| 2589 | sqrt, |
| 2590 | sub, |
| 2591 | tan, |
| 2592 | trunc, |
| 2593 | |
| 2594 | pub fn fromOp(op: Op) FloatOp { |
| 2595 | return switch (op) { |
| 2596 | .add => .add, |
| 2597 | .ceil => .ceil, |
| 2598 | .div => .div, |
| 2599 | .abs => .fabs, |
| 2600 | .floor => .floor, |
| 2601 | .max => .fmax, |
| 2602 | .min => .fmin, |
| 2603 | .mul => .mul, |
| 2604 | .neg => .neg, |
| 2605 | .nearest => .round, |
| 2606 | .sqrt => .sqrt, |
| 2607 | .sub => .sub, |
| 2608 | .trunc => .trunc, |
| 2609 | else => unreachable, |
| 2610 | }; |
| 2611 | } |
| 2612 | |
| 2613 | pub fn toOp(float_op: FloatOp) ?Op { |
| 2614 | return switch (float_op) { |
| 2615 | .add => .add, |
| 2616 | .ceil => .ceil, |
| 2617 | .div => .div, |
| 2618 | .fabs => .abs, |
| 2619 | .floor => .floor, |
| 2620 | .fmax => .max, |
| 2621 | .fmin => .min, |
| 2622 | .mul => .mul, |
| 2623 | .neg => .neg, |
| 2624 | .round => .nearest, |
| 2625 | .sqrt => .sqrt, |
| 2626 | .sub => .sub, |
| 2627 | .trunc => .trunc, |
| 2628 | |
| 2629 | .cos, |
| 2630 | .exp, |
| 2631 | .exp2, |
| 2632 | .fma, |
| 2633 | .fmod, |
| 2634 | .log, |
| 2635 | .log10, |
| 2636 | .log2, |
| 2637 | .sin, |
| 2638 | .tan, |
| 2639 | => null, |
| 2640 | }; |
| 2641 | } |
| 2642 | }; |
| 2643 | |
| 2644 | fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void { |
| 2645 | const un_op = func.air.instructions.items(.data)[inst].un_op; |
| 2646 | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op}); |
| 2647 | const operand = try func.resolveInst(un_op); |
| 2648 | const ty = func.air.typeOf(un_op); |
| 2649 | |
| 2650 | const result = try (try func.floatOp(op, ty, &.{operand})).toLocal(func, ty); |
| 2651 | func.finishAir(inst, result, &.{un_op}); |
| 2652 | } |
| 2653 | |
| 2654 | fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue { |
| 2655 | if (ty.zigTypeTag() == .Vector) { |
| 2656 | return func.fail("TODO: Implement floatOps for vectors", .{}); |
| 2657 | } |
| 2658 | |
| 2659 | const float_bits = ty.floatBits(func.target); |
| 2660 | if (float_bits == 32 or float_bits == 64) { |
| 2661 | if (float_op.toOp()) |op| { |
| 2662 | for (args) |operand| { |
| 2663 | try func.emitWValue(operand); |
| 2664 | } |
| 2665 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, func.target) }); |
| 2666 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2667 | return .stack; |
| 2668 | } |
| 2669 | } else if (float_bits == 16 and float_op == .neg) { |
| 2670 | try func.emitWValue(args[0]); |
| 2671 | try func.addImm32(std.math.minInt(i16)); |
| 2672 | try func.addTag(Mir.Inst.Tag.fromOpcode(.i32_xor)); |
| 2673 | return .stack; |
| 2674 | } else if (float_bits == 128 and float_op == .neg) { |
| 2675 | return func.fail("TODO: Implement neg for f128", .{}); |
| 2676 | } |
| 2677 | |
| 2678 | var fn_name_buf: [64]u8 = undefined; |
| 2679 | const fn_name = switch (float_op) { |
| 2680 | .add, |
| 2681 | .sub, |
| 2682 | .div, |
| 2683 | .mul, |
| 2684 | => std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f3", .{ |
| 2685 | @tagName(float_op), target_util.compilerRtFloatAbbrev(float_bits), |
| 2686 | }) catch unreachable, |
| 2687 | |
| 2688 | .ceil, |
| 2689 | .cos, |
| 2690 | .exp, |
| 2691 | .exp2, |
| 2692 | .fabs, |
| 2693 | .floor, |
| 2694 | .fma, |
| 2695 | .fmax, |
| 2696 | .fmin, |
| 2697 | .fmod, |
| 2698 | .log, |
| 2699 | .log10, |
| 2700 | .log2, |
| 2701 | .round, |
| 2702 | .sin, |
| 2703 | .sqrt, |
| 2704 | .tan, |
| 2705 | .trunc, |
| 2706 | => std.fmt.bufPrint(&fn_name_buf, "{s}{s}{s}", .{ |
| 2707 | target_util.libcFloatPrefix(float_bits), @tagName(float_op), target_util.libcFloatSuffix(float_bits), |
| 2708 | }) catch unreachable, |
| 2709 | .neg => unreachable, // handled above |
| 2710 | }; |
| 2711 | |
| 2712 | // fma requires three operands |
| 2713 | var param_types_buffer: [3]Type = .{ ty, ty, ty }; |
| 2714 | const param_types = param_types_buffer[0..args.len]; |
| 2715 | return func.callIntrinsic(fn_name, param_types, ty, args); |
| 2716 | } |
| 2717 | |
| 2577 | 2718 | fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 2578 | 2719 | const bin_op = func.air.instructions.items(.data)[inst].bin_op; |
| 2579 | 2720 | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| ... | ... | @@ -3310,6 +3451,8 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3310 | 3451 | fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue { |
| 3311 | 3452 | // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction |
| 3312 | 3453 | if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand; |
| 3454 | if (wanted_ty.tag() == .f16 or given_ty.tag() == .f16) return operand; |
| 3455 | if (wanted_ty.bitSize(func.target) > 64) return operand; |
| 3313 | 3456 | assert((wanted_ty.isInt() and given_ty.isAnyFloat()) or (wanted_ty.isAnyFloat() and given_ty.isInt())); |
| 3314 | 3457 | |
| 3315 | 3458 | const opcode = buildOpcode(.{ |
| ... | ... | @@ -4843,7 +4986,7 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError! |
| 4843 | 4986 | try func.emitWValue(operand); |
| 4844 | 4987 | try func.addTag(.f64_promote_f32); |
| 4845 | 4988 | return WValue{ .stack = {} }; |
| 4846 | | } else if (given_bits == 16) { |
| 4989 | } else if (given_bits == 16 and wanted_bits <= 64) { |
| 4847 | 4990 | // call __extendhfsf2(f16) f32 |
| 4848 | 4991 | const f32_result = try func.callIntrinsic( |
| 4849 | 4992 | "__extendhfsf2", |
| ... | ... | @@ -4851,19 +4994,21 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError! |
| 4851 | 4994 | Type.f32, |
| 4852 | 4995 | &.{operand}, |
| 4853 | 4996 | ); |
| 4997 | std.debug.assert(f32_result == .stack); |
| 4854 | 4998 | |
| 4855 | | if (wanted_bits == 32) { |
| 4856 | | return f32_result; |
| 4857 | | } |
| 4858 | 4999 | if (wanted_bits == 64) { |
| 4859 | 5000 | try func.addTag(.f64_promote_f32); |
| 4860 | | return WValue{ .stack = {} }; |
| 4861 | 5001 | } |
| 4862 | | return func.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); |
| 4863 | | } else { |
| 4864 | | // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2 |
| 4865 | | return func.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); |
| 5002 | return WValue{ .stack = {} }; |
| 4866 | 5003 | } |
| 5004 | |
| 5005 | var fn_name_buf: [13]u8 = undefined; |
| 5006 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__extend{s}f{s}f2", .{ |
| 5007 | target_util.compilerRtFloatAbbrev(given_bits), |
| 5008 | target_util.compilerRtFloatAbbrev(wanted_bits), |
| 5009 | }) catch unreachable; |
| 5010 | |
| 5011 | return func.callIntrinsic(fn_name, &.{given}, wanted, &.{operand}); |
| 4867 | 5012 | } |
| 4868 | 5013 | |
| 4869 | 5014 | fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -4887,7 +5032,7 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4887 | 5032 | try func.emitWValue(operand); |
| 4888 | 5033 | try func.addTag(.f32_demote_f64); |
| 4889 | 5034 | return WValue{ .stack = {} }; |
| 4890 | | } else if (wanted_bits == 16) { |
| 5035 | } else if (wanted_bits == 16 and given_bits <= 64) { |
| 4891 | 5036 | const op: WValue = if (given_bits == 64) blk: { |
| 4892 | 5037 | try func.emitWValue(operand); |
| 4893 | 5038 | try func.addTag(.f32_demote_f64); |
| ... | ... | @@ -4896,10 +5041,15 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4896 | 5041 | |
| 4897 | 5042 | // call __truncsfhf2(f32) f16 |
| 4898 | 5043 | return func.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op}); |
| 4899 | | } else { |
| 4900 | | // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2 |
| 4901 | | return func.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{wanted_bits}); |
| 4902 | 5044 | } |
| 5045 | |
| 5046 | var fn_name_buf: [12]u8 = undefined; |
| 5047 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__trunc{s}f{s}f2", .{ |
| 5048 | target_util.compilerRtFloatAbbrev(given_bits), |
| 5049 | target_util.compilerRtFloatAbbrev(wanted_bits), |
| 5050 | }) catch unreachable; |
| 5051 | |
| 5052 | return func.callIntrinsic(fn_name, &.{given}, wanted, &.{operand}); |
| 4903 | 5053 | } |
| 4904 | 5054 | |
| 4905 | 5055 | fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -5857,38 +6007,6 @@ fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 5857 | 6007 | return WValue{ .stack = {} }; |
| 5858 | 6008 | } |
| 5859 | 6009 | |
| 5860 | | fn airCeilFloorTrunc(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 5861 | | const un_op = func.air.instructions.items(.data)[inst].un_op; |
| 5862 | | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op}); |
| 5863 | | |
| 5864 | | const ty = func.air.typeOfIndex(inst); |
| 5865 | | const float_bits = ty.floatBits(func.target); |
| 5866 | | const is_f16 = float_bits == 16; |
| 5867 | | |
| 5868 | | if (ty.zigTypeTag() == .Vector) { |
| 5869 | | return func.fail("TODO: Implement `@ceil` for vectors", .{}); |
| 5870 | | } |
| 5871 | | if (float_bits > 64) { |
| 5872 | | return func.fail("TODO: implement `@ceil`, `@trunc`, `@floor` for floats larger than 64bits", .{}); |
| 5873 | | } |
| 5874 | | |
| 5875 | | const operand = try func.resolveInst(un_op); |
| 5876 | | const op_to_lower = if (is_f16) blk: { |
| 5877 | | break :blk try func.fpext(operand, Type.f16, Type.f32); |
| 5878 | | } else operand; |
| 5879 | | try func.emitWValue(op_to_lower); |
| 5880 | | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, func.target) }); |
| 5881 | | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 5882 | | |
| 5883 | | if (is_f16) { |
| 5884 | | _ = try func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); |
| 5885 | | } |
| 5886 | | |
| 5887 | | const result = try func.allocLocal(ty); |
| 5888 | | try func.addLabel(.local_set, result.local.value); |
| 5889 | | func.finishAir(inst, result, &.{un_op}); |
| 5890 | | } |
| 5891 | | |
| 5892 | 6010 | fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 5893 | 6011 | assert(op == .add or op == .sub); |
| 5894 | 6012 | const bin_op = func.air.instructions.items(.data)[inst].bin_op; |