Skip to content

Commit a2e853b

Browse files
author
James Cox-Morton
committed
Add the ldexp family of functions, proxy to std.math.ldexp
Addresses ziglang#23358
1 parent 80170d0 commit a2e853b

File tree

5 files changed

+124
-0
lines changed

5 files changed

+124
-0
lines changed

lib/compiler_rt.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ comptime {
224224
_ = @import("compiler_rt/fmax.zig");
225225
_ = @import("compiler_rt/fmin.zig");
226226
_ = @import("compiler_rt/fmod.zig");
227+
_ = @import("compiler_rt/ldexp.zig");
227228
_ = @import("compiler_rt/log.zig");
228229
_ = @import("compiler_rt/log10.zig");
229230
_ = @import("compiler_rt/log2.zig");

lib/compiler_rt/ldexp.zig

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const std = @import("std");
2+
const expect = std.testing.expect;
3+
const math = std.math;
4+
const common = @import("common.zig");
5+
6+
comptime {
7+
@export(&ldexp, .{ .name = "ldexp", .linkage = common.linkage, .visibility = common.visibility });
8+
@export(&ldexpf, .{ .name = "ldexpf", .linkage = common.linkage, .visibility = common.visibility });
9+
@export(&ldexpl, .{ .name = "ldexpl", .linkage = common.linkage, .visibility = common.visibility });
10+
}
11+
12+
pub fn ldexp(x: f64, n: i32) callconv(.c) f64 {
13+
return math.ldexp(x, n);
14+
}
15+
16+
test "ldexp" {
17+
// Ported from libc-test
18+
// https://repo.or.cz/libc-test.git/blob/HEAD:/src/math/sanity/ldexp.h
19+
try expect(ldexp(-0x1.02239f3c6a8f1p+3, -2) == -0x1.02239f3c6a8f1p+1);
20+
}
21+
22+
test "ldexp.special" {
23+
// Ported from libc-test
24+
// https://repo.or.cz/libc-test.git/blob/HEAD:/src/math/special/ldexp.h
25+
try expect(math.isNan(ldexp(math.nan(f64), 0)));
26+
try expect(math.isPositiveInf(ldexp(math.inf(f64), 0)));
27+
}
28+
29+
pub fn ldexpf(x: f32, n: i32) callconv(.c) f32 {
30+
return math.ldexp(x, n);
31+
}
32+
33+
test "ldexpf" {
34+
// Ported from libc-test
35+
// https://repo.or.cz/libc-test.git/blob/HEAD:/src/math/sanity/ldexpf.h
36+
try expect(ldexpf(-0x1.0223ap+3, -2) == -0x1.0223ap+1);
37+
}
38+
39+
test "ldexpf.special" {
40+
// Ported from libc-test
41+
// https://repo.or.cz/libc-test.git/blob/HEAD:/src/math/special/ldexpf.h
42+
try expect(math.isNan(ldexpf(math.nan(f32), 0)));
43+
try expect(math.isPositiveInf(ldexpf(math.inf(f32), 0)));
44+
}
45+
46+
pub fn ldexpl(x: c_longdouble, n: i32) callconv(.c) c_longdouble {
47+
switch (@typeInfo(c_longdouble).float.bits) {
48+
16 => return math.ldexp(@as(f16, x), n),
49+
32 => return math.ldexp(@as(f32, x), n),
50+
64 => return math.ldexp(@as(f64, x), n),
51+
80 => return math.ldexp(@as(f80, x), n),
52+
128 => return math.ldexp(@as(f128, x), n),
53+
else => @compileError("unreachable"),
54+
}
55+
}
56+
57+
test "ldexpl" {
58+
// Ported from libc-test
59+
// https://repo.or.cz/libc-test.git/blob/HEAD:/src/math/sanity/ldexpl.h
60+
const x: c_longdouble = -0x1.02239f3c6a8f13dep+3;
61+
const expected: c_longdouble = -0x1.02239f3c6a8f13dep+1;
62+
try expect(ldexpl(x, -2) == expected);
63+
}
64+
65+
test "ldexpl.special" {
66+
// Ported from libc-test
67+
// https://repo.or.cz/libc-test.git/blob/HEAD:/src/math/special/ldexpl.h
68+
try expect(math.isNan(ldexpl(math.nan(c_longdouble), 0)));
69+
try expect(math.isPositiveInf(ldexpl(math.inf(c_longdouble), 0)));
70+
}

test/link/build.zig.zon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@
5454
.wasm_type = .{
5555
.path = "wasm/type",
5656
},
57+
.wasm_ldexp = .{
58+
.path = "wasm/ldexp",
59+
},
5760
},
5861
.paths = .{
5962
"build.zig",

test/link/wasm/ldexp/build.zig

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const std = @import("std");
2+
3+
pub fn build(b: *std.Build) void {
4+
const test_step = b.step("test", "Test it");
5+
b.default_step = test_step;
6+
7+
// Ensure ldexp symbols are available in Release modes
8+
// Regression test for https://github.com/ziglang/zig/issues/23358
9+
add(b, test_step, .ReleaseSafe);
10+
add(b, test_step, .ReleaseFast);
11+
add(b, test_step, .ReleaseSmall);
12+
// Also verify Debug still works
13+
add(b, test_step, .Debug);
14+
}
15+
16+
fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
17+
const exe = b.addExecutable(.{
18+
.name = "ldexp_test",
19+
.root_module = b.createModule(.{
20+
.root_source_file = b.path("lib.zig"),
21+
.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
22+
.optimize = optimize,
23+
}),
24+
});
25+
26+
exe.entry = .disabled;
27+
exe.rdynamic = true;
28+
29+
exe.root_module.export_symbol_names = &.{ "use_double", "use_float", "use_long" };
30+
31+
b.installArtifact(exe);
32+
33+
test_step.dependOn(&exe.step);
34+
}

test/link/wasm/ldexp/lib.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Test that LLVM's exp2 optimization requiring ldexp symbols works correctly.
2+
// In Release modes, LLVM's LibCallSimplifier converts exp2 calls to ldexp
3+
// when the input is an integer converted to float.
4+
// See https://github.com/ziglang/zig/issues/23358
5+
6+
export fn use_double(f: i32) f64 {
7+
return @exp2(@as(f64, @floatFromInt(f)));
8+
}
9+
10+
export fn use_float(f: i32) f32 {
11+
return @exp2(@as(f32, @floatFromInt(f)));
12+
}
13+
14+
export fn use_long(f: i32) f128 {
15+
return @exp2(@as(f128, @floatFromInt(f)));
16+
}

0 commit comments

Comments
 (0)