def fix_slice(builder, slice, size): """ Fix *slice* start and stop to be valid (inclusive and exclusive, resp) indexing bounds for a sequence of the given *size*. """ # See PySlice_GetIndicesEx() zero = ir.Constant(size.type, 0) minus_one = ir.Constant(size.type, -1) def fix_bound(bound_name, lower_repl, upper_repl): bound = getattr(slice, bound_name) bound = fix_index(builder, bound, size) # Store value setattr(slice, bound_name, bound) # Still negative? => clamp to lower_repl underflow = builder.icmp_signed('<', bound, zero) with builder.if_then(underflow, likely=False): setattr(slice, bound_name, lower_repl) # Greater than size? => clamp to upper_repl overflow = builder.icmp_signed('>=', bound, size) with builder.if_then(overflow, likely=False): setattr(slice, bound_name, upper_repl) with builder.if_else(cgutils.is_neg_int(builder, slice.step)) as (if_neg_step, if_pos_step): with if_pos_step: # < 0 => 0; >= size => size fix_bound('start', zero, size) fix_bound('stop', zero, size) with if_neg_step: # < 0 => -1; >= size => size - 1 lower = minus_one upper = builder.add(size, minus_one) fix_bound('start', lower, upper) fix_bound('stop', lower, upper)
def year_to_days(builder, year_val): """ Given a year *year_val* (offset to 1970), return the number of days since the 1970 epoch. """ # The algorithm below is copied from Numpy's get_datetimestruct_days() # (src/multiarray/datetime.c) ret = cgutils.alloca_once(builder, TIMEDELTA64) # First approximation days = scale_by_constant(builder, year_val, 365) # Adjust for leap years with builder.if_else(cgutils.is_neg_int(builder, year_val)) \ as (if_neg, if_pos): with if_pos: # At or after 1970: # 1968 is the closest leap year before 1970. # Exclude the current year, so add 1. from_1968 = add_constant(builder, year_val, 1) # Add one day for each 4 years p_days = builder.add(days, unscale_by_constant(builder, from_1968, 4)) # 1900 is the closest previous year divisible by 100 from_1900 = add_constant(builder, from_1968, 68) # Subtract one day for each 100 years p_days = builder.sub(p_days, unscale_by_constant(builder, from_1900, 100)) # 1600 is the closest previous year divisible by 400 from_1600 = add_constant(builder, from_1900, 300) # Add one day for each 400 years p_days = builder.add(p_days, unscale_by_constant(builder, from_1600, 400)) builder.store(p_days, ret) with if_neg: # Before 1970: # NOTE `year_val` is negative, and so will be `from_1972` and `from_2000`. # 1972 is the closest later year after 1970. # Include the current year, so subtract 2. from_1972 = add_constant(builder, year_val, -2) # Subtract one day for each 4 years (`from_1972` is negative) n_days = builder.add(days, unscale_by_constant(builder, from_1972, 4)) # 2000 is the closest later year divisible by 100 from_2000 = add_constant(builder, from_1972, -28) # Add one day for each 100 years n_days = builder.sub(n_days, unscale_by_constant(builder, from_2000, 100)) # 2000 is also the closest later year divisible by 400 # Subtract one day for each 400 years n_days = builder.add(n_days, unscale_by_constant(builder, from_2000, 400)) builder.store(n_days, ret) return builder.load(ret)
def year_to_days(builder, year_val): """ Given a year *year_val* (offset to 1970), return the number of days since the 1970 epoch. """ # The algorithm below is copied from Numpy's get_datetimestruct_days() # (src/multiarray/datetime.c) ret = cgutils.alloca_once(builder, TIMEDELTA64) # First approximation days = scale_by_constant(builder, year_val, 365) # Adjust for leap years with cgutils.ifelse(builder, cgutils.is_neg_int(builder, year_val)) \ as (if_neg, if_pos): with if_pos: # At or after 1970: # 1968 is the closest leap year before 1970. # Exclude the current year, so add 1. from_1968 = add_constant(builder, year_val, 1) # Add one day for each 4 years p_days = builder.add(days, unscale_by_constant(builder, from_1968, 4)) # 1900 is the closest previous year divisible by 100 from_1900 = add_constant(builder, from_1968, 68) # Subtract one day for each 100 years p_days = builder.sub(p_days, unscale_by_constant(builder, from_1900, 100)) # 1600 is the closest previous year divisible by 400 from_1600 = add_constant(builder, from_1900, 300) # Add one day for each 400 years p_days = builder.add(p_days, unscale_by_constant(builder, from_1600, 400)) builder.store(p_days, ret) with if_neg: # Before 1970: # NOTE `year_val` is negative, and so will be `from_1972` and `from_2000`. # 1972 is the closest later year after 1970. # Include the current year, so subtract 2. from_1972 = add_constant(builder, year_val, -2) # Subtract one day for each 4 years (`from_1972` is negative) n_days = builder.add(days, unscale_by_constant(builder, from_1972, 4)) # 2000 is the closest later year divisible by 100 from_2000 = add_constant(builder, from_1972, -28) # Add one day for each 100 years n_days = builder.sub(n_days, unscale_by_constant(builder, from_2000, 100)) # 2000 is also the closest later year divisible by 400 # Subtract one day for each 400 years n_days = builder.add(n_days, unscale_by_constant(builder, from_2000, 400)) builder.store(n_days, ret) return builder.load(ret)
def slice_indices(context, builder, sig, args): length = args[1] sli = context.make_helper(builder, sig.args[0], args[0]) with builder.if_then(cgutils.is_neg_int(builder, length), likely=False): context.call_conv.return_user_exc(builder, ValueError, ("length should not be negative", )) with builder.if_then(cgutils.is_scalar_zero(builder, sli.step), likely=False): context.call_conv.return_user_exc(builder, ValueError, ("slice step cannot be zero", )) fix_slice(builder, sli, length) return context.make_tuple(builder, sig.return_type, (sli.start, sli.stop, sli.step))
def list_mul_inplace(context, builder, sig, args): inst = ListInstance(context, builder, sig.args[0], args[0]) src_size = inst.size mult = args[1] zero = ir.Constant(mult.type, 0) mult = builder.select(cgutils.is_neg_int(builder, mult), zero, mult) nitems = builder.mul(mult, src_size) inst.resize(nitems) with cgutils.for_range_slice(builder, src_size, nitems, src_size, inc=True) as (dest_offset, _): with cgutils.for_range(builder, src_size) as loop: value = inst.getitem(loop.index) inst.setitem(builder.add(loop.index, dest_offset), value) return impl_ret_borrowed(context, builder, sig.return_type, inst.value)
def list_mul(context, builder, sig, args): src = ListInstance(context, builder, sig.args[0], args[0]) src_size = src.size mult = args[1] zero = ir.Constant(mult.type, 0) mult = builder.select(cgutils.is_neg_int(builder, mult), zero, mult) nitems = builder.mul(mult, src_size) dest = ListInstance.allocate(context, builder, sig.return_type, nitems) dest.size = nitems with cgutils.for_range_slice(builder, zero, nitems, src_size, inc=True) as (dest_offset, _): with cgutils.for_range(builder, src_size) as loop: value = src.getitem(loop.index) dest.setitem(builder.add(loop.index, dest_offset), value) return impl_ret_new_ref(context, builder, sig.return_type, dest.value)
def get_slice_length(builder, slicestruct): """ Given a slice, compute the number of indices it spans, i.e. the number of iterations that for_range_slice() will execute. Pseudo-code: assert step != 0 if step > 0: if stop <= start: return 0 else: return (stop - start - 1) // step + 1 else: if stop >= start: return 0 else: return (stop - start + 1) // step + 1 (see PySlice_GetIndicesEx() in CPython) """ start = slicestruct.start stop = slicestruct.stop step = slicestruct.step one = ir.Constant(start.type, 1) zero = ir.Constant(start.type, 0) is_step_negative = cgutils.is_neg_int(builder, step) delta = builder.sub(stop, start) # Nominal case pos_dividend = builder.sub(delta, one) neg_dividend = builder.add(delta, one) dividend = builder.select(is_step_negative, neg_dividend, pos_dividend) nominal_length = builder.add(one, builder.sdiv(dividend, step)) # Catch zero length is_zero_length = builder.select(is_step_negative, builder.icmp_signed('>=', delta, zero), builder.icmp_signed('<=', delta, zero)) # Clamp to 0 if is_zero_length return builder.select(is_zero_length, zero, nominal_length)