Files
mercury/compiler/mlds_to_java_wrap.m
Zoltan Somogyi d0413592dc Get --auto-comments to print unmangled func names.
In LLDS grades, we have long added a comment before the C code we generated
for a Mercury predicate that gives the name of that predicate. In most cases,
the C code itself will contain that name, but the C code will contain the name
in a mangled form, and in cases where the predicate name contains graphic
characters, the mangling will make the name unrecognizable. The automatically
generate comment helps debug program transformation, such as type
specification, that either can, or always do, include such characters
in the names of the predicates they create.

Until now, --auto-comment printed only the predicate and procedure ids
of the Mercury predicate when invoked in MLDS grades. Change this
to print the unmangled predicate name as well. To make this simpler,
move the comment it generates from the middle of the C function declaration
to before the C function declaration. (When we generate C# or Java,
we already put the comment before the declaration.)

--auto-comments in MLDS grades now also prints more information
for various kinds of auxiliary functions.

compiler/mlds.m:
    Replace a maybe type with a bespoke type, both to improve readability,
    and to preserve the purpose of auxiliary functions.

compiler/mlds_to_target_util.m:
    Rewrite the predicate that wrote out the pre-function (or pre-method)
    comment for C# and Java. The new version

    - prints out the predicate's unmangled name as well its pred and proc ids,
      if the function (or method) implements a Mercury predicate,

    - prints out the kind of auxiliary function, if the function or method
      does not implement a (whole) Mercury predicate,

    - is used for C as well as for C# and Java.

compiler/mlds_to_c_func.m:
compiler/mlds_to_cs_func.m:
compiler/mlds_to_java_func.m:
    Call the rewritten function to generate the pre-function comment
    (if --auto-comment is enabled).

    In mlds_to_c_func.m, also factor out some common code.

compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_commit_gen.m:
compiler/ml_elim_nested.m:
compiler/ml_proc_gen.m:
compiler/ml_type_gen.m:
compiler/mlds_to_c_class.m:
compiler/mlds_to_java_wrap.m:
2024-02-01 19:05:42 +11:00

