compiler_rt: avoid weak aliases on Windows

When exporting math functions for Windows, we provide weak exports of
'l' variants rather than weak aliases. We still use aliases on other
operating systems so that the 'l' variants have one less jump
instruction in this case.
This commit is contained in:
Andrew Kelley 2022-05-08 13:06:21 -07:00
parent 6fde2fcd51
commit cd019ee502
20 changed files with 235 additions and 23 deletions

View File

@ -724,25 +724,25 @@ comptime {
@export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
}
mathExport("ceil", @import("./compiler_rt/ceil.zig"), false);
mathExport("cos", @import("./compiler_rt/cos.zig"), true);
mathExport("exp", @import("./compiler_rt/exp.zig"), true);
mathExport("exp2", @import("./compiler_rt/exp2.zig"), true);
mathExport("fabs", @import("./compiler_rt/fabs.zig"), true);
mathExport("floor", @import("./compiler_rt/floor.zig"), false);
mathExport("fma", @import("./compiler_rt/fma.zig"), true);
mathExport("fmax", @import("./compiler_rt/fmax.zig"), true);
mathExport("fmin", @import("./compiler_rt/fmin.zig"), true);
mathExport("fmod", @import("./compiler_rt/fmod.zig"), true);
mathExport("log", @import("./compiler_rt/log.zig"), true);
mathExport("log10", @import("./compiler_rt/log10.zig"), false);
mathExport("log2", @import("./compiler_rt/log2.zig"), true);
mathExport("round", @import("./compiler_rt/round.zig"), true);
mathExport("sin", @import("./compiler_rt/sin.zig"), true);
mathExport("sincos", @import("./compiler_rt/sincos.zig"), true);
mathExport("sqrt", @import("./compiler_rt/sqrt.zig"), true);
mathExport("tan", @import("./compiler_rt/tan.zig"), true);
mathExport("trunc", @import("./compiler_rt/trunc.zig"), true);
mathExport("ceil", @import("./compiler_rt/ceil.zig"));
mathExport("cos", @import("./compiler_rt/cos.zig"));
mathExport("exp", @import("./compiler_rt/exp.zig"));
mathExport("exp2", @import("./compiler_rt/exp2.zig"));
mathExport("fabs", @import("./compiler_rt/fabs.zig"));
mathExport("floor", @import("./compiler_rt/floor.zig"));
mathExport("fma", @import("./compiler_rt/fma.zig"));
mathExport("fmax", @import("./compiler_rt/fmax.zig"));
mathExport("fmin", @import("./compiler_rt/fmin.zig"));
mathExport("fmod", @import("./compiler_rt/fmod.zig"));
mathExport("log", @import("./compiler_rt/log.zig"));
mathExport("log10", @import("./compiler_rt/log10.zig"));
mathExport("log2", @import("./compiler_rt/log2.zig"));
mathExport("round", @import("./compiler_rt/round.zig"));
mathExport("sin", @import("./compiler_rt/sin.zig"));
mathExport("sincos", @import("./compiler_rt/sincos.zig"));
mathExport("sqrt", @import("./compiler_rt/sqrt.zig"));
mathExport("tan", @import("./compiler_rt/tan.zig"));
mathExport("trunc", @import("./compiler_rt/trunc.zig"));
if (arch.isSPARC()) {
// SPARC systems use a different naming scheme
@ -825,7 +825,7 @@ comptime {
}
}
inline fn mathExport(double_name: []const u8, comptime import: type, win_libc_has_it: bool) void {
inline fn mathExport(double_name: []const u8, comptime import: type) void {
const half_name = "__" ++ double_name ++ "h";
const half_fn = @field(import, half_name);
const float_name = double_name ++ "f";
@ -853,9 +853,12 @@ inline fn mathExport(double_name: []const u8, comptime import: type, win_libc_ha
.{ f128, quad_fn },
};
// Weak aliases don't work on Windows, so we avoid exporting the `l` alias
// on this platform for functions we know will collide.
if (builtin.os.tag != .windows or !builtin.link_libc or !win_libc_has_it) {
if (builtin.os.tag == .windows) {
// Weak aliases don't work on Windows, so we have to provide the 'l' variants
// as additional function definitions that jump to the real definition.
const long_double_fn = @field(import, long_double_name);
@export(long_double_fn, .{ .name = long_double_name, .linkage = linkage });
} else {
inline for (pairs) |pair| {
const F = pair[0];
const func = pair[1];

View File

@ -111,6 +111,17 @@ pub fn ceilq(x: f128) callconv(.C) f128 {
}
}
pub fn ceill(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __ceilh(x),
32 => return ceilf(x),
64 => return ceil(x),
80 => return __ceilx(x),
128 => return ceilq(x),
else => @compileError("unreachable"),
}
}
test "ceil32" {
try expect(ceilf(1.3) == 2.0);
try expect(ceilf(-1.3) == -1.0);

View File

@ -107,6 +107,17 @@ pub fn cosq(a: f128) callconv(.C) f128 {
return cos(@floatCast(f64, a));
}
pub fn cosl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __cosh(x),
32 => return cosf(x),
64 => return cos(x),
80 => return __cosx(x),
128 => return cosq(x),
else => @compileError("unreachable"),
}
}
test "cos32" {
const epsilon = 0.00001;

View File

@ -182,6 +182,17 @@ pub fn expq(a: f128) callconv(.C) f128 {
return exp(@floatCast(f64, a));
}
pub fn expl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __exph(x),
32 => return expf(x),
64 => return exp(x),
80 => return __expx(x),
128 => return expq(x),
else => @compileError("unreachable"),
}
}
test "exp32" {
const epsilon = 0.000001;

View File

@ -149,6 +149,17 @@ pub fn exp2q(x: f128) callconv(.C) f128 {
return exp2(@floatCast(f64, x));
}
pub fn exp2l(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __exp2h(x),
32 => return exp2f(x),
64 => return exp2(x),
80 => return __exp2x(x),
128 => return exp2q(x),
else => @compileError("unreachable"),
}
}
const exp2ft = [_]f64{
0x1.6a09e667f3bcdp-1,
0x1.7a11473eb0187p-1,

View File

@ -20,6 +20,17 @@ pub fn fabsq(a: f128) callconv(.C) f128 {
return generic_fabs(a);
}
pub fn fabsl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __fabsh(x),
32 => return fabsf(x),
64 => return fabs(x),
80 => return __fabsx(x),
128 => return fabsq(x),
else => @compileError("unreachable"),
}
}
inline fn generic_fabs(x: anytype) @TypeOf(x) {
const T = @TypeOf(x);
const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);

