Files
mercury/compiler/llds.m
Simon Taylor 2725b1a331 Aditi update syntax, type and mode checking.
Estimated hours taken: 220

Aditi update syntax, type and mode checking.

Change the hlds_goal for constructions in preparation for
structure reuse to avoid making multiple conflicting changes.

compiler/hlds_goal.m:
	Merge `higher_order_call' and `class_method_call' into a single
	`generic_call' goal type. This also has alternatives for the
	various Aditi builtins for which type declarations can't
	be written.

	Remove the argument types field from higher-order/class method calls.
	It wasn't used often, and wasn't updated by optimizations
	such as inlining. The types can be obtained from the vartypes
	field of the proc_info.

	Add a `lambda_eval_method' field to lambda_goals.

	Add a field to constructions to identify which RL code fragment should
	be used for an top-down Aditi closure.

	Add fields to constructions to hold structure reuse information.
	This is currently ignored -- the changes to implement structure
	reuse will be committed to the alias branch.
	This is included here to avoid lots of CVS conflicts caused by
	changing the definition of `hlds_goal' twice.

	Add a field to `some' goals to specify whether the quantification
	can be removed. This is used to make it easier to ensure that
	indexes are used for updates.

	Add a field to lambda_goals to describe whether the modes were
	guessed by the compiler and may need fixing up after typechecking
	works out the argument types.

	Add predicate `hlds_goal__generic_call_id' to work out a call_id
	for a generic call for use in error messages.

compiler/purity.m:
compiler/post_typecheck.m:
	Fill in the modes of Aditi builtin calls and closure constructions.
	This needs to know which are the `aditi__state' arguments, so
	it must be done after typechecking.

compiler/prog_data.m:
	Added `:- type sym_name_and_arity ---> sym_name/arity'.

	Add a type `lambda_eval_method', which describes how a closure
	is to be executed. The alternatives are normal Mercury execution,
	bottom-up execution by Aditi and top-down execution by Aditi.

compiler/prog_out.m:
	Add predicate `prog_out__write_sym_name_and_arity', which
	replaces duplicated inline code in a few places.

compiler/hlds_data.m:
	Add a `lambda_eval_method' field to `pred_const' cons_ids and
	`pred_closure_tag' cons_tags.

compiler/hlds_pred.m:
	Remove type `pred_call_id', replace it with type `simple_call_id',
	which combines a `pred_or_func' and a `sym_name_and_arity'.

	Add a type `call_id' which describes all the different types of call,
	including normal calls, higher-order and class-method calls
	and Aditi builtins.

	Add `aditi_top_down' to the type `marker'.

	Remove `aditi_interface' from type `marker'. Interfacing to
	Aditi predicates is now handled by `generic_call' hlds_goals.

	Add a type `rl_exprn_id' which identifies a predicate to
	be executed top-down by Aditi.
	Add a `maybe(rl_exprn_id)'  field to type `proc_info'.

	Add predicate `adjust_func_arity' to convert between the arity
	of a function to its arity as a predicate.

	Add predicates `get_state_args' and `get_state_args_det' to
	extract the DCG state arguments from an argument list.

	Add predicate `pred_info_get_call_id' to get a `simple_call_id'
	for a predicate for use in error messages.

compiler/hlds_out.m:
	Write the new representation for call_ids.

	Add a predicate `hlds_out__write_call_arg_id' which
	replaces similar code in mode_errors.m and typecheck.m.

compiler/prog_io_goal.m:
	Add support for `aditi_bottom_up' and `aditi_top_down' annotations
	on pred expressions.

compiler/prog_io_util.m:
compiler/prog_io_pragma.m:
	Add predicates
	- `prog_io_util:parse_name_and_arity' to parse `SymName/Arity'
		(moved from prog_io_pragma.m).
	- `prog_io_util:parse_pred_or_func_name_and_arity to parse
		`pred SymName/Arity' or `func SymName/Arity'.
	- `prog_io_util:parse_pred_or_func_and_args' to parse terms resembling
		a clause head (moved from prog_io_pragma.m).

compiler/type_util.m:
	Add support for `aditi_bottom_up' and `aditi_top_down' annotations
	on higher-order types.

	Add predicates `construct_higher_order_type',
	`construct_higher_order_pred_type' and
	`construct_higher_order_func_type' to avoid some code duplication.

compiler/mode_util.m:
	Add predicate `unused_mode/1', which returns `builtin:unused'.
	Add functions `aditi_di_mode/0', `aditi_ui_mode/0' and
	`aditi_uo_mode/0' which return `in', `in', and `out', but will
	be changed to return `di', `ui' and `uo' when alias tracking
	is implemented.

compiler/goal_util.m:
	Add predicate `goal_util__generic_call_vars' which returns
	any arguments to a generic_call which are not in the argument list,
	for example the closure passed to a higher-order call or
	the typeclass_info for a class method call.

compiler/llds.m:
compiler/exprn_aux.m:
compiler/dupelim.m:
compiler/llds_out.m:
compiler/opt_debug.m:
	Add builtin labels for the Aditi update operations.

compiler/hlds_module.m:
	Add predicate predicate_table_search_pf_sym, used for finding
	possible matches for a call with the wrong number of arguments.

compiler/intermod.m:
	Don't write predicates which build `aditi_top_down' goals,
	because there is currently no way to tell importing modules
	which RL code fragment to use.

compiler/simplify.m:
	Obey the `cannot_remove' field of explicit quantification goals.

compiler/make_hlds.m:
	Parse Aditi updates.

	Don't typecheck clauses for which syntax errors in Aditi updates
	are found - this avoids spurious "undefined predicate `aditi_insert/3'"
	errors.

	Factor out some common code to handle terms of the form `Head :- Body'.
	Factor out common code in the handling of pred and func expressions.

compiler/typecheck.m:
	Typecheck Aditi builtins.

	Allow the argument types of matching predicates to be adjusted
	when typechecking the higher-order arguments of Aditi builtins.

	Change `typecheck__resolve_pred_overloading' to take a list of
	argument types rather than a `map(var, type)' and a list of
	arguments to allow a transformation to be performed on the
	argument types before passing them.

compiler/error_util.m:
	Move the part of `report_error_num_args' which writes
	"wrong number of arguments (<x>; expected <y>)" from
	typecheck.m for use by make_hlds.m when reporting errors
	for Aditi builtins.

