[m-rev.] for review: Fix how large negative integers are written for C89.
Peter Wang
novalazy at gmail.com
Wed Sep 2 10:33:44 AEST 2015
C does not have negative integer constants so "(MR_Integer) -nnnn"
is the negation of a positive integer constant nnnn, converted to
MR_Integer.
In C89/C90 an unsuffixed decimal integer constant must be typed
`int' or `long int' or `unsigned long int', whichever fits first.
The negated result will have the same type. If nnnn > LONG_MAX then
it will be typed `unsigned long int'. If MR_Integer is wider than
`unsigned long int' then the conversion to MR_Integer yields a positive
value, not negative.
The solution here is essentially to write "-(MR_Integer) nnnn" so the
integer constant is converted to a signed MR_Integer before negation.
compiler/c_util.m:
Add predicate `output_int_expr' to write ints in a way that
avoids the problem.
compiler/llds_out_data.m:
compiler/mlds_to_c.m:
Use `output_int_expr' to write ints.
tests/hard_coded/Mmakefile:
tests/hard_coded/c89_neg_int.exp:
tests/hard_coded/c89_neg_int.m:
Add test case.
---
compiler/c_util.m | 63 ++++++++++++++++++++++++++++++++++++++++
compiler/llds_out_data.m | 5 +---
compiler/mlds_to_c.m | 5 +---
tests/hard_coded/Mmakefile | 1 +
tests/hard_coded/c89_neg_int.exp | 2 ++
tests/hard_coded/c89_neg_int.m | 27 +++++++++++++++++
6 files changed, 95 insertions(+), 8 deletions(-)
create mode 100644 tests/hard_coded/c89_neg_int.exp
create mode 100644 tests/hard_coded/c89_neg_int.m
diff --git a/compiler/c_util.m b/compiler/c_util.m
index f815d0e..a6c7dc9 100644
--- a/compiler/c_util.m
+++ b/compiler/c_util.m
@@ -118,6 +118,15 @@
%-----------------------------------------------------------------------------%
%
+% Integer literals.
+%
+
+ % Write out an int as a C expression.
+ %
+:- pred output_int_expr(int::in, io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%
% Float literals.
%
@@ -201,6 +210,8 @@
:- import_module bool.
:- import_module int.
+:- import_module integer.
+:- import_module list.
:- import_module require.
:- import_module string.
@@ -569,6 +580,58 @@ unicode_escape_any_char(Char, EscapeCodeChars) :-
%-----------------------------------------------------------------------------%
%
+% Integer literals.
+%
+
+output_int_expr(N, !IO) :-
+ % We need to cast to (MR_Integer) to ensure things like 1 << 32 work
+ % when `MR_Integer' is 64 bits but `int' is 32 bits.
+ %
+ % C does not have negative integer constants so "(MR_Integer) -nnnn"
+ % is the negation of a positive integer constant nnnn, converted to
+ % MR_Integer.
+ %
+ % In C89/C90 an unsuffixed decimal integer constant must be typed
+ % `int' or `long int' or `unsigned long int', whichever fits first.
+ % The negated result will have the same type. If nnnn > LONG_MAX then
+ % it will be typed `unsigned long int'. If MR_Integer is wider than
+ % `unsigned long int' then the conversion to MR_Integer yields a positive
+ % value, not negative.
+ %
+ % C99 has different integer constant type rules. The unsuffixed decimal
+ % integer constant must be typed `int' or `long int' or `long long int'
+ % but not `unsigned long int'. Therefore the same problem does not occur.
+
+ ( N >= -2147483647 ->
+ % Write integers >= -LONG_MAX in the most readable way.
+ % This is the minimum magnitude of LONG_MAX in C.
+ io.write_string("(MR_Integer) ", !IO),
+ io.write_int(N, !IO)
+ ;
+ ( integer.to_string(integer(N)) = "-2147483648" ->
+ % Write -2^31 without using an integer constant that overflows a
+ % 32-bit signed `long' in two's complement representation.
+ io.write_string("(-(MR_Integer) 2147483647 - 1)", !IO)
+ ; integer.to_string(integer(N)) = "-9223372036854775808" ->
+ % Write -2^63 without using an integer constant that overflows a
+ % 64-bit signed `long' in two's complement representation.
+ io.write_string("(-(MR_Integer) 9223372036854775807 - 1)", !IO)
+ ; int.min_int(N) ->
+ % Avoid negating min_int as it would overflow in two's complement
+ % representation. In practice, one of the preceding two cases
+ % would have been taken.
+ io.write_string("(-(MR_Integer) ", !IO),
+ io.write_int(-(N + 1), !IO),
+ io.write_string("- 1)", !IO)
+ ;
+ % Write other negative values as negation of an MR_Integer value.
+ io.write_string("-(MR_Integer) ", !IO),
+ io.write_int(-N, !IO)
+ )
+ ).
+
+%-----------------------------------------------------------------------------%
+%
% Floating point literals.
%
% XXX These routines do not yet handle infinities and NaNs properly.
diff --git a/compiler/llds_out_data.m b/compiler/llds_out_data.m
index 5ea4cf1..84f40d6 100644
--- a/compiler/llds_out_data.m
+++ b/compiler/llds_out_data.m
@@ -1169,10 +1169,7 @@ output_rval_const(Info, Const, !IO) :-
io.write_string("MR_FALSE", !IO)
;
Const = llconst_int(N),
- % We need to cast to (MR_Integer) to ensure things like 1 << 32 work
- % when `MR_Integer' is 64 bits but `int' is 32 bits.
- output_llds_type_cast(lt_integer, !IO),
- io.write_int(N, !IO)
+ c_util.output_int_expr(N, !IO)
;
Const = llconst_foreign(Value, Type),
io.write_char('(', !IO),
diff --git a/compiler/mlds_to_c.m b/compiler/mlds_to_c.m
index a65e112..358e6d9 100644
--- a/compiler/mlds_to_c.m
+++ b/compiler/mlds_to_c.m
@@ -4661,10 +4661,7 @@ mlds_output_rval_const(_Opts, Const, !IO) :-
( Const = mlconst_int(N)
; Const = mlconst_enum(N, _)
),
- % We need to cast to (MR_Integer) to ensure things like 1 << 32 work
- % when `Integer' is 64 bits but `int' is 32 bits.
- io.write_string("(MR_Integer) ", !IO),
- io.write_int(N, !IO)
+ c_util.output_int_expr(N, !IO)
;
Const = mlconst_char(C),
io.write_string("(MR_Char) ", !IO),
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 54c9fcb..4a0a7e0 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -28,6 +28,7 @@ ORDINARY_PROGS = \
bug383 \
bug392 \
bug_pack_bits \
+ c89_neg_int \
c_write_string \
calendar_test \
cc_and_non_cc_test \
diff --git a/tests/hard_coded/c89_neg_int.exp b/tests/hard_coded/c89_neg_int.exp
new file mode 100644
index 0000000..ab47a15
--- /dev/null
+++ b/tests/hard_coded/c89_neg_int.exp
@@ -0,0 +1,2 @@
+-2147483647
+-2147483648
diff --git a/tests/hard_coded/c89_neg_int.m b/tests/hard_coded/c89_neg_int.m
new file mode 100644
index 0000000..d580a29
--- /dev/null
+++ b/tests/hard_coded/c89_neg_int.m
@@ -0,0 +1,27 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+
+:- module c89_neg_int.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+:- implementation.
+
+main(!IO) :-
+ show(-2147483647, !IO),
+ show(-2147483648, !IO),
+ % Disabled as they will be rejected by a compiler with 32-bit ints.
+ % show(-2147483649, !IO),
+ % show(-9223372036854775807, !IO),
+ % show(-9223372036854775808, !IO),
+ true.
+
+:- pred show(int::in, io::di, io::uo) is det.
+
+show(N, !IO) :-
+ io.write_int(N, !IO),
+ io.nl(!IO).
--
2.1.2
More information about the reviews
mailing list