360 lines
14 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2000-2012 The University of Melbourne.
% Copyright (C) 2013-2018 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.
%---------------------------------------------------------------------------%
%
% Code to create wrapper classes for the implementation of function pointers
% in Java.
%
% As there is no way to take the address of a method in Java, we must create a
% wrapper for that method which implements a common interface. We are then able
% to pass that class around as a java.lang.Object.
%
% XXX This implementation will not handle taking the address of instance
% methods. This is not currently a problem as they will never be generated
% by the MLDS back-end.
%
% XXX This implementation will not correctly handle the case which occurs where
% there are two or more overloaded MLDS functions (that we take the address of)
% with the same name and arity but different argument types, both in the same
% module. This is due to the fact that the names of the generated wrapper
% classes are based purely on the method name.
%
%---------------------------------------------------------------------------%
:- module ml_backend.mlds_to_java_wrap.
:- interface.
:- import_module ml_backend.mlds.
:- import_module ml_backend.mlds_to_java_util.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module list.
:- import_module map.
:- import_module pair.
%---------------------------------------------------------------------------%
:- pred generate_addr_wrapper_class(mlds_module_name::in,
pair(arity, list(mlds_code_addr))::in, mlds_class_defn::out,
map(mlds_code_addr, code_addr_wrapper)::in,
map(mlds_code_addr, code_addr_wrapper)::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module hlds.
:- import_module hlds.hlds_pred.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.java_names.
:- import_module int.
:- import_module maybe.
:- import_module require.
:- import_module set.
:- import_module string.
:- import_module term_context.
%---------------------------------------------------------------------------%
generate_addr_wrapper_class(MLDS_ModuleName, Arity - CodeAddrs, ClassDefn,
!AddrOfMap) :-
% Create a name for this wrapper class based on the fully qualified method
% (predicate) name.
ClassName = "addrOf" ++ string.from_int(Arity),
% If the class is wrapping more than one method, then add a member variable
% which says which predicate to call, and a constructor function to
% initialise that variable.
(
CodeAddrs = [],
unexpected($pred, "no addresses")
;
CodeAddrs = [_],
FieldVarDefns = [],
CtorDefns = []
;
CodeAddrs = [_, _ | _],
Context = dummy_context,
IntType = mlds_builtin_type_int(int_type_int),
% Create the member variable.
CtorArgName = lvn_field_var_as_local(fvn_ptr_num),
Flags = mlds_field_var_decl_flags(per_instance, const),
FieldVarDefn = mlds_field_var_defn(
fvn_env_field_from_local_var(CtorArgName), Context,
Flags, IntType, no_initializer, gc_no_stmt),
FieldVarDefns = [FieldVarDefn],
% Create the constructor function.
QualClassName =
qual_class_name(MLDS_ModuleName, module_qual, ClassName),
ClassType = mlds_class_type(mlds_class_id(QualClassName, 0)),
FieldName =
qual_field_var_name(MLDS_ModuleName, type_qual, fvn_ptr_num),
FieldId = ml_field_named(FieldName, ClassType),
FieldLval = ml_field(no, ml_self(ClassType), ClassType,
FieldId, IntType),
CtorArgs = [mlds_argument(CtorArgName, IntType, gc_no_stmt)],
CtorReturnValues = [],
CtorArgLval = ml_local_var(CtorArgName, IntType),
CtorArgRval = ml_lval(CtorArgLval),
CtorStmt = ml_stmt_atomic(assign(FieldLval, CtorArgRval), Context),
CtorFunctionName = mlds_function_export("<constructor>"),
CtorFlags = mlds_function_decl_flags(func_public, per_instance),
Params = mlds_func_params(CtorArgs, CtorReturnValues),
EnvVarNames = set.init,
CtorDefn = mlds_function_defn(CtorFunctionName, Context, CtorFlags,
mlds_func_source_constructor, Params,
body_defined_here(CtorStmt), EnvVarNames, no),
CtorDefns = [CtorDefn]
),
% Create a method that calls the original predicates.
generate_call_method(Arity, CodeAddrs, MethodDefn),
( if is_specialised_method_ptr_arity(Arity) then
InterfaceName = "MethodPtr" ++ string.from_int(Arity)
else
InterfaceName = "MethodPtrN"
),
InterfaceModuleName = mercury_module_name_to_mlds(
java_mercury_runtime_package_name),
% Create class components.
ClassImports = [],
ClassInherits = inherits_nothing,
InterfaceId = mlds_interface_id(InterfaceModuleName, InterfaceName),
ClassImplements = [InterfaceId],
TypeParams = [],
% Put it all together.
ClassContext = dummy_context,
ClassFlags = mlds_class_decl_flags(class_private, sealed, const),
ClassDefn = mlds_class_defn(ClassName, 0, ClassContext, ClassFlags,
ClassImports, ClassInherits, ClassImplements,
TypeParams, FieldVarDefns, [], [MethodDefn], CtorDefns),
add_to_address_map(ClassName, CodeAddrs, !AddrOfMap).
:- pred generate_call_method(arity::in, list(mlds_code_addr)::in,
mlds_function_defn::out) is det.
generate_call_method(Arity, CodeAddrs, MethodDefn) :-
% Create the arguments to the call method. For low arities, the method
% takes n arguments directly. For higher arities, the arguments are
% passed in as an array.
( if is_specialised_method_ptr_arity(Arity) then
list.map2(create_generic_arg, 1 .. Arity, ArgNames, MethodArgs),
InputArgs = cmi_separate(ArgNames)
else
ArgName = lvn_comp_var(lvnc_args),
ArgType = mlds_array_type(mlds_generic_type),
Arg = mlds_argument(ArgName, ArgType, gc_no_stmt),
MethodArgs = [Arg],
InputArgs = cmi_array(ArgName)
),
% Create a statement to call each of the original methods.
list.map(generate_call_statement_for_addr(InputArgs), CodeAddrs,
CodeAddrStmts),
Context = dummy_context,
% If there is more than one original method, then we need to switch
% on the fvn_ptr_num member variable.
(
CodeAddrStmts = [],
unexpected($pred, "no statements")
;
CodeAddrStmts = [Stmt]
;
CodeAddrStmts = [_, _ | _],
MaxCase = list.length(CodeAddrs) - 1,
MakeCase =
( func(I, CaseStmt) = Case :-
MatchCond = match_value(ml_const(mlconst_int(I))),
Case = mlds_switch_case(MatchCond, [], CaseStmt)
),
Cases = list.map_corresponding(MakeCase, 0 .. MaxCase, CodeAddrStmts),
IntType = mlds_builtin_type_int(int_type_int),
SwitchVarName = lvn_field_var_as_local(fvn_ptr_num),
SwitchVarRval = ml_lval(ml_local_var(SwitchVarName, IntType)),
SwitchRange = mlds_switch_range(0, MaxCase),
Stmt = ml_stmt_switch(IntType, SwitchVarRval, SwitchRange,
Cases, default_is_unreachable, Context)
),
% Create new method name.
PredId = hlds_pred.initial_pred_id,
ProcId = initial_proc_id,
PredLabel = mlds_special_pred_label("call", no, "", 0),
ProcLabel = mlds_proc_label(PredLabel, ProcId),
FuncLabel = mlds_func_label(ProcLabel, proc_func),
PlainFuncName = mlds_plain_func_name(FuncLabel, PredId),
MethodName = mlds_function_name(PlainFuncName),
% Create return type.
MethodRetType = mlds_generic_type,
MethodRets = [MethodRetType],
% Put it all together.
MethodFlags = mlds_function_decl_flags(func_public, per_instance),
MethodParams = mlds_func_params(MethodArgs, MethodRets),
MethodEnvVarNames = set.init,
MethodDefn = mlds_function_defn(MethodName, Context, MethodFlags,
mlds_func_source_wrapper, MethodParams, body_defined_here(Stmt),
MethodEnvVarNames, no).
:- pred create_generic_arg(int::in, mlds_local_var_name::out,
mlds_argument::out) is det.
create_generic_arg(I, ArgName, Arg) :-
ArgName = lvn_comp_var(lvnc_arg(I)),
Arg = mlds_argument(ArgName, mlds_generic_type, gc_no_stmt).
:- type call_method_inputs
---> cmi_separate(list(mlds_local_var_name))
; cmi_array(mlds_local_var_name).
:- pred generate_call_statement_for_addr(call_method_inputs::in,
mlds_code_addr::in, mlds_stmt::out) is det.
generate_call_statement_for_addr(InputArgs, CodeAddr, Stmt) :-
CodeAddr = mlds_code_addr(_QualFuncLabel, OrigFuncSignature),
OrigFuncSignature = mlds_func_signature(OrigArgTypes, OrigRetTypes),
% Create the arguments to pass to the original method.
(
InputArgs = cmi_separate(ArgNames),
list.map_corresponding(generate_call_method_nth_arg,
OrigArgTypes, ArgNames, CallArgs)
;
InputArgs = cmi_array(ArrayVarName),
generate_call_method_args_from_array(OrigArgTypes, ArrayVarName, 0,
[], CallArgs)
),
% Create a temporary variable to store the result of the call to the
% original method.
ReturnVarName = lvn_comp_var(lvnc_return_value),
(
OrigRetTypes = [],
ReturnVarType = mlds_generic_type
;
OrigRetTypes = [CallRetType],
ReturnVarType = CallRetType
;
OrigRetTypes = [_, _ | _],
ReturnVarType = mlds_array_type(mlds_generic_type)
),
ReturnLval = ml_local_var(ReturnVarName, ReturnVarType),
Context = dummy_context,
GCStmt = gc_no_stmt, % The Java back-end does its own GC.
ReturnVarDefn = mlds_local_var_defn(ReturnVarName, Context,
ReturnVarType, no_initializer, GCStmt),
% Create the call to the original method.
CallRval = ml_const(mlconst_code_addr(CodeAddr)),
% If the original method has a return type of void, then we obviously
% cannot assign its return value to "return_value". Thus, in this
% case, the value returned by the call method will just be the value
% that "return_value" was initialised to.
(
OrigRetTypes = [],
CallRetLvals = []
;
OrigRetTypes = [_ | _],
CallRetLvals = [ReturnLval]
),
CallStmt = ml_stmt_call(OrigFuncSignature, CallRval, CallArgs,
CallRetLvals, ordinary_call, Context),
% Create a return statement that returns the result of the call to the
% original method, boxed as a java.lang.Object.
ReturnRval = ml_box(ReturnVarType, ml_lval(ReturnLval)),
ReturnStmt = ml_stmt_return([ReturnRval], Context),
Stmt = ml_stmt_block([ReturnVarDefn], [], [CallStmt, ReturnStmt], Context).
:- pred generate_call_method_nth_arg(mlds_type::in,
mlds_local_var_name::in, mlds_rval::out) is det.
generate_call_method_nth_arg(Type, MethodArgVariable, CallArg) :-
Rval = ml_lval(ml_local_var(MethodArgVariable, mlds_generic_type)),
CallArg = ml_unbox(Type, Rval).
% NOTE Since this predicate constructs Args by continually appending
% to an initially empty list, its complexity is O(N^2) where N is the
% length of first argument. Since most predicates have relatively few
% arguments, this is usually not a problem, but the worst-case behavior
% is not nice. Building up Args in reverse, or using cords, would fix this,
% but may yield some slowdown in the usual cost.
%
:- pred generate_call_method_args_from_array(list(mlds_type)::in,
mlds_local_var_name::in, int::in,
list(mlds_rval)::in, list(mlds_rval)::out) is det.
generate_call_method_args_from_array([], _, _, Args, Args).
generate_call_method_args_from_array([Type | Types], ArrayVar, Counter,
Args0, Args) :-
ArrayRval = ml_lval(ml_local_var(ArrayVar,
mlds_builtin_type_int(int_type_int))),
IndexRval = ml_const(mlconst_int(Counter)),
ElemType = array_elem_scalar(scalar_elem_generic),
Rval = ml_binop(array_index(ElemType), ArrayRval, IndexRval),
UnBoxedRval = ml_unbox(Type, Rval),
Args1 = Args0 ++ [UnBoxedRval],
generate_call_method_args_from_array(Types, ArrayVar, Counter + 1,
Args1, Args).
:- pred add_to_address_map(string::in, list(mlds_code_addr)::in,
map(mlds_code_addr, code_addr_wrapper)::in,
map(mlds_code_addr, code_addr_wrapper)::out) is det.
add_to_address_map(ClassName, CodeAddrs, !AddrOfMap) :-
FlippedClassName = flip_initial_case(ClassName),
(
CodeAddrs = [],
unexpected($pred, "no addresses")
;
CodeAddrs = [CodeAddr],
Wrapper = code_addr_wrapper(FlippedClassName, no),
map.det_insert(CodeAddr, Wrapper, !AddrOfMap)
;
CodeAddrs = [_, _ | _],
add_to_address_map_2(FlippedClassName, CodeAddrs, 0, !AddrOfMap)
).
:- pred add_to_address_map_2(string::in, list(mlds_code_addr)::in, int::in,
map(mlds_code_addr, code_addr_wrapper)::in,
map(mlds_code_addr, code_addr_wrapper)::out) is det.
add_to_address_map_2(_, [], _, !AddrOfMap).
add_to_address_map_2(FlippedClassName, [CodeAddr | CodeAddrs], I,
!AddrOfMap) :-
Wrapper = code_addr_wrapper(FlippedClassName, yes(I)),
map.det_insert(CodeAddr, Wrapper, !AddrOfMap),
add_to_address_map_2(FlippedClassName, CodeAddrs, I + 1, !AddrOfMap).
%---------------------------------------------------------------------------%
:- end_module ml_backend.mlds_to_java_wrap.
%---------------------------------------------------------------------------%