[m-rev.] for review: extend constant propagation to sized integer types
Julien Fischer
jfischer at opturion.com
Wed Apr 19 14:12:26 AEST 2023
On Tue, 18 Apr 2023, Zoltan Somogyi wrote:
> Extend constant propagation to sized integers.
>
> compiler/int_emu.m:
> Give the emulation predicates for arithmetic predicates their inputs
> as two arbitrary-precision integers, and then check whether the results
> fit into the representable range of their expected type.
>
> Give the emulation predicates for shift predicates their inputs
> as an arbitrary-precision integer (the value to be shifted) and as
> an int (the shift amount) and then check whether the results fit into
> the representable range of their expected type.
>
> In both cases, the caller (const_prop.m) will convert the inputs
> to the above types. In both cases, the original code of int_emu.m
> worked by doing the operation on arbitrary precision integers.
> This approach emulates operations on fives times as many types
> using less code (which includes just a bit more than half the
> original number of predicates).
>
> Add predicates to emulate the logical operations AND, OR, XOR and NOT.
> With these, the original code (in const_prop.m) did *not* work by
> doing the operation on arbitrary precision integers. I am not sure
> if that was a deliberate decision to avoid integer.m's logical operations,
> which I expect would not have been tested nearly as thoroughly
> as the arithmetic ops.
I'm fairly certain that they haven't been as extensively tested, but I don't
think that's the reason. (Note that the initial version of const_prop.m was in
1997, which predates the addition of bitwise operations to the integer module
by two years.)
> ZZZ Does anyone know the history here?
AND, OR XOR and NOT are never going to extend a value beyond what can be
represented by its type; the original version of const_prop did not use
int_emu.m (which was added in 2015). The treatment of these bitwise operations
is what const_prop.m has always done.
> To be on the safe side, the emulation of logical ops does the work
> on the actual types themselves, even though this needs much more code.
>
> Split a convenience function into two, for two distinct use cases.
...
>
> diff --git a/compiler/const_prop.m b/compiler/const_prop.m
> index 3082d3e3e..7b7872cea 100644
> --- a/compiler/const_prop.m
> +++ b/compiler/const_prop.m
...
> @@ -270,70 +272,48 @@ evaluate_det_call_uint_1(Globals, ProcName, ModeNum, X, OutputArg, ConsId) :-
>
> %---------------------%
>
> -:- pred evaluate_det_call_int_2(globals::in, string::in, int::in,
> - arg_hlds_info::in, arg_hlds_info::in, arg_hlds_info::out, cons_id::out)
> - is semidet.
> +:- pred evaluate_det_call_int_uint_2(globals::in, op_type::in,
> + string::in, int::in, arg_hlds_info::in,
> + arg_hlds_info::in, arg_hlds_info::out, cons_id::out) is semidet.
>
> -evaluate_det_call_int_2(Globals, ProcName, ModeNum, X, Y,
> - OutputArg, some_int_const(int_const(OutputArgVal))) :-
> +evaluate_det_call_int_uint_2(Globals, OpType, ProcName, ModeNum, X, Y,
> + OutputArg, some_int_const(OutputArgVal)) :-
> ModeNum = 0,
> X ^ arg_inst = bound(_, _, [bound_functor(FunctorX, [])]),
> - FunctorX = some_int_const(int_const(XVal)),
> + FunctorX = some_int_const(ConstX),
> OutputArg = Y,
> + % All of the {int,uint}{,8,16,32,64} modules define unary plus,
> + % unary minus, and bitwise negation. The other procedures below
> + % are defined only in int.m; this is checked by the predicates that
> + % emulate them.
None of the uint* modules provide unary plus and unary minus.
>
> % If we can statically determine the result of a semidet call
> diff --git a/compiler/int_emu.m b/compiler/int_emu.m
> index b767a471b..ef6c6c260 100644
> --- a/compiler/int_emu.m
> +++ b/compiler/int_emu.m
...
> +%---------------------------------------------------------------------------%
> +
> +:- pred to_some_int_const_if_in_in_range(op_type::in, integer::in,
> + some_int_const::out) is semidet.
> +
> +to_some_int_const_if_in_in_range(OpType, Integer, Const) :-
> + (
> + OpType = op_int(OpNumBits),
> + NumBits = get_op_num_bits(OpNumBits),
> + Integer >= -pow(integer(2), integer(NumBits - 1)),
> + % ZZZ This was Integer =< pow(integer(2), integer(NumBits - 1)) - one,
> + % but this one fits in better with the unsigned case below.
> + Integer < pow(integer(2), integer(NumBits - 1)),
> + (
> + OpNumBits = word_bits(_),
> + % Fails if Integer is representable on the target machine,
> + % but not on the host.
> + integer.to_int(Integer, Int),
> + Const = int_const(Int)
> + ;
> + OpNumBits = bits_8,
> + integer.to_int8(Integer, Int8),
> + Const = int8_const(Int8)
> + ;
> + OpNumBits = bits_16,
> + integer.to_int16(Integer, Int16),
> + Const = int16_const(Int16)
> + ;
> + OpNumBits = bits_32,
> + integer.to_int32(Integer, Int32),
> + Const = int32_const(Int32)
> + ;
> + OpNumBits = bits_64,
> + integer.to_int64(Integer, Int64),
> + Const = int64_const(Int64)
> + )
> + ;
> + OpType = op_uint(OpNumBits),
> + NumBits = get_op_num_bits(OpNumBits),
> Integer >= integer.zero,
> - Integer =< pow(integer(2), integer(WordBits)),
> - integer.to_uint(Integer, UInt).
> + % ZZZ This was Integer =< pow(integer(2), integer(NumBits)),
> + % which I (zs) think is a bug.
> + Integer < pow(integer(2), integer(NumBits)),
Yes, it's a bug; it should be a strict inequality as you have here since
the values of any unsigned type will range from 0 .. (2 ^ NumBits) - 1.
That looks fine otherwise.
Julien.
More information about the reviews
mailing list