Commit bf20a4aa9e
Changed files (3)
src
arch
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,
+ },
+ };
+}
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));
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,
+};