| 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); |
| 3 | const Log2Int = std.math.Log2Int; |
| 4 | const compiler_rt = @import("../compiler_rt.zig"); |
| 5 | const symbol = compiler_rt.symbol; |
| 6 | |
| 7 | comptime { |
| 8 | // symbol compatibility with libgcc |
| 9 | symbol(&__ashlsi3, "__ashlsi3"); |
| 10 | symbol(&__ashrsi3, "__ashrsi3"); |
| 11 | symbol(&__lshrsi3, "__lshrsi3"); |
| 12 | |
| 13 | symbol(&__ashlti3, "__ashlti3"); |
| 14 | symbol(&__ashrti3, "__ashrti3"); |
| 15 | symbol(&__lshrti3, "__lshrti3"); |
| 16 | |
| 17 | if (compiler_rt.want_aeabi) { |
| 18 | symbol(&__aeabi_llsl, "__aeabi_llsl"); |
| 19 | symbol(&__aeabi_lasr, "__aeabi_lasr"); |
| 20 | symbol(&__aeabi_llsr, "__aeabi_llsr"); |
| 21 | } else { |
| 22 | symbol(&__ashldi3, "__ashldi3"); |
| 23 | symbol(&__ashrdi3, "__ashrdi3"); |
| 24 | symbol(&__lshrdi3, "__lshrdi3"); |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | // Arithmetic shift left: shift in 0 from right to left |
| 29 | // Precondition: 0 <= b < bits_in_dword |
| 30 | inline fn ashlXi3(comptime T: type, a: T, b: i32) T { |
| 31 | const word_t = compiler_rt.HalveInt(T, false); |
| 32 | |
| 33 | const input = word_t{ .all = a }; |
| 34 | var output: word_t = undefined; |
| 35 | |
| 36 | if (b >= word_t.bits) { |
| 37 | output.s.low = 0; |
| 38 | output.s.high = input.s.low << @intCast(b - word_t.bits); |
| 39 | } else if (b == 0) { |
| 40 | return a; |
| 41 | } else { |
| 42 | output.s.low = input.s.low << @intCast(b); |
| 43 | output.s.high = input.s.high << @intCast(b); |
| 44 | output.s.high |= input.s.low >> @intCast(word_t.bits - b); |
| 45 | } |
| 46 | |
| 47 | return output.all; |
| 48 | } |
| 49 | |
| 50 | // Arithmetic shift right: shift in 1 from left to right |
| 51 | // Precondition: 0 <= b < T.bit_count |
| 52 | inline fn ashrXi3(comptime T: type, a: T, b: i32) T { |
| 53 | const word_t = compiler_rt.HalveInt(T, true); |
| 54 | |
| 55 | const input = word_t{ .all = a }; |
| 56 | var output: word_t = undefined; |
| 57 | |
| 58 | if (b >= word_t.bits) { |
| 59 | output.s.high = input.s.high >> (word_t.bits - 1); |
| 60 | output.s.low = input.s.high >> @intCast(b - word_t.bits); |
| 61 | } else if (b == 0) { |
| 62 | return a; |
| 63 | } else { |
| 64 | output.s.high = input.s.high >> @intCast(b); |
| 65 | output.s.low = input.s.high << @intCast(word_t.bits - b); |
| 66 | // Avoid sign-extension here |
| 67 | output.s.low |= @bitCast(@as(word_t.HalfTU, @bitCast(input.s.low)) >> @intCast(b)); |
| 68 | } |
| 69 | |
| 70 | return output.all; |
| 71 | } |
| 72 | |
| 73 | // Logical shift right: shift in 0 from left to right |
| 74 | // Precondition: 0 <= b < T.bit_count |
| 75 | inline fn lshrXi3(comptime T: type, a: T, b: i32) T { |
| 76 | const word_t = compiler_rt.HalveInt(T, false); |
| 77 | |
| 78 | const input = word_t{ .all = a }; |
| 79 | var output: word_t = undefined; |
| 80 | |
| 81 | if (b >= word_t.bits) { |
| 82 | output.s.high = 0; |
| 83 | output.s.low = input.s.high >> @intCast(b - word_t.bits); |
| 84 | } else if (b == 0) { |
| 85 | return a; |
| 86 | } else { |
| 87 | output.s.high = input.s.high >> @intCast(b); |
| 88 | output.s.low = input.s.high << @intCast(word_t.bits - b); |
| 89 | output.s.low |= input.s.low >> @intCast(b); |
| 90 | } |
| 91 | |
| 92 | return output.all; |
| 93 | } |
| 94 | |
| 95 | pub fn __ashlsi3(a: i32, b: i32) callconv(.c) i32 { |
| 96 | return ashlXi3(i32, a, b); |
| 97 | } |
| 98 | |
| 99 | pub fn __ashrsi3(a: i32, b: i32) callconv(.c) i32 { |
| 100 | return ashrXi3(i32, a, b); |
| 101 | } |
| 102 | |
| 103 | pub fn __lshrsi3(a: i32, b: i32) callconv(.c) i32 { |
| 104 | return lshrXi3(i32, a, b); |
| 105 | } |
| 106 | |
| 107 | pub fn __ashldi3(a: i64, b: i32) callconv(.c) i64 { |
| 108 | return ashlXi3(i64, a, b); |
| 109 | } |
| 110 | fn __aeabi_llsl(a: i64, b: i32) callconv(.{ .arm_aapcs = .{} }) i64 { |
| 111 | return ashlXi3(i64, a, b); |
| 112 | } |
| 113 | |
| 114 | pub fn __ashlti3(a: i128, b: i32) callconv(.c) i128 { |
| 115 | return ashlXi3(i128, a, b); |
| 116 | } |
| 117 | |
| 118 | pub fn __ashrdi3(a: i64, b: i32) callconv(.c) i64 { |
| 119 | return ashrXi3(i64, a, b); |
| 120 | } |
| 121 | fn __aeabi_lasr(a: i64, b: i32) callconv(.{ .arm_aapcs = .{} }) i64 { |
| 122 | return ashrXi3(i64, a, b); |
| 123 | } |
| 124 | |
| 125 | pub fn __ashrti3(a: i128, b: i32) callconv(.c) i128 { |
| 126 | return ashrXi3(i128, a, b); |
| 127 | } |
| 128 | |
| 129 | pub fn __lshrdi3(a: i64, b: i32) callconv(.c) i64 { |
| 130 | return lshrXi3(i64, a, b); |
| 131 | } |
| 132 | fn __aeabi_llsr(a: i64, b: i32) callconv(.{ .arm_aapcs = .{} }) i64 { |
| 133 | return lshrXi3(i64, a, b); |
| 134 | } |
| 135 | |
| 136 | pub fn __lshrti3(a: i128, b: i32) callconv(.c) i128 { |
| 137 | return lshrXi3(i128, a, b); |
| 138 | } |
| 139 | |
| 140 | test { |
| 141 | _ = @import("shift_test.zig"); |
| 142 | } |