authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-07 19:47:22-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
logbf20a4aa9eeca3c1911709bf48a8c476106042dc
tree18a54587b6ffdc8a93562ab56ac1ecbe9269b300
parentc443a7a57fab7118a9793b7f77b7ab817235a896

wasm: use call_intrinsic MIR instruction


3 files changed, 471 insertions(+), 174 deletions(-)

src/arch/wasm/CodeGen.zig+244-168
......@@ -18,7 +18,6 @@ const Compilation = @import("../../Compilation.zig");
1818const link = @import("../../link.zig");
1919const Air = @import("../../Air.zig");
2020const Liveness = @import("../../Liveness.zig");
21const target_util = @import("../../target.zig");
2221const Mir = @import("Mir.zig");
2322const Emit = @import("Emit.zig");
2423const abi = @import("abi.zig");
......@@ -27,6 +26,12 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
2726const errUnionErrorOffset = codegen.errUnionErrorOffset;
2827const Wasm = link.File.Wasm;
2928
29const target_util = @import("../../target.zig");
30const libcFloatPrefix = target_util.libcFloatPrefix;
31const libcFloatSuffix = target_util.libcFloatSuffix;
32const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev;
33const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;
34
3035/// Reference to the function declaration the code
3136/// section belongs to
3237owner_nav: InternPool.Nav.Index,
......@@ -854,7 +859,6 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
854859 }
855860}
856861
857/// Appends a MIR instruction and returns its index within the list of instructions
858862fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
859863 try cg.mir_instructions.append(cg.gpa, inst);
860864}
......@@ -873,14 +877,6 @@ fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void
873877 try cg.addInst(.{ .tag = tag, .data = .{ .label = label } });
874878}
875879
876fn 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
880fn 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
884880/// Accepts an unsigned 32bit integer rather than a signed integer to
885881/// prevent us from having to bitcast multiple times as most values
886882/// within codegen are represented as unsigned rather than signed.
......@@ -1887,8 +1883,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18871883 .shl_sat => cg.airShlSat(inst),
18881884 .shr, .shr_exact => cg.airBinOp(inst, .shr),
18891885 .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),
18921888 .mul_add => cg.airMulAdd(inst),
18931889
18941890 .sqrt => cg.airUnaryFloatOp(inst, .sqrt),
......@@ -2263,7 +2259,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie
22632259 }
22642260
22652261 if (callee) |nav_index| {
2266 try cg.addNav(.call_nav, nav_index);
2262 try cg.addInst(.{ .tag = .call_nav, .data = .{ .nav_index = nav_index } });
22672263 } else {
22682264 // in this case we call a function pointer
22692265 // so load its value onto the stack
......@@ -2669,20 +2665,20 @@ fn binOpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr
26692665 }
26702666
26712667 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 }),
26732669 .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 }),
26762672 },
26772673 .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 }),
26802676 },
26812677 .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 }),
26842680 },
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 }),
26862682 .@"and", .@"or", .xor => {
26872683 const result = try cg.allocStack(ty);
26882684 try cg.emitWValue(result);
......@@ -2802,6 +2798,46 @@ const FloatOp = enum {
28022798 => null,
28032799 };
28042800 }
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 }
28052841};
28062842
28072843fn 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
29192955 }
29202956 }
29212957
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);
29552959
29562960 // fma requires three operands
29572961 var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index };
29582962 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);
29602964}
29612965
29622966/// 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
36053609 return .stack;
36063610 },
36073611 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 });
36143614 return cg.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op);
36153615 },
36163616 else => unreachable,
......@@ -5001,19 +5001,26 @@ fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50015001 }
50025002
50035003 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,
50115021 },
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});
50175024 return cg.finishAir(inst, result, &.{ty_op.operand});
50185025 }
50195026
......@@ -5046,19 +5053,27 @@ fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50465053 }
50475054
50485055 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,
50565073 },
5057 target_util.compilerRtIntAbbrev(op_bitsize),
5058 target_util.compilerRtFloatAbbrev(dest_bits),
5059 }) catch unreachable;
5074 };
50605075
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});
50625077 return cg.finishAir(inst, result, &.{ty_op.operand});
50635078 }
50645079
......@@ -5577,39 +5592,49 @@ fn airFpext(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55775592 return cg.finishAir(inst, result, &.{ty_op.operand});
55785593}
55795594
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.
55825597fn fpext(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
55835598 const given_bits = given.floatBits(cg.target.*);
55845599 const wanted_bits = wanted.floatBits(cg.target.*);
55855600
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});
56135638}
56145639
56155640fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5621,34 +5646,48 @@ fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56215646 return cg.finishAir(inst, result, &.{ty_op.operand});
56225647}
56235648
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.
56265651fn fptrunc(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
56275652 const given_bits = given.floatBits(cg.target.*);
56285653 const wanted_bits = wanted.floatBits(cg.target.*);
56295654
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});
56525691}
56535692
56545693fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5823,7 +5862,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58235862 switch (wasm_bits) {
58245863 32 => {
58255864 const intrin_ret = try cg.callIntrinsic(
5826 "__bitreversesi2",
5865 .__bitreversesi2,
58275866 &.{.u32_type},
58285867 Type.u32,
58295868 &.{operand},
......@@ -5836,7 +5875,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58365875 },
58375876 64 => {
58385877 const intrin_ret = try cg.callIntrinsic(
5839 "__bitreversedi2",
5878 .__bitreversedi2,
58405879 &.{.u64_type},
58415880 Type.u64,
58425881 &.{operand},
......@@ -5853,7 +5892,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58535892 try cg.emitWValue(result);
58545893 const first_half = try cg.load(operand, Type.u64, 8);
58555894 const intrin_ret_first = try cg.callIntrinsic(
5856 "__bitreversedi2",
5895 .__bitreversedi2,
58575896 &.{.u64_type},
58585897 Type.u64,
58595898 &.{first_half},
......@@ -5866,7 +5905,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58665905 try cg.emitWValue(result);
58675906 const second_half = try cg.load(operand, Type.u64, 0);
58685907 const intrin_ret_second = try cg.callIntrinsic(
5869 "__bitreversedi2",
5908 .__bitreversedi2,
58705909 &.{.u64_type},
58715910 Type.u64,
58725911 &.{second_half},
......@@ -6114,19 +6153,19 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61146153 defer rhs_msb.free(cg);
61156154
61166155 const cross_1 = try cg.callIntrinsic(
6117 "__multi3",
6156 .__multi3,
61186157 &[_]InternPool.Index{.i64_type} ** 4,
61196158 Type.i128,
61206159 &.{ lhs_msb, zero, rhs_lsb, zero },
61216160 );
61226161 const cross_2 = try cg.callIntrinsic(
6123 "__multi3",
6162 .__multi3,
61246163 &[_]InternPool.Index{.i64_type} ** 4,
61256164 Type.i128,
61266165 &.{ rhs_msb, zero, lhs_lsb, zero },
61276166 );
61286167 const mul_lsb = try cg.callIntrinsic(
6129 "__multi3",
6168 .__multi3,
61306169 &[_]InternPool.Index{.i64_type} ** 4,
61316170 Type.i128,
61326171 &.{ rhs_lsb, zero, lhs_lsb, zero },
......@@ -6165,7 +6204,7 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61656204 } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: {
61666205 const overflow_ret = try cg.allocStack(Type.i32);
61676206 const res = try cg.callIntrinsic(
6168 "__muloti4",
6207 .__muloti4,
61696208 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
61706209 Type.i128,
61716210 &.{ lhs, rhs, overflow_ret },
......@@ -6185,8 +6224,12 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61856224 return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
61866225}
61876226
6188fn airMaxMin(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
6189 assert(op == .max or op == .min);
6227fn airMaxMin(
6228 cg: *CodeGen,
6229 inst: Air.Inst.Index,
6230 op: enum { fmax, fmin },
6231 cmp_op: std.math.CompareOperator,
6232) InnerError!void {
61906233 const pt = cg.pt;
61916234 const zcu = pt.zcu;
61926235 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 {
62046247 const rhs = try cg.resolveInst(bin_op.rhs);
62056248
62066249 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 });
62156260 try cg.lowerToStack(result);
62166261 } else {
62176262 // operands to select from
62186263 try cg.lowerToStack(lhs);
62196264 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);
62216266
62226267 // based on the result from comparison, return operand 0 or 1.
62236268 try cg.addTag(.select);
......@@ -6247,7 +6292,7 @@ fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
62476292 const addend_ext = try cg.fpext(addend, ty, Type.f32);
62486293 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`
62496294 const result = try cg.callIntrinsic(
6250 "fmaf",
6295 .fmaf,
62516296 &.{ .f32_type, .f32_type, .f32_type },
62526297 Type.f32,
62536298 &.{ rhs_ext, lhs_ext, addend_ext },
......@@ -6508,7 +6553,7 @@ fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
65086553 switch (wasm_bits) {
65096554 32 => {
65106555 const intrin_ret = try cg.callIntrinsic(
6511 "__bswapsi2",
6556 .__bswapsi2,
65126557 &.{.u32_type},
65136558 Type.u32,
65146559 &.{operand},
......@@ -6520,7 +6565,7 @@ fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
65206565 },
65216566 64 => {
65226567 const intrin_ret = try cg.callIntrinsic(
6523 "__bswapdi2",
6568 .__bswapdi2,
65246569 &.{.u64_type},
65256570 Type.u64,
65266571 &.{operand},
......@@ -6777,7 +6822,7 @@ fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67776822 }
67786823 const overflow_ret = try cg.allocStack(Type.i32);
67796824 _ = try cg.callIntrinsic(
6780 "__mulodi4",
6825 .__mulodi4,
67816826 &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type },
67826827 Type.i64,
67836828 &.{ lhs, rhs, overflow_ret },
......@@ -6795,7 +6840,7 @@ fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67956840 }
67966841 const overflow_ret = try cg.allocStack(Type.i32);
67976842 const ret = try cg.callIntrinsic(
6798 "__muloti4",
6843 .__muloti4,
67996844 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
68006845 Type.i128,
68016846 &.{ lhs, rhs, overflow_ret },
......@@ -7044,17 +7089,14 @@ fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70447089/// May leave the return value on the stack.
70457090fn callIntrinsic(
70467091 cg: *CodeGen,
7047 name: []const u8,
7092 intrinsic: Mir.Intrinsic,
70487093 param_types: []const InternPool.Index,
70497094 return_type: Type,
70507095 args: []const WValue,
70517096) InnerError!WValue {
70527097 assert(param_types.len == args.len);
7053 const wasm = cg.wasm;
70547098 const pt = cg.pt;
70557099 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);
70587100
70597101 // Always pass over C-ABI
70607102
......@@ -7074,8 +7116,7 @@ fn callIntrinsic(
70747116 try cg.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg);
70757117 }
70767118
7077 // Actually call our intrinsic
7078 try cg.addLabel(.call_func, func_index);
7119 try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } });
70797120
70807121 if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
70817122 return .none;
......@@ -7097,7 +7138,7 @@ fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70977138 const result_ptr = try cg.allocStack(cg.typeOfIndex(inst));
70987139 try cg.lowerToStack(result_ptr);
70997140 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() } });
71017142
71027143 return cg.finishAir(inst, result_ptr, &.{un_op});
71037144}
......@@ -7514,3 +7555,38 @@ fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type {
75147555 const zcu = pt.zcu;
75157556 return cg.air.typeOfIndex(inst, &zcu.intern_pool);
75167557}
7558
7559fn 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}
src/arch/wasm/Emit.zig+45
......@@ -178,6 +178,51 @@ pub fn lowerToCode(emit: *Emit) Error!void {
178178 continue :loop tags[inst];
179179 },
180180
181 .call_tag_name => {
182 try code.ensureUnusedCapacity(gpa, 6);
183 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
184 if (is_obj) {
185 try wasm.out_relocs.append(gpa, .{
186 .offset = @intCast(code.items.len),
187 .index = try wasm.tagNameSymbolIndex(datas[inst].ip_index),
188 .tag = .FUNCTION_INDEX_LEB,
189 .addend = 0,
190 });
191 code.appendNTimesAssumeCapacity(0, 5);
192 } else {
193 const func_index = try wasm.tagNameFunctionIndex(datas[inst].ip_index);
194 leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable;
195 }
196
197 inst += 1;
198 continue :loop tags[inst];
199 },
200
201 .call_intrinsic => {
202 // Although this currently uses `wasm.internString`, note that it
203 // *could* be changed to directly index into a preloaded strings
204 // table initialized based on the `Mir.Intrinsic` enum.
205 const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic));
206
207 try code.ensureUnusedCapacity(gpa, 6);
208 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
209 if (is_obj) {
210 try wasm.out_relocs.append(gpa, .{
211 .offset = @intCast(code.items.len),
212 .index = try wasm.symbolNameIndex(symbol_name),
213 .tag = .FUNCTION_INDEX_LEB,
214 .addend = 0,
215 });
216 code.appendNTimesAssumeCapacity(0, 5);
217 } else {
218 const func_index = try wasm.symbolNameFunctionIndex(symbol_name);
219 leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable;
220 }
221
222 inst += 1;
223 continue :loop tags[inst];
224 },
225
181226 .global_set => {
182227 try code.ensureUnusedCapacity(gpa, 6);
183228 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
src/arch/wasm/Mir.zig+182-6
......@@ -110,7 +110,7 @@ pub const Inst = struct {
110110 /// represents the label to jump to.
111111 ///
112112 /// Data is extra of which the Payload's type is `JumpTable`
113 br_table = 0x0E,
113 br_table,
114114 /// Returns from the function
115115 ///
116116 /// Uses `tag`.
......@@ -121,15 +121,14 @@ pub const Inst = struct {
121121 /// and index into the function table.
122122 ///
123123 /// Uses `func_ty`
124 call_indirect = 0x11,
125 /// Calls a function using `func_index`.
126 call_func,
124 call_indirect,
127125 /// Calls a function by its index.
128126 ///
129127 /// The function is the auto-generated tag name function for the type
130128 /// provided in `ip_index`.
131129 call_tag_name,
132
130 /// Lowers to a `call` instruction, using `intrinsic`.
131 call_intrinsic,
133132 /// Pops three values from the stack and pushes
134133 /// the first or second value dependent on the third value.
135134 /// Uses `tag`
......@@ -609,8 +608,8 @@ pub const Inst = struct {
609608
610609 ip_index: InternPool.Index,
611610 nav_index: InternPool.Nav.Index,
612 func_index: Wasm.FunctionIndex,
613611 func_ty: Wasm.FunctionType.Index,
612 intrinsic: Intrinsic,
614613
615614 comptime {
616615 switch (builtin.mode) {
......@@ -700,3 +699,180 @@ pub const DbgLineColumn = struct {
700699 line: u32,
701700 column: u32,
702701};
702
703/// Tag names exactly match the corresponding symbol name.
704pub const Intrinsic = enum(u32) {
705 __addhf3,
706 __addtf3,
707 __addxf3,
708 __ashlti3,
709 __ashrti3,
710 __bitreversedi2,
711 __bitreversesi2,
712 __bswapdi2,
713 __bswapsi2,
714 __ceilh,
715 __ceilx,
716 __cosh,
717 __cosx,
718 __divhf3,
719 __divtf3,
720 __divti3,
721 __divxf3,
722 __eqtf2,
723 __eqxf2,
724 __exp2h,
725 __exp2x,
726 __exph,
727 __expx,
728 __extenddftf2,
729 __extenddfxf2,
730 __extendhfsf2,
731 __extendhftf2,
732 __extendhfxf2,
733 __extendsftf2,
734 __extendsfxf2,
735 __extendxftf2,
736 __fabsh,
737 __fabsx,
738 __fixdfdi,
739 __fixdfsi,
740 __fixdfti,
741 __fixhfdi,
742 __fixhfsi,
743 __fixhfti,
744 __fixsfdi,
745 __fixsfsi,
746 __fixsfti,
747 __fixtfdi,
748 __fixtfsi,
749 __fixtfti,
750 __fixunsdfdi,
751 __fixunsdfsi,
752 __fixunsdfti,
753 __fixunshfdi,
754 __fixunshfsi,
755 __fixunshfti,
756 __fixunssfdi,
757 __fixunssfsi,
758 __fixunssfti,
759 __fixunstfdi,
760 __fixunstfsi,
761 __fixunstfti,
762 __fixunsxfdi,
763 __fixunsxfsi,
764 __fixunsxfti,
765 __fixxfdi,
766 __fixxfsi,
767 __fixxfti,
768 __floatdidf,
769 __floatdihf,
770 __floatdisf,
771 __floatditf,
772 __floatdixf,
773 __floatsidf,
774 __floatsihf,
775 __floatsisf,
776 __floatsitf,
777 __floatsixf,
778 __floattidf,
779 __floattihf,
780 __floattisf,
781 __floattitf,
782 __floattixf,
783 __floatundidf,
784 __floatundihf,
785 __floatundisf,
786 __floatunditf,
787 __floatundixf,
788 __floatunsidf,
789 __floatunsihf,
790 __floatunsisf,
791 __floatunsitf,
792 __floatunsixf,
793 __floatuntidf,
794 __floatuntihf,
795 __floatuntisf,
796 __floatuntitf,
797 __floatuntixf,
798 __floorh,
799 __floorx,
800 __fmah,
801 __fmax,
802 __fmaxh,
803 __fmaxx,
804 __fminh,
805 __fminx,
806 __fmodh,
807 __fmodx,
808 __getf2,
809 __gexf2,
810 __gttf2,
811 __gtxf2,
812 __letf2,
813 __lexf2,
814 __log10h,
815 __log10x,
816 __log2h,
817 __log2x,
818 __logh,
819 __logx,
820 __lshrti3,
821 __lttf2,
822 __ltxf2,
823 __modti3,
824 __mulhf3,
825 __mulodi4,
826 __muloti4,
827 __multf3,
828 __multi3,
829 __mulxf3,
830 __netf2,
831 __nexf2,
832 __roundh,
833 __roundx,
834 __sinh,
835 __sinx,
836 __sqrth,
837 __sqrtx,
838 __subhf3,
839 __subtf3,
840 __subxf3,
841 __tanh,
842 __tanx,
843 __trunch,
844 __truncsfhf2,
845 __trunctfdf2,
846 __trunctfhf2,
847 __trunctfsf2,
848 __trunctfxf2,
849 __truncx,
850 __truncxfdf2,
851 __truncxfhf2,
852 __truncxfsf2,
853 __udivti3,
854 __umodti3,
855 ceilq,
856 cosq,
857 exp2q,
858 expq,
859 fabsq,
860 floorq,
861 fmaf,
862 fmaq,
863 fmax,
864 fmaxf,
865 fmaxq,
866 fmin,
867 fminf,
868 fminq,
869 fmodq,
870 log10q,
871 log2q,
872 logq,
873 roundq,
874 sinq,
875 sqrtq,
876 tanq,
877 truncq,
878};