aboutsummaryrefslogtreecommitdiff
path: root/lib/compiler_rt/shift.zig
blob: e38c3973bc2b09130f962bb85152800f7471b1f0 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
const std = @import("std");
const builtin = @import("builtin");
const Log2Int = std.math.Log2Int;
const native_endian = builtin.cpu.arch.endian();
const common = @import("common.zig");

pub const panic = common.panic;

comptime {
    @export(__ashlti3, .{ .name = "__ashlti3", .linkage = common.linkage });
    @export(__ashrti3, .{ .name = "__ashrti3", .linkage = common.linkage });
    @export(__lshrti3, .{ .name = "__lshrti3", .linkage = common.linkage });

    if (common.want_aeabi) {
        @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = common.linkage });
        @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = common.linkage });
        @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = common.linkage });
    } else {
        @export(__ashldi3, .{ .name = "__ashldi3", .linkage = common.linkage });
        @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = common.linkage });
        @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = common.linkage });
    }
}

fn Dwords(comptime T: type, comptime signed_half: bool) type {
    return extern union {
        const bits = @divExact(@typeInfo(T).Int.bits, 2);
        const HalfTU = std.meta.Int(.unsigned, bits);
        const HalfTS = std.meta.Int(.signed, bits);
        const HalfT = if (signed_half) HalfTS else HalfTU;

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

// Arithmetic shift left
// Precondition: 0 <= b < bits_in_dword
inline 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.bits) {
        output.s.low = 0;
        output.s.high = input.s.low << @intCast(S, b - dwords.bits);
    } 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.bits - b);
    }

    return output.all;
}

// Arithmetic shift right
// Precondition: 0 <= b < T.bit_count
inline 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.bits) {
        output.s.high = input.s.high >> (dwords.bits - 1);
        output.s.low = input.s.high >> @intCast(S, b - dwords.bits);
    } 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.bits - 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
inline 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.bits) {
        output.s.high = 0;
        output.s.low = input.s.high >> @intCast(S, b - dwords.bits);
    } 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.bits - b);
        output.s.low |= input.s.low >> @intCast(S, b);
    }

    return output.all;
}

pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 {
    return ashlXi3(i64, a, b);
}
fn __aeabi_llsl(a: i64, b: i32) callconv(.AAPCS) i64 {
    return ashlXi3(i64, a, b);
}

pub fn __ashlti3(a: i128, b: i32) callconv(.C) i128 {
    return ashlXi3(i128, a, b);
}

pub fn __ashrdi3(a: i64, b: i32) callconv(.C) i64 {
    return ashrXi3(i64, a, b);
}
fn __aeabi_lasr(a: i64, b: i32) callconv(.AAPCS) i64 {
    return ashrXi3(i64, a, b);
}

pub fn __ashrti3(a: i128, b: i32) callconv(.C) i128 {
    return ashrXi3(i128, a, b);
}

pub fn __lshrdi3(a: i64, b: i32) callconv(.C) i64 {
    return lshrXi3(i64, a, b);
}
fn __aeabi_llsr(a: i64, b: i32) callconv(.AAPCS) i64 {
    return lshrXi3(i64, a, b);
}

pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 {
    return lshrXi3(i128, 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");
}