Files
mercury/library/uint.m
Julien Fischer 3f28b096ef Add builtin 8, 16 and 32 bit integer types -- Part 2.
Enable support for literals of the new types.

Begin implementing library support for 8, 16, and 32 bit types.

Update the compiler to represent values of their own constants.

library/int8.m:
library/int16.m:
library/int32.m:
library/uint8.m:
library/uint16.m:
library/uint32.m:
    Begin filling these modules out.

library/uint.m:
    Unrelated change: add the predicates plus/2, minus/2 and
    times/2 for uints.

library/integer.m:
    Add predicates for converting integer/0 values into values
    of the new types.

    Add functions for converting values of the new types into
    integer/0 values.

library/string.m:
    Add functions for converting values of the new types to strings.

library/private_builtin.m:
    Replace the placeholder definitions for the builtin unify and compare
    predicates for the new types with their actual definitions.

library/erlang_rtti_implementation.m:
library/rtti_implementation.m:
    Replace placeholder definitions for the new types with their
    actual definitions.

library/io.m:
    Add predicates for writing values of the new types to file streams.

library/stream.string_writer.m:
    Implement generic write and print for values of the new types.

library/string.to_string.m:
    Likewise for string/1.

library/term.m:
library/term_conversion.m:
    Add predicates and functions for converting the new types to
    and from terms.

compiler/builtin_ops.m:
compiler/elds.m:
compiler/hlds_data.m:
compiler/llds.m:
compiler/mlds.m:
compiler/prog_data.m:
    Replace placeholders for the new types with the new types.

compiler/superhomogeneous.m:
    Enable literals of the new types.

compiler/mlds_to_cs.m:
    Avoid a warning from the C# compiler for bitwise-or operators
    with sbyte operands.

compiler/c_util.m:
compiler/elds_to_erlang.m:
compiler/hlds_out_util.m:
compiler/llds_out_data.m:
compiler/lookup_switch.m:
compiler/mlds_to_c.m:
compiler/mlds_to_java.m:
compiler/opt_debug.m:
compiler/parse_tree_out_info.m:
compiler/parse_tree_to_term.m:
compiler/prog_out.m:
compiler/prog_rep.m:
compiler/prog_util.m:
    Replace placeholder code for the new types with code that uses the new
    types.

tests/invalid/invalid_int.m:
tests/invalid/invalid_int.err_exp2:
    Extend this test case to cover the fixed size integer types.
2017-08-21 09:50:16 +10:00

