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}