mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 14:57:03 +00:00
Estimated hours taken: 16 Branches: main The file modules.m contains lots of different kinds of functionality. While much of it belongs together, much of it does not. This diff moves most of the functionality that does not belong with the rest to several new modules: libs.file_util parse_tree.deps_map parse_tree.file_names parse_tree.module_cmds parse_tree.module_imports parse_tree.read_module parse_tree.write_deps_file To make them coherent, move some predicates from hlds.passes_aux, parse_tree.prog_io and parse_tree.prog_out to the new modules, making them more accessible, reducing the required access from the hlds package to parse_tree, or from the parse_tree package to libs. In the same spirit, this diff also moves some simple predicates and functions dealing with sym_names from prog_util.m to mdbcomp/prim_data.m. This allows several modules to avoid depending on parse_tree.prog_util. Rename some of the moved predicates and function symbols where this avoids ambiguity. (There were several that differed from other predicates or function symbols only in arity.) Replace several uses of bools with purpose-specific types. This makes some of the code significantly easier to read. This diff moves modules.m from being by far the largest module, to being only the seventh largest, from 8900+ lines to just 4200+. It also reduces the number of modules that import parse_tree.modules considerably; most modules that imported it now import only one or two of the new modules instead. Despite the size of the diff, there should be no algorithmic changes. compiler/modules.m: compiler/passes_aux.m: compiler/prog_io.m: compiler/prog_out.m: Delete the moved functionality. compiler/file_util.m: New module in the libs package. Its predicates search for files and do simple error or progress reporting. compiler/file_names.m: New module in the parse_tree package. It contains predicates for converting module names to file names. compiler/module_cmds.m: New module in the parse_tree package. Its predicates handle the commands for manipulating interface files of various kinds. compiler/module_import.m: New module in the parse_tree package. It contains the module_imports type and its access predicates, and the predicates that compute various sorts of direct dependencies (those caused by imports) between modules. compiler/deps_map.m: New module in the parse_tree package. It contains the data structure for recording indirect dependencies between modules, and the predicates for creating it. compiler/read_module.m: New module in the parse_tree package. Its job is reading in modules, both human-written and machine-written (such as interface and optimization files). compiler/write_deps_file.m: New module in the parse_tree package. Its job is writing out makefile fragments. compiler/libs.m: compiler/parse_tree.m: Include the new modules. compiler/notes/compiler_design.m: Document the new modules. mdbcomp/prim_data.m: compiler/prog_util.m: Move the predicates that operate on nothing but sym_names from prog_util to prim_data. Move get_ancestors from modules to prim_data. compiler/prog_item.m: Move stuff that looks for foreign code in a list of items here from modules.m. compiler/source_file_map.m: Note why this module needs to be in the parse_tree package. compiler/add_pred.m: compiler/add_special_pred.m: compiler/analysis.file.m: compiler/analysis.m: compiler/assertion.m: compiler/check_typeclass.m: compiler/compile_target_code.m: compiler/cse_detection.m: compiler/det_analysis.m: compiler/elds_to_erlang.m: compiler/exception_analysis.m: compiler/export.m: compiler/fact_table.m: compiler/higher_order.m: compiler/hlds_module.m: compiler/hlds_pred.m: compiler/intermod.m: compiler/llds_out.m: compiler/make.dependencies.m: compiler/make.m: compiler/make.module_dep_file.m: compiler/make.module_target.m: compiler/make.program_target.m: compiler/make.util.m: compiler/make_hlds_passes.m: compiler/maybe_mlds_to_gcc.pp: compiler/mercury_compile.m: compiler/mlds.m: compiler/mlds_to_c.m: compiler/mlds_to_gcc.m: compiler/mlds_to_ilasm.m: compiler/mlds_to_java.m: compiler/mmc_analysis.m: compiler/mode_constraints.m: compiler/mode_debug.m: compiler/modes.m: compiler/module_qual.m: compiler/optimize.m: compiler/passes_aux.m: compiler/proc_gen.m: compiler/prog_foreign.m: compiler/prog_io.m: compiler/prog_io_util.m: compiler/prog_mutable.m: compiler/prog_out.m: compiler/pseudo_type_info.m: compiler/purity.m: compiler/recompilation.check.m: compiler/recompilation.usage.m: compiler/simplify.m: compiler/structure_reuse.analysis.m: compiler/structure_reuse.direct.detect_garbage.m: compiler/structure_reuse.direct.m: compiler/structure_sharing.analysis.m: compiler/tabling_analysis.m: compiler/term_constr_main.m: compiler/termination.m: compiler/trailing_analysis.m: compiler/trans_opt.m: compiler/type_util.m: compiler/typecheck.m: compiler/typecheck_info.m: compiler/unify_proc.m: compiler/unused_args.m: compiler/unused_imports.m: compiler/xml_documentation.m: Minor changes to conform to the changes above.
457 lines
16 KiB
Mathematica
457 lines
16 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2008 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% This module defines predicate for interfacing with foreign languages.
|
|
% that are necessary for the frontend of the compiler to construct
|
|
% the list of items. The predicates in this module should not depend
|
|
% on the HLDS in any way. The predicates for interfacing with foreign
|
|
% languages that do depend on the HLDS are defined in foreign.m.
|
|
%
|
|
% This module also contains the parts of the name mangler that are used
|
|
% by the frontend of the compiler.
|
|
%
|
|
% Warning: any changes to the name mangling algorithms implemented in this
|
|
% module may also require changes to extras/dynamic_linking/name_mangle.m,
|
|
% profiler/demangle.m, util/mdemangle.c and compiler/name_mangle.m.
|
|
%
|
|
% Main authors: trd, dgj.
|
|
% This code was originally part of the foreign module and was moved here.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.prog_foreign.
|
|
:- interface.
|
|
|
|
:- import_module libs.globals.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module mdbcomp.prim_data.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module term.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type foreign_decl_info == list(foreign_decl_code).
|
|
% in reverse order
|
|
:- type foreign_body_info == list(foreign_body_code).
|
|
% in reverse order
|
|
|
|
:- type foreign_decl_code
|
|
---> foreign_decl_code(
|
|
fdecl_lang :: foreign_language,
|
|
fdecl_is_local :: foreign_decl_is_local,
|
|
fdecl_code :: string,
|
|
fdecl_context :: prog_context
|
|
).
|
|
|
|
:- type foreign_body_code
|
|
---> foreign_body_code(
|
|
fbody_lang :: foreign_language,
|
|
fbody_code :: string,
|
|
fbody_context :: prog_context
|
|
).
|
|
|
|
:- type foreign_export_defns == list(foreign_export).
|
|
:- type foreign_export_decls
|
|
---> foreign_export_decls(
|
|
fexp_decls_info :: foreign_decl_info,
|
|
fexp_decls_list :: list(foreign_export_decl)
|
|
).
|
|
|
|
:- type foreign_export_decl
|
|
---> foreign_export_decl(
|
|
fexp_decl_lang :: foreign_language,
|
|
% Language of the export.
|
|
|
|
fexp_decl_ret_type :: string,
|
|
% Return type.
|
|
|
|
fexp_decl_func_name :: string,
|
|
% Function name.
|
|
|
|
fexp_decl_arg_decls :: string
|
|
% Argument declarations.
|
|
).
|
|
|
|
% Some code from a `pragma foreign_code' declaration that is not
|
|
% associated with a given procedure.
|
|
%
|
|
:- type user_foreign_code
|
|
---> user_foreign_code(
|
|
foreign_language, % language of this code
|
|
string, % code
|
|
term.context % source code location
|
|
).
|
|
|
|
% The code for `pragma foreign_export' is generated directly as strings
|
|
% by export.m.
|
|
%
|
|
:- type foreign_export == string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% foreign_import_module_name(ForeignImport)
|
|
%
|
|
% returns the module name which represents the ForeignImport.
|
|
%
|
|
% For instance for the foreign_import_module representing
|
|
% :- foreign_import_module("C#", module)
|
|
% would return the module_name
|
|
% unqualified("module__csharp_code")
|
|
%
|
|
:- func foreign_import_module_name(foreign_import_module_info) = module_name.
|
|
|
|
% foreign_import_module_name_from_module(ForeignImport, CurrentModule)
|
|
%
|
|
% returns the module name needed to refer to ForeignImport from the
|
|
% CurrentModule.
|
|
%
|
|
:- func foreign_import_module_name_from_module(foreign_import_module_info,
|
|
module_name) = module_name.
|
|
|
|
% Sub-type of foreign_language for languages for which
|
|
% we generate external files for foreign code.
|
|
%
|
|
:- inst lang_gen_ext_file
|
|
---> lang_c
|
|
; lang_csharp.
|
|
|
|
% The module name used for this foreign language.
|
|
% Not all foreign languages generate external modules
|
|
% so this function only succeeds for those that do.
|
|
%
|
|
:- func foreign_language_module_name(module_name, foreign_language) =
|
|
module_name.
|
|
:- mode foreign_language_module_name(in, in) = out is semidet.
|
|
:- mode foreign_language_module_name(in, in(lang_gen_ext_file)) = out is det.
|
|
|
|
% The file extension used for this foreign language (including the dot).
|
|
% Not all foreign languages generate external files,
|
|
% so this function only succeeds for those that do.
|
|
%
|
|
:- func foreign_language_file_extension(foreign_language) = string.
|
|
:- mode foreign_language_file_extension(in) = out is semidet.
|
|
:- mode foreign_language_file_extension(in(lang_gen_ext_file)) = out is det.
|
|
|
|
% It is possible that more than one foreign language could be used to
|
|
% implement a particular piece of code.
|
|
% Therefore, foreign languages have an order of preference, from most
|
|
% preferred to least perferred.
|
|
% prefer_foreign_language(Globals, Target, Lang1, Lang2) returns the
|
|
% yes if Lang2 is preferred over Lang1.
|
|
%
|
|
% Otherwise it will return no.
|
|
%
|
|
:- func prefer_foreign_language(globals, compilation_target,
|
|
foreign_language, foreign_language) = bool.
|
|
|
|
% Return all supported foreign languages.
|
|
%
|
|
:- func all_foreign_languages = list(foreign_language).
|
|
|
|
:- func foreign_type_language(foreign_language_type) = foreign_language.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The following are the parts of the name mangler that are needed by
|
|
% the compiler frontend so that it can write out makefile fragments.
|
|
|
|
% Returns the name of the initialization function for a given module.
|
|
%
|
|
:- func make_init_name(module_name) = string.
|
|
|
|
% Mangle a possibly module-qualified Mercury symbol name
|
|
% into a C identifier.
|
|
%
|
|
:- func sym_name_mangle(sym_name) = string.
|
|
|
|
% Mangle an arbitrary name into a C etc identifier.
|
|
%
|
|
:- func name_mangle(string) = string.
|
|
|
|
% Produces a string of the form Module__Name.
|
|
%
|
|
:- func qualify_name(string, string) = string.
|
|
|
|
:- func convert_to_valid_c_identifier(string) = string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module parse_tree.file_names.
|
|
|
|
:- import_module char.
|
|
:- import_module int.
|
|
:- import_module solutions.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foreign_import_module_name(ImportModule) = ModuleName :-
|
|
ImportModule = foreign_import_module_info(Lang, ForeignImportModule, _),
|
|
(
|
|
Lang = lang_c,
|
|
ModuleName = ForeignImportModule
|
|
;
|
|
Lang = lang_il,
|
|
ModuleName = ForeignImportModule
|
|
;
|
|
Lang = lang_java,
|
|
ModuleName = ForeignImportModule
|
|
;
|
|
Lang = lang_erlang,
|
|
ModuleName = ForeignImportModule
|
|
;
|
|
Lang = lang_csharp,
|
|
ModuleName = foreign_language_module_name(ForeignImportModule, Lang)
|
|
).
|
|
|
|
foreign_import_module_name_from_module(ModuleForeignImported, CurrentModule) =
|
|
ImportedForeignCodeModuleName :-
|
|
ModuleForeignImported = foreign_import_module_info(Lang, _, _),
|
|
ImportedForeignCodeModuleName1 = ModuleForeignImported ^
|
|
foreign_import_module_name,
|
|
(
|
|
Lang = lang_c,
|
|
ImportedForeignCodeModuleName = ImportedForeignCodeModuleName1
|
|
;
|
|
Lang = lang_il,
|
|
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
|
|
ImportedForeignCodeModuleName1)
|
|
;
|
|
Lang = lang_csharp,
|
|
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
|
|
ImportedForeignCodeModuleName1)
|
|
;
|
|
Lang = lang_java,
|
|
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
|
|
ImportedForeignCodeModuleName1)
|
|
;
|
|
Lang = lang_erlang,
|
|
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
|
|
ImportedForeignCodeModuleName1)
|
|
).
|
|
|
|
% On the il backend, we need to refer to the module "mercury" when
|
|
% referencing a std library module when we are not actually building
|
|
% the std library.
|
|
%
|
|
:- func handle_std_library(module_name, module_name) = module_name.
|
|
|
|
handle_std_library(CurrentModule, ModuleName0) = ModuleName :-
|
|
(
|
|
mercury_std_library_module_name(ModuleName0),
|
|
\+ mercury_std_library_module_name(CurrentModule)
|
|
->
|
|
ModuleName = unqualified("mercury")
|
|
;
|
|
ModuleName = ModuleName0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foreign_language_module_name(ModuleName, Lang) = FullyQualifiedModuleName :-
|
|
% Only succeed if this language generates external files.
|
|
_ = foreign_language_file_extension(Lang),
|
|
|
|
Ending = "__" ++ simple_foreign_language_string(Lang) ++ "_code",
|
|
(
|
|
ModuleName = unqualified(Name),
|
|
FullyQualifiedModuleName = unqualified(Name ++ Ending)
|
|
;
|
|
ModuleName = qualified(Module, Name),
|
|
FullyQualifiedModuleName = qualified(Module, Name ++ Ending)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foreign_language_file_extension(lang_c) = ".c".
|
|
foreign_language_file_extension(lang_csharp) = ".cs".
|
|
foreign_language_file_extension(lang_java) = ".java".
|
|
foreign_language_file_extension(lang_il) = _ :-
|
|
fail.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Currently we don't use the globals to compare foreign language
|
|
% interfaces, but if we added appropriate options we might want
|
|
% to do this later.
|
|
%
|
|
prefer_foreign_language(_Globals, target_c, Lang1, Lang2) =
|
|
% When compiling to C, C is always preferred over any other language.
|
|
( Lang2 = lang_c, not Lang1 = lang_c ->
|
|
yes
|
|
;
|
|
no
|
|
).
|
|
|
|
prefer_foreign_language(_Globals, target_asm, Lang1, Lang2) =
|
|
% When compiling to asm, C is always preferred over any other language.
|
|
( Lang2 = lang_c, not Lang1 = lang_c ->
|
|
yes
|
|
;
|
|
no
|
|
).
|
|
|
|
prefer_foreign_language(_Globals, target_il, Lang1, Lang2) = Comp :-
|
|
% When compiling to il, first we prefer il, then csharp.
|
|
% After that we don't care.
|
|
PreferredList = [lang_il, lang_csharp],
|
|
|
|
FindLangPriority = (func(L) = X :-
|
|
( list.nth_member_search(PreferredList, L, X0) ->
|
|
X = X0
|
|
;
|
|
X = list.length(PreferredList) + 1
|
|
)),
|
|
N1 = FindLangPriority(Lang1),
|
|
N2 = FindLangPriority(Lang2),
|
|
( N2 < N1 ->
|
|
Comp = yes
|
|
;
|
|
Comp = no
|
|
).
|
|
|
|
prefer_foreign_language(_Globals, target_java, _Lang1, _Lang2) = no.
|
|
% Nothing useful to do here, but when we add Java as a foreign language,
|
|
% we should add it here.
|
|
|
|
prefer_foreign_language(_Globals, target_x86_64, Lang1, Lang2) =
|
|
% When compiling to x86_64 assembler, C is always preferred over any
|
|
% other language.
|
|
( Lang2 = lang_c, not Lang1 = lang_c ->
|
|
yes
|
|
;
|
|
no
|
|
).
|
|
|
|
prefer_foreign_language(_Globals, target_erlang, _Lang1, _Lang2) = no.
|
|
% Nothing useful to do here, but when we add Erlang as a foreign language,
|
|
% we should add it here.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
all_foreign_languages = Langs :-
|
|
GetLangs = (pred(Lang::out) is multi :- valid_foreign_language(Lang)),
|
|
solutions(GetLangs, Langs).
|
|
|
|
:- pred valid_foreign_language(foreign_language).
|
|
:- mode valid_foreign_language(in) is det.
|
|
:- mode valid_foreign_language(out) is multi.
|
|
|
|
valid_foreign_language(lang_c).
|
|
valid_foreign_language(lang_java).
|
|
valid_foreign_language(lang_csharp).
|
|
valid_foreign_language(lang_il).
|
|
valid_foreign_language(lang_erlang).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foreign_type_language(il(_)) = lang_il.
|
|
foreign_type_language(c(_)) = lang_c.
|
|
foreign_type_language(java(_)) = lang_java.
|
|
foreign_type_language(erlang(_)) = lang_erlang.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
make_init_name(ModuleName) = InitName :-
|
|
MangledModuleName = sym_name_mangle(ModuleName),
|
|
InitName = "mercury__" ++ MangledModuleName ++ "__".
|
|
|
|
sym_name_mangle(unqualified(Name)) =
|
|
name_mangle(Name).
|
|
sym_name_mangle(qualified(ModuleName, PlainName)) = MangledName :-
|
|
MangledModuleName = sym_name_mangle(ModuleName),
|
|
MangledPlainName = name_mangle(PlainName),
|
|
MangledName = qualify_name(MangledModuleName, MangledPlainName).
|
|
|
|
name_mangle(Name) = MangledName :-
|
|
% Warning: any changes to the name mangling algorithm here may also
|
|
% require changes to extras/dynamic_linking/name_mangle.m,
|
|
% profiler/demangle.m, util/mdemangle.c and compiler/name_mangle.m.
|
|
|
|
( string.is_all_alnum_or_underscore(Name) ->
|
|
% Any names that start with `f_' are changed so that they start with
|
|
% `f__', so that we can use names starting with `f_' (followed by
|
|
% anything except an underscore) without fear of name collisions.
|
|
|
|
( string.append("f_", Suffix, Name) ->
|
|
MangledName = "f__" ++ Suffix
|
|
;
|
|
MangledName = Name
|
|
)
|
|
;
|
|
MangledName = convert_to_valid_c_identifier(Name)
|
|
).
|
|
|
|
qualify_name(Module0, Name0) = Name :-
|
|
string.append_list([Module0, "__", Name0], Name).
|
|
|
|
convert_to_valid_c_identifier(String) = Name :-
|
|
( name_conversion_table(String, Name0) ->
|
|
Name = Name0
|
|
;
|
|
Name = "f" ++ convert_to_valid_c_identifier_2(String)
|
|
).
|
|
|
|
% A table used to convert Mercury functors into C identifiers.
|
|
% Feel free to add any new translations you want. The C identifiers
|
|
% should start with "f_", to avoid introducing name clashes. If the functor
|
|
% name is not found in the table, then we use a fall-back method which
|
|
% produces ugly names.
|
|
%
|
|
:- pred name_conversion_table(string::in, string::out) is semidet.
|
|
|
|
name_conversion_table("\\=", "f_not_equal").
|
|
name_conversion_table(">=", "f_greater_or_equal").
|
|
name_conversion_table("=<", "f_less_or_equal").
|
|
name_conversion_table("=", "f_equal").
|
|
name_conversion_table("<", "f_less_than").
|
|
name_conversion_table(">", "f_greater_than").
|
|
name_conversion_table("-", "f_minus").
|
|
name_conversion_table("+", "f_plus").
|
|
name_conversion_table("*", "f_times").
|
|
name_conversion_table("/", "f_slash").
|
|
name_conversion_table(",", "f_comma").
|
|
name_conversion_table(";", "f_semicolon").
|
|
name_conversion_table("!", "f_cut").
|
|
name_conversion_table("{}", "f_tuple").
|
|
name_conversion_table("[|]", "f_cons").
|
|
name_conversion_table("[]", "f_nil").
|
|
|
|
% This is the fall-back method. Given a string, produce a C identifier
|
|
% for that string by concatenating the decimal expansions of the character
|
|
% codes in the string, separated by underlines. The C identifier will
|
|
% start with "f_"; this predicate constructs everything except the initial
|
|
% "f".
|
|
%
|
|
% For example, given the input "\n\t" we return "_10_8".
|
|
%
|
|
:- func convert_to_valid_c_identifier_2(string) = string.
|
|
|
|
convert_to_valid_c_identifier_2(String) = Name :-
|
|
( string.first_char(String, Char, Rest) ->
|
|
% XXX This will cause ABI incompatibilities between compilers which are
|
|
% built in grades that have different character representations.
|
|
char.to_int(Char, Code),
|
|
string.int_to_string(Code, CodeString),
|
|
string.append("_", CodeString, ThisCharString),
|
|
Name0 = convert_to_valid_c_identifier_2(Rest),
|
|
string.append(ThisCharString, Name0, Name)
|
|
;
|
|
% String is the empty string
|
|
Name = String
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module prog_foreign.
|
|
%-----------------------------------------------------------------------------%
|