428 lines
11 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
%---------------------------------------------------------------------------%
% Copyright (C) 2016 The Mercury team.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: uint.m
% Main author: juliensf
% Stability: low.
%
% Predicates and functions for dealing with unsigned machine sized integer
% numbers.
%
%---------------------------------------------------------------------------%
:- module uint.
:- interface.
:- import_module pretty_printer.
%---------------------------------------------------------------------------%
% Convert an int to a uint.
% Fails if the int is less than zero.
%
:- pred from_int(int::in, uint::out) is semidet.
% As above, but throw an exception instead of failing.
%
:- func det_from_int(int) = uint.
:- func cast_from_int(int) = uint.
:- func cast_to_int(uint) = int.
% Less than.
%
:- pred (uint::in) < (uint::in) is semidet.
% Greater than.
%
:- pred (uint::in) > (uint::in) is semidet.
% Less than or equal.
%
:- pred (uint::in) =< (uint::in) is semidet.
% Greater than or equal.
%
:- pred (uint::in) >= (uint::in) is semidet.
% Addition.
%
:- func uint + uint = uint.
:- mode in + in = uo is det.
:- mode uo + in = in is det.
:- mode in + uo = in is det.
:- func plus(uint, uint) = uint.
% Subtraction.
%
:- func uint - uint = uint.
:- mode in - in = uo is det.
:- mode uo - in = in is det.
:- mode in - uo = in is det.
:- func minus(uint, uint) = uint.
% Multiplication.
%
:- func (uint::in) * (uint::in) = (uint::uo) is det.
:- func times(uint, uint) = uint.
% Maximum.
%
:- func max(uint, uint) = uint.
% Minimum.
%
:- func min(uint, uint) = uint.
% Truncating integer division.
%
% Throws a `math.domain_error' exception if the right operand is zero.
%
:- func (uint::in) div (uint::in) = (uint::uo) is det.
% Truncating integer division.
%
% Throws a `math.domain_error' exception if the right operand is zero.
%
:- func (uint::in) // (uint::in) = (uint::uo) is det.
% (/)/2 is a synonym for (//)/2.
%
:- func (uint::in) / (uint::in) = (uint::uo) is det.
% unchecked_quotient(X, Y) is the same as X // Y, but the behaviour
% is undefined if the right operand is zero.
%
:- func unchecked_quotient(uint::in, uint::in) = (uint::uo) is det.
% Modulus.
% X mod Y = X - (X div Y) * Y
%
% Throws a `math.domain_error' exception if the right operand is zero.
%
:- func (uint::in) mod (uint::in) = (uint::uo) is det.
% Remainder.
% X rem Y = X - (X // Y) * Y.
%
% Throws a `math.domain_error/` exception if the right operand is zero.
%
:- func (uint::in) rem (uint::in) = (uint::uo) is det.
% unchecked_rem(X, Y) is the same as X rem Y, but the behaviour is
% undefined if the right operand is zero.
%
:- func unchecked_rem(uint::in, uint::in) = (uint::uo) is det.
% Left shift.
% X << Y returns X "left shifted" by Y bits.
% The bit positions vacated by the shift are filled by zeros.
% Throws an exception if Y is not in [0, bits_per_uint).
%
:- func (uint::in) << (int::in) = (uint::uo) is det.
% unchecked_lift_shift(X, Y) is the same as X << Y except that the
% behaviour is undefined if Y is not in [0, bits_per_uint).
% It will typically be be implemented more efficiently than X << Y.
%
:- func unchecked_left_shift(uint::in, int::in) = (uint::uo) is det.
% Right shift.
% X >> Y returns X "right shifted" by Y bits.
% The bit positions vacated by the shift are filled by zeros.
% Throws an exception if Y is not in [0, bits_per_uint).
%
:- func (uint::in) >> (int::in) = (uint::uo) is det.
% unchecked_right_shift(X, Y) is the same as X >> Y except that the
% behaviour is undefined if Y is not in [0, bits_per_uint).
% It will typically be implemented more efficiently than X >> Y.
%
:- func unchecked_right_shift(uint::in, int::in) = (uint::uo) is det.
% even(X) is equivalent to (X mod 2 = 0).
%
:- pred even(uint::in) is semidet.
% odd(X) is equivalent to (not even(X)), i.e. (X mod 2 = 1).
%
:- pred odd(uint::in) is semidet.
% Bitwise and.
%
:- func (uint::in) /\ (uint::in) = (uint::uo) is det.
% Bitwise or.
%
:- func (uint::in) \/ (uint::in) = (uint::uo) is det.
% Bitwise exclusive or (xor).
%
:- func xor(uint, uint) = uint.
:- mode xor(in, in) = uo is det.
:- mode xor(in, uo) = in is det.
:- mode xor(uo, in) = in is det.
% Bitwise complement.
%
:- func \ (uint::in) = (uint::uo) is det.
% max_uint is the maximum value of a uint on this machine.
%
:- func max_uint = uint.
% bits_per_uint is the number of bits in a uint on this machine.
%
:- func bits_per_uint = int.
% Convert a uint to a pretty_printer.doc for formatting.
%
:- func uint_to_doc(uint) = pretty_printer.doc.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module exception.
:- import_module math.
:- import_module require.
:- import_module string.
%---------------------------------------------------------------------------%
:- pragma foreign_proc("C",
from_int(I::in, U::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
"
if (I < 0) {
SUCCESS_INDICATOR = MR_FALSE;
} else {
U = (MR_Unsigned) I;
SUCCESS_INDICATOR = MR_TRUE;
}
").
:- pragma foreign_proc("C#",
from_int(I::in, U::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
U = (uint) I;
SUCCESS_INDICATOR = (I < 0) ? false : true;
").
:- pragma foreign_proc("Java",
from_int(I::in, U::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
U = I;
SUCCESS_INDICATOR = (I < 0) ? false : true;
").
det_from_int(I) = U :-
( if from_int(I, UPrime) then
U = UPrime
else
error("uint.det_from_int: cannot convert int to uint")
).
%---------------------------------------------------------------------------%
:- pragma foreign_proc("C",
cast_from_int(I::in) = (U::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness],
"
U = (MR_Unsigned) I;
").
:- pragma foreign_proc("C#",
cast_from_int(I::in) = (U::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
U = (uint) I;
").
:- pragma foreign_proc("Java",
cast_from_int(I::in) = (U::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
U = I;
").
%---------------------------------------------------------------------------%
cast_from_int(_) = _ :-
sorry($module, "uint.cast_from_int/1 NYI for Erlang").
:- pragma foreign_proc("C",
cast_to_int(U::in) = (I::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness],
"
I = (MR_Integer) U;
").
:- pragma foreign_proc("C#",
cast_to_int(U::in) = (I::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
I = (int) U;
").
:- pragma foreign_proc("Java",
cast_to_int(U::in) = (I::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
I = U;
").
cast_to_int(_) = _ :-
sorry($module, "uint.cast_to_int/1 NYI for Erlang").
%---------------------------------------------------------------------------%
X div Y = X // Y.
:- pragma inline('//'/2).
X // Y = Div :-
( if Y = 0u then
throw(math.domain_error("uint.'//': division by zero"))
else
Div = unchecked_quotient(X, Y)
).
:- pragma inline('/'/2).
X / Y = X // Y.
X mod Y = X rem Y.
:- pragma inline(rem/2).
X rem Y = Rem :-
( if Y = 0u then
throw(math.domain_error("uint.rem: division by zero"))
else
Rem = unchecked_rem(X, Y)
).
%---------------------------------------------------------------------------%
X << Y = Result :-
( if cast_from_int(Y) < cast_from_int(bits_per_uint) then
Result = unchecked_left_shift(X, Y)
else
Msg = "uint.(<<): second operand is out of range",
throw(math.domain_error(Msg))
).
X >> Y = Result :-
( if cast_from_int(Y) < cast_from_int(bits_per_uint) then
Result = unchecked_right_shift(X, Y)
else
Msg = "uint.(>>): second operand is out of range",
throw(math.domain_error(Msg))
).
%---------------------------------------------------------------------------%
max(X, Y) =
( if X > Y then X else Y ).
min(X, Y) =
( if X < Y then X else Y ).
%---------------------------------------------------------------------------%
:- pragma inline(even/1).
even(X) :-
(X /\ 1u) = 0u.
:- pragma inline(odd/1).
odd(X) :-
(X /\ 1u) \= 0u.
%---------------------------------------------------------------------------%
:- pragma foreign_decl("C", "
#include <limits.h>
#define ML_BITS_PER_UINT (sizeof(MR_Unsigned) * CHAR_BIT)
").
:- pragma foreign_proc("C",
max_uint = (Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
if (sizeof(MR_Unsigned) == sizeof(unsigned int)) {
Max = UINT_MAX;
} else if (sizeof(MR_Unsigned) == sizeof(unsigned long)) {
Max = (MR_Unsigned) ULONG_MAX;
#if defined(ULLONG_MAX)
} else if (sizeof(MR_Unsigned) == sizeof(unsigned long long)) {
Max = (MR_Unsigned) ULLONG_MAX;
#endif
} else {
MR_fatal_error(""Unable to figure out max uint size"");
}
").
:- pragma foreign_proc("C#",
max_uint = (U::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
U = uint.MaxValue;
").
:- pragma foreign_proc("Java",
max_uint = (U::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
U = 0xffffffff;
").
:- pragma foreign_proc("C",
bits_per_uint = (Bits::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness],
"
Bits = ML_BITS_PER_UINT;
").
:- pragma foreign_proc("Java",
bits_per_uint = (Bits::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
Bits = 32;
").
:- pragma foreign_proc("C#",
bits_per_uint = (Bits::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
Bits = 32;
").
:- pragma foreign_proc("Erlang",
bits_per_uint = (Bits::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
% XXX Erlang ints are actually arbitrary precision.
Bits = 32
").
%---------------------------------------------------------------------------%
uint_to_doc(X) = str(string.uint_to_string(X)).
%---------------------------------------------------------------------------%
:- end_module uint.
%---------------------------------------------------------------------------%