compiler/modes.m:
compiler/unique_modes.m:
compiler/modecheck_call.m:
	Modecheck Aditi builtins.

compiler/lambda.m:
	Handle the markers for predicates introduced for
	`aditi_top_down' and `aditi_bottom_up' lambda expressions.

compiler/polymorphism.m:
	Add extra type_infos to `aditi_insert' calls
	describing the tuple to insert.

compiler/call_gen.m:
	Generate code for Aditi builtins.

compiler/unify_gen.m:
compiler/bytecode_gen.m:
	Abort on `aditi_top_down' and `aditi_bottom_up' lambda
	expressions - code generation for them is not yet implemented.

compiler/magic.m:
	Use the `aditi_call' generic_call rather than create
	a new procedure for each Aditi predicate called from C.

compiler/rl_out.pp:
compiler/rl_gen.m:
compiler/rl.m:
	Move some utility code used by magic.m and call_gen.m into rl.m.

	Remove an XXX comment about reference counting being not yet
	implemented - Evan has fixed that.

library/ops.m:
compiler/mercury_to_mercury.m:
doc/transition_guide.texi:
	Add unary prefix operators `aditi_bottom_up' and `aditi_top_down',
	used as qualifiers on lambda expressions.
	Add infix operator `==>' to separate the tuples in an
	`aditi_modify' call.

compiler/follow_vars.m:
	Thread a `map(prog_var, type)' through, needed because
	type information is no longer held in higher-order call goals.

compiler/table_gen.m:
	Use the `make_*_construction' predicates in hlds_goal.m
	to construct constants.

