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