mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
Fix some mispelled variable names.
compiler/accumulator.m:
compiler/check_import_accessibility.m:
compiler/decide_type_repn.m:
compiler/du_type_layout.m:
compiler/error_spec.m:
compiler/hlds_data.m:
compiler/hlds_goal.m:
compiler/mlds.m:
compiler/parse_item.m:
compiler/prog_data.m:
compiler/type_util.m:
As above.
3362 lines
138 KiB
Mathematica
3362 lines
138 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1999-2011 The University of Melbourne.
|
|
% Copyright (C) 2013-2026 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: mlds.m.
|
|
% Main author: fjh.
|
|
%
|
|
% MLDS - The Medium-Level Data Structure.
|
|
%
|
|
% This module defines the MLDS data structure itself.
|
|
% The MLDS is an intermediate data structure used in compilation;
|
|
% we compile Mercury source -> parse tree -> HLDS -> MLDS -> target (e.g. C).
|
|
% See notes/compiler_design.html for more information about the MLDS & LLDS.
|
|
%
|
|
% The MLDS is intended to be suitable for generating target code in
|
|
% languages such as Java, Java bytecode, high-level C, C++, or C--, etc.
|
|
% This is as opposed to the LLDS, which is better suited for generating
|
|
% assembler or the very low-level C or GNU C (lower level than C--) that
|
|
% the original Mercury compiler backend generates. In the LLDS, we are
|
|
% doing all our own register allocation and stack manipulation, but with
|
|
% MLDS, those tasks are the responsibility of the target.
|
|
% The one really important simplification that we do make relative to the
|
|
% HLDS is that MLDS does not have any support for non-determinism, so the
|
|
% HLDS->MLDS compiler must compile non-deterministic code into code that
|
|
% uses continuation passing.
|
|
%
|
|
% The MLDS data structure is quite full-featured, including for example
|
|
% support for arbitrary nesting, multiple return values, and tagged pointers.
|
|
% However, many of the intended target languages do not support
|
|
% all of those features. Therefore the HLDS->MLDS compiler must ensure that
|
|
% the final MLDS code that it eventually generates does not use features
|
|
% that the target does not support. This will (presumably) be accomplished
|
|
% by having handle_options.m set various flags according to the selected
|
|
% target, and having the HLDS->MLDS compiler take account of those flags
|
|
% and either generate simpler MLDS code in the first place, or run some
|
|
% extra simplification passes over the MLDS code before invoking the
|
|
% MLDS->target compiler.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Mapping Mercury names to MLDS names
|
|
%
|
|
%
|
|
% 1. Modules
|
|
%
|
|
% Mercury module names map directly to MLDS package names, except that
|
|
% modules in the Mercury standard library get a `mercury' prefix,
|
|
% resulting in MLDS names such as `mercury.builtin', `mercury.io',
|
|
% and `mercury.univ'.
|
|
%
|
|
% [Rationale: omitting the `mercury' prefix would lead to namespace
|
|
% pollution in the generated target language code.]
|
|
%
|
|
% 2. Procedure names
|
|
%
|
|
% HLDS procedure names map directly to MLDS function names.
|
|
% MLDS function names are structured terms that include sufficient
|
|
% information (arity, pred_or_func indicator, mode number, etc.)
|
|
% to avoid any ambiguities.
|
|
%
|
|
% [Rationale: the reason for keeping structured names rather than flattened
|
|
% names at the MLDS level is that since we do not know what restrictions
|
|
% the target language will impose, we want to do name mangling at the
|
|
% MLDS -> target stage, not at the HLDS -> MLDS stage. For example,
|
|
% some target languages (such as C++, Java) support overloading, while others
|
|
% (such as C) do not.]
|
|
%
|
|
% 3. Procedure signatures
|
|
%
|
|
% MLDS function signatures are determined by the HLDS procedure's
|
|
% argument types, modes, and determinism.
|
|
%
|
|
% We omit from procedure signatures any arguments
|
|
%
|
|
% - whose types are dummy types (such as `io.state' or `store(_)'), or
|
|
% - whose top_mode is top_unused.
|
|
%
|
|
% We pass procedure arguments with top_in modes as input.
|
|
%
|
|
% We normally pass procedure arguments with top_out modes by reference.
|
|
% However, several alternative approaches are also supported (see below).
|
|
%
|
|
% Procedures with determinism model_det need no special handling.
|
|
% Procedures with determinism model_semi must return a boolean.
|
|
% Procedures with determinism model_non get passed a continuation;
|
|
% the procedure must call the continuation once for each success,
|
|
% and when it fails, it must return.
|
|
%
|
|
% With the `--copy-out' option, arguments with top_out modes will be returned
|
|
% by value. This requires the target language to support multiple return
|
|
% values. The MLDS->target code generator can of course handle that by mapping
|
|
% functions with multiple return values into functions that return a struct,
|
|
% array, or tuple.
|
|
% With the `--nondet-copy-out' option, arguments for model_non procedures with
|
|
% top_out modes will be passed as arguments to the continuation.
|
|
%
|
|
% [Rationale: this mapping is designed to be as simple as possible,
|
|
% and to map as naturally to the target language as possible.
|
|
% The complication with the `--copy-out' and `--nondet-copy-out'
|
|
% options is needed to cater to different target languages.
|
|
% Some target languages (such as C) fully support pass-by-reference,
|
|
% and then it is most consistent to use it everywhere.
|
|
% Some (such as MSIL) support pass-by-reference, but do not allow references
|
|
% to be stored in the environment structs needed for continuations,
|
|
% and so cannot use pass-by-reference for nondet procedures.
|
|
% Others (such as Java) do not support pass-by-reference at all.]
|
|
%
|
|
% 4. Variables
|
|
%
|
|
% MLDS variable names are determined by the HLDS variable name and
|
|
% (to avoid ambiguity) variable number. The MLDS variable name is
|
|
% a structured term that keeps the original variable name separate
|
|
% from the distinguishing variable number. It is up to each individual backend
|
|
% to mangle the variable name and number to avoid ambiguity where necessary.
|
|
%
|
|
% [Rationale: the reason for keeping structured names rather than
|
|
% mangled names at the MLDS level is that in some cases the mangling is
|
|
% undesirable, as the original HLDS variable names are what is required
|
|
% (for instance, when interfacing with foreign code which includes
|
|
% references to the original HLDS variable names).]
|
|
%
|
|
% 5. Global data
|
|
%
|
|
% MLDS names for global data are structured; they hold some information
|
|
% about the kind of global data (see the mlds_global_var_name type).
|
|
%
|
|
% 6. Types
|
|
%
|
|
% If there is an MLDS type corresponding to a Mercury type, then the Mercury
|
|
% type name maps directly to the MLDS type name, suitably module-qualified
|
|
% of course. The MLDS type name includes the type constructor arity
|
|
% (arity overloading is allowed). However, when we are using a low-level
|
|
% data representation scheme, then some Mercury types may not have
|
|
% corresponding MLDS type names (in other words, the corresponding MLDS type
|
|
% may just be a generic type such as `MR_Word' or `MR_Box').
|
|
%
|
|
% 7. Data constructors.
|
|
%
|
|
% With --high-level-data, Mercury data constructors normally get mapped to
|
|
% MLDS types nested within the MLDS type for the Mercury data type to which
|
|
% the constructors belong. There are some exceptions; see ml_type_gen.m
|
|
% for full details. The MLDS type name includes the constructor arity.
|
|
%
|
|
% [Rationale: Mercury allows data constructors to be overloaded on
|
|
% their result type within a single module, and also allows them
|
|
% to be overloaded on arity within a single type. Nesting resolves
|
|
% the first problem, and keeping the arity in the MLDS type name
|
|
% resolves the second.]
|
|
% XXX There is a problem in the Java back-end with Mercury types for which
|
|
% the constructor name is the same as the type name.
|
|
%
|
|
% XXX Also need to think about equivalence types and no_tag types.
|
|
%
|
|
% XXX There are problems with the RTTI definitions for the Java back-end.
|
|
% Under the current system, the definitions are output as static variables
|
|
% with static initializers, ordered so that subdefinitions always appear before
|
|
% the definition which uses them. This is necessary because in Java, static
|
|
% initializers are performed at runtime in textual order, and if a definition
|
|
% relies on another static variable for its constructor but said variable has
|
|
% not been initialized, then it is treated as `null' by the JVM with no
|
|
% warning.
|
|
% The problem with this approach is that it will not work for
|
|
% cyclic definitions, eg:
|
|
% :- type foo ---> f(bar) ; g.
|
|
% :- type bar ---> f2(foo) ; g2.
|
|
% At some point this should be changed so that initialization is performed
|
|
% by two phases: first allocate all of the objects, then fill in the fields.
|
|
%
|
|
% 8. Insts and modes
|
|
%
|
|
% Inst and mode definitions do not get translated into MLDS.
|
|
%
|
|
% [Inst and mode definitions do however affect the signatures of the
|
|
% MLDS functions used to implement each procedure.]
|
|
%
|
|
% 9. Type classes.
|
|
%
|
|
% Currently type classes are handled early and at a fairly low level.
|
|
% It is not yet clear how easy it will be to convert this to MLDS.
|
|
% It may depend to some degree on the target language.
|
|
% But if it is possible, then when it is done the target language
|
|
% generator will be able to ignore the issue of type classes.
|
|
%
|
|
% For language interoperability, however, it might be nice if the translation
|
|
% were done at higher level.
|
|
%
|
|
% Mercury type classes should map directly to MLDS interfaces.
|
|
%
|
|
% Mercury instance definitions should map to classes which implement the
|
|
% corresponding interface. Note that if there is an instance declaration
|
|
% `:- instance foo(bar)', then the MLDS type for `bar' will *not* implement
|
|
% the MLDS interface for `foo' -- instead, there will be a new MLDS type
|
|
% for `instance foo(bar)' which implements that interface.
|
|
%
|
|
% XXX The discussion about type classes above looks to be quite out-of-date.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Mapping MLDS names to the target language.
|
|
%
|
|
%
|
|
% Ultimately the exact choice of how MLDS names are mapped to
|
|
% the target language is up to the target language generator.
|
|
% So the remarks here are just guidelines, not strict rules.
|
|
%
|
|
% 1. Names.
|
|
%
|
|
% If the target language has standard conventions about certain categories
|
|
% of names beginning with uppercase or lowercase letters, then the target
|
|
% language generator should generate names that respect those conventions.
|
|
%
|
|
% An MLDS name may contain arbitrary characters. If the target language
|
|
% has restrictions on what names can be used in identifiers, then
|
|
% it is the responsibility of the target language generator to mangle
|
|
% MLDS names accordingly to ensure that they abide by those restrictions.
|
|
%
|
|
% 2. Packages.
|
|
%
|
|
% MLDS packages should be mapped directly to the corresponding notion
|
|
% in the target language, if possible.
|
|
%
|
|
% If the target does not have a notion of packages, then they should be
|
|
% mapped to names of the form "foo.bar.baz", or, if dots are not allowed,
|
|
% then to "foo__bar__baz".
|
|
%
|
|
% 3. Overloading.
|
|
%
|
|
% If the target does not support overloading, then any Mercury names which
|
|
% are overloaded within a single Mercury module must be qualified to avoid
|
|
% ambiguity. However, Mercury names which are not overloaded should not
|
|
% be qualified. If a name is overloaded but occurs only once in the module's
|
|
% interface then the version in the interface should not be qualified.
|
|
% The arity-zero version of type should not be arity-qualified
|
|
% unless this would cause ambiguity with an unqualified name generated by
|
|
% the previous rule. Likewise, the arity-zero version of function
|
|
% (i.e. a constant) should not be function-qualified or arity-qualified
|
|
% unless this would cause ambiguity with an unqualified name generated by
|
|
% the aforementioned rule. The first mode of a predicate should not be
|
|
% mode-qualified.
|
|
%
|
|
% [Rationale: name mangling should be avoided where possible, because
|
|
% this makes it easier for the user to interoperate with other languages
|
|
% and to use tools which do not properly demangle Mercury names.]
|
|
%
|
|
% 4. Procedures.
|
|
%
|
|
% If a procedure name needs to be qualified, the qualification should be
|
|
% done by
|
|
% - appending "_f" for functions or "_p" for predicates,
|
|
% - optionally followed by the arity,
|
|
% - optionally followed by an underscore and then the mode number,
|
|
% - optionally followed by "_i" and then the MLDS function sequence number
|
|
% (for internal MLDS functions, used e.g. to implement backtracking).
|
|
%
|
|
% [Rationale: any qualifiers should go at the end of a name, so that
|
|
% command-line completion works even for mangled names (and hopefully
|
|
% even shows all the possible alternatives...).]
|
|
%
|
|
% To avoid ambiguity as to whether a name is qualified or not,
|
|
% any procedures whose unqualified name matches the pattern for qualified
|
|
% names, i.e. the regular expression `.*_[fp][0-9]*(_[0-9]+)?(_i[0-9]+)?',
|
|
% should always be qualified (even if not overloaded).
|
|
%
|
|
% 5. Types.
|
|
%
|
|
% If a type name needs to be qualified, the qualification should be done
|
|
% by appending an underscore followed by the arity.
|
|
%
|
|
% To avoid ambiguity as to whether a name is qualified or not,
|
|
% any types whose unqualified name matches the pattern for qualified names,
|
|
% i.e. the regular expression `.*_[0-9]+', should always be qualified
|
|
% (even if not overloaded).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Notes on garbage collection and liveness.
|
|
%
|
|
%
|
|
% "Liveness-accurate GC" is GC in which the collector does not trace local
|
|
% variables which are definitely not live according to a straight-forward
|
|
% static analysis of definite-deadness/possible-liveness. Liveness-accurate
|
|
% GC is desirable, in general, since tracing through variables which are
|
|
% no longer live may lead to excessive memory retention for some programs.
|
|
% However these programs are relatively rare, so liveness-accurate GC
|
|
% is not always worth the extra complication.
|
|
%
|
|
% The MLDS is therefore designed to optionally support liveness-accurate
|
|
% GC, if the target language supports it. If liveness-accurate GC is
|
|
% supported and enabled, then it is the responsibility of the target
|
|
% language implementation to do whatever is needed to avoid having the GC
|
|
% trace variables which have gone out of scope.
|
|
%
|
|
% That means that to support liveness-accurate GC the HLDS->MLDS code
|
|
% generator just needs to cover the cases where a straight-forward liveness
|
|
% calculation on the generated MLDS does not match up with the desired
|
|
% result of a straight-forward liveness calculation on the HLDS. That is,
|
|
% the HLDS->MLDS code generator must generate code to clobber variables
|
|
% which are no longer live according to a straight-forward liveness
|
|
% calculation on the HLDS but which have not gone out of scope in
|
|
% the generated MLDS. For example, with our current HLDS->MLDS code
|
|
% generation scheme, this is the case for variables in the `else' of a
|
|
% nondet if-then-else once the `then' has been entered.
|
|
% (XXX Currently ml_code_gen.m does _not_ clobber those variables, though.)
|
|
%
|
|
% The rationale for leaving most of the responsibility for liveness-accurate
|
|
% GC up to the MLDS back-end is as follows: at very least we need the
|
|
% target language implementation's _cooperation_, since if the MLDS code
|
|
% generator inserts statements to clobber variables that are no longer live,
|
|
% then an uncooperative target language implementation could just optimize
|
|
% them away, since they are assignments to dead variables. Given this need
|
|
% for the MLDS target back-end's cooperation, it makes sense to assign as
|
|
% much of the responsibility for this task as is possible to the MLDS target
|
|
% back-end, to keep the front-end simple and to keep the responsibility
|
|
% for this task in one place.
|
|
%
|
|
% But in cases such as nondet if-then-else, where HLDS-liveness does not
|
|
% match MLDS-liveness, we cannot just leave it to the MLDS target back-end,
|
|
% because that would require assuming an unreasonably smart liveness
|
|
% calculation in the MLDS target back-end, so in such cases we do need
|
|
% to handle it in the HLDS->MLDS code generator.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ml_backend.mlds.
|
|
:- interface.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module backend_libs.rtti.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module ml_backend.ml_global_data.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
:- import_module parse_tree.prog_data_pragma.
|
|
:- import_module parse_tree.prog_foreign.
|
|
:- import_module parse_tree.prog_type.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The type `mlds' is the actual MLDS.
|
|
:- type mlds
|
|
---> mlds(
|
|
% The original Mercury module name.
|
|
mlds_name :: mercury_module_name,
|
|
|
|
% The MLDS code itself.
|
|
|
|
% Packages/classes to import.
|
|
mlds_toplevel_imports :: list(mlds_import),
|
|
|
|
mlds_global_defns :: ml_global_data,
|
|
|
|
% Definitions of the classes that represent types
|
|
% with high level data, and the types of the environment
|
|
% structures we introduce while flattening nested functions.
|
|
mlds_type_defns :: list(mlds_class_defn),
|
|
mlds_enum_defns :: list(mlds_enum_class_defn),
|
|
mlds_env_defns :: list(mlds_env_defn),
|
|
|
|
% Definitions of the structures that hold the tables
|
|
% of tabled procedures.
|
|
mlds_table_struct_defns :: list(mlds_global_var_defn),
|
|
|
|
% Definitions of the translated procedures.
|
|
mlds_proc_defns :: list(mlds_function_defn),
|
|
|
|
% The names of init and final preds.
|
|
mlds_init_preds :: list(string),
|
|
mlds_final_preds :: list(string),
|
|
|
|
% Code defined in some other language, e.g. for
|
|
% `pragma foreign_decl', etc.
|
|
mlds_foreign_code_map :: map(foreign_language,
|
|
mlds_foreign_code),
|
|
|
|
mlds_exported_enums :: list(mlds_exported_enum)
|
|
).
|
|
|
|
:- func mlds_get_module_name(mlds) = mercury_module_name.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Module names of various kinds.
|
|
%
|
|
|
|
:- type mercury_module_name == sym_name.module_name.
|
|
|
|
% An mlds_module_name specifies the name of an mlds package or class.
|
|
% XXX This is wrongly named as it is also used for class names.
|
|
%
|
|
:- type mlds_module_name.
|
|
|
|
% An mlds_package_name specifies the name of an mlds package.
|
|
% XXX This is wrongly named, as it is used for module names in the Java
|
|
% backend.
|
|
%
|
|
:- type mlds_package_name == mlds_module_name.
|
|
|
|
% Given the name of a Mercury module, return the name of the corresponding
|
|
% MLDS package in which this module is defined.
|
|
%
|
|
:- func mercury_module_name_to_mlds(mercury_module_name) = mlds_module_name.
|
|
|
|
% Given the name of a Mercury module, and a package name, return the
|
|
% name of the corresponding MLDS module name in which this module is
|
|
% defined. In this case, the package is specified as the first parameter
|
|
% (c.f. mercury_module_name_to_mlds above).
|
|
%
|
|
:- func mercury_module_and_package_name_to_mlds(mercury_module_name,
|
|
mercury_module_name) = mlds_module_name.
|
|
|
|
% Given the name of a Mercury module return the fully qualified module
|
|
% name, e.g. for the name System.Object which is defined in the source
|
|
% package mscorlib, it will return System.Object.
|
|
%
|
|
:- func mlds_module_name_to_sym_name(mlds_package_name) = sym_name.
|
|
|
|
% Give the name of a Mercury module, return the name of the corresponding
|
|
% MLDS package.
|
|
%
|
|
:- func mlds_module_name_to_package_name(mlds_module_name) = sym_name.
|
|
|
|
% Is the current module a member of the std library,
|
|
% and if so which module is it?
|
|
%
|
|
:- pred is_std_lib_module(mlds_module_name::in, mercury_module_name::out)
|
|
is semidet.
|
|
|
|
% For the Java back-end, we need to distinguish between module qualifiers
|
|
% and type qualifiers, because type names get the case of their initial
|
|
% letter inverted (i.e. lowercase => uppercase).
|
|
%
|
|
:- type mlds_qual_kind
|
|
---> module_qual
|
|
; type_qual.
|
|
|
|
% Given an MLDS module name (e.g. `foo.bar'), append another class
|
|
% qualifier (e.g. for a class `baz'), and return the result (e.g.
|
|
% `foo.bar.baz'). The `arity' argument specifies the arity of the class.
|
|
% The qual_kind argument specifies the qualifier kind of the module_name
|
|
% argument.
|
|
%
|
|
:- func mlds_append_class_qualifier(mlds_target_lang, mlds_module_name,
|
|
mlds_qual_kind, mlds_class_name, arity) = mlds_module_name.
|
|
:- func mlds_append_class_qualifier_module_qual(mlds_module_name,
|
|
mlds_class_name, arity) = mlds_module_name.
|
|
|
|
% Append an arbitrary qualifier to the module name and leave the package
|
|
% name unchanged.
|
|
%
|
|
:- func mlds_append_name(mlds_module_name, string) = mlds_module_name.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Module imports.
|
|
%
|
|
|
|
% For the C backend, we generate a `.mh' file containing the
|
|
% prototypes for C functions generated for `:- pragma foreign_export'
|
|
% declarations, and a `.mih' file containing the prototypes
|
|
% for the C functions generated by the compiler which should
|
|
% not be visible to the user.
|
|
%
|
|
% For languages which do not support multiple interfaces, it is probably OK
|
|
% to put everything in the compiler-visible interface.
|
|
:- type mercury_mlds_import_type
|
|
---> user_visible_interface
|
|
; compiler_visible_interface.
|
|
|
|
% An mlds_import specifies FIXME
|
|
% Currently an import just gives the name of the package to be imported.
|
|
:- type mlds_import
|
|
---> mlds_import(
|
|
mercury_mlds_import_type :: mercury_mlds_import_type,
|
|
import_name :: module_name
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Global variable definitions.
|
|
%
|
|
|
|
% An mlds_global_var represents a variable or constant that is defined
|
|
% outside all functions and classes.
|
|
%
|
|
:- type mlds_global_var_defn
|
|
---> mlds_global_var_defn(
|
|
mgvd_name :: mlds_global_var_name,
|
|
mgvd_context :: prog_context,
|
|
mgvd_decl_flags :: mlds_global_var_decl_flags,
|
|
|
|
mgvd_type :: mlds_type,
|
|
mgvd_init :: mlds_initializer,
|
|
|
|
% If accurate GC is enabled, we associate with each global
|
|
% variable the compiler-generated gc trace functions that will
|
|
% tell the accurate collector what roots (i.e. pointers to
|
|
% heap cells allocated by the accurate gc system) exist inside
|
|
% that global variable.
|
|
%
|
|
% This field may contain gc_no_stmt if the variable contains
|
|
% no such roots, either because its contents are fully static
|
|
% (i.e. the global variable is actually a global constant),
|
|
% or because all its dynamic parts point to heap memory
|
|
% allocated by some other system (e.g. plain non-gc malloc).
|
|
mgvd_gc :: mlds_gc_statement
|
|
).
|
|
|
|
:- type mlds_global_var_decl_flags
|
|
---> mlds_global_var_decl_flags(
|
|
mgvdf_access :: global_var_access,
|
|
mgvdf_constness :: constness
|
|
).
|
|
|
|
:- func global_dummy_var = qual_global_var_name.
|
|
|
|
%---------------------%
|
|
|
|
% Note that `one_copy' variables *must* have an initializer
|
|
% (the GCC back-end, when it still existed, relied on this).
|
|
% XXX Currently we only record the type for structs.
|
|
% We should do the same for objects and arrays.
|
|
%
|
|
:- type mlds_initializer
|
|
---> init_obj(mlds_rval)
|
|
; init_struct(mlds_type, list(mlds_initializer))
|
|
; init_array(list(mlds_initializer))
|
|
; no_initializer.
|
|
|
|
:- type initializer_array_size
|
|
---> array_size(int)
|
|
; no_size.
|
|
% Either the size is unknown, or the data is not an array.
|
|
|
|
:- func get_initializer_array_size(mlds_initializer) = initializer_array_size.
|
|
|
|
%---------------------%
|
|
|
|
% If accurate GC is enabled, we associate with each variable
|
|
% (including function parameters) the code needed to initialise
|
|
% or trace that variable when doing GC (in the GC trace function).
|
|
% `gc_no_stmt' here indicates that no code is needed (e.g. because
|
|
% accurate GC is not enabled, or because the variable can never
|
|
% contain pointers to objects on the heap).
|
|
% `gc_trace_code' indicates that GC tracing code is required for the
|
|
% variable.
|
|
% `gc_initialiser' indicates that the variable is a compiler-generated
|
|
% variable that needs to be initialised before tracing the other
|
|
% variables for the predicate (used when generating closures).
|
|
%
|
|
:- type mlds_gc_statement
|
|
---> gc_trace_code(mlds_stmt)
|
|
; gc_initialiser(mlds_stmt)
|
|
; gc_no_stmt.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Class definitions.
|
|
%
|
|
% While the general form allows the definition of a class, many do not define
|
|
% any methods, and are effectively just type definitions.
|
|
%
|
|
|
|
% Note that standard C does not support empty structs, so when targeting C,
|
|
% it is the MLDS code generator's responsibility to ensure that each
|
|
% generated MLDS class has at least one base class or non-static
|
|
% data member.
|
|
%
|
|
:- type mlds_class_defn
|
|
---> mlds_class_defn(
|
|
mcd_class_name :: mlds_class_name,
|
|
mcd_class_arity :: arity,
|
|
mcd_context :: prog_context,
|
|
mcd_decl_flags :: mlds_class_decl_flags,
|
|
|
|
% Imports these classes (or modules, packages, ...).
|
|
mcd_imports :: list(mlds_import),
|
|
|
|
% Inherits these base classes.
|
|
mcd_inherits :: mlds_class_inherits,
|
|
|
|
% Implements these interfaces.
|
|
mcd_implements :: list(mlds_interface_id),
|
|
|
|
% Type parameters.
|
|
mcd_tparams :: list(type_param),
|
|
|
|
% Contains these members.
|
|
% The mcd_member_methods field is used only
|
|
% by mlds_to_java_*.m; it should be set to the empty list
|
|
% everywhere else.
|
|
mcd_member_fields :: list(mlds_field_var_defn),
|
|
mcd_member_classes :: list(mlds_class_defn),
|
|
mcd_member_methods :: list(mlds_function_defn),
|
|
|
|
% Has these constructors.
|
|
mcd_ctors :: list(mlds_function_defn)
|
|
).
|
|
|
|
% Classes representing enum types and dummy types. We use these when
|
|
% targeting C# and Java, not when targeting C (since we use low-level
|
|
% data representation for C).
|
|
%
|
|
:- type mlds_enum_class_defn
|
|
---> mlds_enum_class_defn(
|
|
mecd_class_name :: mlds_class_name,
|
|
mecd_class_arity :: arity,
|
|
mecd_context :: prog_context,
|
|
% We don't store any flags; they are always
|
|
% mlds_class_decl_flags(class_public, overridable, modifiable)
|
|
|
|
% The enum class inherits one base class when targeting Java,
|
|
% and no base classes when targeting C#.
|
|
mecd_inherits :: mlds_enum_class_inherits,
|
|
|
|
% This enum class implements one interface when targeting Java,
|
|
% and no interfaces when targeting C#.
|
|
mecd_implements :: maybe(mlds_interface_id),
|
|
|
|
% Type parameters.
|
|
mcd_tparams :: list(type_param),
|
|
|
|
% XXX ml_type_gen.m generates this "value" field variable
|
|
% for each enum type. However, this field var's definition
|
|
% can never be used, because it is not written out by either
|
|
% mlds_to_cs_class.m or mlds_to_java_class.m, and of course
|
|
% mlds_to_c_class.m does not do anything with
|
|
% mlds_enum_class_defns at all.
|
|
%
|
|
% There is a value field in java/runtime/MercuryEnum.java,
|
|
% which is the base type of all enumeration types in Java.
|
|
%
|
|
% Julien speculated on m-rev (2023 jun 13) that this field
|
|
% may have been required by a backend that does not currently
|
|
% exist, either because it was deleted (such as the MSIL
|
|
% backend), or because it was never implemented.
|
|
mecd_value_field :: mlds_field_var_defn,
|
|
|
|
% A list containing one constant definition for each
|
|
% function symbol in the type. There will be one constant
|
|
% for dummy types, and two or more for enum types.
|
|
% XXX We should use a nonempty_list subtype of list.
|
|
mecd_enum_consts :: list(mlds_enum_const_defn),
|
|
|
|
% Has these constructors.
|
|
mecd_ctors :: list(mlds_function_defn)
|
|
).
|
|
|
|
% The environment structure we use to package up the local variables
|
|
% needed by model_non continuations.
|
|
%
|
|
:- type mlds_env_defn
|
|
---> mlds_env_defn(
|
|
med_env_name :: string,
|
|
med_context :: prog_context,
|
|
|
|
% The field vars, both those representing HLDS variables
|
|
% and those implementing accurate gc. Both kinds will
|
|
% always have flags per_instance, modifiable.
|
|
med_field_vars :: list(mlds_field_var_defn)
|
|
|
|
% The following comments specify the relationship between
|
|
% this type and mlds_class_defn (from which mlds_env_defn
|
|
% was derived).
|
|
%
|
|
% Environment structures never have any type parameters.
|
|
% This means that their "arity" is always zero.
|
|
% (We do include the _0 suffix in their names in the
|
|
% C/C#/Java code we generate, at least for now.)
|
|
%
|
|
% Environment structures are always private,
|
|
% overridable, and modifiable.
|
|
% XXX The overridable part is probably just an accident.
|
|
% It is certainly irrelevant, because (a) users cannot create
|
|
% references to environment structures, so they cannot possibly
|
|
% inherit from it, and (b) the compiler itself never tries
|
|
% to inherit from them either.
|
|
% (This XXX was written by zs; juliensf agrees.)
|
|
%
|
|
% We treat environment structures as structs when targeting C,
|
|
% and as classes when targeting C# or Java.
|
|
%
|
|
% Environments structures inherit nothing when targeting C,
|
|
% but inherit the generic env_ptr type when targeting C# or
|
|
% Java.
|
|
%
|
|
% Environment structures never import anything.
|
|
%
|
|
% Environment structures never implement any interfaces.
|
|
%
|
|
% Environment structures never define any classes or methods,
|
|
% and never have any constructors.
|
|
).
|
|
|
|
% An mlds_struct_defn defines the types of the fields in one row
|
|
% of a vector cell group. (A vector cell group is effectively an array
|
|
% of structs; an mlds_struct_defn serves as the struct type of the elements
|
|
% of the array.) The compiler uses these to implement e.g. switches
|
|
% that act as lookup tables.
|
|
%
|
|
:- type mlds_struct_defn
|
|
---> mlds_struct_defn(
|
|
msd_class_name :: mlds_class_name,
|
|
msd_context :: prog_context,
|
|
|
|
% The field vars representing the fields of one row
|
|
% in the vector data structure.
|
|
% All fields are per_instance and const.
|
|
msd_member_fields :: list(mlds_field_var_defn),
|
|
|
|
% The constructor for values of this type, if needed.
|
|
% This "maybe" will always be "no" when targeting C,
|
|
% and "yes(...)" when targeting C# or Java.
|
|
msd_maybe_ctor :: maybe(mlds_function_defn)
|
|
|
|
% The following comments specify the relationship between
|
|
% this type and mlds_class_defn (from which mlds_struct_defn
|
|
% was derived).
|
|
%
|
|
% mlds_struct_defns never have any type parameters.
|
|
% This means that their "arity" is always zero.
|
|
% (We do include the _0 suffix in their names in the
|
|
% C/C#/Java code we generate, at least for now.)
|
|
%
|
|
% These structures are always private, sealed, and modifiable,
|
|
% but the original comment on the "modifiable" part said
|
|
% "The 'modifiable' is only to shut up a gcc warning about
|
|
% constant fields." Certainly, all structures we include
|
|
% in ml_global_data are designed to be read-only.
|
|
%
|
|
% We treat these structures as structs when targeting
|
|
% C or C#, and as classes when targeting Java.
|
|
%
|
|
% These structures inherit nothing, import nothing,
|
|
% and implement no interfaces.
|
|
%
|
|
% They also have no member classes or methods.
|
|
).
|
|
|
|
:- type mlds_class_decl_flags
|
|
---> mlds_class_decl_flags(
|
|
mcdf_access :: class_access,
|
|
mcdf_overridability :: overridability,
|
|
mcdf_constness :: constness
|
|
).
|
|
|
|
:- type mlds_class_inherits
|
|
---> inherits_nothing
|
|
% There is no base class.
|
|
|
|
; inherits_class(mlds_class_id).
|
|
% There is one base class. (We do not have a case for
|
|
% more than one base class, because
|
|
% (a) none of the (current) MLDS target languages support
|
|
% multiple inheritance; and
|
|
% (b) even if some did, we may not want to use that capability.
|
|
|
|
:- type mlds_enum_class_inherits =< mlds_class_inherits
|
|
---> inherits_nothing
|
|
; inherits_class(mlds_class_id).
|
|
|
|
:- type qual_class_name
|
|
---> qual_class_name(mlds_module_name, mlds_qual_kind, mlds_class_name).
|
|
:- type mlds_class_name == string.
|
|
|
|
:- type mlds_class_id
|
|
---> mlds_class_id(qual_class_name, arity).
|
|
:- type mlds_enum_class_id
|
|
---> mlds_enum_class_id(mlds_module_name, string, arity).
|
|
:- type mlds_env_id
|
|
---> mlds_env_id(mlds_module_name, string).
|
|
:- type mlds_struct_id
|
|
---> mlds_struct_id(mlds_module_name, string).
|
|
|
|
:- type mlds_interface_id
|
|
---> mlds_interface_id(mlds_module_name, mlds_class_name).
|
|
% Interfaces are always module qualified.
|
|
% The arity of the interfaces we use is always zero.
|
|
|
|
:- type mlds_field_var_defn
|
|
---> mlds_field_var_defn(
|
|
mfvd_name :: mlds_field_var_name,
|
|
mfvd_context :: prog_context,
|
|
mfvd_decl_flags :: mlds_field_var_decl_flags,
|
|
|
|
mfvd_type :: mlds_type,
|
|
mfvd_init :: mlds_initializer,
|
|
|
|
% If accurate GC is enabled, we associate with each variable
|
|
% the code needed to initialise or trace that variable when
|
|
% doing GC (in the compiler-generated gc trace functions).
|
|
%
|
|
% XXX The only part of the compiler that seems to set this
|
|
% field to any value other than gc_no_stmt is the code
|
|
% that converts a local variable to a field in an environment
|
|
% structure. And all the predicates in mlds_to_*_class.m
|
|
% that output the definitions of fields in enum classes
|
|
% seem to know this, since they ignore this field.
|
|
mfvd_gc :: mlds_gc_statement
|
|
).
|
|
|
|
:- type mlds_enum_const_defn
|
|
---> mlds_enum_const_defn(
|
|
mecd_name :: mlds_field_var_name,
|
|
mecd_context :: prog_context,
|
|
% We don't store any flags for enum const definitions;
|
|
% they are implicitly one_copy and const.
|
|
|
|
% We don't store a type for enum const definitions either;
|
|
% they are always implicitly int. (Even when their value
|
|
% is given by a string, that string is the name of an integer
|
|
% constant in a foreign language.)
|
|
mecd_const_value :: mlds_enum_const
|
|
).
|
|
|
|
:- type mlds_enum_const
|
|
---> mlds_enum_const_uint(uint)
|
|
; mlds_enum_const_foreign(foreign_language, string, mlds_type).
|
|
|
|
:- type mlds_field_var_decl_flags
|
|
---> mlds_field_var_decl_flags(
|
|
% Field variables are implicitly always "public" within
|
|
% the class that defines them.
|
|
mfvdf_per_instance :: per_instance,
|
|
mfvdf_constness :: constness
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Function definitions.
|
|
%
|
|
|
|
:- type mlds_function_defn
|
|
---> mlds_function_defn(
|
|
mfd_function_name :: mlds_function_name,
|
|
mfd_context :: prog_context,
|
|
mfd_decl_flags :: mlds_function_decl_flags,
|
|
|
|
% Identifies the original Mercury procedure, if any.
|
|
mfd_orig_proc :: mlds_func_source,
|
|
|
|
% The argument types and return types.
|
|
mfd_param :: mlds_func_params,
|
|
|
|
mfd_body :: mlds_function_body,
|
|
|
|
% The set of environment variables referred to
|
|
% by the function body.
|
|
mfd_env_vars :: set(string),
|
|
|
|
% Information used to generate tail recursion errors.
|
|
mfd_tail_rec :: maybe(require_tail_recursion)
|
|
).
|
|
|
|
:- type mlds_function_decl_flags
|
|
---> mlds_function_decl_flags(
|
|
mfdf_access :: function_access,
|
|
mfdf_per_instance :: per_instance
|
|
).
|
|
|
|
:- type mlds_func_source
|
|
---> mlds_func_source_proc(pred_proc_id)
|
|
; mlds_func_source_constructor
|
|
; mlds_func_source_continuation
|
|
; mlds_func_source_trace
|
|
; mlds_func_source_wrapper.
|
|
|
|
%---------------------%
|
|
|
|
:- type qual_function_name
|
|
---> qual_function_name(mlds_module_name, mlds_function_name).
|
|
% Function names can only ever be module qualified.
|
|
|
|
:- type qual_func_label
|
|
---> qual_func_label(mlds_module_name, mlds_func_label).
|
|
% Function labels can only ever be module qualified.
|
|
|
|
:- type qual_proc_label
|
|
---> qual_proc_label(mlds_module_name, mlds_proc_label).
|
|
% Procedure labels can only ever be module qualified.
|
|
|
|
%---------------------%
|
|
|
|
:- type mlds_function_name
|
|
---> mlds_function_name(
|
|
mlds_plain_func_name
|
|
)
|
|
; mlds_function_export(
|
|
% A pragma foreign_export name.
|
|
string
|
|
).
|
|
|
|
:- type mlds_plain_func_name
|
|
---> mlds_plain_func_name(
|
|
% Identifies the source code predicate or function.
|
|
pfn_func_label :: mlds_func_label,
|
|
|
|
% Specifies the HLDS pred_id.
|
|
% This should generally not be needed much, since all the
|
|
% necessary information should be in the mlds_pred_label
|
|
% and/or in the mlds_entity_defn. However, the target
|
|
% generator may want to refer to the HLDS for additional
|
|
% information.
|
|
pfn_pred_id :: pred_id
|
|
).
|
|
|
|
:- type mlds_func_label
|
|
---> mlds_func_label(
|
|
mlds_proc_label,
|
|
|
|
% The second field will be proc_func for the MLDS function
|
|
% we generate for a HLDS procedure as a whole, but it will be
|
|
% some other function symbol wrapper around N for the Nth
|
|
% auxiliary MLDS function we generate for that procedure,
|
|
% e.g. to handle backtracking. (It is actually N, not N-1,
|
|
% because mlds_aux_func_seq_nums start at 1.)
|
|
mlds_maybe_aux_func_id
|
|
).
|
|
|
|
:- type mlds_maybe_aux_func_id
|
|
---> proc_func
|
|
; proc_aux_func(int)
|
|
; gc_trace_for_proc_func
|
|
; gc_trace_for_proc_aux_func(int).
|
|
|
|
:- type mlds_proc_label
|
|
---> mlds_proc_label(mlds_pred_label, proc_id).
|
|
|
|
% An mlds_pred_label is a structure containing information that
|
|
% uniquely identifies a HLDS predicate or function within a given module.
|
|
%
|
|
% Note that a predicate or function can have both a declaring and
|
|
% a defining module. The defining module is the module that provides
|
|
% the code for the predicate or function, while the declaring module
|
|
% contains the `:- pred' or `:- func' 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 predicate or function's name.
|
|
%
|
|
:- type mlds_pred_label
|
|
---> mlds_user_pred_label(
|
|
pred_or_func,
|
|
|
|
% The declaring module, if different to the defining module.
|
|
maybe(mercury_module_name),
|
|
|
|
% The name of the predicate or function, its arity,
|
|
% and its code model.
|
|
string,
|
|
pred_form_arity
|
|
)
|
|
; mlds_special_pred_label(
|
|
% The predicate name.
|
|
string,
|
|
|
|
% The module declaring the type, if this is different
|
|
% to module defining the special_pred.
|
|
maybe(mercury_module_name),
|
|
|
|
% The type's name and arity.
|
|
string,
|
|
arity
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% It is possible for the function to be defined externally,
|
|
% if the Mercury procedure has a `:- pragma external_{pred/func}'.
|
|
% (If you want to generate an abstract body, consider adding another
|
|
% alternative here).
|
|
%
|
|
:- type mlds_function_body
|
|
---> body_defined_here(mlds_stmt)
|
|
; body_external.
|
|
|
|
%---------------------%
|
|
|
|
:- type mlds_func_params
|
|
---> mlds_func_params(
|
|
list(mlds_argument), % names and types of arguments (inputs)
|
|
mlds_return_types % types of return values (outputs)
|
|
).
|
|
|
|
:- type mlds_argument
|
|
---> mlds_argument(
|
|
mlds_local_var_name, % Argument name.
|
|
mlds_type, % Argument type.
|
|
mlds_gc_statement % Code for GC tracing this argument.
|
|
).
|
|
|
|
% An mlds_func_signature is like an mlds_func_params, except that
|
|
% it includes only the types of the function's arguments,
|
|
% and not their names.
|
|
%
|
|
:- type mlds_func_signature
|
|
---> mlds_func_signature(
|
|
mlds_arg_types, % argument types
|
|
mlds_return_types % return types
|
|
).
|
|
|
|
:- type mlds_arg_types == list(mlds_type).
|
|
:- type mlds_return_types == list(mlds_type).
|
|
|
|
:- func mlds_get_arg_types(list(mlds_argument)) = list(mlds_type).
|
|
|
|
:- func mlds_get_func_signature(mlds_func_params) = mlds_func_signature.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Local variable definitions.
|
|
%
|
|
|
|
:- type mlds_local_var_defn
|
|
---> mlds_local_var_defn(
|
|
mlvd_name :: mlds_local_var_name,
|
|
mlvd_context :: prog_context,
|
|
% Local variables do not need flags. They are always local
|
|
% and modifiable.
|
|
|
|
mlvd_type :: mlds_type,
|
|
mlvd_init :: mlds_initializer,
|
|
|
|
% If accurate GC is enabled, we associate with each variable
|
|
% the code needed to initialise or trace that variable when
|
|
% doing GC (in the compiler-generated gc trace functions).
|
|
mlvd_gc :: mlds_gc_statement
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Declaration flags.
|
|
%
|
|
|
|
:- type global_var_access
|
|
---> gvar_acc_module_only
|
|
; gvar_acc_whole_program.
|
|
|
|
% The accessibility of classes themselves.
|
|
:- type class_access
|
|
---> class_public
|
|
; class_private.
|
|
|
|
:- type function_access
|
|
---> func_public
|
|
% Accessible to anyone.
|
|
|
|
; func_private.
|
|
% Accessible only to the module or class that defines the function.
|
|
% Also used for nested functions, which get turned into
|
|
% separate func_private functions.
|
|
|
|
:- type constness
|
|
---> modifiable
|
|
; const.
|
|
|
|
:- type per_instance
|
|
---> one_copy
|
|
% I.e. "static" storage duration (but not necessarily static
|
|
% linkage) or static member function. Note that `one_copy'
|
|
% variables *must* have an initializer.
|
|
|
|
; per_instance.
|
|
% I.e. "auto" local variable in function, or non-static
|
|
% member of class.
|
|
|
|
:- type overridability
|
|
---> overridable
|
|
; sealed. % i.e. the class cannot be inherited from,
|
|
% or the virtual method is not overridable.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Types.
|
|
%
|
|
|
|
:- type mlds_type
|
|
---> mercury_nb_type(
|
|
% A Mercury data type that is not a builtin type
|
|
% (the nb is short for "not builtin"). Builtin types should
|
|
% be represented using one of the mlds_builtin_type_*
|
|
% function symbols.
|
|
|
|
% The exact Mercury type. It cannot be builtin_type(_).
|
|
%
|
|
% Most places that process mercury_nb_types need only the type
|
|
% constructor, and not the whole type. However, there are some
|
|
% places in mlds_to_{cs,java}_*.m that need the types of the
|
|
% type constructor's arguments too. Nevertheless, we *could*
|
|
% replace this field with two fields holding the type_ctor
|
|
% and the argument types. That would require a separate
|
|
% function symbol in mlds_type for representing type variables
|
|
% (the only kind of mer_type that has no type_ctor), but
|
|
% it would allow us to dispense with repeated invocations
|
|
% of type_to_ctor on the mer_type field here.
|
|
mer_type,
|
|
|
|
% What kind of type it is: enum, dummy, notag, ...
|
|
% It cannot be ctor_cat_builtin(_), and we enforce this
|
|
% by using nb_type_ctor_category, which is a subtype
|
|
% of type_ctor_category that lacks ctor_cat_builtin.
|
|
% (We could also change the type of the first field
|
|
% from mer_type to nb_mer_type, a subtype of mertype
|
|
% that for now is commented-out in prog_data.m, but
|
|
% using nb_type_ctor_category is sufficient, given that
|
|
% we already assume that the second field accurately
|
|
% describes the first.)
|
|
%
|
|
% We could use a bespoke type here that would be isomorphic
|
|
% to type_ctor_category except for disallowing builtins,
|
|
% and maybe type variables (see above). However, that would
|
|
% require us to duplicate a nontrivial amount of code.
|
|
% XXX This has not been true since Peter implemented subtypes.
|
|
nb_type_ctor_category
|
|
)
|
|
|
|
; mlds_mercury_array_type(mlds_type)
|
|
% The Mercury array type is treated specially, some backends
|
|
% will treat it like any other mercury_type, whereas others may
|
|
% use a special representation for it.
|
|
% Arrays are type constructors in some backends, and so it is
|
|
% easier to represent it here as a special type constructor.
|
|
% (if we used the mercury_type representation above, we would
|
|
% only classify the topmost level of the type, whereas we
|
|
% really want to classify the element type for arrays, so
|
|
% we can generate int[] for array(int)).
|
|
% Note that mlds_mercury_array_type/1 is used for representing
|
|
% Mercury arrays, whereas mlds_array_type/1 (see below)
|
|
% is used for representing the target language's native arrays.
|
|
|
|
; mlds_cont_type(mlds_return_types)
|
|
% The type for the continuation functions used to handle
|
|
% nondeterminism.
|
|
|
|
; mlds_commit_type
|
|
% mlds_commit_type is used for storing information about a
|
|
% commit. This is an abstract type; the exact definition
|
|
% will depend on the back-end. The only operations on this ADT
|
|
% are `try_commit' and `do_commit'. This type holds
|
|
% information about the `try_commit' stack frame that is
|
|
% needed to unwind the stack when a `do_commit' is executed.
|
|
%
|
|
% For the C back-end, if we are implementing do_commit/try_commit
|
|
% using setjmp/longjmp, then mlds_commit_type will be jmp_buf.
|
|
% If we are implementing them using GNU C nested functions, then
|
|
% it will be `__label__'; in this case, the local variable
|
|
% of this "type" is actually a label, and doing a goto to that
|
|
% label will unwind the stack.
|
|
%
|
|
% If the back-end implements commits using the target language's
|
|
% try/catch-style exception handling, as in Java/C++/etc., then
|
|
% the target language implementation's exception handling support
|
|
% will keep track of the information needed to unwind the stack,
|
|
% and so variables of this type do not need to be declared at all.
|
|
%
|
|
% See also the comments in ml_commit_gen.m which show how commits
|
|
% can be implemented for different target languages.
|
|
|
|
; mlds_builtin_type_int(int_type)
|
|
; mlds_builtin_type_float
|
|
; mlds_builtin_type_string
|
|
; mlds_builtin_type_char
|
|
% The representations of the builtin types in the target language.
|
|
%
|
|
% Note that some target languages do not have types corresponding
|
|
% *exactly* to each Mercury builtin type; for example, Java has
|
|
% no unsigned types. For these, we use the closest type available.
|
|
%
|
|
% The following table shows the target language types we use
|
|
% to represent each of the above builtin types in the three
|
|
% MLDS target languages:
|
|
%
|
|
% Mercury type C Java C#
|
|
%
|
|
% int MR_Integer int int
|
|
% uint MR_Unsigned int uint
|
|
% int8 int8_t byte sbyte
|
|
% uint8 uint8_t byte byte
|
|
% int16 int16_t short short
|
|
% uint16 uint16_t short ushort
|
|
% int32 int32_t int int
|
|
% uint32 uint32_t int uint
|
|
% int64 int64_t long long
|
|
% uint64 uint64_t long ulong
|
|
% float MR_Float double double
|
|
% char MR_Char int int
|
|
% string MR_String j.l.String string
|
|
%
|
|
% (j.l.String is of course short for java.lang.String).
|
|
|
|
; mlds_native_bool_type
|
|
% The representation of booleans in the MLDS target language.
|
|
|
|
; mlds_foreign_type(
|
|
% This is a type of the target language.
|
|
generic_language_foreign_type
|
|
)
|
|
|
|
; mlds_class_type(
|
|
% MLDS types defined using mlds_class_defn.
|
|
mlds_class_id
|
|
)
|
|
|
|
; mlds_enum_class_type(
|
|
% MLDS types defined using mlds_enum_class_defn.
|
|
mlds_enum_class_id
|
|
)
|
|
|
|
; mlds_env_type(
|
|
% MLDS types defined using mlds_env_defn.
|
|
mlds_env_id
|
|
)
|
|
|
|
; mlds_struct_type(
|
|
% MLDS types defined using mlds_struct_defn.
|
|
mlds_struct_id
|
|
)
|
|
|
|
; mlds_array_type(mlds_type)
|
|
% MLDS array types.
|
|
% These are single-dimensional, and can be indexed
|
|
% using the `field' lval with an `offset' mlds_field_id;
|
|
% indices start at zero.
|
|
%
|
|
% Note that we use mlds_array_type/1 for representing
|
|
% the target language's native arrays, whereas we use
|
|
% mlds_mercury_array_type/1 (see above) for representing
|
|
% Mercury arrays.
|
|
%
|
|
% Currently we use MLDS array types for
|
|
%
|
|
% - static constants that would otherwise be allocated
|
|
% with a `new_object', if we are using the low-level
|
|
% data representation (--no-high-level-data);
|
|
%
|
|
% - for static constants of certain Mercury types which are
|
|
% always represented using the low-level data representation,
|
|
% regardless of --high-level-data, in particular closures
|
|
% and type_infos;
|
|
%
|
|
% - for any other arrays generated internally by the MLDS
|
|
% code generator, e.g. the arrays used for string switches.
|
|
|
|
; mlds_mostly_generic_array_type(list(mlds_type))
|
|
% A generic array with some float elements. All other elements
|
|
% are mlds_generic_type.
|
|
%
|
|
% This is the same as mlds_array_type(mlds_generic_type) except
|
|
% that some element types are mlds_native_float_type instead of
|
|
% mlds_generic_type. In C, it is not possible to initialize an
|
|
% element of a generic array with a float literal, so we replace
|
|
% the generic array type with a structure type containing a
|
|
% combination of generic fields and float fields.
|
|
|
|
; mlds_ptr_type(mlds_type)
|
|
% Pointer types.
|
|
% Currently these are used for handling output arguments.
|
|
|
|
; mlds_func_type(mlds_func_params)
|
|
% Function types.
|
|
% For the C back-end, these are mapped to function pointer types,
|
|
% since C does not have first-class function types.
|
|
|
|
; mlds_generic_type
|
|
% A generic type (e.g. `MR_Word') that can hold any Mercury value.
|
|
% This is used for implementing polymorphism.
|
|
|
|
; mlds_generic_env_ptr_type
|
|
% A generic pointer type (e.g. `void *' in C) that can be used
|
|
% to point to the environment (set of local variables) of the
|
|
% containing function. This is used for handling nondeterminism,
|
|
% if the target language does not support nested functions, and
|
|
% also for handling closures for higher-order code.
|
|
|
|
; mlds_type_info_type
|
|
|
|
; mlds_pseudo_type_info_type
|
|
|
|
; mlds_rtti_type(rtti_id_maybe_element)
|
|
|
|
; mlds_tabling_type(proc_tabling_struct_id)
|
|
|
|
; mlds_unknown_type.
|
|
% A type used by the ML code generator for references to variables
|
|
% that have yet to be declared. This occurs once in ml_code_util.m
|
|
% where references to env_ptrs are generated but the declaration
|
|
% of these env_ptrs does not occur until the ml_elim_nested pass.
|
|
|
|
:- func mercury_type_to_mlds_type(module_info, mer_type) = mlds_type.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Statements.
|
|
%
|
|
|
|
:- type while_loop_kind
|
|
---> may_loop_zero_times
|
|
; loop_at_least_once.
|
|
|
|
:- type mlds_stmt
|
|
% Sequence.
|
|
|
|
---> ml_stmt_block(
|
|
list(mlds_local_var_defn),
|
|
list(mlds_function_defn),
|
|
list(mlds_stmt),
|
|
prog_context
|
|
)
|
|
|
|
% Iteration.
|
|
|
|
; ml_stmt_while(
|
|
% Is this a "while (Cond) {...}" loop
|
|
% or a "do {...} while (Cond)" loop?
|
|
while_loop_kind,
|
|
|
|
% The condition.
|
|
mlds_rval,
|
|
|
|
% The body.
|
|
mlds_stmt,
|
|
|
|
% A list of the local variables that are used in the loop body
|
|
% *before* being defined there (with their *initial* values
|
|
% being set before the loop itself), and whose definitions
|
|
% in the loop body must therefore be kept even if their values
|
|
% are not used after the loop. This field is used by
|
|
% ml_unused_assign.m.
|
|
%
|
|
% For example, given a loop such as
|
|
%
|
|
% i = 0;
|
|
% while (i < num_rows) {
|
|
% ... do something with row[i] ...
|
|
% i = i + 1;
|
|
% }
|
|
%
|
|
% where i is not used after the loop, i must be included
|
|
% in this field to prevent ml_unused_assign.m from optimizing
|
|
% away the increment of i.
|
|
list(mlds_local_var_name),
|
|
|
|
prog_context
|
|
)
|
|
|
|
% Selection (see also computed_goto).
|
|
|
|
; ml_stmt_if_then_else(
|
|
% The condition.
|
|
mlds_rval,
|
|
|
|
% The then part.
|
|
mlds_stmt,
|
|
|
|
% The else part (if any).
|
|
maybe(mlds_stmt),
|
|
|
|
prog_context
|
|
)
|
|
|
|
; ml_stmt_switch(
|
|
% This representation for switches is very general:
|
|
% it allows switching on any type, and cases can match
|
|
% on ranges as well as on values.
|
|
% Many target languages only allow switches on ints or enums.
|
|
% Some (e.g. C#) also allow switches on strings.
|
|
% Most target languages only allow matching on values;
|
|
% only some (e.g. GNU C) allow matching on ranges.
|
|
% The MLDS code generator should only generate switches
|
|
% that the target will support.
|
|
%
|
|
% Note that unlike C, MLDS cases do NOT fall through; if you
|
|
% want to achieve that effect, you need to use an explicit
|
|
% goto.
|
|
|
|
% The value to switch on.
|
|
mlds_type,
|
|
mlds_rval,
|
|
|
|
% The range of possible values which the value might take
|
|
% (if known).
|
|
mlds_switch_range,
|
|
|
|
% The different cases.
|
|
list(mlds_switch_case),
|
|
|
|
% What to do if none of the cases match.
|
|
mlds_switch_default,
|
|
|
|
prog_context
|
|
)
|
|
|
|
% Transfer of control.
|
|
|
|
; ml_stmt_label(mlds_label, prog_context)
|
|
% Defines a label that can be used as the target of calls,
|
|
% gotos, etc.
|
|
|
|
; ml_stmt_goto(mlds_goto_target, prog_context)
|
|
% goto(Target): Branch to the specified address.
|
|
|
|
; ml_stmt_computed_goto(
|
|
% Evaluate rval, which should be an integer, and jump to the
|
|
% (rval+1)th label in the list.
|
|
% For example, computed_goto(2, [A, B, C, D])
|
|
% will branch to label C.
|
|
mlds_rval,
|
|
list(mlds_label),
|
|
prog_context
|
|
)
|
|
|
|
% Function call/return.
|
|
|
|
; ml_stmt_call(
|
|
% The signature of the callee function.
|
|
mlds_func_signature,
|
|
|
|
% The function to call.
|
|
mlds_rval,
|
|
|
|
% The ordinary function arguments.
|
|
list(mlds_rval),
|
|
|
|
% The places to store the function return value(s).
|
|
list(mlds_lval),
|
|
|
|
% Is this call an ordinary call, a tail call,
|
|
% or a no-return call?
|
|
ml_call_kind,
|
|
|
|
prog_context
|
|
)
|
|
|
|
; ml_stmt_return(
|
|
% Some targets will not support returning more than one value.
|
|
list(mlds_rval),
|
|
prog_context
|
|
)
|
|
|
|
% Commits (a specialized form of exception handling).
|
|
|
|
; ml_stmt_try_commit(mlds_lval, mlds_stmt, mlds_stmt, prog_context)
|
|
; ml_stmt_do_commit(mlds_rval, prog_context)
|
|
% try_commit(Ref, StmtToTry, CommitHandlerStmt, _Context):
|
|
% Execute StmtToTry. If StmtToTry exits via a `do_commit(Ref)'
|
|
% instruction, then execute CommitHandlerStmt.
|
|
%
|
|
% do_commit(Ref, _Context):
|
|
% Unwind the stack to the corresponding `try_commit'
|
|
% statement for Ref, and branch to the CommitHandlerStmt
|
|
% that was specified in that try_commit instruction.
|
|
%
|
|
% For both try_commit and do_commit instructions, Ref should
|
|
% *initially* be the name of a local variable of type
|
|
% mlds_commit_type, though ml_elim_nested may later replace that
|
|
% with a reference to that variable in an environment.
|
|
% (This variable can be used by the back-end's implementation
|
|
% of do_commit and try_commit to store information needed to unwind
|
|
% the stack.) There should be exactly one try_commit instruction
|
|
% for each Ref. do_commit(Ref) instructions should only be used
|
|
% in goals called from the StmtToTry goal in the try_commit
|
|
% instruction with the same Ref.
|
|
%
|
|
% The C back-end requires each try_commit to be put in its own
|
|
% nested function, to avoid problems with setjmp() and local vars
|
|
% not declared volatile. They also require each do_commit to be
|
|
% put in its own function -- this is needed when using
|
|
% __builtin_setjmp()/__builtin_longjmp() to ensure that the
|
|
% call to __builtin_longjmp() is not in the same function
|
|
% as the call to __builtin_setjmp().
|
|
|
|
% Exception handling.
|
|
%
|
|
% We use C++-style exceptions.
|
|
% For C, the back-end can simulate them using setjmp/longjmp.
|
|
%
|
|
% XXX This is tentative -- the current definition may be
|
|
% a bit too specific to C++-style exceptions.
|
|
% It might not be a good choice for different target languages.
|
|
% XXX Full exception handling support is not yet implemented.
|
|
|
|
% ; ml_stmt_throw(mlds_type, mlds_rval, prog_context)
|
|
% % Throw the specified exception.
|
|
|
|
% ; ml_stmt_rethrow(prog_context)
|
|
% % Rethrow the current exception
|
|
% % (only valid inside an exception handler).
|
|
|
|
% ; ml_stmt_try_catch(
|
|
% mlds_stmt,
|
|
% list(mlds_exception_handler),
|
|
% prog_context
|
|
% )
|
|
% % Execute the specified statement, and if it throws an exception,
|
|
% % and the exception matches any of the exception handlers,
|
|
% % then execute the first matching exception handler.
|
|
|
|
% Atomic statements.
|
|
|
|
; ml_stmt_atomic(mlds_atomic_statement, prog_context).
|
|
|
|
:- inst ml_stmt_is_block for mlds_stmt/0
|
|
---> ml_stmt_block(ground, ground, ground, ground).
|
|
:- inst ml_stmt_is_while for mlds_stmt/0
|
|
---> ml_stmt_while(ground, ground, ground, ground, ground).
|
|
:- inst ml_stmt_is_if_then_else for mlds_stmt/0
|
|
---> ml_stmt_if_then_else(ground, ground, ground, ground).
|
|
:- inst ml_stmt_is_switch for mlds_stmt/0
|
|
---> ml_stmt_switch(ground, ground, ground, ground, ground, ground).
|
|
:- inst ml_stmt_is_label for mlds_stmt/0
|
|
---> ml_stmt_label(ground, ground).
|
|
:- inst ml_stmt_is_goto for mlds_stmt/0
|
|
---> ml_stmt_goto(ground, ground).
|
|
:- inst ml_stmt_is_computed_goto for mlds_stmt/0
|
|
---> ml_stmt_computed_goto(ground, ground, ground).
|
|
:- inst ml_stmt_is_call for mlds_stmt/0
|
|
---> ml_stmt_call(ground, ground, ground, ground, ground, ground).
|
|
:- inst ml_stmt_is_return for mlds_stmt/0
|
|
---> ml_stmt_return(ground, ground).
|
|
:- inst ml_stmt_is_try_commit for mlds_stmt/0
|
|
---> ml_stmt_try_commit(ground, ground, ground, ground).
|
|
:- inst ml_stmt_is_do_commit for mlds_stmt/0
|
|
---> ml_stmt_do_commit(ground, ground).
|
|
:- inst ml_stmt_is_atomic for mlds_stmt/0
|
|
---> ml_stmt_atomic(ground, ground).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Extra info for switches.
|
|
%
|
|
|
|
% The range of possible values which the switch variable might take
|
|
% (if known).
|
|
:- type mlds_switch_range
|
|
---> mlds_switch_range_unknown
|
|
; mlds_switch_range(range_min::int, range_max::int).
|
|
% From range_min to range_max, both inclusive.
|
|
|
|
% Each switch case consists of the conditions to match against,
|
|
% and the statement to execute if the match succeeds.
|
|
% Unlike C, cases do NOT fall through; if you want to achieve that
|
|
% effect, you need to use an explicit goto.
|
|
:- type mlds_switch_case
|
|
---> mlds_switch_case(
|
|
% Each switch case contains one or more conditions.
|
|
% If _any_ of the conditions match, this case will be selected.
|
|
mlds_case_match_cond,
|
|
list(mlds_case_match_cond),
|
|
|
|
mlds_stmt
|
|
).
|
|
|
|
% A case_match_cond specifies when a switch case will be selected
|
|
:- type mlds_case_match_cond
|
|
---> match_value(mlds_rval)
|
|
% match_value(Val) matches if the switch value is equal to
|
|
% the specified Val.
|
|
|
|
; match_range(mlds_rval, mlds_rval).
|
|
% match_range(Min, Max) matches if the switch value is between
|
|
% Min and Max, both inclusive. Note that this should only be used
|
|
% if the target supports it; currently the C back-end supports
|
|
% this only if you are using the GNU C compiler.
|
|
|
|
% The switch_default specifies what to do if none of the switch
|
|
% conditions match.
|
|
:- type mlds_switch_default
|
|
---> default_is_unreachable
|
|
% The switch is exhaustive, so the default case should
|
|
% never be reached.
|
|
|
|
; default_do_nothing
|
|
% The default action is to just fall through to the statement
|
|
% after the switch.
|
|
|
|
; default_case(mlds_stmt).
|
|
% The default is to execute the specified statement.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Extra info for labels.
|
|
%
|
|
|
|
:- type mlds_label
|
|
---> mlds_label(string).
|
|
|
|
:- type mlds_goto_target
|
|
---> goto_label(mlds_label)
|
|
% Branch to the specified label.
|
|
|
|
; goto_break_switch
|
|
% Branch to just after the end of the immediately enclosing switch,
|
|
% just like a C/C++/Java `break' statement.
|
|
% Not supported by all target languages.
|
|
|
|
; goto_break_loop
|
|
% Branch to just after the end of the immediately enclosing loop
|
|
% just like a C/C++/Java `break' statement.
|
|
% Not supported by all target languages.
|
|
|
|
; goto_continue_loop.
|
|
% Branch to the end of the loop body for the immediately enclosing
|
|
% loop, just like a C/C++/Java/C# `continue' statement.
|
|
% Not supported by all target languages.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Extra info for calls.
|
|
%
|
|
|
|
% The `ml_call_kind' type indicates whether a call is a tail call
|
|
% and whether the call is known to never return.
|
|
%
|
|
% Marking a call as a tail_call is intended as a hint to the target
|
|
% language compiler that this call can be implemented by removing
|
|
% the caller's stack frame and jumping to the destination.
|
|
% However, the target code generator need not obey this hint; it may
|
|
% generate the same kind of code it would generate for a non-tail call.
|
|
%
|
|
:- type ml_call_kind
|
|
---> no_return_call % A call that never returns
|
|
% (this is a special case of a tail call).
|
|
; tail_call % A tail call.
|
|
; ordinary_call. % Just an ordinary call.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Extra info for exception handling.
|
|
%
|
|
|
|
% XXX This is tentative -- the current definition may be
|
|
% a bit too specific to C++-style exceptions.
|
|
% It might not be a good choice for different target languages.
|
|
%
|
|
:- type mlds_exception_handler
|
|
---> handler(
|
|
maybe(mlds_type),
|
|
% If `yes(T)', specifies the type of exceptions to catch.
|
|
% If `no', it means catch all exceptions.
|
|
|
|
maybe(string)
|
|
% If `yes(Name)', gives the variable name to use for the
|
|
% exception value.
|
|
% If `no', then exception value will not be used.
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Info for atomic statements.
|
|
%
|
|
|
|
:- type mlds_atomic_statement
|
|
---> comment(string)
|
|
% A comment to put into the generated code. Should not contain
|
|
% newlines.
|
|
|
|
; assign(mlds_lval, mlds_rval)
|
|
% assign(Location, Value):
|
|
% Assign the value specified by rval to the location
|
|
% specified by lval.
|
|
|
|
; assign_if_in_heap(mlds_lval, mlds_rval)
|
|
% assign_if_in_heap(Location, Value):
|
|
% Assign the address specified by rval to the location specified
|
|
% by lval if the address is in the bounds of the garbage collected
|
|
% heap. Otherwise assign a null pointer to lval.
|
|
|
|
% Heap management.
|
|
|
|
; delete_object(mlds_rval)
|
|
% Compile time garbage collect (i.e. explicitly deallocate)
|
|
% the memory at the address given by the rval.
|
|
|
|
; new_object(
|
|
% new_object(Target, Tag, Type, Size, CtorName,
|
|
% ArgAndTypes, MayUseAtomic, MaybeAllocId):
|
|
%
|
|
% Allocate a memory block of the given size,
|
|
% initialize it with a new object of the given type
|
|
% by calling the constructor with the specified arguments,
|
|
% and put its address in the given lval, possibly after
|
|
% putting the given ptag on the address. The low level
|
|
% data representation support ptags; the high level data
|
|
% representation does not.
|
|
%
|
|
% XXX ARG_PACK Some of the following fields are meaningful
|
|
% only for the low level data representation, some are
|
|
% meaningful only for the high level data representation.
|
|
% We should encode these invariants in the data structure,
|
|
% either by putting each set of such arguments into one
|
|
% or other alternative of a type such as
|
|
% :- type t ---> low_level_data(...) ; high_level_data(...).
|
|
% or by having separate new_object_lld and new_object_hld
|
|
% atomic statements.
|
|
|
|
% The target to assign the new object's address to.
|
|
mlds_lval,
|
|
|
|
% A primary tag to tag the address with before assigning
|
|
% the result to the target. If the primary tag is zero,
|
|
% then the tagging operation is not required, since
|
|
% it would be a no-op.
|
|
%
|
|
% Needed only by ml_accurate_gc.m; not needed by any of
|
|
% the code generators.
|
|
ptag,
|
|
|
|
% Indicates whether or not the first argument in the list (see
|
|
% below) is a secondary tag. For target languages with
|
|
% constructors, secondary tags are implicitly initialised by
|
|
% the constructor, not passed in.
|
|
% XXX update me
|
|
% XXX document relationship with the maybe(qual_ctor_id) field.
|
|
bool,
|
|
|
|
% The type of the object being allocated.
|
|
mlds_type,
|
|
|
|
% The amount of memory that needs to be allocated for the new
|
|
% object, measured in words (NOT bytes!).
|
|
maybe(mlds_rval),
|
|
|
|
% The name of the constructor to invoke.
|
|
maybe(qual_ctor_id),
|
|
|
|
% The arguments to the constructor, and their types.
|
|
% Any arguments which are supposed to be packed together should
|
|
% be packed in this list by the HLDS->MLDS code generator.
|
|
%
|
|
% For boxed fields, the types here should be mlds_generic_type;
|
|
% it is the responsibility of the HLDS->MLDS code generator to
|
|
% insert code to box/unbox the arguments.
|
|
list(mlds_typed_rval),
|
|
|
|
% Can we use a cell allocated with GC_malloc_atomic to hold
|
|
% this object in the C backend?
|
|
may_use_atomic_alloc,
|
|
|
|
% The allocation site identifier.
|
|
maybe(mlds_alloc_id)
|
|
)
|
|
|
|
; gc_check
|
|
% Check to see if we need to do a garbage collection,
|
|
% and if so, do it.
|
|
% This is used for accurate garbage collection
|
|
% with the MLDS->C back-end.
|
|
|
|
; mark_hp(mlds_lval)
|
|
% Tell the heap sub-system to store a marker (for later use in
|
|
% restore_hp/1 instructions) in the specified lval.
|
|
%
|
|
% It is OK for the target to treat this as a no-op, and probably
|
|
% that is what most targets will do.
|
|
|
|
; restore_hp(mlds_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.
|
|
%
|
|
% It is OK for the target to treat this as a no-op,
|
|
% and probably that is what most targets will do.
|
|
|
|
% Trail management.
|
|
|
|
; trail_op(trail_op)
|
|
|
|
% Foreign language interfacing.
|
|
|
|
; inline_target_code(mlds_target_lang, list(target_code_component))
|
|
% Do whatever is specified by the target_code_components, which
|
|
% can be any piece of code in the specified target language (C,
|
|
% assembler, or whatever) that does not have any non-local flow
|
|
% of control. This is implemented by embedding the target code
|
|
% in the output stream of instructions or statements.
|
|
|
|
; outline_foreign_proc(
|
|
% Do whatever is specified by the string, which can be any
|
|
% piece of code in the specified foreign language (C#,
|
|
% managed C++, or whatever). This is implemented by calling
|
|
% an externally defined function, which the backend must
|
|
% generate the definition for (in some other file perhaps)
|
|
% and calling it. The lvals are used to generate the
|
|
% appropriate forwarding code.
|
|
% XXX We should also store the list of mlds_rvals where
|
|
% the input values come from.
|
|
|
|
% The foreign language this code is written in.
|
|
foreign_language,
|
|
|
|
list(outline_arg),
|
|
|
|
% Where to store return value(s).
|
|
list(mlds_lval),
|
|
|
|
% The user's foreign language code fragment.
|
|
string
|
|
).
|
|
|
|
:- inst atomic_stmt_is_new_object for mlds_atomic_statement/0
|
|
---> new_object(ground, ground, ground, ground, ground, ground,
|
|
ground, ground, ground).
|
|
|
|
% Stores information about each argument to an outline_foreign_proc.
|
|
%
|
|
:- type outline_arg
|
|
---> ola_in(
|
|
mlds_type, % The type of the argument.
|
|
string, % The name of the argument in the foreign code.
|
|
mlds_rval % The rval which holds the value of this
|
|
% argument.
|
|
)
|
|
; ola_out(
|
|
mlds_type, % The type of the argument.
|
|
string, % The name of the argument in the foreign code.
|
|
mlds_lval % The lval where we are to place the result
|
|
% calculated by the foreign code into.
|
|
)
|
|
; ola_unused.
|
|
|
|
:- type mlds_target_lang
|
|
---> ml_target_c
|
|
; ml_target_csharp
|
|
; ml_target_java.
|
|
|
|
% This is just a random selection of possible languages
|
|
% that we might want to target later ...
|
|
% ; ml_target_gnu_c
|
|
% ; ml_target_c_minus_minus
|
|
% ; ml_target_java_asm
|
|
% ; ml_target_java_bytecode.
|
|
|
|
:- type target_code_component
|
|
---> user_target_code(
|
|
% User_target_code holds C code from the user's
|
|
% `pragma foreign_proc' declaration.
|
|
|
|
string,
|
|
maybe(prog_context)
|
|
)
|
|
; raw_target_code(
|
|
% Raw_target_code holds C code that the compiler has generated.
|
|
% To ensure that following `#line' directives work OK, either
|
|
% the string in a raw_target_code must end in `\n' (or `\n'
|
|
% followed by whitespace), or the following
|
|
% target_code_component must be a `name(Name)' component,
|
|
% for which we do not output #line directives.
|
|
|
|
string
|
|
)
|
|
|
|
; target_code_input(mlds_rval)
|
|
; target_code_output(mlds_lval)
|
|
; target_code_type(mlds_type)
|
|
; target_code_function_name(qual_function_name)
|
|
; target_code_alloc_id(mlds_alloc_id).
|
|
|
|
:- type mlds_alloc_id
|
|
---> mlds_alloc_id(int).
|
|
|
|
% Constructor id.
|
|
%
|
|
:- type mlds_ctor_id
|
|
---> ctor_id(mlds_class_name, arity).
|
|
:- type qual_ctor_id
|
|
---> qual_ctor_id(mlds_module_name, mlds_qual_kind, mlds_ctor_id).
|
|
|
|
% Trail management.
|
|
% For documentation, see the corresponding LLDS instructions in llds.m.
|
|
%
|
|
:- type trail_op
|
|
---> store_ticket(mlds_lval)
|
|
; reset_ticket(mlds_rval, mlds_reset_trail_reason)
|
|
; discard_ticket
|
|
; prune_ticket
|
|
; mark_ticket_stack(mlds_lval)
|
|
; prune_tickets_to(mlds_rval).
|
|
% ; discard_tickets_to(mlds_rval). % used only by the library
|
|
|
|
% Note: the types `reset_trail_reason' and `tag' here are both defined
|
|
% exactly the same as the ones in llds.m. The definitions are duplicated here
|
|
% because we do not want mlds.m to depend on llds.m. (Alternatively, we could
|
|
% move both these definitions into a new module in the backend_libs.m package,
|
|
% but these definitions are small enough and simple enough that it is probably
|
|
% not worth creating a new module just for them.)
|
|
|
|
% See runtime/mercury_trail.h.
|
|
%
|
|
:- type mlds_reset_trail_reason
|
|
---> undo
|
|
; commit
|
|
; solve
|
|
; exception
|
|
; gc.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Lvals.
|
|
%
|
|
|
|
% An lval represents a data location or variable that can be used
|
|
% as the target of an assignment.
|
|
%
|
|
:- type mlds_lval
|
|
|
|
% Values on the heap or fields of a structure.
|
|
|
|
---> ml_field(
|
|
% field(MaybePtag, Ptr, PtrType, FieldId, FieldType):
|
|
% Selects one field of a compound term.
|
|
%
|
|
% Ptr is a tagged pointer to a cell on the heap.
|
|
% The position in the cell, FieldId, is represented either
|
|
% as a field name or a number of words offset.
|
|
%
|
|
% If MaybePtag is yes(Ptag), then the primary tag on Ptr
|
|
% is Ptag; if MaybePtag is no, then the primary tag bits will
|
|
% have to be masked off of Ptr.
|
|
%
|
|
% The value of the primary tag should be given if it is known,
|
|
% since this will lead to faster code.
|
|
%
|
|
% The FieldType is the type of the field. The PtrType is the
|
|
% type of the pointer from which we are fetching the field.
|
|
%
|
|
% For boxed fields, the type here should be mlds_generic_type,
|
|
% not the actual type of the field. If the actual type is
|
|
% different, then it is the HLDS->MLDS code generator's job
|
|
% to insert the required boxing/unboxing code.
|
|
|
|
field_ptag :: maybe(ptag),
|
|
field_ptr :: mlds_rval,
|
|
field_ptr_type :: mlds_type,
|
|
field_field_id :: mlds_field_id,
|
|
field_field_type :: mlds_type
|
|
)
|
|
|
|
% Values somewhere in memory.
|
|
% This is the dereference operator (e.g. unary `*' in C).
|
|
|
|
; ml_mem_ref(
|
|
% The rval should have originally come from a mem_addr rval.
|
|
% The type is the type of the value being dereferenced,
|
|
% i.e. the type of the pointed-to value.
|
|
|
|
mlds_rval,
|
|
mlds_type
|
|
)
|
|
|
|
; ml_target_global_var_ref(
|
|
% A reference to the value of the global variable in the target
|
|
% language with the given name. At least for now, the global
|
|
% variable's type must be mlds_generic_type.
|
|
%
|
|
% XXX This functionality is currently only supported for
|
|
% the C backend.
|
|
|
|
global_var_ref
|
|
)
|
|
|
|
% Variables.
|
|
|
|
; ml_local_var(
|
|
mlds_local_var_name,
|
|
mlds_type
|
|
)
|
|
|
|
; ml_global_var(
|
|
qual_global_var_name,
|
|
mlds_type
|
|
).
|
|
|
|
% The information contained in an ml_local_var mlds_lval.
|
|
% Use this type when you need a way to store information about
|
|
% an mlds_lval that is definitely a local variable.
|
|
%
|
|
:- type mlds_local_var_name_type
|
|
---> mlds_local_var_name_type(
|
|
mlds_local_var_name,
|
|
mlds_type
|
|
).
|
|
|
|
% An mlds_field_id represents some data within an object.
|
|
%
|
|
:- type mlds_field_id
|
|
---> ml_field_offset(mlds_rval)
|
|
% offset(N) represents the field at offset N Words.
|
|
|
|
; ml_field_named(
|
|
% ml_field_named(Name, MaybeCtorType) represents the field with
|
|
% the specified name.
|
|
%
|
|
% CtorType gives the MLDS type for this particular constructor.
|
|
% The type of the object is given by the PtrType in the
|
|
% field(..) lval. CtorType may either be the same as PtrType,
|
|
% or it may be a pointer to a derived class. In the latter
|
|
% case, the MLDS->target code back-end is responsible for
|
|
% inserting a downcast from PtrType to CtorType before
|
|
% accessing the field.
|
|
|
|
qual_field_var_name,
|
|
mlds_type
|
|
).
|
|
|
|
:- type global_var_ref
|
|
---> env_var_ref(string).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Rvals.
|
|
%
|
|
|
|
:- type mlds_typed_rval
|
|
---> ml_typed_rval(mlds_rval, mlds_type).
|
|
|
|
% An rval is an expression that represents a value.
|
|
%
|
|
:- type mlds_rval
|
|
---> ml_lval(mlds_lval)
|
|
% The value of an `lval' rval is just the value stored in
|
|
% the specified lval.
|
|
|
|
; ml_mkword(ptag, mlds_rval)
|
|
% Given a pointer and a primary tag, return a tagged pointer.
|
|
%
|
|
% In theory, we could make `mkword' a binary_op, but this
|
|
% representation is tighter: it makes clear that the tag
|
|
% we want to put on is *always* a constant.
|
|
|
|
; ml_const(mlds_rval_const)
|
|
|
|
; ml_box(mlds_type, mlds_rval)
|
|
% ml_box(MLDSType, Rval); convert Rval from MLDSType to
|
|
% mlds_generic_type, by boxing if that is necessary,
|
|
% or just by casting if it is not.
|
|
|
|
; ml_unbox(mlds_type, mlds_rval)
|
|
% ml_unbox(MLDSType, Rval): convert Rval from mlds_generic_type
|
|
% to MLDSType, applying the inverse transformation to ml_box,
|
|
% which means unboxing if boxing was necessary, and just casting
|
|
% if it was not.
|
|
|
|
; ml_cast(mlds_type, mlds_rval)
|
|
% ml_cast(MLDSType, Rval): Coerce the type of Rval to be MLDSType.
|
|
%
|
|
% XXX It might be worthwhile adding the type that we cast from.
|
|
%
|
|
% XXX When the rval is an enum object, and MLDSType's unboxed form
|
|
% is "int", then output_cast_rval_for_java also outputs code
|
|
% to access the .MR_value field of the enum object. I (zs) believe
|
|
% that this should be an *separate* operation from the cast,
|
|
% represented e.g. as
|
|
%
|
|
% ml_cast(MLDSType, ml_get_enum_value(Rval))
|
|
|
|
; ml_unop(unary_op, mlds_rval)
|
|
|
|
; ml_binop(binary_op, mlds_rval, mlds_rval)
|
|
|
|
; ml_mem_addr(mlds_lval)
|
|
% The address of a variable, etc.
|
|
|
|
; ml_scalar_common(mlds_scalar_common)
|
|
% A reference to the given common structure. The reference is NOT
|
|
% the address; that is ml_scalar_common_addr.
|
|
%
|
|
% This rval is intended to be used as the name of an array
|
|
% to be indexed into. This is possible because the elements
|
|
% of a scalar common cell are all boxed and thus of the same size.
|
|
% XXX This need NOT be true in the presence of double-word
|
|
% float, int64 or uint64 arguments on 32 bit platforms.
|
|
|
|
; ml_scalar_common_addr(mlds_scalar_common)
|
|
% The address of the ml_scalar_common(...) for the same common.
|
|
|
|
; ml_vector_common_row_addr(mlds_vector_common, mlds_rval)
|
|
% ml_vector_common_row_addr(VectorCommon, Index),
|
|
% The address of the given row (selected by the second argument)
|
|
% of the given common structure. If VectorCommon is equal e.g.
|
|
% to ml_vector_common(ModuleName, Type, TypeNum, StartRow, NumRows)
|
|
% then Index must be between 0 and NumRows-1 (both inclusive),
|
|
% and the reference will be row StartRow + Index in the 2D table
|
|
% represented by the 2D table holding all the vector static data of
|
|
% this type.
|
|
%
|
|
% The selected row should be a struct holding unboxed values,
|
|
% which can be retrieved using field names (not field offsets).
|
|
|
|
; ml_self(mlds_type).
|
|
% The equivalent of the `this' pointer in C++ with the type of the
|
|
% object. Note that this rval is valid iff we are targeting an
|
|
% object oriented backend and we are in an instance method
|
|
% (procedures which have the per_instance flag set).
|
|
|
|
:- type mlds_rval_const
|
|
---> mlconst_true
|
|
; mlconst_false
|
|
; mlconst_int(int)
|
|
; mlconst_uint(uint)
|
|
; mlconst_int8(int8)
|
|
; mlconst_uint8(uint8)
|
|
; mlconst_int16(int16)
|
|
; mlconst_uint16(uint16)
|
|
; mlconst_int32(int32)
|
|
; mlconst_uint32(uint32)
|
|
; mlconst_int64(int64)
|
|
; mlconst_uint64(uint64)
|
|
; mlconst_enum(int, mlds_type)
|
|
; mlconst_char(int)
|
|
; mlconst_float(float)
|
|
; mlconst_string(string)
|
|
; mlconst_multi_string(list(string))
|
|
% A multi_string_const is a string containing embedded NULs
|
|
% between each substring in the list.
|
|
|
|
; mlconst_foreign(foreign_language, string, mlds_type)
|
|
|
|
; mlconst_named_const(target_prefixes, string)
|
|
% mlconst_named_const(TargetPrefixes, ConstName) represents
|
|
% an enum constant in the runtime system whose name in the target
|
|
% language is ConstName prefixed by
|
|
%
|
|
% - nothing, if the target language is C,
|
|
% - TargetPrefixes ^ java_prefix, if the target language is Java,
|
|
% - TargetPrefixes ^ csharp_prefix, if the target language is C#.
|
|
%
|
|
% It is the responsibility of the code that creates named constants
|
|
% to ensure that the constant's name is meaningful for the target
|
|
% language. This includes making sure that the name is acceptable
|
|
% as an identifier, does not need quoting, and the constant it
|
|
% refers to is accessible.
|
|
|
|
; mlconst_code_addr(mlds_code_addr)
|
|
|
|
; mlconst_data_addr_local_var(mlds_local_var_name)
|
|
% The address of a local variable.
|
|
|
|
; mlconst_data_addr_global_var(mlds_module_name,
|
|
mlds_global_var_name)
|
|
% The address of a global variable.
|
|
|
|
; mlconst_data_addr_rtti(mlds_module_name, rtti_id)
|
|
% The address of an RTTI data structure.
|
|
|
|
; mlconst_data_addr_tabling(qual_proc_label,
|
|
proc_tabling_struct_id)
|
|
% The address of the given tabling data structure
|
|
% for the given procedure.
|
|
|
|
; mlconst_null(mlds_type).
|
|
% A null value, of the given type. Usually the type will be
|
|
% a pointer (mlds_ptr_type) but it could also be string or
|
|
% a func_type. (Null is not a valid value of type string or
|
|
% func_type, but null values of those types may be useful
|
|
% as placeholders in cases where the value will never be used.)
|
|
|
|
:- type mlds_code_addr
|
|
---> mlds_code_addr(
|
|
qual_func_label,
|
|
mlds_func_signature
|
|
).
|
|
|
|
:- type ml_scalar_common_type_num
|
|
---> ml_scalar_common_type_num(int).
|
|
:- type ml_vector_common_type_num
|
|
---> ml_vector_common_type_num(int).
|
|
|
|
:- type mlds_scalar_common
|
|
---> mlds_scalar_common(mlds_module_name, mlds_type,
|
|
ml_scalar_common_type_num, int).
|
|
% module name, type, type number, row number
|
|
|
|
:- type mlds_vector_common
|
|
---> mlds_vector_common(mlds_module_name, mlds_type,
|
|
ml_vector_common_type_num, int, int).
|
|
% module name, type, type number,
|
|
% starting row number, number of rows
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Foreign code interfacing.
|
|
%
|
|
|
|
% Foreign code required for the foreign language interface.
|
|
% When compiling to a language other than the foreign language,
|
|
% this part still needs to be generated as C (or whatever) code
|
|
% and compiled with a C (or whatever) compiler.
|
|
%
|
|
:- type mlds_foreign_code
|
|
---> mlds_foreign_code(
|
|
list(foreign_decl_code),
|
|
list(foreign_body_code),
|
|
list(fim_spec),
|
|
list(mlds_pragma_export)
|
|
).
|
|
|
|
% Information required to generate code for each `pragma foreign_export'.
|
|
%
|
|
:- type mlds_pragma_export
|
|
---> ml_pragma_export(
|
|
foreign_language,
|
|
string, % Exported name
|
|
qual_function_name, % MLDS name for exported entity
|
|
mlds_func_params, % MLDS function parameters
|
|
list(tvar), % Universally quantified type
|
|
% variables.
|
|
prog_context
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type mlds_exported_enum
|
|
---> mlds_exported_enum(
|
|
exported_enum_lang :: foreign_language,
|
|
exported_enum_context :: prog_context,
|
|
|
|
% mlds_to_cs_*.m and mlds_to_java_*.m need to know
|
|
% the type_ctor.
|
|
exported_enum_type_ctor :: type_ctor,
|
|
|
|
% The name of each constant plus the value to initialize it to.
|
|
exported_enum_constants :: list(mlds_exported_enum_constant)
|
|
).
|
|
|
|
:- type mlds_exported_enum_constant
|
|
---> mlds_exported_enum_constant(
|
|
exported_enum_constant_name :: string,
|
|
exported_enum_constant_value :: mlds_initializer
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Global variable names.
|
|
%
|
|
|
|
% XXX Some global constants need to be visible throughout the program,
|
|
% including outside the module that defines them, and therefore their
|
|
% names need to be module qualified. However, some global variables and
|
|
% constants need not be (and therefore *should* not be) visible outside
|
|
% their modules, and these should *not* need to be module qualified.
|
|
% Removing the module qualification from such variables would make
|
|
% the generated code considerably less cluttered and therefore
|
|
% easier to read.
|
|
%
|
|
:- type qual_global_var_name
|
|
---> qual_global_var_name(mlds_module_name, mlds_global_var_name).
|
|
% Global variables can only ever be module qualified.
|
|
|
|
:- type mlds_global_var_name
|
|
---> gvn_rtti_var(rtti_id)
|
|
|
|
; gvn_tabling_var(mlds_proc_label, proc_tabling_struct_id)
|
|
% A variable that contains a pointer that points to the table
|
|
% used to implement memoization, loopcheck or minimal model
|
|
% semantics for the given procedure.
|
|
|
|
; gvn_const_var(mlds_global_const_var, int)
|
|
% These MLDS variables are global variables holding constant,
|
|
% immutable data. What kind of data is given by the first argument.
|
|
% The integer is a sequence number (unique within the whole module)
|
|
% allocated from a counter that is shared between all
|
|
% gvn_const_vars.
|
|
|
|
; gvn_dummy_var.
|
|
% A reference to private_builtin.dummy_var.
|
|
|
|
:- type mlds_global_const_var
|
|
---> mgcv_const_var
|
|
; mgcv_float
|
|
; mgcv_int64
|
|
; mgcv_uint64
|
|
; mgcv_closure_layout
|
|
; mgcv_typevar_vector
|
|
; mgcv_bit_vector.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Field variable names.
|
|
%
|
|
|
|
% An mlds_field_var represents a variable or constant that is defined
|
|
% as a field (or member) of a class.
|
|
%
|
|
% XXX Some classes, and therefore their public fields, need to be visible
|
|
% throughout the program, including outside the module that defines them,
|
|
% and therefore their names need to be module qualified. However,
|
|
% fields in classes that are local to their modules should *not* need
|
|
% to be module qualified. Removing the module qualification from
|
|
% such field variables would make the generated code less cluttered
|
|
% and therefore easier to read.
|
|
%
|
|
:- type qual_field_var_name
|
|
---> qual_field_var_name(mlds_module_name, mlds_qual_kind,
|
|
mlds_field_var_name).
|
|
% Some field variables are module qualified, while others
|
|
% are type qualified.
|
|
|
|
:- type mlds_field_var_name
|
|
---> fvn_global_data_field(int, int)
|
|
% When implementing lookup switches (and some related HLDS
|
|
% constructs, such as lookup disjunctions, which are like
|
|
% lookup switches without the switch), the compiler generates
|
|
% a vector of rows, with each row holding one solution
|
|
% generated by the switch (or the disjunction).
|
|
% We store all solutions that contain the same vector of types
|
|
% in the same static (immutable) global variable. The type
|
|
% of this variable is thus an array of structures, with the
|
|
% types of the field being given by the types of the variables that
|
|
% comprise the outputs of the HLDS construct being implemented.
|
|
%
|
|
% The two arguments of fvn_global_data_field are the type number
|
|
% and the field number. The type number uniquely identifies
|
|
% the list of field types in each row (among all the lists of
|
|
% field types for which we generate static arrays of rows),
|
|
% while the second just gives the position of the field
|
|
% in that list. Field numbers start at 0.
|
|
|
|
; fvn_du_ctor_field_hld(string)
|
|
% When compiling with --high-level-data, we generate a type
|
|
% in the target language for each data constructor in a
|
|
% discriminated union type. This is the name of one of the fields
|
|
% of this type. For a subtype, this has to be the name of the
|
|
% corresponding field in the base type.
|
|
% NOTE: See the XXX on the code that turns these variables
|
|
% into strings.
|
|
|
|
; fvn_mr_value
|
|
; fvn_data_tag
|
|
; fvn_enum_const(string)
|
|
% These represent member variables in the classes we generate
|
|
% with --high-level-data for discriminated union types
|
|
% (both enum and non-enum). I (zs) do not know exactly what
|
|
% they are used for.
|
|
% NOTE: See the XXX on the code that turns some of these
|
|
% MLDS variables into strings.
|
|
|
|
; fvn_base_class(int)
|
|
% This variable name is used in the implementation of base classes
|
|
% in the C backend, but I (zs) do not know exactly what it is
|
|
% used for.
|
|
|
|
; fvn_ptr_num
|
|
% This variable is used by the Java backend.
|
|
% I (zs) do not know what its semantics is.
|
|
|
|
; fvn_env_field_from_local_var(mlds_local_var_name)
|
|
% This is a field of an environment structure that represents
|
|
% the given local variable.
|
|
|
|
; fvn_prev
|
|
; fvn_trace.
|
|
% These MLDS variables represent two fixed fields in the frames
|
|
% that hold variables when we compiler for accurate gc. fvn_prev
|
|
% points to the previous frame (if any) in the chain of frames,
|
|
% while fvn_trace points to the function that traces the contents
|
|
% of the current frame.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Local variable names.
|
|
%
|
|
|
|
% An mlds_local_var represents a variable that is either a parameter
|
|
% of a function, or is defined inside a block of a function.
|
|
%
|
|
:- type mlds_local_var_name
|
|
---> lvn_prog_var(string, int)
|
|
% This MLDS variable represents a variable in the HLDS procedure
|
|
% being translated to MLDS. The string gives its name
|
|
% (if it is unnamed, the string will be ""), and the int gives
|
|
% its HLDS variable number.
|
|
|
|
; lvn_prog_var_foreign(string)
|
|
% This MLDS variable represents a HLDS variable. It does not
|
|
% have the usual numeric suffix derived from its HLDS variable
|
|
% number because it is one of the arguments of a foreign_proc
|
|
% goal. Its name in the target language must be exactly the same
|
|
% as the name written by the programmer, since that is the name
|
|
% by which the foreign language code will refer to it.
|
|
|
|
; lvn_prog_var_boxed(string, int)
|
|
% This MLDS variable represents the boxed version of the HLDS
|
|
% variable with the given name and number.
|
|
|
|
; lvn_prog_var_conv(int, string, int)
|
|
% This MLDS variable represents a version of a HLDS variable
|
|
% (of the name and number given by the second and third args)
|
|
% created by a boxing or unboxing operation. The first integer
|
|
% is a sequence number that uniquely identifies the operation
|
|
% within the MLDS code generated for a Mercury procedure.
|
|
% XXX Why is lvn_prog_var_boxed separate from this?
|
|
|
|
; lvn_prog_var_next_value(string, int)
|
|
% This MLDS variable represents the next value to be assigned
|
|
% to the HLDS variable with the given name and number.
|
|
%
|
|
% The compiler must generate such temporaries when generating code
|
|
% for tail calls that switch input arguments. For example,
|
|
% if a procedure with head p(X, Y, ...) contains a tail call
|
|
% p(Y, X, ...), then having the tail call assign the first
|
|
% two arguments as X := Y, Y := X would not work, since
|
|
% the first assignment would clobber X. The compiler therefore
|
|
% generates next_value_of_X := Y, next_value_of_Y := X,
|
|
% followed by X := next_value_of_X, Y := next_value_of_Y.
|
|
|
|
; lvn_local_var(string, int)
|
|
% This MLDS variable represents a local copy of a HLDS variable
|
|
% (with the given name and number) that is an output variable
|
|
% of a commit scope. With --nondet-copy-out, the code inside
|
|
% the scope that produces the HLDS variable assigns its value
|
|
% to the local copy; this value is then copied to the actual
|
|
% lvn_prog_var representing the HLDS variable when the scope
|
|
% succeeds.
|
|
|
|
; lvn_tscc_proc_input_var(proc_id_in_tscc, int, string)
|
|
; lvn_tscc_output_var(int, string)
|
|
; lvn_tscc_output_var_ptr(int, string)
|
|
; lvn_tscc_output_var_succeeded
|
|
% All four of these are used when we generate code for
|
|
% a set of mutually *tail* recursive procedures.
|
|
% (Such procedures form a TSCC, i.e. an SCC of a dependency
|
|
% graph formed only from tail calls.)
|
|
%
|
|
% Our general scheme for optimizing mutually recursive tail calls
|
|
% in TSCCs is described in notes/mlds_tail_recursion.html.
|
|
% Here we just document the meanings of these MLDS variables.
|
|
%
|
|
% lvn_tscc_proc_input_var(ProcIdInTscc, ArgNum, VarName) represents
|
|
% one of the input arguments of one of the procedures in a TSCC
|
|
% (tail-call SCC); the ProcIdInTscc and ArgNum say which.
|
|
% The VarName is the output of ml_local_var_name_to_string
|
|
% on the name of the actual variable representing that argument.
|
|
%
|
|
% lvn_tscc_output_var(ArgNum, VarName) represents one of the output
|
|
% arguments of a TSCC (tail-call SCC); the ArgNum says which.
|
|
% Note that while each procedure in a TSCC has its own list
|
|
% of inputs arguments, all procedures in a TSCC, by definition,
|
|
% return a list of output arguments that is identical in everything
|
|
% except possibly name. This allows us to use a single variable
|
|
% to represent e.g. output arg N of *every* procedure in the TSCC;
|
|
% This is why lvn_tscc_output_vars don't have a ProcIdInTscc field.
|
|
% (The VarName recorded in a lvn_tscc_output_var is the name of
|
|
% the output argument in *one* of the procedures in the TSCC.)
|
|
%
|
|
% An lvn_tscc_output_var will contain the value of the
|
|
% corresponding output argument. For arguments passed by
|
|
% reference, we need a way to store the address passed to us
|
|
% by the caller as the address at which the value being output
|
|
% should be put. An lvn_tscc_output_var_ptr contains this address.
|
|
%
|
|
% Model semi procedures implicitly have an extra output argument:
|
|
% the procedure's success/failure indicator. We store this
|
|
% indicator in a lvn_tscc_output_var_succeeded variable,
|
|
% which functions as a special kind of lvn_tscc_output_var.
|
|
% The success indicator is always returned by value, so we
|
|
% do not need a lvn_tscc_output_var_succeeded_ptr variable
|
|
% in the MLDS.
|
|
|
|
; lvn_field_var_as_local(mlds_field_var_name)
|
|
% When we (specifically, ml_elim_nested.m) create continuation
|
|
% functions, we give them the values of the local variables
|
|
% as fields of an environment structure. To take the values
|
|
% stored there *out* of the environment structure, we refer
|
|
% to them as if they were local variables, using this construct.
|
|
|
|
; lvn_comp_var(mlds_compiler_var).
|
|
% This MLDS variable was created by the compiler as part of
|
|
% the translation process; it does NOT correspond to a variable
|
|
% in the HLDS.
|
|
|
|
% Identifies a procedure in a TSCC (an SCC that is computed by taking
|
|
% only tail calls into account.
|
|
%
|
|
% TSCCs are typically small, most containing only two or three procedures.
|
|
% The ids are sequentially allocated small integers starting at 1.
|
|
%
|
|
:- type proc_id_in_tscc
|
|
---> proc_id_in_tscc(int).
|
|
|
|
:- inst lvn_prog_var for mlds_local_var_name/0
|
|
---> lvn_prog_var(ground, ground).
|
|
|
|
:- type mlds_compiler_var
|
|
---> lvnc_non_prog_var_boxed(string)
|
|
; lvnc_non_prog_var_conv(int, string)
|
|
; lvnc_non_prog_var_next_value(string)
|
|
% What the lvn_prog_var_boxed, lvn_prog_var_conv, and
|
|
% lvn_prog_var_next_value are to lvn_prog_vars, these are
|
|
% to lvn_comp_vars. The string is the output of
|
|
% ml_local_var_name_to_string of the original lvn_comp_var.
|
|
|
|
; lvnc_succeeded
|
|
% The MLDS variable indicating whether a compiler generated
|
|
% semidet goal has succeeded.
|
|
|
|
; lvnc_success_indicator
|
|
% The MLDS variable representing the target language variable
|
|
% SUCCESS_INDICATOR used by hand-written code in semidet
|
|
% foreign_procs to indicate success or failure.
|
|
|
|
; lvnc_tscc_proc_selector
|
|
% The MLDS variable that says which member of the TSCC
|
|
% a (self- or) mutually recursive tail call is targeting.
|
|
% Used when the body of each procedure in the TSCC is made
|
|
% one case of a switch on lvnc_tscc_proc_selector, which in turn
|
|
% is the body of a "while (true)" loop. Tail recursive calls
|
|
% set lvnc_tscc_proc_selector to select the target procedure,
|
|
% set up its input arguments, and "continue" to the next iteration
|
|
% of the loop.
|
|
|
|
; lvnc_new_obj(int)
|
|
% A temporary variable holding the address of a newly allocated
|
|
% object, used by accurate gc. The integer is a unique sequence
|
|
% number allocated from a per-procedure counter that is dedicated
|
|
% for this purpose.
|
|
|
|
; lvnc_cond(int)
|
|
% This MLDS variable records whether the condition of a model_non
|
|
% if-then-else has ever succeeded. The integer is a sequence number
|
|
% allocated from a counter that is dedicated for this purpose.
|
|
|
|
; lvnc_conv_var(int)
|
|
% This MLDS variable holds the address of the callee of a generic
|
|
% call (according to the comment creating it, we need a variable
|
|
% to hold the address to work around limitations in old C
|
|
% compilers). The integer is a sequence number allocated from
|
|
% a counter that is dedicated for this purpose.
|
|
|
|
; lvnc_arg(int)
|
|
% This MLDS variable represents the Nth output parameter of
|
|
% a success continuation, where N is the given integer.
|
|
|
|
; lvnc_wrapper_arg(int)
|
|
% This MLDS variable represents the Nth parameter of the wrapper
|
|
% function around a procedure, where N is the given integer.
|
|
% We put a wrapper function around procedures when they are used
|
|
% as the code in a closure.
|
|
|
|
; lvnc_param(int)
|
|
% This MLDS variable represents the Nth parameter of an MLDS
|
|
% function, where N is the given integer, when the declaration
|
|
% of that function is standardized for printing in an automatically
|
|
% generated header file. This standardization avoids the need
|
|
% to regenerate every file that depends on that header file
|
|
% when the actual names of the corresponding procedure's arguments
|
|
% change in the HLDS.
|
|
|
|
; lvnc_out_param(int)
|
|
% This MLDS variable represents the Nth output parameter of an MLDS
|
|
% function being compiled to C#, where N is the given integer,
|
|
% and N > 1. (The C# backend returns the first output parameter
|
|
% as the return value, but returns the others as the "out"
|
|
% parameters represented by these MLDS variables.)
|
|
|
|
; lvnc_return_value
|
|
% This MLDS variable represents the return value(s) of method
|
|
% created by the Java backend.
|
|
% XXX document me in more detail.
|
|
|
|
; lvnc_closure
|
|
; lvnc_closure_arg
|
|
% These two MLDS variables respectively represent the unboxed
|
|
% and the boxed versions of a closure.
|
|
|
|
; lvnc_closure_layout_ptr
|
|
% This MLDS variable holds a pointer to a closure's
|
|
% MR_Closure_Layout structure. This is used for accurate gc.
|
|
|
|
; lvnc_type_params
|
|
% This MLDS variable holds a pointer to a closure's materialized
|
|
% type_infos. This is used for accurate gc.
|
|
|
|
; lvnc_type_info
|
|
% This MLDS variable holds a pointer to a materialized type_info.
|
|
% This is used for accurate gc.
|
|
|
|
; lvnc_cont
|
|
; lvnc_cont_env_ptr
|
|
% These MLDS variables hold respectively a nondet continuation,
|
|
% and the pointer to the environment containing the variables
|
|
% for the continuation.
|
|
|
|
; lvnc_env
|
|
; lvnc_env_ptr
|
|
% These MLDS variables refer to environments needed to simulate
|
|
% nested functions in targets that do not support them.
|
|
% The lvnc_env variable is the environment structure itself,
|
|
% while lvnc_env_ptr is its address.
|
|
|
|
; lvnc_env_ptr_arg
|
|
% This MLDS variable, like lvnc_env_ptr, points to an environment
|
|
% structure, but its type is generic; it is not specific to the
|
|
% structure of the environment it actually points to. Therefore
|
|
% lvnc_env_ptr_args are cast to lvnc_env_ptrs before use.
|
|
|
|
; lvnc_frame
|
|
; lvnc_frame_ptr
|
|
% These MLDS variables refer to the frames that hold variables
|
|
% when we compile code for accurate gc. The lvnc_frame variable
|
|
% is the frame structure itself, while lvnc_frame_ptr
|
|
% is its address.
|
|
|
|
; lvnc_this_frame
|
|
% This MLDS variable, like lvnc_frame_ptr, points to a frame
|
|
% that holds variables when we compile for accurate gc, but
|
|
% its type is generic; it is not specific to the structure
|
|
% of the frame it actually points to. Therefore lvnc_this_frames
|
|
% are cast to lvnc_frame_ptrs before use.
|
|
|
|
; lvnc_stack_chain
|
|
% This MLDS variable represents the global variable that holds
|
|
% the pointer to the current chain of frames.
|
|
|
|
; lvnc_saved_stack_chain(int)
|
|
% This MLDS variable represents a locally-saved copy of the
|
|
% global variable represented by lvnc_stack_chain.
|
|
|
|
; lvnc_args
|
|
% This variable is used by the Java backend.
|
|
% I (zs) do not know what its semantics is.
|
|
|
|
; lvnc_aux_var(mlds_compiler_aux_var, int)
|
|
% These MLDS variables contain values (most, but not all of which
|
|
% are very short-lived) used to implement Mercury constructs
|
|
% such as commits and switches. They contain such information
|
|
% as the boundaries of a binary search or slot numbers in the
|
|
% hash tables used to implement switches.
|
|
% The integer is a sequence number (unique within the procedure)
|
|
% allocated from a counter that is shared between all
|
|
% lvnc_aux_vars.
|
|
|
|
; lvnc_packed_word(int).
|
|
% Each of these MLDS variables contains a copy of a word that
|
|
% contains the values of tags and/or arguments packed together.
|
|
% The idea is that for HLDS code such as
|
|
%
|
|
% ...
|
|
% X0 = f(A0, B0, C0), % deconstruction
|
|
% ...
|
|
% X = f(A , B0, C0), % construction
|
|
% ...
|
|
%
|
|
% where the B0 and C0 arguments are packed together into one word,
|
|
% we should generate code that copies B0 and C0 from X0 to X
|
|
% using just one load and one store. The shifts and masks needed
|
|
% to compute the values of B0 and C0 should be needed only if
|
|
% the HLDS code actually uses those variables in their own right.
|
|
%
|
|
% When the MLDS code generator sees a deconstruction such as the
|
|
% above, which contains a word containing two or more entities
|
|
% (tags and/or arguments), all of which are being generated,
|
|
% it allocates a unique integer for a new lvnc_packed_word variable
|
|
% and assigns that word to it. Later code that needs the contents
|
|
% of a word with the same bitfields, filled with some of the same
|
|
% values, may use this new variable instead. As its name indicates,
|
|
% the ml_unused_assign optimization will delete the unused
|
|
% assignments.
|
|
|
|
:- type mlds_compiler_aux_var
|
|
---> mcav_commit
|
|
; mcav_slot
|
|
; mcav_later_slot
|
|
; mcav_num_later_solns
|
|
; mcav_limit
|
|
; mcav_str
|
|
; mcav_lo
|
|
; mcav_mid
|
|
; mcav_hi
|
|
; mcav_stop_loop
|
|
; mcav_result
|
|
; mcav_case_num.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func ml_global_const_var_name_to_string(mlds_global_const_var, int)
|
|
= string.
|
|
:- func ml_field_var_name_to_string(mlds_field_var_name) = string.
|
|
:- func ml_local_var_name_to_string(mlds_local_var_name) = string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.type_util.
|
|
:- import_module mdbcomp.builtin_modules.
|
|
:- import_module parse_tree.builtin_lib_types.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.java_names.
|
|
:- import_module parse_tree.prog_type_construct.
|
|
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mlds_get_module_name(MLDS) = MLDS ^ mlds_name.
|
|
|
|
% For Java:
|
|
% The "package_name" is really the name of the Mercury module, and the
|
|
% "module_name" is the "package_name" plus additional qualifiers (if any).
|
|
% For example, a type `c' in module `a.b' would have package_name == `a.b'
|
|
% and module_name == `a.b.c'.
|
|
% XXX clean this up
|
|
%
|
|
% Note that modules in the Mercury standard library map get a `mercury'
|
|
% prefix e.g. `mercury.builtin', `mercury.io', `mercury.univ', etc.,
|
|
% when mapped to MLDS package names.
|
|
|
|
:- type mlds_module_name
|
|
---> mlds_module_name(
|
|
mmn_package_name :: mdbcomp.sym_name.module_name,
|
|
mmn_module_name :: mdbcomp.sym_name.module_name
|
|
).
|
|
|
|
mercury_module_name_to_mlds(MercuryModule) = Name :-
|
|
( if mercury_std_library_module_name(MercuryModule) then
|
|
MLDS_Package = add_outermost_qualifier("mercury", MercuryModule)
|
|
else
|
|
MLDS_Package = MercuryModule
|
|
),
|
|
Name = mlds_module_name(MLDS_Package, MLDS_Package).
|
|
|
|
mercury_module_and_package_name_to_mlds(MLDS_Package, MercuryModule)
|
|
= mlds_module_name(MLDS_Package, MercuryModule).
|
|
|
|
mlds_module_name_to_sym_name(Module) = Module ^ mmn_module_name.
|
|
|
|
mlds_module_name_to_package_name(Module) = Module ^ mmn_package_name.
|
|
|
|
is_std_lib_module(Module, Name) :-
|
|
Name0 = Module ^ mmn_module_name,
|
|
strip_outermost_qualifier(Name0, "mercury", Name),
|
|
mercury_std_library_module_name(Name).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mlds_append_class_qualifier(Target, ModuleName0, QualKind,
|
|
ClassName, ClassArity) = ModuleName :-
|
|
ModuleName0 = mlds_module_name(Package, Module0),
|
|
% For the Java back-end, we flip the initial case of an type qualifiers,
|
|
% in order to match the usual Java conventions.
|
|
( if
|
|
Target = ml_target_java,
|
|
QualKind = type_qual
|
|
then
|
|
AdjustedModule0 = flip_initial_case_of_final_part(Module0)
|
|
else
|
|
AdjustedModule0 = Module0
|
|
),
|
|
ModuleName = mlds_append_class_qualifier_base(Package, AdjustedModule0,
|
|
ClassName, ClassArity).
|
|
|
|
mlds_append_class_qualifier_module_qual(ModuleName0, ClassName, ClassArity)
|
|
= ModuleName :-
|
|
ModuleName0 = mlds_module_name(Package, Module0),
|
|
ModuleName = mlds_append_class_qualifier_base(Package, Module0,
|
|
ClassName, ClassArity).
|
|
|
|
:- func mlds_append_class_qualifier_base(module_name, module_name,
|
|
mlds_class_name, arity) = mlds_module_name.
|
|
:- pragma inline(func(mlds_append_class_qualifier_base/4)).
|
|
|
|
mlds_append_class_qualifier_base(Package, Module, ClassName, ClassArity)
|
|
= ModuleName :-
|
|
string.format("%s_%d", [s(ClassName), i(ClassArity)], ClassQualifier),
|
|
ModuleName = mlds_module_name(Package, qualified(Module, ClassQualifier)).
|
|
|
|
mlds_append_name(mlds_module_name(Package, Module), Name)
|
|
= mlds_module_name(Package, qualified(Module, Name)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
global_dummy_var = DummyVar :-
|
|
MLDS_ModuleName =
|
|
mercury_module_name_to_mlds(mercury_private_builtin_module),
|
|
DummyVar = qual_global_var_name(MLDS_ModuleName, gvn_dummy_var).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_initializer_array_size(no_initializer) = no_size.
|
|
get_initializer_array_size(init_obj(_)) = no_size.
|
|
get_initializer_array_size(init_struct(_, _)) = no_size.
|
|
get_initializer_array_size(init_array(Elems)) = array_size(list.length(Elems)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mlds_get_arg_types(Parameters) = ArgTypes :-
|
|
GetArgType = (func(mlds_argument(_, Type, _)) = Type),
|
|
ArgTypes = list.map(GetArgType, Parameters).
|
|
|
|
mlds_get_func_signature(Params) = Signature :-
|
|
Params = mlds_func_params(Parameters, RetTypes),
|
|
ParamTypes = mlds_get_arg_types(Parameters),
|
|
Signature = mlds_func_signature(ParamTypes, RetTypes).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% There is some special-case handling for arrays, foreign types, subtypes, and
|
|
% some other types here, but apart from that, currently we return mlds_types
|
|
% that are just the same as Mercury types, except that we also store the type
|
|
% category, so that we can tell if the type is an enumeration or not, without
|
|
% needing to refer to the HLDS type_table.
|
|
% XXX It might be a better idea to get rid of the mercury_nb_type/2 MLDS type
|
|
% and instead fully convert all Mercury types to MLDS types.
|
|
|
|
mercury_type_to_mlds_type(ModuleInfo, Type) = MLDSType :-
|
|
( if type_to_ctor_and_args(Type, TypeCtor, TypeArgs) then
|
|
TypeCtor = type_ctor(TypeCtorSymName, TypeCtorArity),
|
|
( if
|
|
TypeCtorSymName = qualified(unqualified("array"), "array"),
|
|
TypeArgs = [ElemType]
|
|
then
|
|
expect(unify(TypeCtorArity, 1), $pred, "TypeCtorArity != 1"),
|
|
MLDSElemType = mercury_type_to_mlds_type(ModuleInfo, ElemType),
|
|
MLDSType = mlds_mercury_array_type(MLDSElemType)
|
|
else if
|
|
TypeCtorSymName = qualified(mercury_private_builtin_module,
|
|
"store_at_ref_type"),
|
|
TypeArgs = [RefType]
|
|
then
|
|
expect(unify(TypeCtorArity, 1), $pred, "TypeCtorArity != 1"),
|
|
MLDSRefType = mercury_type_to_mlds_type(ModuleInfo, RefType),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Target = target_csharp,
|
|
% `mlds_ptr_type' confuses the C# backend because it is also
|
|
% used for `out' parameters. `store_at_ref_type' is not
|
|
% applicable on that backend anyway.
|
|
MLDSType = MLDSRefType
|
|
;
|
|
( Target = target_c
|
|
; Target = target_java
|
|
),
|
|
MLDSType = mlds_ptr_type(MLDSRefType)
|
|
)
|
|
else
|
|
module_info_get_type_table(ModuleInfo, TypeTable),
|
|
( if search_type_ctor_defn(TypeTable, TypeCtor, TypeDefn) then
|
|
MLDSType = mercury_type_ctor_defn_to_mlds_type(ModuleInfo,
|
|
Type, TypeCtor, TypeDefn)
|
|
else
|
|
% classify_type_ctor will check to see whether calling
|
|
% classify_type_ctor_if_special will succeed on TypeCtor.
|
|
% It should, because if it doesn't, then it will proceed
|
|
% to look up TypeCtor in TypeTable, and since the condition
|
|
% above failed, that lookup will throw an exception.
|
|
CtorCat = classify_type_ctor(ModuleInfo, TypeCtor),
|
|
MLDSType = type_and_category_to_mlds_type(Type, CtorCat)
|
|
)
|
|
)
|
|
else
|
|
Category = ctor_cat_variable,
|
|
MLDSType = mercury_nb_type(Type, Category)
|
|
).
|
|
|
|
:- func mercury_type_ctor_defn_to_mlds_type(module_info, mer_type,
|
|
type_ctor, hlds_type_defn) = mlds_type.
|
|
|
|
mercury_type_ctor_defn_to_mlds_type(ModuleInfo, Type, TypeCtor, TypeDefn) =
|
|
MLDSType :-
|
|
hlds_data.get_type_defn_body(TypeDefn, TypeBody),
|
|
(
|
|
TypeBody = hlds_du_type(TypeBodyDu),
|
|
TypeBodyDu = type_body_du(_, _, MaybeSuperType, _, _, _),
|
|
( if
|
|
MaybeSuperType = subtype_of(SuperType),
|
|
compilation_target_uses_high_level_data(ModuleInfo)
|
|
then
|
|
% In high-level data grades, a subtype is represented with the
|
|
% same class as its base type ctor.
|
|
type_to_ctor_det(SuperType, SuperTypeCtor),
|
|
get_base_type_maybe_phony_arg_types(ModuleInfo, SuperTypeCtor,
|
|
BaseType),
|
|
CtorCat = classify_type(ModuleInfo, BaseType),
|
|
MLDSType = type_and_category_to_mlds_type(BaseType, CtorCat)
|
|
else
|
|
CtorCat = classify_type_defn_body(TypeBody),
|
|
MLDSType = type_and_category_to_mlds_type(Type, CtorCat)
|
|
)
|
|
;
|
|
TypeBody = hlds_abstract_type(DetailsAbstract),
|
|
(
|
|
( DetailsAbstract = abstract_type_general
|
|
; DetailsAbstract = abstract_type_fits_in_n_bits(_)
|
|
; DetailsAbstract = abstract_dummy_type
|
|
; DetailsAbstract = abstract_notag_type
|
|
; DetailsAbstract = abstract_solver_type
|
|
),
|
|
CtorCat = classify_type_ctor(ModuleInfo, TypeCtor),
|
|
MLDSType = type_and_category_to_mlds_type(Type, CtorCat)
|
|
;
|
|
DetailsAbstract = abstract_subtype(SuperTypeCtor),
|
|
( if compilation_target_uses_high_level_data(ModuleInfo) then
|
|
% In high-level data grades, a subtype is represented with the
|
|
% same class as its base type ctor.
|
|
get_base_type_maybe_phony_arg_types(ModuleInfo, SuperTypeCtor,
|
|
BaseType),
|
|
CtorCat = classify_type(ModuleInfo, BaseType),
|
|
MLDSType = type_and_category_to_mlds_type(BaseType, CtorCat)
|
|
else
|
|
CtorCat = classify_type_ctor(ModuleInfo, TypeCtor),
|
|
MLDSType = type_and_category_to_mlds_type(Type, CtorCat)
|
|
)
|
|
)
|
|
;
|
|
TypeBody = hlds_foreign_type(ForeignTypeBody),
|
|
MLDSType = foreign_type_to_mlds_type(ModuleInfo, ForeignTypeBody)
|
|
;
|
|
( TypeBody = hlds_eqv_type(_)
|
|
; TypeBody = hlds_solver_type(_)
|
|
),
|
|
CtorCat = classify_type_defn_body(TypeBody),
|
|
MLDSType = type_and_category_to_mlds_type(Type, CtorCat)
|
|
).
|
|
|
|
:- func type_and_category_to_mlds_type(mer_type, type_ctor_category)
|
|
= mlds_type.
|
|
|
|
type_and_category_to_mlds_type(Type, CtorCat) = MLDSType :-
|
|
(
|
|
CtorCat = ctor_cat_builtin(BuiltinTypeCat),
|
|
(
|
|
BuiltinTypeCat = cat_builtin_int(IntType),
|
|
MLDSType = mlds_builtin_type_int(IntType)
|
|
;
|
|
BuiltinTypeCat = cat_builtin_float,
|
|
MLDSType = mlds_builtin_type_float
|
|
;
|
|
BuiltinTypeCat = cat_builtin_string,
|
|
MLDSType = mlds_builtin_type_string
|
|
;
|
|
BuiltinTypeCat = cat_builtin_char,
|
|
MLDSType = mlds_builtin_type_char
|
|
)
|
|
;
|
|
( CtorCat = ctor_cat_builtin_dummy
|
|
; CtorCat = ctor_cat_void
|
|
; CtorCat = ctor_cat_variable
|
|
; CtorCat = ctor_cat_higher_order
|
|
; CtorCat = ctor_cat_tuple
|
|
; CtorCat = ctor_cat_enum(_)
|
|
; CtorCat = ctor_cat_system(_)
|
|
; CtorCat = ctor_cat_user(_)
|
|
),
|
|
MLDSType = mercury_nb_type(Type, coerce(CtorCat))
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred compilation_target_uses_high_level_data(module_info::in) is semidet.
|
|
|
|
compilation_target_uses_high_level_data(ModuleInfo) :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_target(Globals, Target),
|
|
compilation_target_high_level_data(Target) = yes.
|
|
|
|
:- pred get_base_type_maybe_phony_arg_types(module_info::in, type_ctor::in,
|
|
mer_type::out) is det.
|
|
|
|
get_base_type_maybe_phony_arg_types(ModuleInfo, SuperTypeCtor, BaseType) :-
|
|
% XXX If a type is a subtype of a subtype, we should substitute the type
|
|
% arguments into the supertype recursively until we get to the base type.
|
|
% This requires that the tvarset containing the variables of the original
|
|
% type be passed into mercury_type_to_mlds_type. But that would still not
|
|
% solve the next problem.
|
|
%
|
|
% XXX If a type is a subtype of an abstract supertype, we will only know
|
|
% the type ctor of the supertype, as supertype arguments are not written to
|
|
% interface files (we may need to revisit that decision).
|
|
%
|
|
% Instead, we find the base type ctor of a type ctor and simply apply
|
|
% `c_pointer' for all its type parameters. `c_pointer' corresponds to
|
|
% `Object' in Java and `object' in C#.
|
|
%
|
|
% In almost all cases, only the type ctor in a mercury_nb_type is used,
|
|
% not the type arguments. The phony type arguments only makes a difference
|
|
% when generating the Java class corresponding to a Mercury type, e.g. for
|
|
%
|
|
% :- type mytype(T)
|
|
% ---> mytype(abs(T)). % abstract subtype
|
|
%
|
|
% we generate
|
|
%
|
|
% public static class Mytype_1<MR_tvar_1>
|
|
% implements jmercury.runtime.MercuryType
|
|
% {
|
|
% public Abs_1<Object> F1;
|
|
%
|
|
% public Mytype_1(Abs_1<Object> F1)
|
|
% {
|
|
% this.F1 = F1;
|
|
% }
|
|
% }
|
|
%
|
|
% The member F1 and constructor argument has type Abs_1<Object> instead of
|
|
% Abs_1<MR_tvar_1>. The Java code will still compile and run, but there is
|
|
% a slight loss of type information for hand written code that uses the F1
|
|
% member. This is a very minor problem and does not seem worth fixing
|
|
% unless other problems arise.
|
|
%
|
|
module_info_get_type_table(ModuleInfo, TypeTable),
|
|
( if get_base_type_ctor(TypeTable, SuperTypeCtor, BaseTypeCtor) then
|
|
BaseTypeCtor = type_ctor(_, Arity),
|
|
list.duplicate(Arity, c_pointer_type, PhonyTypeArgs),
|
|
construct_type(BaseTypeCtor, PhonyTypeArgs, BaseType)
|
|
else
|
|
unexpected($pred, "cannot get base type ctor")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- func foreign_type_to_mlds_type(module_info, foreign_type_body) = mlds_type.
|
|
|
|
foreign_type_to_mlds_type(ModuleInfo, ForeignTypeBody) = MLDSType :-
|
|
% The body of this function is very similar to the function
|
|
% foreign_type_body_to_exported_type in foreign.m.
|
|
% Any changes here may require changes there as well.
|
|
ForeignTypeBody = foreign_type_body(MaybeC, MaybeJava, MaybeCSharp),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Target = target_c,
|
|
(
|
|
MaybeC = yes(Data),
|
|
Data = type_details_foreign(CForeignType, _, _),
|
|
ForeignType = c(CForeignType)
|
|
;
|
|
MaybeC = no,
|
|
% This is checked by check_foreign_type in make_hlds.
|
|
unexpected($pred, "no C foreign type")
|
|
)
|
|
;
|
|
Target = target_csharp,
|
|
(
|
|
MaybeCSharp = yes(Data),
|
|
Data = type_details_foreign(CSharpForeignType, _, _),
|
|
ForeignType = csharp(CSharpForeignType)
|
|
;
|
|
MaybeCSharp = no,
|
|
% This is checked by check_foreign_type in make_hlds.
|
|
unexpected($pred, "no C# foreign type")
|
|
)
|
|
;
|
|
Target = target_java,
|
|
(
|
|
MaybeJava = yes(Data),
|
|
Data = type_details_foreign(JavaForeignType, _, _),
|
|
ForeignType = java(JavaForeignType)
|
|
;
|
|
MaybeJava = no,
|
|
% This is checked by check_foreign_type in make_hlds.
|
|
unexpected($pred, "no Java foreign type")
|
|
)
|
|
),
|
|
MLDSType = mlds_foreign_type(ForeignType).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
ml_global_const_var_name_to_string(ConstVar, Num) = Str :-
|
|
(
|
|
ConstVar = mgcv_const_var,
|
|
ConstVarStr = "const_var"
|
|
;
|
|
ConstVar = mgcv_float,
|
|
ConstVarStr = "float"
|
|
;
|
|
ConstVar = mgcv_int64,
|
|
ConstVarStr = "int64"
|
|
;
|
|
ConstVar = mgcv_uint64,
|
|
ConstVarStr = "uint64"
|
|
;
|
|
ConstVar = mgcv_closure_layout,
|
|
ConstVarStr = "closure_layout"
|
|
;
|
|
ConstVar = mgcv_typevar_vector,
|
|
ConstVarStr = "typevar_vector"
|
|
;
|
|
ConstVar = mgcv_bit_vector,
|
|
ConstVarStr = "bit_vector"
|
|
),
|
|
Str = string.format("%s_%d", [s(ConstVarStr), i(Num)]).
|
|
|
|
ml_field_var_name_to_string(FieldVar) = Str :-
|
|
(
|
|
FieldVar = fvn_global_data_field(TypeRawNum, FieldNum),
|
|
Str = string.format("vct_%d_f_%d", [i(TypeRawNum), i(FieldNum)])
|
|
;
|
|
FieldVar = fvn_du_ctor_field_hld(FieldName),
|
|
% XXX There is nothing to stop the variable names we generate here
|
|
% from clashing with the strings we generate for other kinds of
|
|
% mlds_var_names. In particular, the values of FieldName that
|
|
% ml_gen_hld_field_name generates for unnamed field in a Mercury
|
|
% type, which are generated with "F" ++ .int_to_string(ArgNum),
|
|
% *will* conflict with the lvn_prog_vars that correspond
|
|
% to any variables that the user names "F1", "F2" and so on.
|
|
Str = FieldName
|
|
;
|
|
FieldVar = fvn_mr_value,
|
|
Str = "MR_value"
|
|
;
|
|
FieldVar = fvn_data_tag,
|
|
Str = "data_tag"
|
|
;
|
|
FieldVar = fvn_enum_const(ConstName),
|
|
% XXX There is nothing to stop the variable names we generate here
|
|
% from clashing with the strings we generate for other kinds of
|
|
% mlds_var_names.
|
|
Str = ConstName
|
|
;
|
|
FieldVar = fvn_ptr_num,
|
|
Str = "ptr_num"
|
|
;
|
|
FieldVar = fvn_env_field_from_local_var(LocalVar),
|
|
Str = ml_local_var_name_to_string(LocalVar)
|
|
;
|
|
FieldVar = fvn_base_class(BaseNum),
|
|
Str = string.format("base_%d", [i(BaseNum)])
|
|
;
|
|
FieldVar = fvn_prev,
|
|
Str = "prev"
|
|
;
|
|
FieldVar = fvn_trace,
|
|
Str = "trace"
|
|
).
|
|
|
|
ml_local_var_name_to_string(LocalVar) = Str :-
|
|
(
|
|
LocalVar = lvn_prog_var(ProgVarName, ProgVarNum),
|
|
( if ProgVarName = "" then
|
|
Str = string.format("Var_%d", [i(ProgVarNum)])
|
|
else
|
|
Str = string.format("%s_%d", [s(ProgVarName), i(ProgVarNum)])
|
|
)
|
|
;
|
|
LocalVar = lvn_prog_var_foreign(ProgVarName),
|
|
( if ProgVarName = "" then
|
|
unexpected($pred, "lvn_prog_var_foreign with empty name")
|
|
else
|
|
Str = ProgVarName
|
|
)
|
|
;
|
|
LocalVar = lvn_prog_var_boxed(ProgVarName, ProgVarNum),
|
|
( if ProgVarName = "" then
|
|
Str = string.format("boxed_Var_%d", [i(ProgVarNum)])
|
|
else
|
|
Str = string.format("boxed_%s_%d", [s(ProgVarName), i(ProgVarNum)])
|
|
)
|
|
;
|
|
LocalVar = lvn_prog_var_conv(ConvSeq, ProgVarName, ProgVarNum),
|
|
( if ProgVarName = "" then
|
|
Str = string.format("conv%d_Var_%d", [i(ConvSeq), i(ProgVarNum)])
|
|
else
|
|
Str = string.format("conv%d_%s_%d",
|
|
[i(ConvSeq), s(ProgVarName), i(ProgVarNum)])
|
|
)
|
|
;
|
|
LocalVar = lvn_prog_var_next_value(ProgVarName, ProgVarNum),
|
|
% Before the change to structure representations of mlds_var_names,
|
|
% we used to add "__tmp_copy" as a suffix, not "next_value_of_"
|
|
% as a prefix. Using a suffix created the possibility of conflict
|
|
% with other compiler generated names (which could have added a
|
|
% known prefix to a string controlled by the user, which in turn
|
|
% *could* have ended in "__tmp_copy"), and the "tmp_copy" part
|
|
% was also misleading, since these variables are not copied *from*
|
|
% the variable they are named after; in fact, they are copied *to*
|
|
% that variable.
|
|
( if ProgVarName = "" then
|
|
Str = string.format("next_value_of_Var_%d", [i(ProgVarNum)])
|
|
else
|
|
Str = string.format("next_value_of_%s_%d",
|
|
[s(ProgVarName), i(ProgVarNum)])
|
|
)
|
|
;
|
|
LocalVar = lvn_local_var(ProgVarName, ProgVarNum),
|
|
( if ProgVarName = "" then
|
|
Str = string.format("local_Var_%d", [i(ProgVarNum)])
|
|
else
|
|
Str = string.format("local_%s_%d", [s(ProgVarName), i(ProgVarNum)])
|
|
)
|
|
;
|
|
LocalVar = lvn_tscc_proc_input_var(proc_id_in_tscc(ProcNum), ArgNum,
|
|
VarName),
|
|
Str = string.format("tscc_proc_%d_input_%d_%s",
|
|
[i(ProcNum), i(ArgNum), s(VarName)])
|
|
;
|
|
LocalVar = lvn_tscc_output_var(ArgNum, VarName),
|
|
Str = string.format("tscc_output_%d_%s", [i(ArgNum), s(VarName)])
|
|
;
|
|
LocalVar = lvn_tscc_output_var_ptr(ArgNum, VarName),
|
|
Str = string.format("tscc_output_ptr_%d_%s", [i(ArgNum), s(VarName)])
|
|
;
|
|
LocalVar = lvn_tscc_output_var_succeeded,
|
|
Str = string.format("tscc_output_succeeded", [])
|
|
;
|
|
LocalVar = lvn_field_var_as_local(FieldVar),
|
|
Str = ml_field_var_name_to_string(FieldVar)
|
|
;
|
|
LocalVar = lvn_comp_var(CompVar),
|
|
(
|
|
CompVar = lvnc_non_prog_var_boxed(BaseVarStr),
|
|
Str = string.format("boxed_%s", [s(BaseVarStr)])
|
|
;
|
|
CompVar = lvnc_non_prog_var_conv(ConvSeq, BaseVarStr),
|
|
Str = string.format("conv%d_%s", [i(ConvSeq), s(BaseVarStr)])
|
|
;
|
|
CompVar = lvnc_non_prog_var_next_value(BaseVarStr),
|
|
Str = string.format("next_value_of_%s", [s(BaseVarStr)])
|
|
;
|
|
CompVar = lvnc_succeeded,
|
|
Str = "succeeded"
|
|
;
|
|
CompVar = lvnc_success_indicator,
|
|
Str = "SUCCESS_INDICATOR"
|
|
;
|
|
CompVar = lvnc_tscc_proc_selector,
|
|
Str = "tscc_proc_selector"
|
|
;
|
|
CompVar = lvnc_new_obj(Id),
|
|
Str = string.format("new_obj_%d", [i(Id)])
|
|
;
|
|
CompVar = lvnc_cond(CondNum),
|
|
Str = string.format("cond_%d", [i(CondNum)])
|
|
;
|
|
CompVar = lvnc_conv_var(ConvVarNum),
|
|
Str = string.format("func_%d", [i(ConvVarNum)])
|
|
;
|
|
CompVar = lvnc_arg(ArgNum),
|
|
Str = string.format("arg%d", [i(ArgNum)])
|
|
;
|
|
CompVar = lvnc_wrapper_arg(ArgNum),
|
|
Str = string.format("wrapper_arg_%d", [i(ArgNum)])
|
|
;
|
|
CompVar = lvnc_param(ArgNum),
|
|
Str = string.format("param_%d", [i(ArgNum)])
|
|
;
|
|
CompVar = lvnc_out_param(ArgNum),
|
|
Str = string.format("out_param_%d", [i(ArgNum)])
|
|
;
|
|
CompVar = lvnc_return_value,
|
|
Str = "return_value"
|
|
;
|
|
CompVar = lvnc_closure,
|
|
Str = "closure"
|
|
;
|
|
CompVar = lvnc_closure_arg,
|
|
Str = "closure_arg"
|
|
;
|
|
CompVar = lvnc_closure_layout_ptr,
|
|
Str = "closure_layout_ptr"
|
|
;
|
|
CompVar = lvnc_type_params,
|
|
Str = "type_params"
|
|
;
|
|
CompVar = lvnc_type_info,
|
|
Str = "type_info"
|
|
;
|
|
CompVar = lvnc_cont,
|
|
Str = "cont"
|
|
;
|
|
CompVar = lvnc_cont_env_ptr,
|
|
Str = "cont_env_ptr"
|
|
;
|
|
CompVar = lvnc_env,
|
|
Str = "env"
|
|
;
|
|
CompVar = lvnc_env_ptr,
|
|
Str = "env_ptr"
|
|
;
|
|
CompVar = lvnc_env_ptr_arg,
|
|
Str = "env_ptr_arg"
|
|
;
|
|
CompVar = lvnc_frame,
|
|
Str = "frame"
|
|
;
|
|
CompVar = lvnc_frame_ptr,
|
|
Str = "frame_ptr"
|
|
;
|
|
CompVar = lvnc_this_frame,
|
|
Str = "this_frame"
|
|
;
|
|
CompVar = lvnc_stack_chain,
|
|
Str = "stack_chain"
|
|
;
|
|
CompVar = lvnc_saved_stack_chain(Id),
|
|
Str = string.format("saved_stack_chain_%d", [i(Id)])
|
|
;
|
|
CompVar = lvnc_args,
|
|
Str = "args"
|
|
;
|
|
CompVar = lvnc_aux_var(AuxVar, Num),
|
|
(
|
|
AuxVar = mcav_commit,
|
|
AuxVarStr = "commit"
|
|
;
|
|
AuxVar = mcav_slot,
|
|
AuxVarStr = "slot"
|
|
;
|
|
AuxVar = mcav_later_slot,
|
|
AuxVarStr = "later_slot"
|
|
;
|
|
AuxVar = mcav_num_later_solns,
|
|
AuxVarStr = "num_later_solns"
|
|
;
|
|
AuxVar = mcav_limit,
|
|
AuxVarStr = "limit"
|
|
;
|
|
AuxVar = mcav_str,
|
|
AuxVarStr = "str"
|
|
;
|
|
AuxVar = mcav_lo,
|
|
AuxVarStr = "lo"
|
|
;
|
|
AuxVar = mcav_mid,
|
|
AuxVarStr = "mid"
|
|
;
|
|
AuxVar = mcav_hi,
|
|
AuxVarStr = "hi"
|
|
;
|
|
AuxVar = mcav_stop_loop,
|
|
AuxVarStr = "stop_loop"
|
|
;
|
|
AuxVar = mcav_result,
|
|
AuxVarStr = "result"
|
|
;
|
|
AuxVar = mcav_case_num,
|
|
AuxVarStr = "case_num"
|
|
),
|
|
Str = string.format("%s_%d", [s(AuxVarStr), i(Num)])
|
|
;
|
|
CompVar = lvnc_packed_word(Id),
|
|
Str = string.format("packed_word_%d", [i(Id)])
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ml_backend.mlds.
|
|
%---------------------------------------------------------------------------%
|