Skip to content

Commit d563cf7

Browse files
committed
Port clzsi2 from compiler_rt, required for using std.fmt.format on some ARM architecture.
1 parent bac2773 commit d563cf7

File tree

3 files changed

+415
-1
lines changed

3 files changed

+415
-1
lines changed

lib/std/special/compiler_rt.zig

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ comptime {
146146
@export(@import("compiler_rt/negXf2.zig").__negsf2, .{ .name = "__negsf2", .linkage = linkage });
147147
@export(@import("compiler_rt/negXf2.zig").__negdf2, .{ .name = "__negdf2", .linkage = linkage });
148148

149+
@export(@import("compiler_rt/clzsi2.zig").__clzsi2, .{ .name = "__clzsi2", .linkage = linkage });
150+
149151
if (is_arm_arch and !is_arm_64 and !is_test) {
150152
@export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage });
151153
@export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage });
@@ -177,7 +179,9 @@ comptime {
177179
@export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage });
178180
@export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage });
179181

180-
@export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage });
182+
if (builtin.os == .linux) {
183+
@export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage });
184+
}
181185

182186
@export(@import("compiler_rt/extendXfYf2.zig").__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage });
183187
@export(@import("compiler_rt/floatsiXf.zig").__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage });
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Ported from:
2+
//
3+
// https://github.com/llvm-mirror/compiler-rt/blob/f0745e8476f069296a7c71accedd061dce4cdf79/lib/builtins/clzsi2.c
4+
// https://github.com/llvm-mirror/compiler-rt/blob/f0745e8476f069296a7c71accedd061dce4cdf79/lib/builtins/arm/clzsi2.S
5+
const builtin = @import("builtin");
6+
7+
// Precondition: a != 0
8+
fn __clzsi2_generic(a: i32) callconv(.C) i32 {
9+
@setRuntimeSafety(builtin.is_test);
10+
11+
var x = @bitCast(u32, a);
12+
var n: i32 = 32;
13+
14+
// Count first bit set using binary search, from Hacker's Delight
15+
var y: u32 = 0;
16+
inline for ([_]i32{ 16, 8, 4, 2, 1 }) |shift| {
17+
y = x >> shift;
18+
if (y != 0) {
19+
n = n - shift;
20+
x = y;
21+
}
22+
}
23+
24+
return n - @bitCast(i32, x);
25+
}
26+
27+
fn __clzsi2_arm_clz(a: i32) callconv(.Naked) noreturn {
28+
asm volatile (
29+
\\ clz r0,r0
30+
\\ bx lr
31+
);
32+
unreachable;
33+
}
34+
35+
fn __clzsi2_arm32(a: i32) callconv(.Naked) noreturn {
36+
asm volatile (
37+
\\ // Assumption: n != 0
38+
\\ // r0: n
39+
\\ // r1: count of leading zeros in n + 1
40+
\\ // r2: scratch register for shifted r0
41+
\\ mov r1, #1
42+
\\
43+
\\ // Basic block:
44+
\\ // if ((r0 >> SHIFT) == 0)
45+
\\ // r1 += SHIFT;
46+
\\ // else
47+
\\ // r0 >>= SHIFT;
48+
\\ // for descending powers of two as SHIFT.
49+
\\ lsrs r2, r0, #16
50+
\\ movne r0, r2
51+
\\ addeq r1, #16
52+
\\
53+
\\ lsrs r2, r0, #8
54+
\\ movne r0, r2
55+
\\ addeq r1, #8
56+
\\
57+
\\ lsrs r2, r0, #4
58+
\\ movne r0, r2
59+
\\ addeq r1, #4
60+
\\
61+
\\ lsrs r2, r0, #2
62+
\\ movne r0, r2
63+
\\ addeq r1, #2
64+
\\
65+
\\ // The basic block invariants at this point are (r0 >> 2) == 0 and
66+
\\ // r0 != 0. This means 1 <= r0 <= 3 and 0 <= (r0 >> 1) <= 1.
67+
\\ //
68+
\\ // r0 | (r0 >> 1) == 0 | (r0 >> 1) == 1 | -(r0 >> 1) | 1 - (r0 >> 1)f
69+
\\ // ---+----------------+----------------+------------+--------------
70+
\\ // 1 | 1 | 0 | 0 | 1
71+
\\ // 2 | 0 | 1 | -1 | 0
72+
\\ // 3 | 0 | 1 | -1 | 0
73+
\\ //
74+
\\ // The r1's initial value of 1 compensates for the 1 here.
75+
\\ sub r0, r1, r0, lsr #1
76+
\\ bx lr
77+
);
78+
unreachable;
79+
}
80+
81+
const can_use_arm_clz = switch (builtin.arch) {
82+
.arm, .armeb => |sub_arch| switch (sub_arch) {
83+
.v4t => false,
84+
.v6m => false,
85+
else => true,
86+
},
87+
.thumb, .thumbeb => |sub_arch| switch (sub_arch) {
88+
.v6,
89+
.v6k,
90+
.v5,
91+
.v5te,
92+
.v4t,
93+
=> false,
94+
else => true,
95+
},
96+
else => false,
97+
};
98+
99+
const is_arm32_no_thumb = switch (builtin.arch) {
100+
builtin.Arch.arm,
101+
builtin.Arch.armeb,
102+
=> true,
103+
else => false,
104+
};
105+
106+
pub const __clzsi2 = blk: {
107+
if (comptime can_use_arm_clz) {
108+
break :blk __clzsi2_arm_clz;
109+
} else if (comptime is_arm32_no_thumb) {
110+
break :blk __clzsi2_arm32;
111+
} else {
112+
break :blk __clzsi2_generic;
113+
}
114+
};
115+
116+
test "test clzsi2" {
117+
_ = @import("clzsi2_test.zig");
118+
}

0 commit comments

Comments
 (0)