aboutsummaryrefslogtreecommitdiff
path: root/lib/std/special/compiler_rt/shift.zig
blob: f811c5124d66dfc71197daa8f63c6e4cfe7d20ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
const std = @import("std");
const builtin = std.builtin;
const Log2Int = std.math.Log2Int;

fn Dwords(comptime T: type, comptime signed_half: bool) type {
    return extern union {
        pub const HalfTU = std.meta.Int(false, @divExact(T.bit_count, 2));
        pub const HalfTS = std.meta.Int(true, @divExact(T.bit_count, 2));
        pub const HalfT = if (signed_half) HalfTS else HalfTU;

        all: T,
        s: if (builtin.endian == .Little)
            struct { low: HalfT, high: HalfT }
        else
            struct { high: HalfT, low: HalfT },
    };
}

// Arithmetic shift left
// Precondition: 0 <= b < bits_in_dword
pub fn ashlXi3(comptime T: type, a: T, b: i32) T {
    const dwords = Dwords(T, false);
    const S = Log2Int(dwords.HalfT);

    const input = dwords{ .all = a };
    var output: dwords = undefined;

    if (b >= dwords.HalfT.bit_count) {
        output.s.low = 0;
        output.s.high = input.s.low << @intCast(S, b - dwords.HalfT.bit_count);
    } else if (b == 0) {
        return a;
    } else {
        output.s.low = input.s.low << @intCast(S, b);
        output.s.high = input.s.high << @intCast(S, b);
        output.s.high |= input.s.low >> @intCast(S, dwords.HalfT.bit_count - b);
    }

    return output.all;
}

// Arithmetic shift right
// Precondition: 0 <= b < T.bit_count
pub fn ashrXi3(comptime T: type, a: T, b: i32) T {
    const dwords = Dwords(T, true);
    const S = Log2Int(dwords.HalfT);

    const input = dwords{ .all = a };
    var output: dwords = undefined;

    if (b >= dwords.HalfT.bit_count) {
        output.s.high = input.s.high >> (dwords.HalfT.bit_count - 1);
        output.s.low = input.s.high >> @intCast(S, b - dwords.HalfT.bit_count);
    } else if (b == 0) {
        return a;
    } else {
        output.s.high = input.s.high >> @intCast(S, b);
        output.s.low = input.s.high << @intCast(S, dwords.HalfT.bit_count - b);
        // Avoid sign-extension here
        output.s.low |= @bitCast(
            dwords.HalfT,
            @bitCast(dwords.HalfTU, input.s.low) >> @intCast(S, b),
        );
    }

    return output.all;
}

// Logical shift right
// Precondition: 0 <= b < T.bit_count
pub fn lshrXi3(comptime T: type, a: T, b: i32) T {
    const dwords = Dwords(T, false);
    const S = Log2Int(dwords.HalfT);

    const input = dwords{ .all = a };
    var output: dwords = undefined;

    if (b >= dwords.HalfT.bit_count) {
        output.s.high = 0;
        output.s.low = input.s.high >> @intCast(S, b - dwords.HalfT.bit_count);
    } else if (b == 0) {
        return a;
    } else {
        output.s.high = input.s.high >> @intCast(S, b);
        output.s.low = input.s.high << @intCast(S, dwords.HalfT.bit_count - b);
        output.s.low |= input.s.low >> @intCast(S, b);
    }

    return output.all;
}

pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 {
    return @call(.{ .modifier = .always_inline }, ashlXi3, .{ i64, a, b });
}
pub fn __ashlti3(a: i128, b: i32) callconv(.C) i128 {
    return @call(.{ .modifier = .always_inline }, ashlXi3, .{ i128, a, b });
}
pub fn __ashrdi3(a: i64, b: i32) callconv(.C) i64 {
    return @call(.{ .modifier = .always_inline }, ashrXi3, .{ i64, a, b });
}
pub fn __ashrti3(a: i128, b: i32) callconv(.C) i128 {
    return @call(.{ .modifier = .always_inline }, ashrXi3, .{ i128, a, b });
}
pub fn __lshrdi3(a: i64, b: i32) callconv(.C) i64 {
    return @call(.{ .modifier = .always_inline }, lshrXi3, .{ i64, a, b });
}
pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 {
    return @call(.{ .modifier = .always_inline }, lshrXi3, .{ i128, a, b });
}

pub fn __aeabi_llsl(a: i64, b: i32) callconv(.AAPCS) i64 {
    return __ashldi3(a, b);
}
pub fn __aeabi_lasr(a: i64, b: i32) callconv(.AAPCS) i64 {
    return __ashrdi3(a, b);
}
pub fn __aeabi_llsr(a: i64, b: i32) callconv(.AAPCS) i64 {
    return __lshrdi3(a, b);
}

test "" {
    _ = @import("ashrdi3_test.zig");
    _ = @import("ashrti3_test.zig");

    _ = @import("ashldi3_test.zig");
    _ = @import("ashlti3_test.zig");

    _ = @import("lshrdi3_test.zig");
    _ = @import("lshrti3_test.zig");
}