mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 23:05:21 +00:00
Estimated hours taken: 4 Branches: main Fix an inconsistency between the compiler's handling of foreign_import_module pragmas (which was itself inconsistent between backends) and what is documented about that pragma in the reference manual. According to the reference manual if foreign code (in either a foreign_proc or foreign_code pragma) wants to refer to pragma foreign_exported procedures from the same module then there needs to be a foreign_import_module in the module for itself. The high-level C backend ignores this entirely and modules always effectively implicitly foreign import themselves. The situation with the low-level C backend varies depending on whether a module has any children or not; if it does then it does not implicitly foreign import itself otherwise it does. This diff changes the language to require that modules always implicitly foreign import themselves and adds a workaround to the low-level C backend to make sure this always happens. (A better fix would be to modify the frontend of the compiler to handle the implicit foreign imports for both backends but the lack of structure in the parse tree makes this quite awkward at the moment.) Fix a bunch of comments that were out-of-date. doc/reference_manual.texi: Change the description of behaviour of `:- pragma foreign_import_module' so that it matches what the implementation actually does (well, at least what it actually does most of the time.) The new behaviour removes the requirement that modules need to foreign_import themselves. This is pretty much what users (and implementors) have assumed anyway compiler/mercury_compile.m: Make sure that in LLDS grades modules always foreign import themselves (this was the exception to "most of the time" above.) Change an if-then-else into a switch. tests/hard_coded/sub-modules/Makefile: tests/hard_coded/sub-modules/sm_exp_bug.m: tests/hard_coded/sub-modules/sm_exp_bug.child.m: tests/hard_coded/sub-modules/sm_exp_bug.exp: Test case for the above. compiler/mlds.m: Update a comment. pragma foreign_export has been implemented for other backends. compiler/modules.m: Fix the formatting of the module_imports structure. Divide some of this module more visibly into sections. compiler/process_util.m: Replace a pragma_export with pragma foreign_export. compiler/add_pragma.m: compiler/det_analysis.m: compiler/export.m: compiler/hlds_module.m: compiler/make.m: compiler/make_hlds_passes.m: compiler/ml_code_gen.m: compiler/ml_elim_nested.m: compiler/mlds_to_c.m: compiler/prog_foreign.m: s/pragma export/pragma foreign_export/ compiler/make_hlds.m: compiler/prog_io_pragma.m: compiler/prog_item.m: Minor formatting fixes.
447 lines
16 KiB
Mathematica
447 lines
16 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2007 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("MC++", module)
|
|
% would return the module_name
|
|
% unqualified("module__cpp_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_managed_cplusplus
|
|
; 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.
|
|
|
|
% The `multi' mode returns all supported foreign languages.
|
|
%
|
|
:- pred foreign_language(foreign_language).
|
|
:- mode foreign_language(in) is det.
|
|
:- mode foreign_language(out) is multi.
|
|
|
|
:- 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.modules.
|
|
|
|
:- import_module char.
|
|
:- import_module int.
|
|
:- 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_managed_cplusplus,
|
|
ModuleName = foreign_language_module_name(ForeignImportModule, Lang)
|
|
;
|
|
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_managed_cplusplus,
|
|
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
|
|
ImportedForeignCodeModuleName1)
|
|
;
|
|
Lang = lang_csharp,
|
|
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
|
|
ImportedForeignCodeModuleName1)
|
|
;
|
|
Lang = lang_java,
|
|
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_managed_cplusplus) = ".cpp".
|
|
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 :-
|
|
% Whe compiling to il, first we prefer il, then csharp, then
|
|
% managed_cplusplus, after that we don't care.
|
|
PreferredList = [lang_il, lang_csharp, lang_managed_cplusplus],
|
|
|
|
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
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foreign_language(lang_c).
|
|
foreign_language(lang_java).
|
|
foreign_language(lang_csharp).
|
|
foreign_language(lang_managed_cplusplus).
|
|
foreign_language(lang_il).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foreign_type_language(il(_)) = lang_il.
|
|
foreign_type_language(c(_)) = lang_c.
|
|
foreign_type_language(java(_)) = lang_java.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
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.
|
|
%-----------------------------------------------------------------------------%
|