mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
... and other minor fixes.
library/*.m:
library/LIB_FLAGS.in:
compiler/*.m:
mdbcomp/*.m:
Fix and update copyright notices.
Fix spelling.
Delete trailing whitespace.
554 lines
19 KiB
Mathematica
554 lines
19 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2015, 2020, 2022-2024 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: int_emu.m.
|
|
% Main author: wangp.
|
|
%
|
|
% Emulate `int' operations for a given number of bits per int. These predicates
|
|
% succeed only if the result is defined for the given arguments, and the result
|
|
% can be represented by the `int' type of the host compiler.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.int_emu.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module integer.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type op_type
|
|
---> op_int(op_num_bits)
|
|
; op_uint(op_num_bits).
|
|
|
|
:- type op_num_bits
|
|
---> word_bits(int)
|
|
; bits_8
|
|
; bits_16
|
|
; bits_32
|
|
; bits_64.
|
|
|
|
% Return the number of bits per word (and therefore per int and per uint)
|
|
% for the selected compilation target, optionally wrapped in word_bits().
|
|
%
|
|
:- func target_word_bits(globals) = int.
|
|
:- func target_op_type(globals) = op_num_bits.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred emu_plus(op_type::in, integer::in, integer::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_minus(op_type::in, integer::in, integer::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_times(op_type::in, integer::in, integer::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_quotient(op_type::in, integer::in, integer::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_mod(op_type::in, integer::in, integer::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_rem(op_type::in, integer::in, integer::in,
|
|
some_int_const::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred emu_left_shift(op_type::in, integer::in, int::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_right_shift(op_type::in, integer::in, int::in,
|
|
some_int_const::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred emu_int_floor_to_multiple_of_bits_per_int(op_type::in,
|
|
some_int_const::in, some_int_const::out) is semidet.
|
|
|
|
:- pred emu_int_quot_bits_per_int(op_type::in, some_int_const::in,
|
|
some_int_const::out) is semidet.
|
|
|
|
:- pred emu_int_times_bits_per_int(op_type::in, some_int_const::in,
|
|
some_int_const::out) is semidet.
|
|
|
|
:- pred emu_int_rem_bits_per_int(op_type::in, some_int_const::in,
|
|
some_int_const::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred emu_not(op_type::in, cons_id::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_and(op_type::in, cons_id::in, cons_id::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_or(op_type::in, cons_id::in, cons_id::in,
|
|
some_int_const::out) is semidet.
|
|
:- pred emu_xor(op_type::in, cons_id::in, cons_id::in,
|
|
some_int_const::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.options.
|
|
|
|
:- import_module int.
|
|
:- import_module int16.
|
|
:- import_module int32.
|
|
:- import_module int64.
|
|
:- import_module int8.
|
|
:- import_module uint.
|
|
:- import_module uint16.
|
|
:- import_module uint32.
|
|
:- import_module uint64.
|
|
:- import_module uint8.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
target_word_bits(Globals) = WordBits :-
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Target = target_c,
|
|
globals.lookup_int_option(Globals, bits_per_word, WordBits)
|
|
;
|
|
( Target = target_csharp
|
|
; Target = target_java
|
|
),
|
|
WordBits = 32
|
|
).
|
|
|
|
target_op_type(Globals) = word_bits(WordBits) :-
|
|
WordBits = target_word_bits(Globals).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
emu_plus(WordBits, X, Y, Z) :-
|
|
to_some_int_const_if_in_in_range(WordBits, X + Y, Z).
|
|
|
|
emu_minus(WordBits, X, Y, Z) :-
|
|
to_some_int_const_if_in_in_range(WordBits, X - Y, Z).
|
|
|
|
emu_times(WordBits, X, Y, Z) :-
|
|
to_some_int_const_if_in_in_range(WordBits, X * Y, Z).
|
|
|
|
emu_quotient(WordBits, X, Y, Z) :-
|
|
not is_zero(Y),
|
|
to_some_int_const_if_in_in_range(WordBits, X // Y, Z).
|
|
|
|
emu_mod(WordBits, X, Y, Z) :-
|
|
not is_zero(Y),
|
|
to_some_int_const_if_in_in_range(WordBits, X mod Y, Z).
|
|
|
|
emu_rem(WordBits, X, Y, Z) :-
|
|
not is_zero(Y),
|
|
to_some_int_const_if_in_in_range(WordBits, X rem Y, Z).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
emu_left_shift(OpType, X, Y, Z) :-
|
|
Y >= 0,
|
|
Y < get_op_type_bits(OpType),
|
|
to_some_int_const_if_in_in_range(OpType, X << Y, Z).
|
|
|
|
emu_right_shift(OpType, X, Y, Z) :-
|
|
Y >= 0,
|
|
Y < get_op_type_bits(OpType),
|
|
to_some_int_const_if_in_in_range(OpType, X >> Y, Z).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
emu_int_floor_to_multiple_of_bits_per_int(OpType, ConstX, FloorConst) :-
|
|
% This operation is defined only for word-size signed integers.
|
|
OpType = op_int(word_bits(BitsPerInt)),
|
|
ConstX = int_const(IntX),
|
|
Trunc = integer(IntX) // integer(BitsPerInt),
|
|
Floor0 = Trunc * integer(BitsPerInt),
|
|
( if Floor0 > integer(IntX) then
|
|
Floor = Floor0 - integer(BitsPerInt)
|
|
else
|
|
Floor = Floor0
|
|
),
|
|
to_some_int_const_if_in_in_range(OpType, Floor, FloorConst).
|
|
|
|
emu_int_quot_bits_per_int(OpType, ConstX, ConstZ) :-
|
|
% This operation is defined only for word-size signed integers.
|
|
OpType = op_int(word_bits(BitsPerInt)),
|
|
ConstX = int_const(IntX),
|
|
emu_quotient(OpType, integer(IntX), integer(BitsPerInt), ConstZ).
|
|
|
|
emu_int_times_bits_per_int(OpType, ConstX, ConstZ) :-
|
|
% This operation is defined only for word-size signed integers.
|
|
OpType = op_int(word_bits(BitsPerInt)),
|
|
ConstX = int_const(IntX),
|
|
emu_times(OpType, integer(IntX), integer(BitsPerInt), ConstZ).
|
|
|
|
emu_int_rem_bits_per_int(OpType, ConstX, ConstZ) :-
|
|
% This operation is defined only for word-size signed integers.
|
|
OpType = op_int(word_bits(BitsPerInt)),
|
|
ConstX = int_const(IntX),
|
|
emu_rem(OpType, integer(IntX), integer(BitsPerInt), ConstZ).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The bitwise logical operations NOT, AND, OR and XOR *could* be implemented
|
|
% using the corresponding operations in library/integer.m, and such an
|
|
% implementation would be much shorter and simpler than the implementation
|
|
% here. The reason why we use the case-analysis-based implementation we do
|
|
% is because it is not clear how well those operations in integer.m have
|
|
% been tested. Once that testing happens, and those operations are found to be
|
|
% correct and reliable, we should switch to using them.
|
|
%
|
|
|
|
emu_not(OpType, FunctorX, ConstY) :-
|
|
FunctorX = some_int_const(ConstX),
|
|
require_complete_switch [OpType]
|
|
(
|
|
OpType = op_int(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(TargetNumBits),
|
|
ConstX = int_const(IntX),
|
|
% Evaluate bitwise negations at compile time ONLY if the host
|
|
% has the same number of bits per int as the target. Otherwise,
|
|
% the result we compute here may differ from the result that
|
|
% would be computed at runtime. This test was half of the code
|
|
% added by commit a9d13d5fa2b871b3888de505c9c36425cd6ea3ed,
|
|
% which fixed this bug; the other half is in our caller.
|
|
int.bits_per_int = TargetNumBits,
|
|
ConstY = int_const(int.(\ IntX))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = int8_const(Int8X),
|
|
ConstY = int8_const(int8.(\ Int8X))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = int16_const(Int16X),
|
|
ConstY = int16_const(int16.(\ Int16X))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = int32_const(Int32X),
|
|
ConstY = int32_const(int32.(\ Int32X))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = int64_const(Int64X),
|
|
ConstY = int64_const(int64.(\ Int64X))
|
|
)
|
|
;
|
|
OpType = op_uint(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(TargetNumBits),
|
|
ConstX = uint_const(UIntX),
|
|
% The comment for the signed case above also applies here.
|
|
uint.bits_per_uint = TargetNumBits,
|
|
ConstY = uint_const(uint.(\ UIntX))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = uint8_const(UInt8X),
|
|
ConstY = uint8_const(uint8.(\ UInt8X))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = uint16_const(UInt16X),
|
|
ConstY = uint16_const(uint16.(\ UInt16X))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = uint32_const(UInt32X),
|
|
ConstY = uint32_const(uint32.(\ UInt32X))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = uint64_const(UInt64X),
|
|
ConstY = uint64_const(uint64.(\ UInt64X))
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
emu_and(OpType, FunctorX, FunctorY, ConstZ) :-
|
|
FunctorX = some_int_const(ConstX),
|
|
FunctorY = some_int_const(ConstY),
|
|
require_complete_switch [OpType]
|
|
(
|
|
OpType = op_int(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(_),
|
|
ConstX = int_const(IntX),
|
|
ConstY = int_const(IntY),
|
|
ConstZ = int_const(int.(IntX /\ IntY))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = int8_const(Int8X),
|
|
ConstY = int8_const(Int8Y),
|
|
ConstZ = int8_const(int8.(Int8X /\ Int8Y))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = int16_const(Int16X),
|
|
ConstY = int16_const(Int16Y),
|
|
ConstZ = int16_const(int16.(Int16X /\ Int16Y))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = int32_const(Int32X),
|
|
ConstY = int32_const(Int32Y),
|
|
ConstZ = int32_const(int32.(Int32X /\ Int32Y))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = int64_const(Int64X),
|
|
ConstY = int64_const(Int64Y),
|
|
ConstZ = int64_const(int64.(Int64X /\ Int64Y))
|
|
)
|
|
;
|
|
OpType = op_uint(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(_),
|
|
ConstX = uint_const(UIntX),
|
|
ConstY = uint_const(UIntY),
|
|
ConstZ = uint_const(uint.(UIntX /\ UIntY))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = uint8_const(UInt8X),
|
|
ConstY = uint8_const(UInt8Y),
|
|
ConstZ = uint8_const(uint8.(UInt8X /\ UInt8Y))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = uint16_const(UInt16X),
|
|
ConstY = uint16_const(UInt16Y),
|
|
ConstZ = uint16_const(uint16.(UInt16X /\ UInt16Y))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = uint32_const(UInt32X),
|
|
ConstY = uint32_const(UInt32Y),
|
|
ConstZ = uint32_const(uint32.(UInt32X /\ UInt32Y))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = uint64_const(UInt64X),
|
|
ConstY = uint64_const(UInt64Y),
|
|
ConstZ = uint64_const(uint64.(UInt64X /\ UInt64Y))
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
emu_or(OpType, FunctorX, FunctorY, ConstZ) :-
|
|
FunctorX = some_int_const(ConstX),
|
|
FunctorY = some_int_const(ConstY),
|
|
require_complete_switch [OpType]
|
|
(
|
|
OpType = op_int(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(_),
|
|
ConstX = int_const(IntX),
|
|
ConstY = int_const(IntY),
|
|
ConstZ = int_const(int.(IntX \/ IntY))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = int8_const(Int8X),
|
|
ConstY = int8_const(Int8Y),
|
|
ConstZ = int8_const(int8.(Int8X \/ Int8Y))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = int16_const(Int16X),
|
|
ConstY = int16_const(Int16Y),
|
|
ConstZ = int16_const(int16.(Int16X \/ Int16Y))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = int32_const(Int32X),
|
|
ConstY = int32_const(Int32Y),
|
|
ConstZ = int32_const(int32.(Int32X \/ Int32Y))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = int64_const(Int64X),
|
|
ConstY = int64_const(Int64Y),
|
|
ConstZ = int64_const(int64.(Int64X \/ Int64Y))
|
|
)
|
|
;
|
|
OpType = op_uint(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(_),
|
|
ConstX = uint_const(UIntX),
|
|
ConstY = uint_const(UIntY),
|
|
ConstZ = uint_const(uint.(UIntX \/ UIntY))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = uint8_const(UInt8X),
|
|
ConstY = uint8_const(UInt8Y),
|
|
ConstZ = uint8_const(uint8.(UInt8X \/ UInt8Y))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = uint16_const(UInt16X),
|
|
ConstY = uint16_const(UInt16Y),
|
|
ConstZ = uint16_const(uint16.(UInt16X \/ UInt16Y))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = uint32_const(UInt32X),
|
|
ConstY = uint32_const(UInt32Y),
|
|
ConstZ = uint32_const(uint32.(UInt32X \/ UInt32Y))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = uint64_const(UInt64X),
|
|
ConstY = uint64_const(UInt64Y),
|
|
ConstZ = uint64_const(uint64.(UInt64X \/ UInt64Y))
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
emu_xor(OpType, FunctorX, FunctorY, ConstZ) :-
|
|
FunctorX = some_int_const(ConstX),
|
|
FunctorY = some_int_const(ConstY),
|
|
require_complete_switch [OpType]
|
|
(
|
|
OpType = op_int(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(_),
|
|
ConstX = int_const(IntX),
|
|
ConstY = int_const(IntY),
|
|
ConstZ = int_const(int.xor(IntX, IntY))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = int8_const(Int8X),
|
|
ConstY = int8_const(Int8Y),
|
|
ConstZ = int8_const(int8.xor(Int8X, Int8Y))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = int16_const(Int16X),
|
|
ConstY = int16_const(Int16Y),
|
|
ConstZ = int16_const(int16.xor(Int16X, Int16Y))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = int32_const(Int32X),
|
|
ConstY = int32_const(Int32Y),
|
|
ConstZ = int32_const(int32.xor(Int32X, Int32Y))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = int64_const(Int64X),
|
|
ConstY = int64_const(Int64Y),
|
|
ConstZ = int64_const(int64.xor(Int64X, Int64Y))
|
|
)
|
|
;
|
|
OpType = op_uint(OpNumBits),
|
|
require_complete_switch [OpNumBits]
|
|
(
|
|
OpNumBits = word_bits(_),
|
|
ConstX = uint_const(UIntX),
|
|
ConstY = uint_const(UIntY),
|
|
ConstZ = uint_const(uint.xor(UIntX, UIntY))
|
|
;
|
|
OpNumBits = bits_8,
|
|
ConstX = uint8_const(UInt8X),
|
|
ConstY = uint8_const(UInt8Y),
|
|
ConstZ = uint8_const(uint8.xor(UInt8X, UInt8Y))
|
|
;
|
|
OpNumBits = bits_16,
|
|
ConstX = uint16_const(UInt16X),
|
|
ConstY = uint16_const(UInt16Y),
|
|
ConstZ = uint16_const(uint16.xor(UInt16X, UInt16Y))
|
|
;
|
|
OpNumBits = bits_32,
|
|
ConstX = uint32_const(UInt32X),
|
|
ConstY = uint32_const(UInt32Y),
|
|
ConstZ = uint32_const(uint32.xor(UInt32X, UInt32Y))
|
|
;
|
|
OpNumBits = bits_64,
|
|
ConstX = uint64_const(UInt64X),
|
|
ConstY = uint64_const(UInt64Y),
|
|
ConstZ = uint64_const(uint64.xor(UInt64X, UInt64Y))
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- 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.two, integer(NumBits - 1)),
|
|
Integer < pow(integer.two, 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.two, integer(NumBits)),
|
|
(
|
|
OpNumBits = word_bits(_),
|
|
% Fails if Integer is representable on the target machine,
|
|
% but not on the host.
|
|
integer.to_uint(Integer, UInt),
|
|
Const = uint_const(UInt)
|
|
;
|
|
OpNumBits = bits_8,
|
|
integer.to_uint8(Integer, UInt8),
|
|
Const = uint8_const(UInt8)
|
|
;
|
|
OpNumBits = bits_16,
|
|
integer.to_uint16(Integer, UInt16),
|
|
Const = uint16_const(UInt16)
|
|
;
|
|
OpNumBits = bits_32,
|
|
integer.to_uint32(Integer, UInt32),
|
|
Const = uint32_const(UInt32)
|
|
;
|
|
OpNumBits = bits_64,
|
|
integer.to_uint64(Integer, UInt64),
|
|
Const = uint64_const(UInt64)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func get_op_type_bits(op_type) = int.
|
|
|
|
get_op_type_bits(OpType) = NumBits :-
|
|
( OpType = op_int(OpNumBits)
|
|
; OpType = op_uint(OpNumBits)
|
|
),
|
|
NumBits = get_op_num_bits(OpNumBits).
|
|
|
|
:- func get_op_num_bits(op_num_bits) = int.
|
|
|
|
get_op_num_bits(OpNumBits) = NumBits :-
|
|
( OpNumBits = word_bits(NumBits)
|
|
; OpNumBits = bits_8, NumBits = 8
|
|
; OpNumBits = bits_16, NumBits = 16
|
|
; OpNumBits = bits_32, NumBits = 32
|
|
; OpNumBits = bits_64, NumBits = 64
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module parse_tree.int_emu.
|
|
%---------------------------------------------------------------------------%
|