View File

@ -141,6 +141,17 @@ pub fn floorq(x: f128) callconv(.C) f128 {
}
}
pub fn floorl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __floorh(x),
32 => return floorf(x),
64 => return floor(x),
80 => return __floorx(x),
128 => return floorq(x),
else => @compileError("unreachable"),
}
}
test "floor16" {
try expect(__floorh(1.3) == 1.0);
try expect(__floorh(-1.3) == -2.0);

View File

@ -135,6 +135,17 @@ pub fn fmaq(x: f128, y: f128, z: f128) callconv(.C) f128 {
}
}
pub fn fmal(x: c_longdouble, y: c_longdouble, z: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __fmah(x, y, z),
32 => return fmaf(x, y, z),
64 => return fma(x, y, z),
80 => return __fmax(x, y, z),
128 => return fmaq(x, y, z),
else => @compileError("unreachable"),
}
}
const dd = struct {
hi: f64,
lo: f64,

View File

@ -21,6 +21,17 @@ pub fn fmaxq(x: f128, y: f128) callconv(.C) f128 {
return generic_fmax(f128, x, y);
}
pub fn fmaxl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __fmaxh(x, y),
32 => return fmaxf(x, y),
64 => return fmax(x, y),
80 => return __fmaxx(x, y),
128 => return fmaxq(x, y),
else => @compileError("unreachable"),
}
}
inline fn generic_fmax(comptime T: type, x: T, y: T) T {
if (math.isNan(x))
return y;

View File

@ -21,6 +21,17 @@ pub fn fminq(x: f128, y: f128) callconv(.C) f128 {
return generic_fmin(f128, x, y);
}
pub fn fminl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __fminh(x, y),
32 => return fminf(x, y),
64 => return fmin(x, y),
80 => return __fminx(x, y),
128 => return fminq(x, y),
else => @compileError("unreachable"),
}
}
inline fn generic_fmin(comptime T: type, x: T, y: T) T {
if (math.isNan(x))
return y;

View File

@ -237,6 +237,17 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 {
return amod;
}
pub fn fmodl(a: c_longdouble, b: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __fmodh(a, b),
32 => return fmodf(a, b),
64 => return fmod(a, b),
80 => return __fmodx(a, b),
128 => return fmodq(a, b),
else => @compileError("unreachable"),
}
}
inline fn generic_fmod(comptime T: type, x: T, y: T) T {
@setRuntimeSafety(false);

View File

@ -131,6 +131,17 @@ pub fn logq(a: f128) callconv(.C) f128 {
return log(@floatCast(f64, a));
}
pub fn logl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __logh(x),
32 => return logf(x),
64 => return log(x),
80 => return __logx(x),
128 => return logq(x),
else => @compileError("unreachable"),
}
}
test "ln32" {
const epsilon = 0.000001;

View File

@ -159,6 +159,17 @@ pub fn log10q(a: f128) callconv(.C) f128 {
return log10(@floatCast(f64, a));
}
pub fn log10l(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __log10h(x),
32 => return log10f(x),
64 => return log10(x),
80 => return __log10x(x),
128 => return log10q(x),
else => @compileError("unreachable"),
}
}
test "log10_32" {
const epsilon = 0.000001;

View File

@ -150,6 +150,17 @@ pub fn log2q(a: f128) callconv(.C) f128 {
return math.log2(a);
}
pub fn log2l(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __log2h(x),
32 => return log2f(x),
64 => return log2(x),
80 => return __log2x(x),
128 => return log2q(x),
else => @compileError("unreachable"),
}
}
test "log2_32" {
const epsilon = 0.000001;

View File

@ -123,6 +123,17 @@ pub fn roundq(x_: f128) callconv(.C) f128 {
}
}
pub fn roundl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __roundh(x),
32 => return roundf(x),
64 => return round(x),
80 => return __roundx(x),
128 => return roundq(x),
else => @compileError("unreachable"),
}
}
test "round32" {
try expect(roundf(1.3) == 1.0);
try expect(roundf(-1.3) == -1.0);

View File

@ -111,6 +111,17 @@ pub fn sinq(x: f128) callconv(.C) f128 {
return sin(@floatCast(f64, x));
}
pub fn sinl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __sinh(x),
32 => return sinf(x),
64 => return sin(x),
80 => return __sinx(x),
128 => return sinq(x),
else => @compileError("unreachable"),
}
}
test "sin32" {
const epsilon = 0.00001;

View File

@ -181,6 +181,17 @@ pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void {
r_cos.* = small_cos;
}
pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.C) void {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __sincosh(x, r_sin, r_cos),
32 => return sincosf(x, r_sin, r_cos),
64 => return sincos(x, r_sin, r_cos),
80 => return __sincosx(x, r_sin, r_cos),
128 => return sincosq(x, r_sin, r_cos),
else => @compileError("unreachable"),
}
}
const rem_pio2_generic = @compileError("TODO");
/// Ported from musl sincosl.c. Needs the following dependencies to be complete:

