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