[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