zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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 }