View File

@ -225,6 +225,17 @@ pub fn sqrtq(x: f128) callconv(.C) f128 {
return sqrt(@floatCast(f64, x));
}
pub fn sqrtl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __sqrth(x),
32 => return sqrtf(x),
64 => return sqrt(x),
80 => return __sqrtx(x),
128 => return sqrtq(x),
else => @compileError("unreachable"),
}
}
test "sqrtf" {
const V = [_]f32{
0.0,

View File

@ -96,6 +96,17 @@ pub fn tanq(x: f128) callconv(.C) f128 {
return tan(@floatCast(f64, x));
}
pub fn tanl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __tanh(x),
32 => return tanf(x),
64 => return tan(x),
80 => return __tanx(x),
128 => return tanq(x),
else => @compileError("unreachable"),
}
}
test "tan" {
try expect(tan(@as(f32, 0.0)) == tanf(0.0));
try expect(tan(@as(f64, 0.0)) == tan(0.0));

View File

@ -81,6 +81,17 @@ pub fn truncq(x: f128) callconv(.C) f128 {
}
}
pub fn truncl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __trunch(x),
32 => return truncf(x),
64 => return trunc(x),
80 => return __truncx(x),
128 => return truncq(x),
else => @compileError("unreachable"),
}
}
test "trunc32" {
try expect(truncf(1.3) == 1.0);
try expect(truncf(-1.3) == -1.0);