compiler/*.m:
	Trivial changes to add extra fields to hlds_goal structures.

doc/reference_manual.texi:
	Document Aditi updates.

	Use @samp{pragma base_relation} instead of
	@samp{:- pragma base_relation} throughout the Aditi documentation
	to be consistent with other parts of the reference manual.

tests/valid/Mmakefile:
tests/valid/aditi_update.m:
tests/valid/aditi.m:
	Test case.

tests/valid/Mmakefile:
	Remove some hard-coded --intermodule-optimization rules which are
	no longer needed because `mmake depend' is now run in this directory.

tests/invalid/*.err_exp:
	Fix expected output for changes in reporting of call_ids
	in typecheck.m.

tests/invalid/Mmakefile
tests/invalid/aditi_update_errors.{m,err_exp}:
tests/invalid/aditi_update_mode_errors.{m,err_exp}:
	Test error messages for Aditi updates.

tests/valid/aditi.m:
tests/invalid/aditi.m:
	Cut down version of extras/aditi/aditi.m to provide basic declarations
	for Aditi compilation such as `aditi__state' and the modes
	`aditi_di', `aditi_uo' and `aditi_ui'. Installing extras/aditi/aditi.m
	somewhere would remove the need for these.
1999-07-13 08:55:28 +00:00

1216 lines
42 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1993-1999 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
% LLDS - The Low-Level Data Structure.
% This module defines the LLDS data structure itself.
% Main authors: conway, fjh.
%-----------------------------------------------------------------------------%
:- module llds.
:- interface.
:- import_module hlds_pred, hlds_data, tree, prog_data, (inst).
:- import_module builtin_ops.
:- import_module bool, assoc_list, list, map, set, std_util.
%-----------------------------------------------------------------------------%
:- type code_model
---> model_det % functional & total
; model_semi % just functional
; model_non. % not functional
%-----------------------------------------------------------------------------%
% c_interface_info holds information used when generating
% code that uses the C interface.
:- type c_interface_info
---> c_interface_info(
module_name,
% info about stuff imported from C:
c_header_info,
c_body_info,
% info about stuff exported to C:
c_export_decls,
c_export_defns
).
:- type c_header_info == list(c_header_code). % in reverse order
:- type c_body_info == list(c_body_code). % in reverse order
:- type c_header_code == pair(string, prog_context).
:- type c_body_code == pair(string, prog_context).
:- type c_export_defns == list(c_export).
:- type c_export_decls == list(c_export_decl).
:- type c_export_decl
---> c_export_decl(
string, % return type
string, % function name
string % argument declarations
).
% the code for `pragma export' is generated directly as strings
% by export.m.
:- type c_export == string.
%-----------------------------------------------------------------------------%
:- import_module continuation_info.
:- type global_data.
:- pred global_data_init(global_data::out) is det.
:- pred global_data_add_new_proc_var(global_data::in,
pred_proc_id::in, comp_gen_c_var::in, global_data::out) is det.
:- pred global_data_add_new_proc_layout(global_data::in,
pred_proc_id::in, proc_layout_info::in, global_data::out) is det.
:- pred global_data_update_proc_layout(global_data::in,
pred_proc_id::in, proc_layout_info::in, global_data::out) is det.
:- pred global_data_add_new_non_common_static_datas(global_data::in,
list(comp_gen_c_data)::in, global_data::out) is det.
:- pred global_data_maybe_get_proc_layout(global_data::in, pred_proc_id::in,
proc_layout_info::out) is semidet.
:- pred global_data_get_proc_layout(global_data::in, pred_proc_id::in,
proc_layout_info::out) is det.
:- pred global_data_get_all_proc_vars(global_data::in,
list(comp_gen_c_var)::out) is det.
:- pred global_data_get_all_proc_layouts(global_data::in,
list(proc_layout_info)::out) is det.
:- pred global_data_get_all_non_common_static_data(global_data::in,
list(comp_gen_c_data)::out) is det.
%-----------------------------------------------------------------------------%
%
% The type `c_file' is the actual LLDS.
%
:- type c_file
---> c_file(
module_name,
c_header_info,
list(user_c_code),
list(c_export),
list(comp_gen_c_var),
list(comp_gen_c_data),
list(comp_gen_c_module)
).
% Some C code from a `pragma c_code' declaration that is not
% associated with a given procedure.
:- type user_c_code
---> user_c_code(
string, % C code
term__context % source code location
).
% Global variables generated by the compiler.
:- type comp_gen_c_var
---> tabling_pointer_var(
module_name, % The basename of this C file.
proc_label % The id of the procedure
% whose table this variable
% represents.
).
% Global data generated by the compiler. Usually readonly,
% with one exception: data containing code addresses must
% be initialized.
:- type comp_gen_c_data
---> comp_gen_c_data(
module_name, % The basename of this C file.
data_name, % A representation of the name
% of the variable; it will be
% qualified with the basename.
bool, % Should this item be exported
% from this Mercury module?
% XXX Actually this field is
% redundant; see linkage/2
% in llds_out.m.
list(maybe(rval)), % The arguments of the create.
create_arg_types, % May specify the types of the
% arguments of the create.
list(pred_proc_id) % The procedures referenced.
% Used by dead_proc_elim.
)
; trace_call_info(
% This structure contains all the information
% we pass to a particular call to MR_trace_struct.
label, % The label corresponding
% to this point in the code,
% whose layout structure
% describes the current
% contents of registers and
% stack slots.
string, % A representation of the
% goal_path of the current
% position in the procedure.
int, % The number of the highest
% numbered r register that is
% in use at the time of the
% call.
trace_port % The type of port we are
% tracing.
).
:- type comp_gen_c_module
---> comp_gen_c_module(
string, % the name of this C module
list(c_procedure) % code
).
:- type c_procedure
---> c_procedure(
string, % predicate name
int, % arity
pred_proc_id, % the pred_proc_id this code
list(instruction) % the code for this procedure
).
:- type llds_proc_id == int.
% we build up instructions as trees and then flatten
% the tree to get a list.
:- type code_tree == tree(list(instruction)).
:- type instruction == pair(instr, string).
% instruction, comment
:- type call_model
---> det
; semidet
; nondet(bool).
% `instr' defines the various LLDS virtual machine instructions.
% Each instruction gets compiled to a simple piece of C code
% or a macro invocation.
:- type instr
---> comment(string)
% Insert a comment into the output code.
; livevals(set(lval))
% A list of which registers and stack locations
% are currently live.
; block(int, int, list(instruction))
% block(NumIntTemps, NumFloatTemps, Instrs):
% A list of instructions that make use of
% some local temporary variables.
; assign(lval, rval)
% assign(Location, Value):
% Assign the value specified by rval to the location
% specified by lval.
; call(code_addr, code_addr, list(liveinfo), call_model)
% call(Target, Continuation, _, _) is the same as
% succip = Continuation; goto(Target).
% The third argument is the live value info for the
% values live on return. The last gives the model
% of the called procedure, and if it is nondet,
% says whether tail recursion elimination is
% potentially applicable to the call.
; mkframe(nondet_frame_info, code_addr)
% mkframe(NondetFrameInfo, CodeAddr) creates a nondet
% stack frame. NondetFrameInfo says whether the frame
% is an ordinary frame, containing the variables of a
% model_non procedure, or a temp frame used only for
% its redoip/redofr slots. If the former, it also
% gives the details of the size of the variable parts
% of the frame (temp frames have no variable sized
% parts). CodeAddr is the code address to branch to
% when trying to generate the next solution from this
% choice point.
; label(label)
% Defines a label that can be used as the
% target of calls, gotos, etc.
; goto(code_addr)
% goto(Target)
% Branch to the specified address.
% Note that jumps to do_fail, do_redo, etc., can get
% optimized into the invocations of macros
% fail(), redo(), etc..
; computed_goto(rval, list(label))
% Evaluate rval, which should be an integer,
% and jump to the (rval+1)th label in the list.
% e.g. computed_goto(2, [A, B, C, D])
% will branch to label C.
; c_code(string)
% Do whatever is specified by the string,
% which can be any piece of C code that
% does not have any non-local flow of control.
; if_val(rval, code_addr)
% If rval is true, then goto code_addr.
; incr_hp(lval, maybe(tag), rval, string)
% Get a memory block of a size given by an rval
% and put its address in the given lval,
% possibly after tagging it with a given tag.
% The string gives the name of the type constructor
% of the memory cell for use in memory profiling.
; mark_hp(lval)
% Tell the heap sub-system to store a marker
% (for later use in restore_hp/1 instructions)
% in the specified lval
; restore_hp(rval)
% The rval must be a marker as returned by mark_hp/1.
% The effect is to deallocate all the memory which
% was allocated since that call to mark_hp.
; store_ticket(lval)
% Allocate a new "ticket" and store it in the lval.
; reset_ticket(rval, reset_trail_reason)
% The rval must specify a ticket allocated with
% `store_ticket' and not yet invalidated or
% deallocated.
% If undo_reason is `undo' or `exception', restore
% any mutable global state to the state it was in when
% the ticket was obtained with store_ticket();
% invalidates any tickets allocated after this one.
% If undo_reason is `commit' or `solve', leave the state
% unchanged, just check that it is safe to commit
% to this solution (i.e. that there are no outstanding
% delayed goals -- this is the "floundering" check).
% Note that we do not discard trail entries after
% commits, because that would in general be unsafe.
%
% Any invalidated ticket is useless and should
% be deallocated with either `discard_ticket'
% or `discard_tickets_to'.
; discard_ticket
% Deallocates the most-recently allocated ticket.
; mark_ticket_stack(lval)
% Tell the trail sub-system to store a ticket counter
% (for later use in discard_tickets_upto)
% in the specified lval.
; discard_tickets_to(rval)
% The rval must be a ticket counter obtained via
% `mark_ticket_stack' and not yet invalidated.
% Deallocates any trail tickets allocated after
% the corresponding call to mark_ticket_stack.
% Invalidates any later ticket counters.
; incr_sp(int, string)
% Increment the det stack pointer. The string is
% the name of the procedure, for use in stack dumps.
% It is used only in grades in which stack dumps are
% enabled (i.e. not in grades where SPEED is defined).
; decr_sp(int)
% Decrement the det stack pointer.
; pragma_c(list(pragma_c_decl), list(pragma_c_component),
may_call_mercury, maybe(label), bool)
% The first argument says what local variable
% declarations are required for the following
% components, which in turn can specify how
% the inputs should be placed in their variables,
% how the outputs should be picked up from their
% variables, and C code both from the program
% and the compiler. These components can be
% sequenced in various ways. This flexibility
% is needed for nondet pragma C codes, which
% need different copies of several components
% for different paths tthrough the code.
%
% The third argument says whether the user C code
% components may call Mercury; certain optimizations
% can be performed across pragma_c instructions that
% cannot call Mercury.
%
% Some components in some pragma_c instructions
% refer to a Mercury label. If they do, we must
% prevent the label from being optimized away.
% To make it known to labelopt, we mention it in
% the fourth arg.
%
% The fifth argument says whether the contents
% of the pragma C code can refer to stack slots.
% User-written shouldn't refer to stack slots,
% the question is whether the compiler-generated
% C code does.
; init_sync_term(lval, int)
% Initialize a synchronization term.
% the first arguement contains the lvalue into
% which we will store the synchronization term,
% and the second argument indicates how many
% branches we expect to join at the end of the
% parallel conjunction.
% (See the documentation in par_conj_gen.m and
% runtime/context.{c,h} for further information about
% synchronisation terms.)
; fork(label, label, int)
% Create a new context.
% fork(Child, Parent, NumSlots) creates a new thread
% which will start executing at Child. After spawning
% execution in the child, control branches to Parent.
% NumSlots is the number of stack slots that need to
% be copied to the child's stack (see comments in
% runtime/context.{h,c}).
; join_and_terminate(lval)
% Signal that this thread of execution has finished in
% the current parallel conjunction, then terminate it.
% The synchronisation term is specified by the
% given lval. (See the documentation in par_conj_gen.m
% and runtime/context.{c,h} for further information
% about synchronisation terms.)
; join_and_continue(lval, label)
% Signal that this thread of execution has finished
% in the current parallel conjunction, then branch to
% the given label. The synchronisation
% term is specified by the given lval.
.
:- type nondet_frame_info
---> temp_frame(
temp_frame_type
)
; ordinary_frame(
string, % Name of the predicate.
int, % Number of framevar slots.
maybe(pragma_c_struct) % If yes, the frame should
% also contain this struct
% (for use by a model_non
% pragma C code).
).
% Temporary frames on the nondet stack exist only to provide a failure
% environment, i.e. a place to store a redoip and a redofr. Accurate
% garbage collection and execution tracing need to know how to
% interpret the layout information associated with the label whose
% address is in the redoip slot. If the label is in a procedure that
% stores its variables on the nondet stack, the redofr slot will give
% the address of the relevant stack frame. If the label is in a
% procedure that stores its variables on the det stack, the temporary
% frame will contain an extra slot containing the address of the
% relevant frame on the det stack.
:- type temp_frame_type
---> det_stack_proc
; nondet_stack_proc.
% Procedures defined by nondet pragma C codes must have some way of
% preserving information after a success, so that when control
% backtracks to the procedure, the C code knows what to do.
% Our implementation saves this information in a C struct.
% Programmers must include the declaration of the fields of this
% C struct in the `pragma c_code' declaration itself.
% A pragma_c_struct holds information about this C struct.
:- type pragma_c_struct
---> pragma_c_struct(
string, % The name of the struct tag.
string, % The field declarations, supplied
% by the user in the `pragma c_code'
% declaration.
maybe(prog_context)
% Where the field declarations
% originally appeared.
).
% A pragma_c_decl holds the information needed for the declaration
% of a local variable in a block of C code emitted for a pragma_c
% instruction.
:- type pragma_c_decl
---> pragma_c_arg_decl(
% This local variable corresponds to a procedure arg.
type, % The Mercury type of the argument.
string % The name of the local variable that
% will hold the value of that argument
% inside the C block.
)
; pragma_c_struct_ptr_decl(
% This local variable holds the address of the
% save struct.
string, % The name of the C struct tag of the save
% struct; the type of the local variable
% will be a pointer to a struct with this tag.
string % The name of the local variable.
).
% A pragma_c_component holds one component of a pragma_c instruction.
:- type pragma_c_component
---> pragma_c_inputs(list(pragma_c_input))
; pragma_c_outputs(list(pragma_c_output))
; pragma_c_user_code(maybe(prog_context), string)
; pragma_c_raw_code(string).
% A pragma_c_input represents the code that initializes one
% of the input variables for a pragma_c instruction.
:- type pragma_c_input
---> pragma_c_input(string, type, rval).
% variable name, type, variable value.
% A pragma_c_output represents the code that stores one of
% of the outputs for a pragma_c instruction.
:- type pragma_c_output
---> pragma_c_output(lval, type, string).
% where to put the output val, type and name
% of variable containing the output val
% see runtime/mercury_trail.h
:- type reset_trail_reason
---> undo
; commit
; solve
; exception
; gc
.
% The kinds of ports for which the code we generate will
% call MR_trace. The redo port is not on this list, because for that
% port the code that calls MR_trace is not in compiler-generated code,
% but in the runtime system. Likewise for the exception port.
% (The same comment applies to the type `external_trace_port'
% in trace.m.)
:- type trace_port
---> call
; exit
; fail
; ite_then
; ite_else
; switch
; disj
; nondet_pragma_first
; nondet_pragma_later.
% Each call instruction has a list of liveinfo, which stores
% information about which variables are live after the call
% (that is, on return). The information is intended for use by
% the non-conservative garbage collector.
:- type liveinfo
---> live_lvalue(
layout_locn,
% What location does this lifeinfo structure
% refer to?
live_value_type,
% What is the type of this live value?
map(tvar, set(layout_locn))
% For each tvar that is a parameter of the
% type of this value, give the set of
% locations where the type_info variable
% describing the actual type bound to the
% type parameter may be found.
%
% We record all the locations of the typeinfo,
% in case different paths of arriving a this
% program point leave the typeinfo in different
% sets of locations. However, there must be at
% least type_info location that is valid
% along all paths leading to this point.
).
% For an explanation of this type, see the comment on
% stack_layout__represent_locn.
:- type layout_locn
---> direct(lval)
; indirect(lval, int).
% live_value_type describes the different sorts of data that
% can be considered live.
:- type live_value_type
---> succip % A stored succip.
; curfr % A stored curfr.
; maxfr % A stored maxfr.
; redoip % A stored redoip.
; redofr % A stored redofr.
; hp % A stored heap pointer.
; var(prog_var, string, type, inst)
% A variable (the var number
% and name are for execution
% tracing; we have to store
% the name here because when
% we want to use the
% live_value_type, we won't
% have access to the varset).
; unwanted. % Something we don't need,
% or at least don't need
% information about.
% An lval represents a data location or register that can be used
% as the target of an assignment.
:- type lval --->
/* virtual machine registers */
reg(reg_type, int)
% One of the general-purpose virtual machine
% registers (either an int or float reg).
; succip % Virtual machine register holding the
% return address for det/semidet code.
; maxfr % Virtual machine register holding a pointer
% to the top of nondet stack.
; curfr % Virtual machine register holding a pointer
% to the current nondet stack frame.
; hp % Virtual machine register holding the heap
% pointer.
; sp % Virtual machine register point to the
% top of det stack.
; temp(reg_type, int)
% A local temporary register.
% These temporary registers are actually
% local variables declared in `block'
% instructions. They may only be
% used inside blocks. The code generator
% doesn't generate these; they are introduced
% by value numbering. The basic idea is
% to improve efficiency by using local
% variables that the C compiler may be
% able to allocate in a register rather than
% using stack slots.
/* values on the stack */
; stackvar(int) % A det stack slot. The number is the offset
% relative to the current value of `sp'.
% These are used in both det and semidet code.
% Stackvar slot numbers start at 1.
; framevar(int) % A nondet stack slot. The reference is
% relative to the current value of `curfr'.
% These are used in nondet code.
% Framevar slot numbers start at 0.
; succip(rval) % The succip slot of the specified
% nondet stack frame; holds the code address
% to jump to on successful exit from this
% nondet procedure.
; redoip(rval) % The redoip slot of the specified
% nondet stack frame; holds the code address
% to jump to on failure.
; redofr(rval) % the redofr slot of the specified
% nondet stack frame; holds the address of
% the frame that the curfr register should be
% set to when backtracking through the redoip
% slot.
; succfr(rval) % The succfr slot of the specified
% nondet stack frame; holds the address of
% caller's nondet stack frame. On successful
% exit from this nondet procedure, we will
% set curfr to this value.
; prevfr(rval) % The prevfr slot of the specified
% nondet stack frame; holds the address of
% the previous frame on the nondet stack.
/* values on the heap */
; field(maybe(tag), rval, rval)
% field(Tag, Address, FieldNum)
% selects a field of a compound term.
% Address is a tagged pointer to a cell
% on the heap; the offset into the cell
% is FieldNum words. If Tag is yes, the
% arg gives the value of the tag; if it is
% no, the tag bits will have to be masked off.
% The value of the tag should be given if
% it is known, since this will lead to
% faster code.
/* values somewhere in memory */
; mem_ref(rval) % A word in the heap, in the det stack or
% in the nondet stack. The rval should have
% originally come from a mem_addr rval.
/* pseudo-values */
; lvar(prog_var). % The location of the specified variable.
% `var' lvals are used during code generation,
% but should not be present in the LLDS at any
% stage after code generation.
% An rval is an expression that represents a value.
:- type rval
---> lval(lval)
% The value of an `lval' rval is just the value stored in
% the specified lval.
; var(prog_var)
% The value of a `var' rval is just the value of the
% specified variable.
% `var' rvals are used during code generation,
% but should not be present in the LLDS at any
% stage after code generation.
; create(tag, list(maybe(rval)), create_arg_types,
static_or_dynamic, int, string)
% create(Tag, Arguments, MaybeArgTypes, StaticOrDynamic,
% LabelNumber, CellKind):
% A `create' instruction is used during code generation
% for creating a term, either on the heap or
% (if the term is constant) as a static constant.
% After code generation, only constant term create() rvals
% should be present in the LLDS, others will get transformed
% to incr_hp(..., Tag, Size) plus assignments to the fields.
%
% MaybeArgTypes may explicitly give the C level types of
% the arguments, although usually these types will be implicit.
%
% StaticOrDynamic may say that the cell must be allocated
% dynamically on the heap, because the resulting data structure
% must be unique (e.g. if we're doing to do destructive update
% on it). It may say that the cell must be allocated
% statically, e.g. because the MaybeArgTypes includes
% explicitly specified types that differ in size from Word
% (the code generator cannot fill in such cells).
% Or it may say that this cell can be allocated either way,
% subject to other constraints (e.g. a cell cannot be allocated
% statically unless all of its components are statically
% allocated as well).
%
% The label number is needed for the case when
% we can construct the term at compile-time
% and just reference the label.
%
% The last argument gives the name of the type constructor
% of the function symbol of which this is a cell, for use
% in memory profiling.
%
% For the time being, you must leave the argument types
% implicit if the cell is to be unique. This is because
% (a) the code generator assumes that each argument of a cell
% it creates on the heap is the same size as a Word; (b)
% this assumption may be incorrect with explicitly defined
% argument types.
; mkword(tag, rval)
% Given a pointer and a tag, mkword returns a tagged pointer.
; const(rval_const)
; unop(unary_op, rval)
; binop(binary_op, rval, rval)
; mem_addr(mem_ref).
% The address of a word in the heap, the det stack or
% the nondet stack.
:- type static_or_dynamic
---> must_be_static
; can_be_either
; must_be_dynamic.
% Values of this type specify the C types and therefore the sizes
% of the arguments of a create rval.
%
% If the type is given as yes(LldsType), then the type is the C type
% corresponding to LldsType. If the type is given as no, then the
% type is implicit; it is what llds_out__rval_type_as_arg says
% when given the actual argument.
:- type create_arg_types
---> uniform(maybe(llds_type)) % All the arguments have
% the given C type.
; initial(initial_arg_types, create_arg_types)
% Each element of the assoc
% list N - T specifies that
% the next N arguments have
% type T. The types of the
% remainder of the arguments
% are given by the recursive
% create_arg_types.
; none. % There ought to be no more
% arguments.
:- type initial_arg_types == assoc_list(int, maybe(llds_type)).
:- type mem_ref
---> stackvar_ref(int) % stack slot number
; framevar_ref(int) % stack slot number
; heap_ref(rval, int, int). % the cell pointer,
% the tag to subtract,
% and the field number
:- type rval_const
---> true
; false
; int_const(int)
; float_const(float)
; string_const(string)
; multi_string_const(int, string)
% a string containing embedded NULLs,
% whose real length is given by the integer,
% and not the location of the first NULL
; code_addr_const(code_addr)
; data_addr_const(data_addr)
; label_entry(label).
% the address of the label (uses ENTRY macro).
:- type data_addr
---> data_addr(module_name, data_name).
% module name; which var
:- type data_name
---> common(int)
; type_ctor(base_data, string, arity)
% base_data, type name, type arity
; base_typeclass_info(class_id, string)
% class name & class arity, names and arities of the
% types
; module_layout
% Layout information for the current module.
; proc_layout(label)
% Layout structure for the procedure with the given
% entry label.
; internal_layout(label)
% Layout structure for the given internal label.
; tabling_pointer(proc_label).
% A variable that contains a pointer that points to
% the table used to implement memoization, loopcheck
% or minimal model semantics for the given procedure.
:- type base_data
---> info
% basic information, including special preds
; layout
% layout information
; functors.
% information on functors
:- type reg_type
---> r % general-purpose (integer) regs
; f. % floating point regs
:- type label
---> local(proc_label, int) % not proc entry; internal to a
% procedure
; c_local(proc_label) % proc entry; internal to a C module
; local(proc_label) % proc entry; internal to a Mercury
% module
; exported(proc_label). % proc entry; exported from a Mercury
% module
:- type code_addr
---> label(label) % A label defined in this Mercury
% module.
; imported(proc_label) % A label from another Mercury module.
; succip % The address in the `succip'
% register.
; do_succeed(bool) % The bool is `yes' if there are any
% alternatives left. If the bool is
% `no', we do a succeed_discard()
% rather than a succeed().
; do_redo
; do_fail
; do_trace_redo_fail
% A label in the runtime, the code
% at which calls MR_trace with a
% REDO event and then fails.
; do_call_closure
; do_call_class_method
; do_det_aditi_call
; do_semidet_aditi_call
; do_nondet_aditi_call
; do_aditi_insert
; do_aditi_delete
; do_aditi_bulk_insert
; do_aditi_bulk_delete
; do_aditi_modify
; do_not_reached. % We should never jump to this address.
% A proc_label is a label used for the entry point to a procedure.
% The defining module is the module that provides the code for the
% predicate, the declaring module contains the `:- pred' declaration.
% When these are different, as for specialised versions of predicates
% from `.opt' files, the defining module's name is added as a
% qualifier to the label.
:- type proc_label
---> proc(module_name, pred_or_func, module_name, string,
int, proc_id)
% defining module, predicate/function,
% declaring module, name, arity, mode #
; special_proc(module_name, string, module_name, string, int,
proc_id).
% defining module, pred name, type module,
% type name, type arity, mode #
% A tag (used in mkword, create and field expressions
% and in incr_hp instructions) is a small integer.
:- type tag == int.
% We categorize the data types used in the LLDS into
% a small number of categories, for purposes such as
% choosing the right sort of register for a given value
% to avoid unnecessary boxing/unboxing of floats.
:- type llds_type
---> bool % A boolean value
% represented using the C type `Integer'.
; int_least8 % A signed value that fits that contains
% at least eight bits, represented using the
% C type int_least8_t. Intended for use in
% static data declarations, not for data
% that gets stored in registers, stack slots
% etc.
; uint_least8 % An unsigned version of int_least8,
% represented using the C type uint_least8_t.
; int_least16 % A signed value that fits that contains
% at least sixteen bits, represented using the
% C type int_least16_t. Intended for use in
% static data declarations, not for data
% that gets stored in registers, stack slots
% etc.
; uint_least16 % An unsigned version of int_least16,
% represented using the C type uint_least16_t.
; int_least32 % A signed value that fits that contains
% at least 32 bits, represented using the
% C type int_least32_t. Intended for use in
% static data declarations, not for data
% that gets stored in registers, stack slots
% etc.
; uint_least32 % An unsigned version of intleast_32,
% represented using the C type uint_least32_t.
; integer % A Mercury `int', represented in C as a
% value of type `Integer' (which is
% a signed integral type of the same
% size as a pointer).
; unsigned % Something whose C type is `Unsigned'
% (the unsigned equivalent of `Integer').
; float % A Mercury `float', represented in C as a
% value of type `Float' (which may be either
% `float' or `double', but is usually
% `double').
; string % A Mercury string; represented in C as a
% value of type `String'.
; data_ptr % A pointer to data; represented in C
% as a value of C type `Word *'.
; code_ptr % A pointer to code; represented in C
% as a value of C type `Code *'.
; word. % Something that can be assigned to a value
% of C type `Word', i.e., something whose
% size is a word but which may be either
% signed or unsigned
% (used for registers, stack slots, etc).
% given a non-var rval, figure out its type
:- pred llds__rval_type(rval::in, llds_type::out) is det.
% given a non-var lval, figure out its type
:- pred llds__lval_type(lval::in, llds_type::out) is det.
% given a constant, figure out its type
:- pred llds__const_type(rval_const::in, llds_type::out) is det.
% given a unary operator, figure out its return type
:- pred llds__unop_return_type(unary_op::in, llds_type::out) is det.
% given a unary operator, figure out the type of its argument
:- pred llds__unop_arg_type(unary_op::in, llds_type::out) is det.
% given a binary operator, figure out its return type
:- pred llds__binop_return_type(binary_op::in, llds_type::out) is det.
% given a register, figure out its type
:- pred llds__register_type(reg_type::in, llds_type::out) is det.
% check whether the types of all argument are the same size as word
:- pred llds__all_args_are_word_size(create_arg_types::in, bool::out) is det.
% check whether an arg of the given type is the same size as word
% (floats may be bigger than a word, but if so, they are boxed)
:- pred llds__type_is_word_size_as_arg(llds_type::in, bool::out) is det.
:- implementation.
:- import_module require.
llds__lval_type(reg(RegType, _), Type) :-
llds__register_type(RegType, Type).
llds__lval_type(succip, code_ptr).
llds__lval_type(maxfr, data_ptr).
llds__lval_type(curfr, data_ptr).
llds__lval_type(hp, data_ptr).
llds__lval_type(sp, data_ptr).
llds__lval_type(temp(RegType, _), Type) :-
llds__register_type(RegType, Type).
llds__lval_type(stackvar(_), word).
llds__lval_type(framevar(_), word).
llds__lval_type(succip(_), code_ptr).
llds__lval_type(redoip(_), code_ptr).
llds__lval_type(redofr(_), data_ptr).
llds__lval_type(succfr(_), data_ptr).
llds__lval_type(prevfr(_), data_ptr).
llds__lval_type(field(_, _, _), word).
llds__lval_type(lvar(_), _) :-
error("lvar unexpected in llds__lval_type").
llds__lval_type(mem_ref(_), word).
llds__rval_type(lval(Lval), Type) :-
llds__lval_type(Lval, Type).
llds__rval_type(var(_), _) :-
error("var unexpected in llds__rval_type").
llds__rval_type(create(_, _, _, _, _, _), data_ptr).
%
% Note that create and mkword must both be of type data_ptr,
% not of type word, to ensure that static consts containing
% them get type `const Word *', not type `Word'; this is
% necessary because casts from pointer to int must not be used
% in the initializers for constant expressions -- if they are,
% then lcc barfs, and gcc generates bogus code on some systems,
% (e.g. IRIX with shared libs). If the second argument to mkword
% is an integer, not a pointer, then we will end up casting it
% to a pointer, but casts from integer to pointer are OK, it's
% only the reverse direction we need to avoid.
%
llds__rval_type(mkword(_, _), data_ptr).
llds__rval_type(const(Const), Type) :-
llds__const_type(Const, Type).
llds__rval_type(unop(UnOp, _), Type) :-
llds__unop_return_type(UnOp, Type).
llds__rval_type(binop(BinOp, _, _), Type) :-
llds__binop_return_type(BinOp, Type).
llds__rval_type(mem_addr(_), data_ptr).
llds__const_type(true, bool).
llds__const_type(false, bool).
llds__const_type(int_const(_), integer).
llds__const_type(float_const(_), float).
llds__const_type(string_const(_), data_ptr).
llds__const_type(multi_string_const(_, _), data_ptr).
llds__const_type(code_addr_const(_), code_ptr).
llds__const_type(data_addr_const(_), data_ptr).
llds__const_type(label_entry(_), code_ptr).
llds__unop_return_type(mktag, word).
llds__unop_return_type(tag, word).
llds__unop_return_type(unmktag, word).
llds__unop_return_type(mkbody, word).
llds__unop_return_type(unmkbody, word).
llds__unop_return_type(body, word).
llds__unop_return_type(cast_to_unsigned, unsigned).
llds__unop_return_type(hash_string, integer).
llds__unop_return_type(bitwise_complement, integer).
llds__unop_return_type(not, bool).
llds__unop_arg_type(mktag, word).
llds__unop_arg_type(tag, word).
llds__unop_arg_type(unmktag, word).
llds__unop_arg_type(mkbody, word).
llds__unop_arg_type(unmkbody, word).
llds__unop_arg_type(body, word).
llds__unop_arg_type(cast_to_unsigned, word).
llds__unop_arg_type(hash_string, word).
llds__unop_arg_type(bitwise_complement, integer).
llds__unop_arg_type(not, bool).
llds__binop_return_type((+), integer).
llds__binop_return_type((-), integer).
llds__binop_return_type((*), integer).
llds__binop_return_type((/), integer).
llds__binop_return_type((mod), integer).
llds__binop_return_type((<<), integer).
llds__binop_return_type((>>), integer).
llds__binop_return_type((&), integer).
llds__binop_return_type(('|'), integer).
llds__binop_return_type((^), integer).
llds__binop_return_type((and), bool).
llds__binop_return_type((or), bool).
llds__binop_return_type(eq, bool).
llds__binop_return_type(ne, bool).
llds__binop_return_type(array_index, word).
llds__binop_return_type(str_eq, bool).
llds__binop_return_type(str_ne, bool).
llds__binop_return_type(str_lt, bool).
llds__binop_return_type(str_gt, bool).
llds__binop_return_type(str_le, bool).
llds__binop_return_type(str_ge, bool).
llds__binop_return_type((<), bool).
llds__binop_return_type((>), bool).
llds__binop_return_type((<=), bool).
llds__binop_return_type((>=), bool).
llds__binop_return_type(float_plus, float).
llds__binop_return_type(float_minus, float).
llds__binop_return_type(float_times, float).
llds__binop_return_type(float_divide, float).
llds__binop_return_type(float_eq, bool).
llds__binop_return_type(float_ne, bool).
llds__binop_return_type(float_lt, bool).
llds__binop_return_type(float_gt, bool).
llds__binop_return_type(float_le, bool).
llds__binop_return_type(float_ge, bool).
llds__register_type(r, word).
llds__register_type(f, float).
llds__all_args_are_word_size(uniform(MaybeType), AllWordSize) :-
llds__maybe_type_is_word_size(MaybeType, AllWordSize).
llds__all_args_are_word_size(initial(Init, Rest), AllWordSize) :-
assoc_list__values(Init, MaybeTypes),
list__map(llds__maybe_type_is_word_size, MaybeTypes, InitWordSizes),
llds__all_args_are_word_size(Rest, RestWordSize),
bool__and_list([RestWordSize | InitWordSizes], AllWordSize).
llds__all_args_are_word_size(none, yes).
:- pred llds__maybe_type_is_word_size(maybe(llds_type)::in, bool::out) is det.
llds__maybe_type_is_word_size(no, yes).
llds__maybe_type_is_word_size(yes(Type), IsWordSize) :-
llds__type_is_word_size_as_arg(Type, IsWordSize).
llds__type_is_word_size_as_arg(int_least8, no).
llds__type_is_word_size_as_arg(uint_least8, no).
llds__type_is_word_size_as_arg(int_least16, no).
llds__type_is_word_size_as_arg(uint_least16, no).
llds__type_is_word_size_as_arg(int_least32, no).
llds__type_is_word_size_as_arg(uint_least32, no).
llds__type_is_word_size_as_arg(bool, yes).
llds__type_is_word_size_as_arg(integer, yes).
llds__type_is_word_size_as_arg(unsigned, yes).
llds__type_is_word_size_as_arg(float, yes).
llds__type_is_word_size_as_arg(string, yes).
llds__type_is_word_size_as_arg(data_ptr, yes).
llds__type_is_word_size_as_arg(code_ptr, yes).
llds__type_is_word_size_as_arg(word, yes).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type proc_var_map == map(pred_proc_id, comp_gen_c_var).
:- type proc_layout_map == map(pred_proc_id, proc_layout_info).
:- type global_data
---> global_data(
proc_var_map, % Information about the global
% variables defined by each
% procedure.
proc_layout_map, % Information about the
% layout structures defined
% by each procedure.
list(comp_gen_c_data) % The list of global data
% structures that do not need
% to be checked by llds_common,
% because their construction
% ensures no overlaps.
).
global_data_init(global_data(EmptyDataMap, EmptyLayoutMap, [])) :-
map__init(EmptyDataMap),
map__init(EmptyLayoutMap).
global_data_add_new_proc_var(GlobalData0, PredProcId, ProcVar,
GlobalData) :-
global_data_get_proc_var_map(GlobalData0, ProcVarMap0),
map__det_insert(ProcVarMap0, PredProcId, ProcVar, ProcVarMap),
global_data_set_proc_var_map(GlobalData0, ProcVarMap,
GlobalData).
global_data_add_new_proc_layout(GlobalData0, PredProcId, ProcLayout,
GlobalData) :-
global_data_get_proc_layout_map(GlobalData0, ProcLayoutMap0),
map__det_insert(ProcLayoutMap0, PredProcId, ProcLayout, ProcLayoutMap),
global_data_set_proc_layout_map(GlobalData0, ProcLayoutMap,
GlobalData).
global_data_update_proc_layout(GlobalData0, PredProcId, ProcLayout,
GlobalData) :-
global_data_get_proc_layout_map(GlobalData0, ProcLayoutMap0),
map__det_update(ProcLayoutMap0, PredProcId, ProcLayout, ProcLayoutMap),
global_data_set_proc_layout_map(GlobalData0, ProcLayoutMap,
GlobalData).
global_data_add_new_non_common_static_datas(GlobalData0, NewNonCommonStatics,
GlobalData) :-
global_data_get_non_common_static_data(GlobalData0, NonCommonStatics0),
list__append(NewNonCommonStatics, NonCommonStatics0, NonCommonStatics),
global_data_set_non_common_static_data(GlobalData0, NonCommonStatics,
GlobalData).
global_data_maybe_get_proc_layout(GlobalData0, PredProcId, ProcLayout) :-
global_data_get_proc_layout_map(GlobalData0, ProcLayoutMap),
map__search(ProcLayoutMap, PredProcId, ProcLayout).
global_data_get_proc_layout(GlobalData0, PredProcId, ProcLayout) :-
global_data_get_proc_layout_map(GlobalData0, ProcLayoutMap),
map__lookup(ProcLayoutMap, PredProcId, ProcLayout).
global_data_get_all_proc_vars(GlobalData, ProcVars) :-
global_data_get_proc_var_map(GlobalData, ProcVarMap),
map__values(ProcVarMap, ProcVars).
global_data_get_all_proc_layouts(GlobalData, ProcLayouts) :-
global_data_get_proc_layout_map(GlobalData, ProcLayoutMap),
map__values(ProcLayoutMap, ProcLayouts).
global_data_get_all_non_common_static_data(GlobalData, NonCommonStatics) :-
global_data_get_non_common_static_data(GlobalData, NonCommonStatics).
%-----------------------------------------------------------------------------%
:- pred global_data_get_proc_var_map(global_data::in, proc_var_map::out)
is det.
:- pred global_data_get_proc_layout_map(global_data::in, proc_layout_map::out)
is det.
:- pred global_data_get_non_common_static_data(global_data::in,
list(comp_gen_c_data)::out) is det.
:- pred global_data_set_proc_var_map(global_data::in, proc_var_map::in,
global_data::out) is det.
:- pred global_data_set_proc_layout_map(global_data::in, proc_layout_map::in,
global_data::out) is det.
:- pred global_data_set_non_common_static_data(global_data::in,
list(comp_gen_c_data)::in, global_data::out) is det.
global_data_get_proc_var_map(GD, A) :-
GD = global_data(A, _, _).
global_data_get_proc_layout_map(GD, B) :-
GD = global_data(_, B, _).
global_data_get_non_common_static_data(GD, C) :-
GD = global_data(_, _, C).
global_data_set_proc_var_map(GD0, A, GD) :-
GD0 = global_data(_, B, C),
GD = global_data(A, B, C).
global_data_set_proc_layout_map(GD0, B, GD) :-
GD0 = global_data(A, _, C),
GD = global_data(A, B, C).
global_data_set_non_common_static_data(GD0, C, GD) :-
GD0 = global_data(A, B, _),
GD = global_data(A, B, C).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%