zig/lib/compiler_rt/sincos.zig
Cody Tapscott c50f33b111 compiler_rt: Always export "standard" symbol names
The Zig LLVM backend emits calls to softfloat methods with the "standard
compiler-rt" names. Rather than add complexity to the backend and
have to synchronize the naming scheme across all targets, the simplest
fix is just to export these symbols under both the "standard" and the
platform-specific naming convention.
2022-10-22 17:19:33 -07:00

269 lines
7.9 KiB
Zig

const std = @import("std");
const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const math = std.math;
const trig = @import("trig.zig");
const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
const common = @import("common.zig");
pub const panic = common.panic;
comptime {
@export(__sincosh, .{ .name = "__sincosh", .linkage = common.linkage });
@export(sincosf, .{ .name = "sincosf", .linkage = common.linkage });
@export(sincos, .{ .name = "sincos", .linkage = common.linkage });
@export(__sincosx, .{ .name = "__sincosx", .linkage = common.linkage });
if (common.want_ppc_abi) {
@export(sincosq, .{ .name = "sincosf128", .linkage = common.linkage });
}
@export(sincosq, .{ .name = "sincosq", .linkage = common.linkage });
@export(sincosl, .{ .name = "sincosl", .linkage = common.linkage });
}
pub fn __sincosh(x: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void {
// TODO: more efficient implementation
var big_sin: f32 = undefined;
var big_cos: f32 = undefined;
sincosf(x, &big_sin, &big_cos);
r_sin.* = @floatCast(f16, big_sin);
r_cos.* = @floatCast(f16, big_cos);
}
pub fn sincosf(x: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void {
const sc1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
const sc2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
const sc3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
const sc4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
const pre_ix = @bitCast(u32, x);
const sign = pre_ix >> 31 != 0;
const ix = pre_ix & 0x7fffffff;
// |x| ~<= pi/4
if (ix <= 0x3f490fda) {
// |x| < 2**-12
if (ix < 0x39800000) {
// raise inexact if x!=0 and underflow if subnormal
math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
r_sin.* = x;
r_cos.* = 1.0;
return;
}
r_sin.* = trig.__sindf(x);
r_cos.* = trig.__cosdf(x);
return;
}
// |x| ~<= 5*pi/4
if (ix <= 0x407b53d1) {
// |x| ~<= 3pi/4
if (ix <= 0x4016cbe3) {
if (sign) {
r_sin.* = -trig.__cosdf(x + sc1pio2);
r_cos.* = trig.__sindf(x + sc1pio2);
} else {
r_sin.* = trig.__cosdf(sc1pio2 - x);
r_cos.* = trig.__sindf(sc1pio2 - x);
}
return;
}
// -sin(x+c) is not correct if x+c could be 0: -0 vs +0
r_sin.* = -trig.__sindf(if (sign) x + sc2pio2 else x - sc2pio2);
r_cos.* = -trig.__cosdf(if (sign) x + sc2pio2 else x - sc2pio2);
return;
}
// |x| ~<= 9*pi/4
if (ix <= 0x40e231d5) {
// |x| ~<= 7*pi/4
if (ix <= 0x40afeddf) {
if (sign) {
r_sin.* = trig.__cosdf(x + sc3pio2);
r_cos.* = -trig.__sindf(x + sc3pio2);
} else {
r_sin.* = -trig.__cosdf(x - sc3pio2);
r_cos.* = trig.__sindf(x - sc3pio2);
}
return;
}
r_sin.* = trig.__sindf(if (sign) x + sc4pio2 else x - sc4pio2);
r_cos.* = trig.__cosdf(if (sign) x + sc4pio2 else x - sc4pio2);
return;
}
// sin(Inf or NaN) is NaN
if (ix >= 0x7f800000) {
const result = x - x;
r_sin.* = result;
r_cos.* = result;
return;
}
// general argument reduction needed
var y: f64 = undefined;
const n = rem_pio2f(x, &y);
const s = trig.__sindf(y);
const c = trig.__cosdf(y);
switch (n & 3) {
0 => {
r_sin.* = s;
r_cos.* = c;
},
1 => {
r_sin.* = c;
r_cos.* = -s;
},
2 => {
r_sin.* = -s;
r_cos.* = -c;
},
else => {
r_sin.* = -c;
r_cos.* = s;
},
}
}
pub fn sincos(x: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void {
const ix = @truncate(u32, @bitCast(u64, x) >> 32) & 0x7fffffff;
// |x| ~< pi/4
if (ix <= 0x3fe921fb) {
// if |x| < 2**-27 * sqrt(2)
if (ix < 0x3e46a09e) {
// raise inexact if x != 0 and underflow if subnormal
math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
r_sin.* = x;
r_cos.* = 1.0;
return;
}
r_sin.* = trig.__sin(x, 0.0, 0);
r_cos.* = trig.__cos(x, 0.0);
return;
}
// sincos(Inf or NaN) is NaN
if (ix >= 0x7ff00000) {
const result = x - x;
r_sin.* = result;
r_cos.* = result;
return;
}
// argument reduction needed
var y: [2]f64 = undefined;
const n = rem_pio2(x, &y);
const s = trig.__sin(y[0], y[1], 1);
const c = trig.__cos(y[0], y[1]);
switch (n & 3) {
0 => {
r_sin.* = s;
r_cos.* = c;
},
1 => {
r_sin.* = c;
r_cos.* = -s;
},
2 => {
r_sin.* = -s;
r_cos.* = -c;
},
else => {
r_sin.* = -c;
r_cos.* = s;
},
}
}
pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.C) void {
// TODO: more efficient implementation
//return sincos_generic(f80, x, r_sin, r_cos);
var big_sin: f128 = undefined;
var big_cos: f128 = undefined;
sincosq(x, &big_sin, &big_cos);
r_sin.* = @floatCast(f80, big_sin);
r_cos.* = @floatCast(f80, big_cos);
}
pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void {
// TODO: more correct implementation
//return sincos_generic(f128, x, r_sin, r_cos);
var small_sin: f64 = undefined;
var small_cos: f64 = undefined;
sincos(@floatCast(f64, x), &small_sin, &small_cos);
r_sin.* = small_sin;
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"),
}
}
pub const rem_pio2_generic = @compileError("TODO");
/// Ported from musl sincosl.c. Needs the following dependencies to be complete:
/// * rem_pio2_generic ported from __rem_pio2l.c
/// * trig.sin_generic ported from __sinl.c
/// * trig.cos_generic ported from __cosl.c
inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void {
const sc1pio4: F = 1.0 * math.pi / 4.0;
const bits = @typeInfo(F).Float.bits;
const I = std.meta.Int(.unsigned, bits);
const ix = @bitCast(I, x) & (math.maxInt(I) >> 1);
const se = @truncate(u16, ix >> (bits - 16));
if (se == 0x7fff) {
const result = x - x;
r_sin.* = result;
r_cos.* = result;
return;
}
if (@bitCast(F, ix) < sc1pio4) {
if (se < 0x3fff - math.floatFractionalBits(F) - 1) {
// raise underflow if subnormal
if (se == 0) {
math.doNotOptimizeAway(x * 0x1p-120);
}
r_sin.* = x;
// raise inexact if x!=0
r_cos.* = 1.0 + x;
return;
}
r_sin.* = trig.sin_generic(F, x, 0, 0);
r_cos.* = trig.cos_generic(F, x, 0);
return;
}
var y: [2]F = undefined;
const n = rem_pio2_generic(F, x, &y);
const s = trig.sin_generic(F, y[0], y[1], 1);
const c = trig.cos_generic(F, y[0], y[1]);
switch (n & 3) {
0 => {
r_sin.* = s;
r_cos.* = c;
},
1 => {
r_sin.* = c;
r_cos.* = -s;
},
2 => {
r_sin.* = -s;
r_cos.* = -c;
},
else => {
r_sin.* = -c;
r_cos.* = s;
},
}
}