Files
mercury/tests/hard_coded/float_gv.m
Zoltan Somogyi 4d377aba26 Fix a bug reported by Greg Duck. The bug was that the compiler always generated
Estimated hours taken: 5
Branches: main

Fix a bug reported by Greg Duck. The bug was that the compiler always generated
a global variable of type MR_Word for each mutable, but could then assign
variables of other types to and from this global. If this other type is
float, the assignments discard the fractional part. If this other type
is something else, the result can be even worse.

There are two ways to fix this discrepancy. One is to change the type of the
global, and the other is to change the type of the variables it is assigned
to and from. The former looks cleaner, but it would mean that every call
to the get predicate would require a boxing operation, and that can be
relatively slow, since (e.g. for floats) it may require allocating a heap cell.
This diff implements both solutions.

We use the second solution on the LLDS backend because of its better
performance. We have to use the first solution on the MLDS backend,
because on that backend the type of the mutable variable is reflected
in the signature of the getter and setter predicates (whereas on the
LLDS backend all arguments are always MR_Words).

compiler/options.m:
	Add an internal-only option that controls whether we use the first
	solution or the second.

compiler/handle_options.m:
	Make the MLDS backend imply the first solution.

compiler/prog_data.m:
	For each argument of a foreign_proc item, record whether we want to
	keep it boxed in the foreign code.

	Add a foreign_proc attribute that asks for every arg to be kept boxed.
	We can attach this to the mutable implementation foreign_procs we write
	out to .opt files. This attribute is deliberately undocumented since
	users should never use it.

compiler/make_hlds_passes.m:
	For each argument of the get and set foreign_procs we create for
	mutables, record that we do want to keep it boxed.

	Move the action of creating the foreign code for the mutable's
	declaration and definition to the third pass, since during the second
	pass we don't necessarily know yet what its foreign type is (we may not
	have processed a foreign_type declaration affecting it). Move the code
	for creating the foreign code here from prog_mutable, since it depends
	on the HLDS (and prog_mutable.m is in the parse_tree package).

	Hoist some error handling code to put it where it belongs,
	and to avoid some errors being reported twice.

compiler/hlds_goal.m:
	For each argument of a foreign_proc goal, record whether we want to
	keep it boxed in the foreign code.

compiler/llds_out.m:
compiler/pragma_c_gen.m:
compiler/ml_code_gen.m:
compiler/ml_call_gen.m:
	If a foreign_proc argument is noted as being kept boxed in the
	foreign_proc code, then keep it that way.

compiler/prog_io_pragma.m:
	Parse the new foreign_proc annotation.

compiler/simplify.m:
	If a foreign_proc has the always_boxed annotation, attach this info
	to each of its args. We do this here because simplify is guaranteed
	to be executed before all the code that may inspect these arguments.
	Since nothing ever deletes an always_boxed annotation of a foreign_proc
	arg, the code that attaches the annotation is idempotent, so the fact
	that the compiler executes simplify twice is not a problem.

