diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-12-07 19:47:22 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-01-15 15:11:35 -0800 |
| commit | bf20a4aa9eeca3c1911709bf48a8c476106042dc (patch) | |
| tree | 18a54587b6ffdc8a93562ab56ac1ecbe9269b300 /src | |
| parent | c443a7a57fab7118a9793b7f77b7ab817235a896 (diff) | |
| download | zig-bf20a4aa9eeca3c1911709bf48a8c476106042dc.tar.gz zig-bf20a4aa9eeca3c1911709bf48a8c476106042dc.zip | |
wasm: use call_intrinsic MIR instruction
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 412 | ||||
| -rw-r--r-- | src/arch/wasm/Emit.zig | 45 | ||||
| -rw-r--r-- | src/arch/wasm/Mir.zig | 188 |
3 files changed, 471 insertions, 174 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 4fd5ee5c1e..b533360b28 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -18,7 +18,6 @@ const Compilation = @import("../../Compilation.zig"); const link = @import("../../link.zig"); const Air = @import("../../Air.zig"); const Liveness = @import("../../Liveness.zig"); -const target_util = @import("../../target.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const abi = @import("abi.zig"); @@ -27,6 +26,12 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset; const errUnionErrorOffset = codegen.errUnionErrorOffset; const Wasm = link.File.Wasm; +const target_util = @import("../../target.zig"); +const libcFloatPrefix = target_util.libcFloatPrefix; +const libcFloatSuffix = target_util.libcFloatSuffix; +const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; +const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; + /// Reference to the function declaration the code /// section belongs to owner_nav: InternPool.Nav.Index, @@ -854,7 +859,6 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void { } } -/// Appends a MIR instruction and returns its index within the list of instructions fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { try cg.mir_instructions.append(cg.gpa, inst); } @@ -873,14 +877,6 @@ fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void try cg.addInst(.{ .tag = tag, .data = .{ .label = label } }); } -fn addIpIndex(cg: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Index) Allocator.Error!void { - try cg.addInst(.{ .tag = tag, .data = .{ .ip_index = i } }); -} - -fn addNav(cg: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Nav.Index) Allocator.Error!void { - try cg.addInst(.{ .tag = tag, .data = .{ .nav_index = i } }); -} - /// Accepts an unsigned 32bit integer rather than a signed integer to /// prevent us from having to bitcast multiple times as most values /// within codegen are represented as unsigned rather than signed. @@ -1887,8 +1883,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .shl_sat => cg.airShlSat(inst), .shr, .shr_exact => cg.airBinOp(inst, .shr), .xor => cg.airBinOp(inst, .xor), - .max => cg.airMaxMin(inst, .max), - .min => cg.airMaxMin(inst, .min), + .max => cg.airMaxMin(inst, .fmax, .gt), + .min => cg.airMaxMin(inst, .fmin, .lt), .mul_add => cg.airMulAdd(inst), .sqrt => cg.airUnaryFloatOp(inst, .sqrt), @@ -2263,7 +2259,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie } if (callee) |nav_index| { - try cg.addNav(.call_nav, nav_index); + try cg.addInst(.{ .tag = .call_nav, .data = .{ .nav_index = nav_index } }); } else { // in this case we call a function pointer // so load its value onto the stack @@ -2669,20 +2665,20 @@ fn binOpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr } switch (op) { - .mul => return cg.callIntrinsic("__multi3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), + .mul => return cg.callIntrinsic(.__multi3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), .div => switch (int_info.signedness) { - .signed => return cg.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), - .unsigned => return cg.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), + .signed => return cg.callIntrinsic(.__divti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), + .unsigned => return cg.callIntrinsic(.__udivti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), }, .rem => switch (int_info.signedness) { - .signed => return cg.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), - .unsigned => return cg.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), + .signed => return cg.callIntrinsic(.__modti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), + .unsigned => return cg.callIntrinsic(.__umodti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), }, .shr => switch (int_info.signedness) { - .signed => return cg.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), - .unsigned => return cg.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), + .signed => return cg.callIntrinsic(.__ashrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), + .unsigned => return cg.callIntrinsic(.__lshrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), }, - .shl => return cg.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), + .shl => return cg.callIntrinsic(.__ashlti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), .@"and", .@"or", .xor => { const result = try cg.allocStack(ty); try cg.emitWValue(result); @@ -2802,6 +2798,46 @@ const FloatOp = enum { => null, }; } + + fn intrinsic(op: FloatOp, bits: u16) Mir.Intrinsic { + return switch (op) { + inline .add, .sub, .div, .mul => |ct_op| switch (bits) { + inline 16, 80, 128 => |ct_bits| @field( + Mir.Intrinsic, + "__" ++ @tagName(ct_op) ++ compilerRtFloatAbbrev(ct_bits) ++ "f3", + ), + else => unreachable, + }, + + inline .ceil, + .cos, + .exp, + .exp2, + .fabs, + .floor, + .fma, + .fmax, + .fmin, + .fmod, + .log, + .log10, + .log2, + .round, + .sin, + .sqrt, + .tan, + .trunc, + => |ct_op| switch (bits) { + inline 16, 80, 128 => |ct_bits| @field( + Mir.Intrinsic, + libcFloatPrefix(ct_bits) ++ @tagName(ct_op) ++ libcFloatSuffix(ct_bits), + ), + else => unreachable, + }, + + .neg => unreachable, + }; + } }; 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 } } - var fn_name_buf: [64]u8 = undefined; - const fn_name = switch (float_op) { - .add, - .sub, - .div, - .mul, - => std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f3", .{ - @tagName(float_op), target_util.compilerRtFloatAbbrev(float_bits), - }) catch unreachable, - - .ceil, - .cos, - .exp, - .exp2, - .fabs, - .floor, - .fma, - .fmax, - .fmin, - .fmod, - .log, - .log10, - .log2, - .round, - .sin, - .sqrt, - .tan, - .trunc, - => std.fmt.bufPrint(&fn_name_buf, "{s}{s}{s}", .{ - target_util.libcFloatPrefix(float_bits), @tagName(float_op), target_util.libcFloatSuffix(float_bits), - }) catch unreachable, - .neg => unreachable, // handled above - }; + const intrinsic = float_op.intrinsic(float_bits); // fma requires three operands var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index }; const param_types = param_types_buffer[0..args.len]; - return cg.callIntrinsic(fn_name, param_types, ty, args); + return cg.callIntrinsic(intrinsic, param_types, ty, args); } /// 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 return .stack; }, 80, 128 => { - var fn_name_buf: [32]u8 = undefined; - const fn_name = std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f2", .{ - @tagName(op), target_util.compilerRtFloatAbbrev(float_bits), - }) catch unreachable; - - const result = try cg.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs }); + const intrinsic = floatCmpIntrinsic(cmp_op, float_bits); + const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs }); return cg.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op); }, else => unreachable, @@ -5001,19 +5001,26 @@ fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } if ((op_bits != 32 and op_bits != 64) or dest_info.bits > 64) { - const dest_bitsize = if (dest_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits); - - var fn_name_buf: [16]u8 = undefined; - const fn_name = std.fmt.bufPrint(&fn_name_buf, "__fix{s}{s}f{s}i", .{ - switch (dest_info.signedness) { - .signed => "", - .unsigned => "uns", + const dest_bitsize = if (dest_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits); + + const intrinsic = switch (dest_info.signedness) { + inline .signed, .unsigned => |ct_s| switch (op_bits) { + inline 16, 32, 64, 80, 128 => |ct_op_bits| switch (dest_bitsize) { + inline 32, 64, 128 => |ct_dest_bits| @field( + Mir.Intrinsic, + "__fix" ++ switch (ct_s) { + .signed => "", + .unsigned => "uns", + } ++ + compilerRtFloatAbbrev(ct_op_bits) ++ "f" ++ + compilerRtIntAbbrev(ct_dest_bits) ++ "i", + ), + else => unreachable, + }, + else => unreachable, }, - target_util.compilerRtFloatAbbrev(op_bits), - target_util.compilerRtIntAbbrev(dest_bitsize), - }) catch unreachable; - - const result = try cg.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand}); + }; + const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand}); return cg.finishAir(inst, result, &.{ty_op.operand}); } @@ -5046,19 +5053,27 @@ fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } if (op_info.bits > 64 or (dest_bits > 64 or dest_bits < 32)) { - const op_bitsize = if (op_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits); - - var fn_name_buf: [16]u8 = undefined; - const fn_name = std.fmt.bufPrint(&fn_name_buf, "__float{s}{s}i{s}f", .{ - switch (op_info.signedness) { - .signed => "", - .unsigned => "un", + const op_bitsize = if (op_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits); + + const intrinsic = switch (op_info.signedness) { + inline .signed, .unsigned => |ct_s| switch (op_bitsize) { + inline 32, 64, 128 => |ct_int_bits| switch (dest_bits) { + inline 16, 32, 64, 80, 128 => |ct_float_bits| @field( + Mir.Intrinsic, + "__float" ++ switch (ct_s) { + .signed => "", + .unsigned => "un", + } ++ + compilerRtIntAbbrev(ct_int_bits) ++ "i" ++ + compilerRtFloatAbbrev(ct_float_bits) ++ "f", + ), + else => unreachable, + }, + else => unreachable, }, - target_util.compilerRtIntAbbrev(op_bitsize), - target_util.compilerRtFloatAbbrev(dest_bits), - }) catch unreachable; + }; - const result = try cg.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand}); + const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand}); return cg.finishAir(inst, result, &.{ty_op.operand}); } @@ -5577,39 +5592,49 @@ fn airFpext(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { return cg.finishAir(inst, result, &.{ty_op.operand}); } -/// Extends a float from a given `Type` to a larger wanted `Type` -/// NOTE: Leaves the result on the stack +/// Extends a float from a given `Type` to a larger wanted `Type`, leaving the +/// result on the stack. fn fpext(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { const given_bits = given.floatBits(cg.target.*); const wanted_bits = wanted.floatBits(cg.target.*); - if (wanted_bits == 64 and given_bits == 32) { - try cg.emitWValue(operand); - try cg.addTag(.f64_promote_f32); - return .stack; - } else if (given_bits == 16 and wanted_bits <= 64) { - // call __extendhfsf2(f16) f32 - const f32_result = try cg.callIntrinsic( - "__extendhfsf2", - &.{.f16_type}, - Type.f32, - &.{operand}, - ); - assert(f32_result == .stack); - - if (wanted_bits == 64) { - try cg.addTag(.f64_promote_f32); - } - return .stack; - } - - var fn_name_buf: [13]u8 = undefined; - const fn_name = std.fmt.bufPrint(&fn_name_buf, "__extend{s}f{s}f2", .{ - target_util.compilerRtFloatAbbrev(given_bits), - target_util.compilerRtFloatAbbrev(wanted_bits), - }) catch unreachable; - - return cg.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand}); + const intrinsic: Mir.Intrinsic = switch (given_bits) { + 16 => switch (wanted_bits) { + 32 => { + assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand})); + return .stack; + }, + 64 => { + assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand})); + try cg.addTag(.f64_promote_f32); + return .stack; + }, + 80 => .__extendhfxf2, + 128 => .__extendhftf2, + else => unreachable, + }, + 32 => switch (wanted_bits) { + 64 => { + try cg.emitWValue(operand); + try cg.addTag(.f64_promote_f32); + return .stack; + }, + 80 => .__extendsfxf2, + 128 => .__extendsftf2, + else => unreachable, + }, + 64 => switch (wanted_bits) { + 80 => .__extenddfxf2, + 128 => .__extenddftf2, + else => unreachable, + }, + 80 => switch (wanted_bits) { + 128 => .__extendxftf2, + else => unreachable, + }, + else => unreachable, + }; + return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand}); } fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { @@ -5621,34 +5646,48 @@ fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { return cg.finishAir(inst, result, &.{ty_op.operand}); } -/// Truncates a float from a given `Type` to its wanted `Type` -/// NOTE: The result value remains on the stack +/// Truncates a float from a given `Type` to its wanted `Type`, leaving the +/// result on the stack. fn fptrunc(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { const given_bits = given.floatBits(cg.target.*); const wanted_bits = wanted.floatBits(cg.target.*); - if (wanted_bits == 32 and given_bits == 64) { - try cg.emitWValue(operand); - try cg.addTag(.f32_demote_f64); - return .stack; - } else if (wanted_bits == 16 and given_bits <= 64) { - const op: WValue = if (given_bits == 64) blk: { - try cg.emitWValue(operand); - try cg.addTag(.f32_demote_f64); - break :blk .stack; - } else operand; - - // call __truncsfhf2(f32) f16 - return cg.callIntrinsic("__truncsfhf2", &.{.f32_type}, Type.f16, &.{op}); - } - - var fn_name_buf: [12]u8 = undefined; - const fn_name = std.fmt.bufPrint(&fn_name_buf, "__trunc{s}f{s}f2", .{ - target_util.compilerRtFloatAbbrev(given_bits), - target_util.compilerRtFloatAbbrev(wanted_bits), - }) catch unreachable; - - return cg.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand}); + const intrinsic: Mir.Intrinsic = switch (given_bits) { + 32 => switch (wanted_bits) { + 16 => { + return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{operand}); + }, + else => unreachable, + }, + 64 => switch (wanted_bits) { + 16 => { + try cg.emitWValue(operand); + try cg.addTag(.f32_demote_f64); + return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{.stack}); + }, + 32 => { + try cg.emitWValue(operand); + try cg.addTag(.f32_demote_f64); + return .stack; + }, + else => unreachable, + }, + 80 => switch (wanted_bits) { + 16 => .__truncxfhf2, + 32 => .__truncxfsf2, + 64 => .__truncxfdf2, + else => unreachable, + }, + 128 => switch (wanted_bits) { + 16 => .__trunctfhf2, + 32 => .__trunctfsf2, + 64 => .__trunctfdf2, + 80 => .__trunctfxf2, + else => unreachable, + }, + else => unreachable, + }; + return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand}); } fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { @@ -5823,7 +5862,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { switch (wasm_bits) { 32 => { const intrin_ret = try cg.callIntrinsic( - "__bitreversesi2", + .__bitreversesi2, &.{.u32_type}, Type.u32, &.{operand}, @@ -5836,7 +5875,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { }, 64 => { const intrin_ret = try cg.callIntrinsic( - "__bitreversedi2", + .__bitreversedi2, &.{.u64_type}, Type.u64, &.{operand}, @@ -5853,7 +5892,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { try cg.emitWValue(result); const first_half = try cg.load(operand, Type.u64, 8); const intrin_ret_first = try cg.callIntrinsic( - "__bitreversedi2", + .__bitreversedi2, &.{.u64_type}, Type.u64, &.{first_half}, @@ -5866,7 +5905,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { try cg.emitWValue(result); const second_half = try cg.load(operand, Type.u64, 0); const intrin_ret_second = try cg.callIntrinsic( - "__bitreversedi2", + .__bitreversedi2, &.{.u64_type}, Type.u64, &.{second_half}, @@ -6114,19 +6153,19 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { defer rhs_msb.free(cg); const cross_1 = try cg.callIntrinsic( - "__multi3", + .__multi3, &[_]InternPool.Index{.i64_type} ** 4, Type.i128, &.{ lhs_msb, zero, rhs_lsb, zero }, ); const cross_2 = try cg.callIntrinsic( - "__multi3", + .__multi3, &[_]InternPool.Index{.i64_type} ** 4, Type.i128, &.{ rhs_msb, zero, lhs_lsb, zero }, ); const mul_lsb = try cg.callIntrinsic( - "__multi3", + .__multi3, &[_]InternPool.Index{.i64_type} ** 4, Type.i128, &.{ rhs_lsb, zero, lhs_lsb, zero }, @@ -6165,7 +6204,7 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: { const overflow_ret = try cg.allocStack(Type.i32); const res = try cg.callIntrinsic( - "__muloti4", + .__muloti4, &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, Type.i128, &.{ lhs, rhs, overflow_ret }, @@ -6185,8 +6224,12 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); } -fn airMaxMin(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { - assert(op == .max or op == .min); +fn airMaxMin( + cg: *CodeGen, + inst: Air.Inst.Index, + op: enum { fmax, fmin }, + cmp_op: std.math.CompareOperator, +) InnerError!void { const pt = cg.pt; const zcu = pt.zcu; 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 { const rhs = try cg.resolveInst(bin_op.rhs); if (ty.zigTypeTag(zcu) == .float) { - var fn_name_buf: [64]u8 = undefined; - const float_bits = ty.floatBits(cg.target.*); - const fn_name = std.fmt.bufPrint(&fn_name_buf, "{s}f{s}{s}", .{ - target_util.libcFloatPrefix(float_bits), - @tagName(op), - target_util.libcFloatSuffix(float_bits), - }) catch unreachable; - const result = try cg.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); + const intrinsic = switch (op) { + inline .fmin, .fmax => |ct_op| switch (ty.floatBits(cg.target.*)) { + inline 16, 32, 64, 80, 128 => |bits| @field( + Mir.Intrinsic, + libcFloatPrefix(bits) ++ @tagName(ct_op) ++ libcFloatSuffix(bits), + ), + else => unreachable, + }, + }; + const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); try cg.lowerToStack(result); } else { // operands to select from try cg.lowerToStack(lhs); try cg.lowerToStack(rhs); - _ = try cg.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); + _ = try cg.cmp(lhs, rhs, ty, cmp_op); // based on the result from comparison, return operand 0 or 1. try cg.addTag(.select); @@ -6247,7 +6292,7 @@ fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const addend_ext = try cg.fpext(addend, ty, Type.f32); // call to compiler-rt `fn fmaf(f32, f32, f32) f32` const result = try cg.callIntrinsic( - "fmaf", + .fmaf, &.{ .f32_type, .f32_type, .f32_type }, Type.f32, &.{ rhs_ext, lhs_ext, addend_ext }, @@ -6508,7 +6553,7 @@ fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { switch (wasm_bits) { 32 => { const intrin_ret = try cg.callIntrinsic( - "__bswapsi2", + .__bswapsi2, &.{.u32_type}, Type.u32, &.{operand}, @@ -6520,7 +6565,7 @@ fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { }, 64 => { const intrin_ret = try cg.callIntrinsic( - "__bswapdi2", + .__bswapdi2, &.{.u64_type}, Type.u64, &.{operand}, @@ -6777,7 +6822,7 @@ fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } const overflow_ret = try cg.allocStack(Type.i32); _ = try cg.callIntrinsic( - "__mulodi4", + .__mulodi4, &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type }, Type.i64, &.{ lhs, rhs, overflow_ret }, @@ -6795,7 +6840,7 @@ fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } const overflow_ret = try cg.allocStack(Type.i32); const ret = try cg.callIntrinsic( - "__muloti4", + .__muloti4, &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, Type.i128, &.{ lhs, rhs, overflow_ret }, @@ -7044,17 +7089,14 @@ fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { /// May leave the return value on the stack. fn callIntrinsic( cg: *CodeGen, - name: []const u8, + intrinsic: Mir.Intrinsic, param_types: []const InternPool.Index, return_type: Type, args: []const WValue, ) InnerError!WValue { assert(param_types.len == args.len); - const wasm = cg.wasm; const pt = cg.pt; const zcu = pt.zcu; - const func_type_index = try genFunctype(wasm, .{ .wasm_watc = .{} }, param_types, return_type, pt, cg.target); - const func_index = wasm.getOutputFunction(try wasm.internString(name), func_type_index); // Always pass over C-ABI @@ -7074,8 +7116,7 @@ fn callIntrinsic( try cg.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg); } - // Actually call our intrinsic - try cg.addLabel(.call_func, func_index); + try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } }); if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { return .none; @@ -7097,7 +7138,7 @@ fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const result_ptr = try cg.allocStack(cg.typeOfIndex(inst)); try cg.lowerToStack(result_ptr); try cg.emitWValue(operand); - try cg.addIpIndex(.call_tag_name, enum_ty.toIntern()); + try cg.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = enum_ty.toIntern() } }); return cg.finishAir(inst, result_ptr, &.{un_op}); } @@ -7514,3 +7555,38 @@ fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type { const zcu = pt.zcu; return cg.air.typeOfIndex(inst, &zcu.intern_pool); } + +fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic { + return switch (op) { + .lt => switch (bits) { + 80 => .__ltxf2, + 128 => .__lttf2, + else => unreachable, + }, + .lte => switch (bits) { + 80 => .__lexf2, + 128 => .__letf2, + else => unreachable, + }, + .eq => switch (bits) { + 80 => .__eqxf2, + 128 => .__eqtf2, + else => unreachable, + }, + .neq => switch (bits) { + 80 => .__nexf2, + 128 => .__netf2, + else => unreachable, + }, + .gte => switch (bits) { + 80 => .__gexf2, + 128 => .__getf2, + else => unreachable, + }, + .gt => switch (bits) { + 80 => .__gtxf2, + 128 => .__gttf2, + else => unreachable, + }, + }; +} diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 20d45247a9..15b4b3292d 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -178,6 +178,51 @@ pub fn lowerToCode(emit: *Emit) Error!void { continue :loop tags[inst]; }, + .call_tag_name => { + try code.ensureUnusedCapacity(gpa, 6); + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); + if (is_obj) { + try wasm.out_relocs.append(gpa, .{ + .offset = @intCast(code.items.len), + .index = try wasm.tagNameSymbolIndex(datas[inst].ip_index), + .tag = .FUNCTION_INDEX_LEB, + .addend = 0, + }); + code.appendNTimesAssumeCapacity(0, 5); + } else { + const func_index = try wasm.tagNameFunctionIndex(datas[inst].ip_index); + leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable; + } + + inst += 1; + continue :loop tags[inst]; + }, + + .call_intrinsic => { + // Although this currently uses `wasm.internString`, note that it + // *could* be changed to directly index into a preloaded strings + // table initialized based on the `Mir.Intrinsic` enum. + const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic)); + + try code.ensureUnusedCapacity(gpa, 6); + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); + if (is_obj) { + try wasm.out_relocs.append(gpa, .{ + .offset = @intCast(code.items.len), + .index = try wasm.symbolNameIndex(symbol_name), + .tag = .FUNCTION_INDEX_LEB, + .addend = 0, + }); + code.appendNTimesAssumeCapacity(0, 5); + } else { + const func_index = try wasm.symbolNameFunctionIndex(symbol_name); + leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable; + } + + inst += 1; + continue :loop tags[inst]; + }, + .global_set => { try code.ensureUnusedCapacity(gpa, 6); code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 54b427228e..9f5ddf189a 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -110,7 +110,7 @@ pub const Inst = struct { /// represents the label to jump to. /// /// Data is extra of which the Payload's type is `JumpTable` - br_table = 0x0E, + br_table, /// Returns from the function /// /// Uses `tag`. @@ -121,15 +121,14 @@ pub const Inst = struct { /// and index into the function table. /// /// Uses `func_ty` - call_indirect = 0x11, - /// Calls a function using `func_index`. - call_func, + call_indirect, /// Calls a function by its index. /// /// The function is the auto-generated tag name function for the type /// provided in `ip_index`. call_tag_name, - + /// Lowers to a `call` instruction, using `intrinsic`. + call_intrinsic, /// Pops three values from the stack and pushes /// the first or second value dependent on the third value. /// Uses `tag` @@ -609,8 +608,8 @@ pub const Inst = struct { ip_index: InternPool.Index, nav_index: InternPool.Nav.Index, - func_index: Wasm.FunctionIndex, func_ty: Wasm.FunctionType.Index, + intrinsic: Intrinsic, comptime { switch (builtin.mode) { @@ -700,3 +699,180 @@ pub const DbgLineColumn = struct { line: u32, column: u32, }; + +/// Tag names exactly match the corresponding symbol name. +pub const Intrinsic = enum(u32) { + __addhf3, + __addtf3, + __addxf3, + __ashlti3, + __ashrti3, + __bitreversedi2, + __bitreversesi2, + __bswapdi2, + __bswapsi2, + __ceilh, + __ceilx, + __cosh, + __cosx, + __divhf3, + __divtf3, + __divti3, + __divxf3, + __eqtf2, + __eqxf2, + __exp2h, + __exp2x, + __exph, + __expx, + __extenddftf2, + __extenddfxf2, + __extendhfsf2, + __extendhftf2, + __extendhfxf2, + __extendsftf2, + __extendsfxf2, + __extendxftf2, + __fabsh, + __fabsx, + __fixdfdi, + __fixdfsi, + __fixdfti, + __fixhfdi, + __fixhfsi, + __fixhfti, + __fixsfdi, + __fixsfsi, + __fixsfti, + __fixtfdi, + __fixtfsi, + __fixtfti, + __fixunsdfdi, + __fixunsdfsi, + __fixunsdfti, + __fixunshfdi, + __fixunshfsi, + __fixunshfti, + __fixunssfdi, + __fixunssfsi, + __fixunssfti, + __fixunstfdi, + __fixunstfsi, + __fixunstfti, + __fixunsxfdi, + __fixunsxfsi, + __fixunsxfti, + __fixxfdi, + __fixxfsi, + __fixxfti, + __floatdidf, + __floatdihf, + __floatdisf, + __floatditf, + __floatdixf, + __floatsidf, + __floatsihf, + __floatsisf, + __floatsitf, + __floatsixf, + __floattidf, + __floattihf, + __floattisf, + __floattitf, + __floattixf, + __floatundidf, + __floatundihf, + __floatundisf, + __floatunditf, + __floatundixf, + __floatunsidf, + __floatunsihf, + __floatunsisf, + __floatunsitf, + __floatunsixf, + __floatuntidf, + __floatuntihf, + __floatuntisf, + __floatuntitf, + __floatuntixf, + __floorh, + __floorx, + __fmah, + __fmax, + __fmaxh, + __fmaxx, + __fminh, + __fminx, + __fmodh, + __fmodx, + __getf2, + __gexf2, + __gttf2, + __gtxf2, + __letf2, + __lexf2, + __log10h, + __log10x, + __log2h, + __log2x, + __logh, + __logx, + __lshrti3, + __lttf2, + __ltxf2, + __modti3, + __mulhf3, + __mulodi4, + __muloti4, + __multf3, + __multi3, + __mulxf3, + __netf2, + __nexf2, + __roundh, + __roundx, + __sinh, + __sinx, + __sqrth, + __sqrtx, + __subhf3, + __subtf3, + __subxf3, + __tanh, + __tanx, + __trunch, + __truncsfhf2, + __trunctfdf2, + __trunctfhf2, + __trunctfsf2, + __trunctfxf2, + __truncx, + __truncxfdf2, + __truncxfhf2, + __truncxfsf2, + __udivti3, + __umodti3, + ceilq, + cosq, + exp2q, + expq, + fabsq, + floorq, + fmaf, + fmaq, + fmax, + fmaxf, + fmaxq, + fmin, + fminf, + fminq, + fmodq, + log10q, + log2q, + logq, + roundq, + sinq, + sqrtq, + tanq, + truncq, +}; |
