mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 23:52:31 +00:00
c50f33b111
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.
269 lines
7.9 KiB
Zig
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;
|
|
},
|
|
}
|
|
}
|