compiler/*.m:
	Minor changes to conform to the changes in data structures above.

compiler/prog_type.m:
	Move a function definition from prog_mutable to prog_type, and
	fix the lack of abstraction in its code.

compiler/prog_mutable.m:
	Delete the code moved to make_hlds_passes.m and prog_type.m.

compiler/notes/compiler_design.html:
	Make the documentation of prog_mutable.m easier to read in source.

tests/hard_coded/float_gv.{m,exp}:
	An extended version of Greg's code as a new test case.

tests/hard_coded/Mmakefile:
	Enable the new test case.

tests/hard_coded/sub-modules/non_word_mutable.{m,exp}:
tests/hard_coded/sub-modules/non_word_mutable.child.m:
	A version of the float_gv test case in which the compiler-generated
	get and set foreign_procs should end up in .opt files.

tests/hard_coded/sub-modules/Mmakefile:
tests/hard_coded/sub-modules/Mercury_options:
	Enable the new test case, and make it execute with intermodule
	optimization.

tests/invalid/bad_mutable.err_exp:
	Expect the new output (in which an error is reported just once,
	not twice).
2006-02-24 01:42:19 +00:00

152 lines
3.2 KiB
Mathematica

% vim: ft=mercury sw=4 ts=4 expandtab
%
% This regression test is an expanded version of the program files by Greg Duck
% in a bug report on Feb 22 2006.
:- module float_gv.
:- interface.
:- import_module io.
:- pred main(io::di,io::uo) is det.
:- implementation.
:- import_module float.
:- type c ---> c(int, int).
:- type coord.
:- pragma foreign_type(c, coord, "coord *").
:- pragma foreign_decl(c, "
typedef struct {
int x, y;
} coord;
").
:- func new_coord(int, int) = coord.
:- func x(coord) = int.
:- func y(coord) = int.
:- pragma foreign_proc(c,
new_coord(X::in, Y::in) = (C::out),
[will_not_call_mercury, promise_pure],
"
C = MR_GC_NEW(coord);
C->x = X;
C->y = Y;
").
:- pragma foreign_proc(c,
x(C::in) = (X::out),
[will_not_call_mercury, promise_pure],
"
X = C->x;
").
:- pragma foreign_proc(c,
y(C::in) = (Y::out),
[will_not_call_mercury, promise_pure],
"
Y = C->y;
").
:- mutable(gv1,float,0.0,ground,[untrailed]).
:- mutable(gv2,float,2.3,ground,[untrailed]).
:- mutable(gv3,string,"",ground,[untrailed]).
:- mutable(gv4,string,"def",ground,[untrailed]).
:- mutable(gv5,coord,new_coord(0, 0),ground,[untrailed]).
:- mutable(gv6,coord,new_coord(2, 3),ground,[untrailed]).
:- pragma promise_pure(main/2).
main(!IO) :-
% Check whether we get back the same value as we set.
GV1Init = 1.2,
write(GV1Init,!IO),
nl(!IO),
impure set_gv1(GV1Init),
semipure get_gv1(GV1Final),
write(GV1Final,!IO),
nl(!IO),
( GV1Init = GV1Final ->
true
;
write_string("GV1 NOT SAME!\n",!IO)
),
% Check whether we get back the same value as the initialization.
GV2Init = 2.3,
write(GV2Init,!IO),
nl(!IO),
semipure get_gv2(GV2Final),
write(GV2Final,!IO),
nl(!IO),
( GV2Init = GV2Final ->
true
;
write_string("GV2 NOT SAME!\n",!IO)
),
% Check whether we get back the same value as we set.
GV3Init = "abc",
write(GV3Init,!IO),
nl(!IO),
impure set_gv3(GV3Init),
semipure get_gv3(GV3Final),
write(GV3Final,!IO),
nl(!IO),
( GV3Init = GV3Final ->
true
;
write_string("GV3 NOT SAME!\n",!IO)
),
% Check whether we get back the same value as the initialization.
GV4Init = "def",
write(GV4Init,!IO),
nl(!IO),
semipure get_gv4(GV4Final),
write(GV4Final,!IO),
nl(!IO),
( GV4Init = GV4Final ->
true
;
write_string("GV4 NOT SAME!\n",!IO)
),
% Check whether we get back the same value as we set.
GV5Init = new_coord(1, 2),
write(c(x(GV5Init), y(GV5Init)),!IO),
nl(!IO),
impure set_gv5(GV5Init),
semipure get_gv5(GV5Final),
write(c(x(GV5Final), y(GV5Final)),!IO),
nl(!IO),
(
x(GV5Init) = x(GV5Final),
y(GV5Init) = y(GV5Final)
->
true
;
write_string("GV5 NOT SAME!\n",!IO)
),
% Check whether we get back the same value as the initialization.
GV6Init = new_coord(2, 3),
write(c(x(GV6Init), y(GV6Init)),!IO),
nl(!IO),
semipure get_gv6(GV6Final),
write(c(x(GV6Final), y(GV6Final)),!IO),
nl(!IO),
(
x(GV6Init) = x(GV6Final),
y(GV6Init) = y(GV6Final)
->
true
;
write_string("GV6 NOT SAME!\n",!IO)
).