master
1//! Implementation of ARM specific builtins for Run-time ABI
2//! This file includes all ARM-only functions.
3const std = @import("std");
4const builtin = @import("builtin");
5const target = builtin.target;
6const arch = builtin.cpu.arch;
7const common = @import("common.zig");
8
9pub const panic = common.panic;
10
11comptime {
12 if (!builtin.is_test) {
13 if (arch.isArm()) {
14 @export(&__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = common.linkage, .visibility = common.visibility });
15 @export(&__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = common.linkage, .visibility = common.visibility });
16 @export(&__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = common.linkage, .visibility = common.visibility });
17
18 if (common.want_windows_arm_abi) {
19 @export(&__aeabi_ldivmod, .{ .name = "__rt_sdiv64", .linkage = common.linkage, .visibility = common.visibility });
20 @export(&__aeabi_uldivmod, .{ .name = "__rt_udiv64", .linkage = common.linkage, .visibility = common.visibility });
21 @export(&__aeabi_idivmod, .{ .name = "__rt_sdiv", .linkage = common.linkage, .visibility = common.visibility });
22 @export(&__aeabi_uidivmod, .{ .name = "__rt_udiv", .linkage = common.linkage, .visibility = common.visibility });
23 }
24 @export(&__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = common.linkage, .visibility = common.visibility });
25 @export(&__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = common.linkage, .visibility = common.visibility });
26 @export(&__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = common.linkage, .visibility = common.visibility });
27 @export(&__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = common.linkage, .visibility = common.visibility });
28
29 @export(&__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = common.linkage, .visibility = common.visibility });
30 @export(&__aeabi_memcpy4, .{ .name = "__aeabi_memcpy4", .linkage = common.linkage, .visibility = common.visibility });
31 @export(&__aeabi_memcpy8, .{ .name = "__aeabi_memcpy8", .linkage = common.linkage, .visibility = common.visibility });
32
33 @export(&__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = common.linkage, .visibility = common.visibility });
34 @export(&__aeabi_memmove4, .{ .name = "__aeabi_memmove4", .linkage = common.linkage, .visibility = common.visibility });
35 @export(&__aeabi_memmove8, .{ .name = "__aeabi_memmove8", .linkage = common.linkage, .visibility = common.visibility });
36
37 @export(&__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = common.linkage, .visibility = common.visibility });
38 @export(&__aeabi_memset4, .{ .name = "__aeabi_memset4", .linkage = common.linkage, .visibility = common.visibility });
39 @export(&__aeabi_memset8, .{ .name = "__aeabi_memset8", .linkage = common.linkage, .visibility = common.visibility });
40
41 @export(&__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = common.linkage, .visibility = common.visibility });
42 @export(&__aeabi_memclr4, .{ .name = "__aeabi_memclr4", .linkage = common.linkage, .visibility = common.visibility });
43 @export(&__aeabi_memclr8, .{ .name = "__aeabi_memclr8", .linkage = common.linkage, .visibility = common.visibility });
44
45 if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
46 @export(&__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = common.linkage, .visibility = common.visibility });
47 }
48
49 // floating-point helper functions (single+double-precision reverse subtraction, y – x), see subdf3.zig
50 @export(&__aeabi_frsub, .{ .name = "__aeabi_frsub", .linkage = common.linkage, .visibility = common.visibility });
51 @export(&__aeabi_drsub, .{ .name = "__aeabi_drsub", .linkage = common.linkage, .visibility = common.visibility });
52 }
53 }
54}
55
56const __divmodsi4 = @import("int.zig").__divmodsi4;
57const __udivmodsi4 = @import("int.zig").__udivmodsi4;
58const __divmoddi4 = @import("int.zig").__divmoddi4;
59const __udivmoddi4 = @import("int.zig").__udivmoddi4;
60
61extern fn memset(dest: ?[*]u8, c: i32, n: usize) ?[*]u8;
62extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8;
63extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8;
64
65pub fn __aeabi_memcpy(dest: [*]u8, src: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
66 @setRuntimeSafety(false);
67 _ = memcpy(dest, src, n);
68}
69pub fn __aeabi_memcpy4(dest: [*]u8, src: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
70 @setRuntimeSafety(false);
71 _ = memcpy(dest, src, n);
72}
73pub fn __aeabi_memcpy8(dest: [*]u8, src: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
74 @setRuntimeSafety(false);
75 _ = memcpy(dest, src, n);
76}
77
78pub fn __aeabi_memmove(dest: [*]u8, src: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
79 @setRuntimeSafety(false);
80 _ = memmove(dest, src, n);
81}
82pub fn __aeabi_memmove4(dest: [*]u8, src: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
83 @setRuntimeSafety(false);
84 _ = memmove(dest, src, n);
85}
86pub fn __aeabi_memmove8(dest: [*]u8, src: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
87 @setRuntimeSafety(false);
88 _ = memmove(dest, src, n);
89}
90
91pub fn __aeabi_memset(dest: [*]u8, n: usize, c: i32) callconv(.{ .arm_aapcs = .{} }) void {
92 @setRuntimeSafety(false);
93 // This is dentical to the standard `memset` definition but with the last
94 // two arguments swapped
95 _ = memset(dest, c, n);
96}
97pub fn __aeabi_memset4(dest: [*]u8, n: usize, c: i32) callconv(.{ .arm_aapcs = .{} }) void {
98 @setRuntimeSafety(false);
99 _ = memset(dest, c, n);
100}
101pub fn __aeabi_memset8(dest: [*]u8, n: usize, c: i32) callconv(.{ .arm_aapcs = .{} }) void {
102 @setRuntimeSafety(false);
103 _ = memset(dest, c, n);
104}
105
106pub fn __aeabi_memclr(dest: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
107 @setRuntimeSafety(false);
108 _ = memset(dest, 0, n);
109}
110pub fn __aeabi_memclr4(dest: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
111 @setRuntimeSafety(false);
112 _ = memset(dest, 0, n);
113}
114pub fn __aeabi_memclr8(dest: [*]u8, n: usize) callconv(.{ .arm_aapcs = .{} }) void {
115 @setRuntimeSafety(false);
116 _ = memset(dest, 0, n);
117}
118
119// Dummy functions to avoid errors during the linking phase
120pub fn __aeabi_unwind_cpp_pr0() callconv(.{ .arm_aapcs = .{} }) void {}
121pub fn __aeabi_unwind_cpp_pr1() callconv(.{ .arm_aapcs = .{} }) void {}
122pub fn __aeabi_unwind_cpp_pr2() callconv(.{ .arm_aapcs = .{} }) void {}
123
124// This function can only clobber r0 according to the ABI
125pub fn __aeabi_read_tp() callconv(.naked) void {
126 @setRuntimeSafety(false);
127 asm volatile (
128 \\ mrc p15, 0, r0, c13, c0, 3
129 \\ bx lr
130 );
131 unreachable;
132}
133
134// The following functions are wrapped in an asm block to ensure the required
135// calling convention is always respected
136
137pub fn __aeabi_uidivmod() callconv(.naked) void {
138 @setRuntimeSafety(false);
139 // Divide r0 by r1; the quotient goes in r0, the remainder in r1
140 asm volatile (
141 \\ push {lr}
142 \\ sub sp, #4
143 \\ mov r2, sp
144 \\ bl %[__udivmodsi4]
145 \\ ldr r1, [sp]
146 \\ add sp, #4
147 \\ pop {pc}
148 :
149 : [__udivmodsi4] "X" (&__udivmodsi4),
150 : .{ .memory = true });
151 unreachable;
152}
153
154pub fn __aeabi_uldivmod() callconv(.naked) void {
155 @setRuntimeSafety(false);
156 // Divide r1:r0 by r3:r2; the quotient goes in r1:r0, the remainder in r3:r2
157 asm volatile (
158 \\ push {r4, lr}
159 \\ sub sp, #16
160 \\ add r4, sp, #8
161 \\ str r4, [sp]
162 \\ bl %[__udivmoddi4]
163 \\ ldr r2, [sp, #8]
164 \\ ldr r3, [sp, #12]
165 \\ add sp, #16
166 \\ pop {r4, pc}
167 :
168 : [__udivmoddi4] "X" (&__udivmoddi4),
169 : .{ .memory = true });
170 unreachable;
171}
172
173pub fn __aeabi_idivmod() callconv(.naked) void {
174 @setRuntimeSafety(false);
175 // Divide r0 by r1; the quotient goes in r0, the remainder in r1
176 asm volatile (
177 \\ push {lr}
178 \\ sub sp, #4
179 \\ mov r2, sp
180 \\ bl %[__divmodsi4]
181 \\ ldr r1, [sp]
182 \\ add sp, #4
183 \\ pop {pc}
184 :
185 : [__divmodsi4] "X" (&__divmodsi4),
186 : .{ .memory = true });
187 unreachable;
188}
189
190pub fn __aeabi_ldivmod() callconv(.naked) void {
191 @setRuntimeSafety(false);
192 // Divide r1:r0 by r3:r2; the quotient goes in r1:r0, the remainder in r3:r2
193 asm volatile (
194 \\ push {r4, lr}
195 \\ sub sp, #16
196 \\ add r4, sp, #8
197 \\ str r4, [sp]
198 \\ bl %[__divmoddi4]
199 \\ ldr r2, [sp, #8]
200 \\ ldr r3, [sp, #12]
201 \\ add sp, #16
202 \\ pop {r4, pc}
203 :
204 : [__divmoddi4] "X" (&__divmoddi4),
205 : .{ .memory = true });
206 unreachable;
207}
208
209// Float Arithmetic
210
211fn __aeabi_frsub(a: f32, b: f32) callconv(.{ .arm_aapcs = .{} }) f32 {
212 const neg_a: f32 = @bitCast(@as(u32, @bitCast(a)) ^ (@as(u32, 1) << 31));
213 return b + neg_a;
214}
215
216fn __aeabi_drsub(a: f64, b: f64) callconv(.{ .arm_aapcs = .{} }) f64 {
217 const neg_a: f64 = @bitCast(@as(u64, @bitCast(a)) ^ (@as(u64, 1) << 63));
218 return b + neg_a;
219}
220
221test "__aeabi_frsub" {
222 if (!builtin.cpu.arch.isArm() or builtin.cpu.arch.isThumb()) return error.SkipZigTest;
223 const inf32 = std.math.inf(f32);
224 const maxf32 = std.math.floatMax(f32);
225 const frsub_data = [_][3]f32{
226 [_]f32{ 0.0, 0.0, -0.0 },
227 [_]f32{ 0.0, -0.0, -0.0 },
228 [_]f32{ -0.0, 0.0, 0.0 },
229 [_]f32{ -0.0, -0.0, -0.0 },
230 [_]f32{ 0.0, 1.0, 1.0 },
231 [_]f32{ 1.0, 0.0, -1.0 },
232 [_]f32{ 1.0, 1.0, 0.0 },
233 [_]f32{ 1234.56789, 9876.54321, 8641.97532 },
234 [_]f32{ 9876.54321, 1234.56789, -8641.97532 },
235 [_]f32{ -8641.97532, 1234.56789, 9876.54321 },
236 [_]f32{ 8641.97532, 9876.54321, 1234.56789 },
237 [_]f32{ -maxf32, -maxf32, 0.0 },
238 [_]f32{ maxf32, maxf32, 0.0 },
239 [_]f32{ maxf32, -maxf32, -inf32 },
240 [_]f32{ -maxf32, maxf32, inf32 },
241 };
242 for (frsub_data) |data| {
243 try std.testing.expectApproxEqAbs(data[2], __aeabi_frsub(data[0], data[1]), 0.001);
244 }
245}
246
247test "__aeabi_drsub" {
248 if (!builtin.cpu.arch.isArm() or builtin.cpu.arch.isThumb()) return error.SkipZigTest;
249 if (builtin.cpu.arch == .armeb and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/22061
250 const inf64 = std.math.inf(f64);
251 const maxf64 = std.math.floatMax(f64);
252 const frsub_data = [_][3]f64{
253 [_]f64{ 0.0, 0.0, -0.0 },
254 [_]f64{ 0.0, -0.0, -0.0 },
255 [_]f64{ -0.0, 0.0, 0.0 },
256 [_]f64{ -0.0, -0.0, -0.0 },
257 [_]f64{ 0.0, 1.0, 1.0 },
258 [_]f64{ 1.0, 0.0, -1.0 },
259 [_]f64{ 1.0, 1.0, 0.0 },
260 [_]f64{ 1234.56789, 9876.54321, 8641.97532 },
261 [_]f64{ 9876.54321, 1234.56789, -8641.97532 },
262 [_]f64{ -8641.97532, 1234.56789, 9876.54321 },
263 [_]f64{ 8641.97532, 9876.54321, 1234.56789 },
264 [_]f64{ -maxf64, -maxf64, 0.0 },
265 [_]f64{ maxf64, maxf64, 0.0 },
266 [_]f64{ maxf64, -maxf64, -inf64 },
267 [_]f64{ -maxf64, maxf64, inf64 },
268 };
269 for (frsub_data) |data| {
270 try std.testing.expectApproxEqAbs(data[2], __aeabi_drsub(data[0], data[1]), 0.000001);
271 }
272}