CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
slicing.py303 linesDownload Raw Back to cpython
1"""
2Implement slices and various slice computations.
3"""
4
5from itertools import zip_longest
6
7from llvmlite import ir
8from numba.core import cgutils, types, typing, utils
9from numba.core.imputils import (impl_ret_borrowed, impl_ret_new_ref,
10                                 impl_ret_untracked, iternext_impl,
11                                 lower_builtin, lower_cast, lower_constant,
12                                 lower_getattr)
13
14
15def fix_index(builder, idx, size):
16    """
17    Fix negative index by adding *size* to it.  Positive
18    indices are left untouched.
19    """
20    is_negative = builder.icmp_signed('<', idx, ir.Constant(size.type, 0))
21    wrapped_index = builder.add(idx, size)
22    return builder.select(is_negative, wrapped_index, idx)
23
24
25def fix_slice(builder, slice, size):
26    """
27    Fix *slice* start and stop to be valid (inclusive and exclusive, resp)
28    indexing bounds for a sequence of the given *size*.
29    """
30    # See PySlice_GetIndicesEx()
31    zero = ir.Constant(size.type, 0)
32    minus_one = ir.Constant(size.type, -1)
33
34    def fix_bound(bound_name, lower_repl, upper_repl):
35        bound = getattr(slice, bound_name)
36        bound = fix_index(builder, bound, size)
37        # Store value
38        setattr(slice, bound_name, bound)
39        # Still negative? => clamp to lower_repl
40        underflow = builder.icmp_signed('<', bound, zero)
41        with builder.if_then(underflow, likely=False):
42            setattr(slice, bound_name, lower_repl)
43        # Greater than size? => clamp to upper_repl
44        overflow = builder.icmp_signed('>=', bound, size)
45        with builder.if_then(overflow, likely=False):
46            setattr(slice, bound_name, upper_repl)
47
48    with builder.if_else(cgutils.is_neg_int(builder, slice.step)) as (if_neg_step, if_pos_step):
49        with if_pos_step:
50            # < 0 => 0; >= size => size
51            fix_bound('start', zero, size)
52            fix_bound('stop', zero, size)
53        with if_neg_step:
54            # < 0 => -1; >= size => size - 1
55            lower = minus_one
56            upper = builder.add(size, minus_one)
57            fix_bound('start', lower, upper)
58            fix_bound('stop', lower, upper)
59
60
61def get_slice_length(builder, slicestruct):
62    """
63    Given a slice, compute the number of indices it spans, i.e. the
64    number of iterations that for_range_slice() will execute.
65
66    Pseudo-code:
67        assert step != 0
68        if step > 0:
69            if stop <= start:
70                return 0
71            else:
72                return (stop - start - 1) // step + 1
73        else:
74            if stop >= start:
75                return 0
76            else:
77                return (stop - start + 1) // step + 1
78
79    (see PySlice_GetIndicesEx() in CPython)
80    """
81    start = slicestruct.start
82    stop = slicestruct.stop
83    step = slicestruct.step
84    one = ir.Constant(start.type, 1)
85    zero = ir.Constant(start.type, 0)
86
87    is_step_negative = cgutils.is_neg_int(builder, step)
88    delta = builder.sub(stop, start)
89
90    # Nominal case
91    pos_dividend = builder.sub(delta, one)
92    neg_dividend = builder.add(delta, one)
93    dividend  = builder.select(is_step_negative, neg_dividend, pos_dividend)
94    nominal_length = builder.add(one, builder.sdiv(dividend, step))
95
96    # Catch zero length
97    is_zero_length = builder.select(is_step_negative,
98                                    builder.icmp_signed('>=', delta, zero),
99                                    builder.icmp_signed('<=', delta, zero))
100
101    # Clamp to 0 if is_zero_length
102    return builder.select(is_zero_length, zero, nominal_length)
103
104
105def get_slice_bounds(builder, slicestruct):
106    """
107    Return the [lower, upper) indexing bounds of a slice.
108    """
109    start = slicestruct.start
110    stop = slicestruct.stop
111    zero = start.type(0)
112    one = start.type(1)
113    # This is a bit pessimal, e.g. it will return [1, 5) instead
114    # of [1, 4) for `1:5:2`
115    is_step_negative = builder.icmp_signed('<', slicestruct.step, zero)
116    lower = builder.select(is_step_negative,
117                           builder.add(stop, one), start)
118    upper = builder.select(is_step_negative,
119                           builder.add(start, one), stop)
120    return lower, upper
121
122
123def fix_stride(builder, slice, stride):
124    """
125    Fix the given stride for the slice's step.
126    """
127    return builder.mul(slice.step, stride)
128
129def guard_invalid_slice(context, builder, typ, slicestruct):
130    """
131    Guard against *slicestruct* having a zero step (and raise ValueError).
132    """
133    if typ.has_step:
134        cgutils.guard_null(context, builder, slicestruct.step,
135                           (ValueError, "slice step cannot be zero"))
136
137
138def get_defaults(context):
139    """
140    Get the default values for a slice's members:
141    (start for positive step, start for negative step,
142     stop for positive step, stop for negative step, step)
143    """
144    maxint = (1 << (context.address_size - 1)) - 1
145    return (0, maxint, maxint, - maxint - 1, 1)
146
147
148#---------------------------------------------------------------------------
149# The slice structure
150
151@lower_builtin(slice, types.VarArg(types.Any))
152def slice_constructor_impl(context, builder, sig, args):
153    (
154        default_start_pos,
155        default_start_neg,
156        default_stop_pos,
157        default_stop_neg,
158        default_step,
159    ) = [context.get_constant(types.intp, x) for x in get_defaults(context)]
160
161    slice_args = [None] * 3
162
163    # Fetch non-None arguments
164    if len(args) == 1 and sig.args[0] is not types.none:
165        slice_args[1] = args[0]
166    else:
167        for i, (ty, val) in enumerate(zip(sig.args, args)):
168            if ty is not types.none:
169                slice_args[i] = val
170
171    # Fill omitted arguments
172    def get_arg_value(i, default):
173        val = slice_args[i]
174        if val is None:
175            return default
176        else:
177            return val
178
179    step = get_arg_value(2, default_step)
180    is_step_negative = builder.icmp_signed('<', step,
181                                           context.get_constant(types.intp, 0))
182    default_stop = builder.select(is_step_negative,
183                                  default_stop_neg, default_stop_pos)
184    default_start = builder.select(is_step_negative,
185                                   default_start_neg, default_start_pos)
186    stop = get_arg_value(1, default_stop)
187    start = get_arg_value(0, default_start)
188
189    ty = sig.return_type
190    sli = context.make_helper(builder, sig.return_type)
191    sli.start = start
192    sli.stop = stop
193    sli.step = step
194
195    res = sli._getvalue()
196    return impl_ret_untracked(context, builder, sig.return_type, res)
197
198
199@lower_getattr(types.SliceType, "start")
200def slice_start_impl(context, builder, typ, value):
201    sli = context.make_helper(builder, typ, value)
202    return sli.start
203
204@lower_getattr(types.SliceType, "stop")
205def slice_stop_impl(context, builder, typ, value):
206    sli = context.make_helper(builder, typ, value)
207    return sli.stop
208
209@lower_getattr(types.SliceType, "step")
210def slice_step_impl(context, builder, typ, value):
211    if typ.has_step:
212        sli = context.make_helper(builder, typ, value)
213        return sli.step
214    else:
215        return context.get_constant(types.intp, 1)
216
217
218@lower_builtin("slice.indices", types.SliceType, types.Integer)
219def slice_indices(context, builder, sig, args):
220    length = args[1]
221    sli = context.make_helper(builder, sig.args[0], args[0])
222
223    with builder.if_then(cgutils.is_neg_int(builder, length), likely=False):
224        context.call_conv.return_user_exc(
225            builder, ValueError,
226            ("length should not be negative",)
227        )
228    with builder.if_then(cgutils.is_scalar_zero(builder, sli.step), likely=False):
229        context.call_conv.return_user_exc(
230            builder, ValueError,
231            ("slice step cannot be zero",)
232        )
233
234    fix_slice(builder, sli, length)
235
236    return context.make_tuple(
237        builder,
238        sig.return_type,
239        (sli.start, sli.stop, sli.step)
240    )
241
242
243def make_slice_from_constant(context, builder, ty, pyval):
244    sli = context.make_helper(builder, ty)
245    lty = context.get_value_type(types.intp)
246
247    (
248        default_start_pos,
249        default_start_neg,
250        default_stop_pos,
251        default_stop_neg,
252        default_step,
253    ) = [context.get_constant(types.intp, x) for x in get_defaults(context)]
254
255    step = pyval.step
256    if step is None:
257        step_is_neg = False
258        step = default_step
259    else:
260        step_is_neg = step < 0
261        step = lty(step)
262
263    start = pyval.start
264    if start is None:
265        if step_is_neg:
266            start = default_start_neg
267        else:
268            start = default_start_pos
269    else:
270        start = lty(start)
271
272    stop = pyval.stop
273    if stop is None:
274        if step_is_neg:
275            stop = default_stop_neg
276        else:
277            stop = default_stop_pos
278    else:
279        stop = lty(stop)
280
281    sli.start = start
282    sli.stop = stop
283    sli.step = step
284
285    return sli._getvalue()
286
287
288@lower_constant(types.SliceType)
289def constant_slice(context, builder, ty, pyval):
290    if isinstance(ty, types.Literal):
291        typ = ty.literal_type
292    else:
293        typ = ty
294
295    return make_slice_from_constant(context, builder, typ, pyval)
296
297
298@lower_cast(types.misc.SliceLiteral, types.SliceType)
299def cast_from_literal(context, builder, fromty, toty, val):
300    return make_slice_from_constant(
301        context, builder, toty, fromty.literal_value,
302    )
303 
Aluode/PerceptionLabPortable · CoolFace