%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 1999-2012 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. %---------------------------------------------------------------------------% % % File: ml_closure_gen.m % Main author: fjh % % This module is part of the MLDS code generator. % It handles generation of MLDS code to construct closures. % %---------------------------------------------------------------------------% :- module ml_backend.ml_closure_gen. :- interface. :- import_module hlds. :- import_module hlds.hlds_goal. :- import_module hlds.hlds_pred. :- import_module ml_backend.ml_gen_info. :- import_module ml_backend.mlds. :- import_module parse_tree. :- import_module parse_tree.prog_data. :- import_module list. %---------------------------------------------------------------------------% % ml_construct_closure(PredId, ProcId, Var, ArgVars, ArgModes, % HowToConstruct, Context, Defns, Stmts, !Info): % % Generate code to construct a closure for the procedure specified % by PredId and ProcId, with the partially applied arguments specified % by ArgVars (and ArgModes), and to store the pointer to the resulting % closure in Var. % :- pred ml_construct_closure(pred_id::in, proc_id::in, prog_var::in, list(prog_var)::in, list(unify_mode)::in, how_to_construct::in, prog_context::in, list(mlds_local_var_defn)::out, list(mlds_stmt)::out, ml_gen_info::in, ml_gen_info::out) is det. % ml_gen_closure_wrapper(PredId, ProcId, Offset, NumClosureArgs, % Context, WrapperFuncRval, WrapperFuncType): % % Generates a wrapper function which unboxes the input arguments, % calls the specified procedure, passing it some extra arguments % from the closure, and then boxes the output arguments. % It adds the definition of this wrapper function to the extra_defns field % in the ml_gen_info, and returns the wrapper function's rval and type. % % The ClosureKind parameter specifies whether the closure is % % - an ordinary closure, used for higher-order procedure calls, % - a typeclass_info, used for class method calls, or % - a call to a special pred. % % The NumClosuresArgs parameter specifies how many arguments % to extract from the closure. % :- pred ml_gen_closure_wrapper(pred_id::in, proc_id::in, closure_kind::in, int::in, prog_context::in, mlds_rval::out, mlds_type::out, ml_gen_info::in, ml_gen_info::out) is det. :- type closure_kind ---> higher_order_proc_closure ; typeclass_info_closure ; special_pred_closure. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. % XXX The modules from the LLDS backend should not be used here. :- import_module backend_libs. :- import_module backend_libs.pseudo_type_info. :- import_module backend_libs.rtti. :- import_module check_hlds. :- import_module check_hlds.type_util. :- import_module hlds.code_model. :- import_module hlds.hlds_module. :- import_module hlds.mark_tail_calls. % for ntrcr_program :- import_module libs. :- import_module libs.globals. :- import_module ll_backend. :- import_module ll_backend.continuation_info. % for `generate_closure_layout' :- import_module ll_backend.llds. % for `layout_locn' :- import_module ll_backend.stack_layout. % for `represent_locn_as_int' :- import_module mdbcomp. :- import_module mdbcomp.prim_data. :- import_module ml_backend.ml_accurate_gc. :- import_module ml_backend.ml_args_util. :- import_module ml_backend.ml_call_gen. :- import_module ml_backend.ml_code_util. :- import_module ml_backend.ml_global_data. :- import_module ml_backend.ml_unify_gen_construct. :- import_module ml_backend.ml_unify_gen_util. :- import_module ml_backend.rtti_to_mlds. :- import_module parse_tree.builtin_lib_types. :- import_module parse_tree.prog_type. :- import_module parse_tree.var_table. :- import_module assoc_list. :- import_module bool. :- import_module int. :- import_module map. :- import_module maybe. :- import_module pair. :- import_module require. :- import_module set. :- import_module string. :- import_module term. %---------------------------------------------------------------------------% ml_construct_closure(PredId, ProcId, Var, ArgVars, ArgModes, HowToConstruct, Context, Defns, Stmts, !Info) :- % This constructs a closure. % The representation of closures for the LLDS backend is defined in % runtime/mercury_ho_call.h. % XXX should we use a different representation for closures % in the MLDS backend? % Generate a value for the closure layout; this is a static constant % that holds information about the structure of this closure. ml_gen_closure_layout(PredId, ProcId, Context, ClosureLayoutRval0, ClosureLayoutType0, !Info), % Generate a wrapper function which just unboxes the arguments and then % calls the specified procedure, and put the address of the wrapper % function in the closure. % % ml_gen_closure_wrapper will insert the wrapper function in the % extra_defns field in the ml_gen_info; ml_gen_proc will extract it % and will insert it before the mlds_defn for the current procedure. % list.length(ArgVars, NumArgs), ml_gen_closure_wrapper(PredId, ProcId, higher_order_proc_closure, NumArgs, Context, WrapperFuncRval0, WrapperFuncType0, !Info), % Compute the rval which holds the number of arguments NumArgsRval0 = ml_const(mlconst_int(NumArgs)), NumArgsType0 = mlds_builtin_type_int(int_type_int), % Put all the extra arguments of the closure together % Note that we need to box these arguments. NumArgsRval = ml_box(NumArgsType0, NumArgsRval0), NumArgsType = mlds_generic_type, WrapperFuncRval = ml_box(WrapperFuncType0, WrapperFuncRval0), WrapperFuncType = mlds_generic_type, ClosureLayoutRval = ml_box(ClosureLayoutType0, ClosureLayoutRval0), ClosureLayoutType = mlds_generic_type, ExtraArgRvalsTypes = [rval_type_and_width(ClosureLayoutRval, ClosureLayoutType, apw_full(arg_only_offset(0), cell_offset(0))), rval_type_and_width(WrapperFuncRval, WrapperFuncType, apw_full(arg_only_offset(1), cell_offset(1))), rval_type_and_width(NumArgsRval, NumArgsType, apw_full(arg_only_offset(2), cell_offset(2)))], NumExtraArgRvalsTypes = 3, % MaybeConsId = no means that the pointer will not be tagged % (i.e. its primary tag bits will be zero). % XXX Passing a real cons_id would simplify the code of ml_gen_new_object, % even if we created a cons_id specifically for this purpose. MaybeConsId = no, MaybeConsName = no, Ptag = ptag(0u8), MaybeStag = no, % Generate a `new_object' statement (or static constant) for the closure. ml_gen_info_get_var_table(!.Info, VarTable), lookup_var_entry(VarTable, Var, VarEntry), specified_arg_types_and_consecutive_full_words(ml_make_boxed_type, NumExtraArgRvalsTypes, ArgVars, ArgVarsTypesWidths), FirstArgNum = 1, TakeAddr = [], ml_gen_new_object(MaybeConsId, MaybeConsName, Ptag, MaybeStag, Var, VarEntry, ExtraArgRvalsTypes, ArgVarsTypesWidths, ArgModes, FirstArgNum, TakeAddr, HowToConstruct, Context, Defns, Stmts, !Info). % Generate a value for the closure layout struct. % See MR_Closure_Layout in ../runtime/mercury_ho_call.h. % % Note that the code here is similar to code in stack_layout.m; % any changes here may need to be reflected there, and vice versa. % :- pred ml_gen_closure_layout(pred_id::in, proc_id::in, prog_context::in, mlds_rval::out, mlds_type::out, ml_gen_info::in, ml_gen_info::out) is det. ml_gen_closure_layout(PredId, ProcId, Context, ClosureLayoutAddrRval, ClosureLayoutType, !Info) :- ml_gen_info_get_module_info(!.Info, ModuleInfo), continuation_info.generate_closure_layout(ModuleInfo, PredId, ProcId, ClosureLayoutInfo), some [!GlobalData] ( ml_gen_info_get_global_data(!.Info, !:GlobalData), ml_gen_closure_proc_id(ModuleInfo, Context, InitProcId, _ProcIdType, !GlobalData), ClosureLayoutInfo = closure_layout_info(ClosureArgs, TVarLocnMap), ml_gen_info_get_target(!.Info, Target), ml_stack_layout_construct_closure_args(ModuleInfo, Target, ClosureArgs, ClosureArgInitsAndTypes, !GlobalData), assoc_list.keys(ClosureArgInitsAndTypes, ClosureArgInits), ml_stack_layout_construct_tvar_vector(ModuleInfo, mgcv_typevar_vector, Context, TVarLocnMap, TVarVectorRval, TVarVectorType, !GlobalData), InitTVarVector = init_obj(ml_box(TVarVectorType, TVarVectorRval)), Inits = [InitProcId, InitTVarVector | ClosureArgInits], % _ArgTypes = [ProcIdType, TVarVectorType | ClosureArgTypes], % XXX There is no way in C to properly represent this type, % since it is a struct that ends with a variable-length array. % For now we just treat the whole struct as an array. module_info_get_name(ModuleInfo, ModuleName), MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName), ClosureLayoutType = mlds_array_type(mlds_generic_type), ml_gen_static_scalar_const_addr(MLDS_ModuleName, mgcv_closure_layout, ClosureLayoutType, init_array(Inits), Context, ClosureLayoutAddrRval, !GlobalData), ml_gen_info_set_global_data(!.GlobalData, !Info) ). :- pred ml_gen_closure_proc_id(module_info::in, prog_context::in, mlds_initializer::out, mlds_type::out, ml_global_data::in, ml_global_data::out) is det. ml_gen_closure_proc_id(_ModuleInfo, _Context, InitProcId, ProcIdType, !GlobalData) :- % XXX currently we don't fill in the ProcId field! InitProcId = init_obj(ml_const(mlconst_null(ProcIdType))), ProcIdType = mlds_generic_type. % module_info_get_name(ModuleInfo, ModuleName), % term.context_file(Context, FileName), % term.context_line(Context, LineNumber), % % XXX We don't have the GoalInfo here, % % so we can't compute the goal path correctly % % goal_info_get_goal_path(GoalInfo, GoalPath), % % trace.path_to_string(GoalPath, GoalPathStr), % GoalPathStr = "", % % DataAddr = layout_addr( % % closure_proc_id(CallerProcLabel, SeqNo, ClosureProcLabel)), % % Data = layout_data(closure_proc_id_data(CallerProcLabel, SeqNo, % % ClosureProcLabel, ModuleName, FileName, LineNumber, GoalPath)), % % InitProcId = init_obj(const(data_addr_const(DataAddr))), % % ProcIdType = ... :- pred ml_stack_layout_construct_closure_args(module_info::in, mlds_target_lang::in, list(closure_arg_info)::in, assoc_list(mlds_initializer, mlds_type)::out, ml_global_data::in, ml_global_data::out) is det. ml_stack_layout_construct_closure_args(ModuleInfo, Target, ClosureArgs, ClosureArgInits, !GlobalData) :- list.map_foldl( ml_stack_layout_construct_closure_arg_rval(ModuleInfo, Target), ClosureArgs, ArgInitsAndTypes, !GlobalData), Length = list.length(ArgInitsAndTypes), LengthRval = ml_const(mlconst_int(Length)), CastLengthRval = ml_box(LengthType, LengthRval), LengthType = mlds_builtin_type_int(int_type_int), LengthInitAndType = init_obj(CastLengthRval) - LengthType, ClosureArgInits = [LengthInitAndType | ArgInitsAndTypes]. :- pred ml_stack_layout_construct_closure_arg_rval(module_info::in, mlds_target_lang::in, closure_arg_info::in, pair(mlds_initializer, mlds_type)::out, ml_global_data::in, ml_global_data::out) is det. ml_stack_layout_construct_closure_arg_rval(ModuleInfo, Target, ClosureArg, ArgInit - ArgType, !GlobalData) :- ClosureArg = closure_arg_info(Type, _Inst), % For a stack layout, we can treat all type variables as universally % quantified. This is not the argument of a constructor, so we do not need % to distinguish between type variables that are and aren't in scope; % we can take the variable number directly from the procedure's tvar set. ExistQTvars = [], NumUnivQTvars = -1, pseudo_type_info.construct_pseudo_type_info(Type, NumUnivQTvars, ExistQTvars, PseudoTypeInfo), ml_gen_pseudo_type_info(ModuleInfo, Target, PseudoTypeInfo, ArgRval, ArgType, !GlobalData), CastArgRval = ml_box(ArgType, ArgRval), ArgInit = init_obj(CastArgRval). :- pred ml_gen_maybe_pseudo_type_info_defn(module_info::in, mlds_target_lang::in, rtti_maybe_pseudo_type_info::in, ml_global_data::in, ml_global_data::out) is det. ml_gen_maybe_pseudo_type_info_defn(ModuleInfo, Target, MaybePTI, !GlobalData) :- ml_gen_maybe_pseudo_type_info(ModuleInfo, Target, MaybePTI, _Rval, _Type, !GlobalData). :- pred ml_gen_type_info_defn(module_info::in, mlds_target_lang::in, rtti_type_info::in, ml_global_data::in, ml_global_data::out) is det. ml_gen_type_info_defn(ModuleInfo, Target, TI, !GlobalData) :- ml_gen_type_info(ModuleInfo, Target, TI, _Rval, _Type, !GlobalData). :- pred ml_gen_maybe_pseudo_type_info(module_info::in, mlds_target_lang::in, rtti_maybe_pseudo_type_info::in, mlds_rval::out, mlds_type::out, ml_global_data::in, ml_global_data::out) is det. ml_gen_maybe_pseudo_type_info(ModuleInfo, Target, MaybePseudoTypeInfo, Rval, Type, !GlobalData) :- ( MaybePseudoTypeInfo = pseudo(PseudoTypeInfo), ml_gen_pseudo_type_info(ModuleInfo, Target, PseudoTypeInfo, Rval, Type, !GlobalData) ; MaybePseudoTypeInfo = plain(TypeInfo), ml_gen_type_info(ModuleInfo, Target, TypeInfo, Rval, Type, !GlobalData) ). :- pred ml_gen_pseudo_type_info(module_info::in, mlds_target_lang::in, rtti_pseudo_type_info::in, mlds_rval::out, mlds_type::out, ml_global_data::in, ml_global_data::out) is det. ml_gen_pseudo_type_info(ModuleInfo, Target, PseudoTypeInfo, Rval, Type, !GlobalData) :- ( PseudoTypeInfo = type_var(N), % Type variables are represented just as integers. Rval = ml_const(mlconst_int(N)), Type = mlds_builtin_type_int(int_type_int) ; ( PseudoTypeInfo = plain_arity_zero_pseudo_type_info(_) ; PseudoTypeInfo = plain_pseudo_type_info(_, _) ; PseudoTypeInfo = var_arity_pseudo_type_info(_, _) ), ( PseudoTypeInfo = plain_arity_zero_pseudo_type_info(RttiTypeCtor0), % For zero-arity types, we just generate a reference to the % type_ctor_info, which will always be generated by other code. % (mercury_compile.m has code to generate type_ctor_infos for % all type definitions in the module.) RttiName = type_ctor_type_ctor_info, RttiTypeCtor0 = rtti_type_ctor(ModuleName0, _, _), ModuleName = fixup_builtin_module(ModuleName0), RttiTypeCtor = RttiTypeCtor0, RttiId = ctor_rtti_id(RttiTypeCtor, RttiName), MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName), Rval = ml_const(mlconst_data_addr_rtti(MLDS_ModuleName, RttiId)), Type = mlds_rtti_type(item_type(RttiId)) ; ( PseudoTypeInfo = plain_pseudo_type_info(_, _) ; PseudoTypeInfo = var_arity_pseudo_type_info(_, _) ), % For other types, we need to generate a definition of the % pseudo_type_info for that type, in the current module. RttiData = rtti_data_pseudo_type_info(PseudoTypeInfo), rtti_data_to_id(RttiData, RttiId), ml_global_data_get_pdup_rval_type_map(!.GlobalData, PDupRvalTypeMap), ( if map.search(PDupRvalTypeMap, RttiId, OldRvalType) then OldRvalType = ml_rval_and_type(Rval, Type) else module_info_get_name(ModuleInfo, ModuleName), MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName), Rval = ml_const(mlconst_data_addr_rtti(MLDS_ModuleName, RttiId)), Type = mlds_rtti_type(item_type(RttiId)), add_rtti_data_to_mlds(ModuleInfo, Target, RttiData, !GlobalData), % Generate definitions of any type_infos and pseudo_type_infos % referenced by this pseudo_type_info. % XXX Is this guaranteed to add nothing? (zs) list.foldl( ml_gen_maybe_pseudo_type_info_defn(ModuleInfo, Target), arg_maybe_pseudo_type_infos(PseudoTypeInfo), !GlobalData) ) ) ). :- pred ml_gen_type_info(module_info::in, mlds_target_lang::in, rtti_type_info::in, mlds_rval::out, mlds_type::out, ml_global_data::in, ml_global_data::out) is det. ml_gen_type_info(ModuleInfo, Target, TypeInfo, Rval, Type, !GlobalData) :- ( TypeInfo = plain_arity_zero_type_info(RttiTypeCtor0), % For zero-arity types, we just generate a reference to the % already-existing type_ctor_info. RttiName = type_ctor_type_ctor_info, RttiTypeCtor0 = rtti_type_ctor(ModuleName0, _, _), ModuleName = fixup_builtin_module(ModuleName0), RttiId = ctor_rtti_id(RttiTypeCtor0, RttiName), MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName), Rval = ml_const(mlconst_data_addr_rtti(MLDS_ModuleName, RttiId)), Type = mlds_rtti_type(item_type(RttiId)) ; ( TypeInfo = plain_type_info(_, _) ; TypeInfo = var_arity_type_info(_, _) ), % For other types, we need to generate a definition of the type_info % for that type, in the current module. RttiData = rtti_data_type_info(TypeInfo), rtti_data_to_id(RttiData, RttiId), ml_global_data_get_pdup_rval_type_map(!.GlobalData, PDupRvalTypeMap), ( if map.search(PDupRvalTypeMap, RttiId, OldRvalType) then OldRvalType = ml_rval_and_type(Rval, Type) else module_info_get_name(ModuleInfo, ModuleName), MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName), Rval = ml_const(mlconst_data_addr_rtti(MLDS_ModuleName, RttiId)), Type = mlds_rtti_type(item_type(RttiId)), add_rtti_data_to_mlds(ModuleInfo, Target, RttiData, !GlobalData), % Generate definitions of any type_infos referenced % by this type_info. % XXX Is this guaranteed to add nothing? (zs) list.foldl(ml_gen_type_info_defn(ModuleInfo, Target), arg_type_infos(TypeInfo), !GlobalData) ) ). :- func arg_maybe_pseudo_type_infos(rtti_pseudo_type_info) = list(rtti_maybe_pseudo_type_info). arg_maybe_pseudo_type_infos(type_var(_)) = []. arg_maybe_pseudo_type_infos(plain_arity_zero_pseudo_type_info(_)) = []. arg_maybe_pseudo_type_infos(plain_pseudo_type_info(_TypeCtor, ArgMPTIs)) = ArgMPTIs. arg_maybe_pseudo_type_infos(var_arity_pseudo_type_info(_VarArityId, ArgMPTIs)) = ArgMPTIs. :- func arg_type_infos(rtti_type_info) = list(rtti_type_info). arg_type_infos(plain_arity_zero_type_info(_)) = []. arg_type_infos(plain_type_info(_TypeCtor, ArgTIs)) = ArgTIs. arg_type_infos(var_arity_type_info(_VarArityId, ArgTIs)) = ArgTIs. :- pred ml_stack_layout_construct_tvar_vector(module_info::in, mlds_global_const_var::in, prog_context::in, map(tvar, set(layout_locn))::in, mlds_rval::out, mlds_type::out, ml_global_data::in, ml_global_data::out) is det. ml_stack_layout_construct_tvar_vector(ModuleInfo, ConstVarKind, Context, TVarLocnMap, TVarVectorAddrRval, ArrayType, !GlobalData) :- IntType = mlds_builtin_type_int(int_type_int), ArrayType = mlds_array_type(IntType), ( if map.is_empty(TVarLocnMap) then TVarVectorAddrRval = ml_const(mlconst_null(ArrayType)) else ml_stack_layout_construct_tvar_rvals(TVarLocnMap, Vector, _VectorTypes), Initializer = init_array(Vector), module_info_get_name(ModuleInfo, ModuleName), MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName), ml_gen_static_scalar_const_addr(MLDS_ModuleName, ConstVarKind, ArrayType, Initializer, Context, TVarVectorAddrRval, !GlobalData) ). :- pred ml_stack_layout_construct_tvar_rvals(map(tvar, set(layout_locn))::in, list(mlds_initializer)::out, list(mlds_type)::out) is det. ml_stack_layout_construct_tvar_rvals(TVarLocnMap, Vector, VectorTypes) :- map.to_assoc_list(TVarLocnMap, TVarLocns), ml_stack_layout_construct_type_param_locn_vector(TVarLocns, 1, TypeParamLocs), list.length(TypeParamLocs, TypeParamsLength), LengthRval = ml_const(mlconst_int(TypeParamsLength)), Vector = [init_obj(LengthRval) | TypeParamLocs], IntType = mlds_builtin_type_int(int_type_int), VectorTypes = list.duplicate(TypeParamsLength + 1, IntType). % Given a association list of type variables and their locations sorted % on the type variables, represent them in an array of location % descriptions indexed by the type variable. The next slot to fill is given % by the second argument. % :- pred ml_stack_layout_construct_type_param_locn_vector( assoc_list(tvar, set(layout_locn))::in, int::in, list(mlds_initializer)::out) is det. ml_stack_layout_construct_type_param_locn_vector([], _, []). ml_stack_layout_construct_type_param_locn_vector([TVar - Locns | TVarLocns], CurSlot, Vector) :- term.var_to_int(TVar, TVarNum), NextSlot = CurSlot + 1, ( if TVarNum = CurSlot then ( if set.remove_least(LeastLocn, Locns, _) then Locn = LeastLocn else unexpected($pred, "tvar has empty set of locations") ), stack_layout.represent_locn_as_int(Locn, LocnAsInt), Rval = ml_const(mlconst_int(LocnAsInt)), ml_stack_layout_construct_type_param_locn_vector(TVarLocns, NextSlot, VectorTail), Vector = [init_obj(Rval) | VectorTail] else if TVarNum > CurSlot then % This slot will never be referred to. ml_stack_layout_construct_type_param_locn_vector( [TVar - Locns | TVarLocns], NextSlot, VectorTail), Vector = [init_obj(ml_const(mlconst_int(0))) | VectorTail] else unexpected($pred, "unsorted tvars") ). ml_gen_closure_wrapper(PredId, ProcId, ClosureKind, NumClosureArgs, Context, WrapperFuncRval, WrapperFuncType, !Info) :- % This predicate creates wrappers both for ordinary closures and % for type class methods. % % The generated function will look something like this: % % MR_Box % foo_wrapper(void *closure_arg, % MR_Box wrapper_arg1, MR_Box *wrapper_arg2, % ..., MR_Box wrapper_argn) % { % void *closure; % % // declarations needed for converting output args % Arg2Type conv_arg2; % RetType conv_retval; % ... % % // declarations needed for by-value outputs % MR_Box retval; % % closure = closure_arg; // XXX should add cast % % // call function, unboxing inputs if needed % conv_retval = foo(closure->f1, unbox(closure->f2), ..., % unbox(wrapper_arg1), &conv_arg2, % wrapper_arg3, ...); % % // box output arguments % *wrapper_arg2 = box(conv_arg2); % ... % retval = box(conv_retval); % % return retval; % } % % Actually, that is a simplified form. % Also, when calling a special pred, the closure argument isn't required. % In full generality, it will look more like this: % % #if MODEL_SEMI % bool % #elif FUNC_IN_FORWARDS_MODE % MR_Box % #else % void % #endif % foo_wrapper( % void *closure_arg, // with appropriate GC trace code % MR_Box wrapper_arg1, MR_Box *wrapper_arg2, % ..., MR_Box wrapper_argn) % // No GC tracing code needed for the wrapper_* parameters, % // because output parameters point to the stack, and % // input parameters won't be live across a GC. % // Likewise for the local var `closure' below. % // But we do need GC tracing code for the closure_arg parameter % // since that may be referenced _during_ GC, because it is % // mentioned in the GC tracing code for the conv_* variables below. % { % #if 0 // XXX we should do this for HIGH_LEVEL_DATA % FooClosure *closure; % #else % void *closure; % #endif % % #if defined(MR_NATIVE_GC) % MR_Closure_Layout *closure_layout_ptr; % MR_TypeInfo *type_params; % #if 0 // GC tracing code % #if CLOSURE_KIND == HIGHER_ORDER_PROC_CLOSURE % closure_layout_ptr = % ((MR_Closure *) closure_arg)->MR_closure_layout; % type_params = MR_materialize_closure_typeinfos(closure_arg); % #else // CLOSURE_KIND == TYPECLASS_INFO_CLOSURE % { % static const MR_Closure_Layout closure_layout = ...; % closure_layout_ptr = &closure_layout; % } % type_params = MR_materialize_closure_typeinfos(closure_arg); % #endif % #endif // GC tracing code % #endif % % // declarations needed for converting output args % Arg2Type conv_arg2; % // GC tracing code same as below % ... % % // declarations needed for by-value outputs % RetType conv_retval; % #if defined(MR_NATIVE_GC) % #if 0 // GC tracing code % { % MR_TypeInfo type_info; % MR_MemoryList allocated_memory_cells = NULL; % type_info = MR_make_type_info_maybe_existq(type_params, % closure_layout_ptr->MR_closure_arg_pseudo_type_info % [ - 1], % NULL, NULL, &allocated_memory_cells); % mercury__private_builtin__gc_trace_1_0(type_info, &conv_retval); % MR_deallocate(allocated_memory_cells); % } % #endif % #endif % % #if MODEL_SEMI % MR_bool succeeded; % #elif FUNC_IN_FORWARDS_MODE % MR_Box retval; // GC tracing code as above % #endif % % closure = closure_arg; // XXX should add cast % % CONJ(code_model, % // call function, unboxing inputs if needed % conv_retval = foo(closure->f1, unbox(closure->f2), ..., % unbox(wrapper_arg1), &conv_arg2, % wrapper_arg3, ...); % , % // box output arguments % *wrapper_arg2 = box(conv_arg2); % ... % retval = box(conv_retval); % ) % #if MODEL_SEMI % return succeeded; % #else % return retval; % #endif % } % % The stuff in CONJ() expands to the appropriate code % for a conjunction, which depends on the code model: % % #if MODEL_DET % // call function, boxing/unboxing inputs if needed % foo(closure->f1, unbox(closure->f2), ..., % unbox(wrapper_arg1), &conv_arg2, % wrapper_arg3, ...); % % // box output arguments % *wrapper_arg2 = box(conv_arg2); % ... % #elif MODEL_SEMI % // call function, boxing/unboxing inputs if needed % succeeded = foo(closure->f1, unbox(closure->f2), ..., % unbox(wrapper_arg1), &conv_arg2, % wrapper_arg3, ...); % % if (succeeded) { % // box output arguments % *wrapper_arg2 = box(conv_arg2); % ... % } % % return succeeded; % } % #else // MODEL_NON % foo_1() { % // box output arguments % *wrapper_arg2 = box(conv_arg2); % ... % (*succ_cont)(); % } % % // call function, boxing/unboxing inputs if needed % foo(closure->f1, unbox(closure->f2), ..., % unbox(wrapper_arg1), &conv_arg2, % wrapper_arg3, ..., % foo_1); % #endif % % Grab the relevant information about the called procedure. ml_gen_info_get_module_info(!.Info, ModuleInfo), module_info_pred_proc_info(ModuleInfo, PredId, ProcId, PredInfo, ProcInfo), pred_info_get_purity(PredInfo, Purity), pred_info_get_arg_types(PredInfo, ProcArgTypes), PredOrFunc = pred_info_is_pred_or_func(PredInfo), proc_info_get_headvars(ProcInfo, ProcHeadVars), proc_info_get_argmodes(ProcInfo, ProcArgModes), CodeModel = proc_info_interface_code_model(ProcInfo), proc_info_get_var_table(ProcInfo, ProcVarTable), ProcArity = list.length(ProcHeadVars), ProcHeadVarNames = ml_gen_local_var_names(ProcVarTable, ProcHeadVars), % Allocate some fresh type variables to use as the Mercury types % of the boxed arguments. % XXX While the type variables that ml_make_boxed_types returns, % it creates out of thin air, there is no guarantee that they won't collide % with the type variables used by any of the other constructs we process. ProcBoxedArgTypes = ml_make_boxed_types(ProcArity), % Compute the parameters for the wrapper function % (void *closure_arg, % MR_Box wrapper_arg1, MR_Box *wrapper_arg2, ..., % MR_Box wrapper_argn) % First generate the declarations for the boxed arguments. ( if list.drop(NumClosureArgs, ProcHeadVars, WrapperHeadVars0), list.drop(NumClosureArgs, ProcArgModes, WrapperArgModes0), list.drop(NumClosureArgs, ProcArgTypes, WrapperArgTypes0), list.drop(NumClosureArgs, ProcBoxedArgTypes, WrapperBoxedArgTypes0) then WrapperHeadVars = WrapperHeadVars0, WrapperArgModes = WrapperArgModes0, WrapperArgTypes = WrapperArgTypes0, WrapperBoxedArgTypes = WrapperBoxedArgTypes0 else unexpected($pred, "list.drop failed") ), NumWrapperHeadVars = list.length(WrapperHeadVars), WrapperHeadVarNames = ml_gen_wrapper_head_var_names(1, NumWrapperHeadVars), % We can't generate correct gc statements for the wrapper args, because % we don't have type_infos for the type variables in WrapperBoxedArgTypes. % We handle this by simply not generating such statements, since they are % not needed anyway. The WrapperParams are only live in the time interval % from the entry point of the wrapper function to its call to the wrapped % function, and since the code executed in that interval does not allocate % any memory (it has only an assignment to `closure_arg' and some unbox % operations), it cannot trigger garbage collection. % % XXX PARAMS We could call a specialized version of % ml_gen_params_no_gc_stmts that % % - allocates each WrapperHeadVarName from a sounter as it goes along, % and then returns them, saving a nil/cons switch, and % % - knows that every BoxedArgType is a free type var % (the fact that they are different type vars should not matter). ml_gen_params_no_gc_stmts(ModuleInfo, PredOrFunc, CodeModel, WrapperHeadVars, WrapperHeadVarNames, WrapperBoxedArgTypes, WrapperArgModes, ArgTuples, WrapperParams0), WrapperParams0 = mlds_func_params(WrapperArgs0, WrapperRetType), % Then insert the `closure_arg' parameter, if needed. ( ClosureKind = special_pred_closure, MaybeClosureA = no, WrapperArgs = WrapperArgs0 ; ( ClosureKind = higher_order_proc_closure ; ClosureKind = typeclass_info_closure ), ClosureArgType = mlds_generic_type, ClosureArgName = lvn_comp_var(lvnc_closure_arg), ClosureArgDeclType = ml_make_boxed_type, gen_closure_gc_statement(ClosureArgName, ClosureArgDeclType, ClosureKind, WrapperArgTypes, Purity, PredOrFunc, Context, ClosureArgGCStmt, !Info), ClosureArg = mlds_argument(ClosureArgName, ClosureArgType, ClosureArgGCStmt), MaybeClosureA = yes({ClosureArgType, ClosureArgName}), WrapperArgs = [ClosureArg | WrapperArgs0] ), WrapperParams = mlds_func_params(WrapperArgs, WrapperRetType), % Also compute the lvals for the parameters, % and local declarations for any by-value output parameters. ml_gen_info_get_copy_out(!.Info, CodeModel, CopyOut), CopyOutWhen = compute_when_to_copy_out(CopyOut, CodeModel, PredOrFunc), ml_gen_wrapper_arg_lvals(CopyOutWhen, Context, 1, ArgTuples, WrapperHeadVarDefns, WrapperHeadVarLvals, WrapperCopyOutRvals, WrapperOutputLvalsTypes, !Info), % Generate code to declare and initialize the closure pointer, % if needed. % XXX We should use a struct type for the closure, but currently we are % using a low-level data representation in the closure. % % #if 0 // HIGH_LEVEL_DATA % FooClosure *closure; % #else % void *closure; % #endif % closure = closure_arg; % ( MaybeClosureA = yes({ClosureArgType1, ClosureArgName1}), ClosureName = lvn_comp_var(lvnc_closure), ClosureType = mlds_generic_type, % If we were to generate GC tracing code for the closure % pointer, it would look like this: % ClosureDeclType = list.det_head(ml_make_boxed_types(1)), % gen_closure_gc_statement(ClosureName, ClosureDeclType, % ClosureKind, WrapperArgTypes, Purity, % PredOrFunc, Context, ClosureGCStmt), % But we don't need any GC tracing code for the closure pointer, % because it won't be live across an allocation, and because % (unlike the closure_arg parameter) it isn't referenced from % the GC tracing for other variables. ClosureGCStmt = gc_no_stmt, ClosureDefn = ml_gen_mlds_var_decl(ClosureName, ClosureType, ClosureGCStmt, Context), ClosureLval = ml_local_var(ClosureName, ClosureType), ClosureArgLval = ml_local_var(ClosureArgName1, ClosureArgType1), InitClosure = ml_gen_assign(ClosureLval, ml_lval(ClosureArgLval), Context), MaybeClosureB = yes({ClosureDefn, InitClosure}), MaybeClosureC = yes(ClosureLval) ; MaybeClosureA = no, MaybeClosureB = no, MaybeClosureC = no ), % If the wrapper function is model_non, then set up the initial success % continuation; this is needed by ml_gen_plain_non_tail_call, % which we call below. ( CodeModel = model_det ; CodeModel = model_semi ; CodeModel = model_non, ml_gen_info_get_nondet_copy_out(!.Info, NondetCopyOut), ( NondetCopyOut = yes, ml_initial_cont(!.Info, WrapperOutputLvalsTypes, InitialCont) ; NondetCopyOut = no, ml_initial_cont(!.Info, [], InitialCont) ), ml_gen_info_push_success_cont(InitialCont, !Info) ), % Generate code to call the function: % XXX Currently, we are using a low-level data representation % in the closure. % % foo( % #if HIGH_LEVEL_DATA % closure->arg1, closure->arg2, ..., % #else % MR_field(MR_mktag(0), closure, 3), % MR_field(MR_mktag(0), closure, 4), % ... % #endif % unbox(wrapper_arg1), &conv_arg2, wrapper_arg3, ... % ); % % `Offset' specifies the offset to add to the argument number to % get the field number within the closure. (Argument numbers start % from 1, and field numbers start from 0.) ( MaybeClosureC = yes(ClosureLval1), ( ClosureKind = higher_order_proc_closure, Offset = ml_closure_arg_offset ; ClosureKind = typeclass_info_closure, Offset = ml_typeclass_info_arg_offset ; ClosureKind = special_pred_closure, unexpected($pred, "special_pred_closure") ), ml_gen_closure_field_lvals(ClosureLval1, Offset, 1, NumClosureArgs, ClosureArgLvals, !Info) ; MaybeClosureC = no, ClosureArgLvals = [] ), CallLvals = ClosureArgLvals ++ WrapperHeadVarLvals, create_for_closure_wrapper_args(ProcHeadVarNames, CallLvals, ProcBoxedArgTypes, ForClosureWrapperArgs), set.init(Features), ml_gen_plain_non_tail_call(proc(PredId, ProcId), CodeModel, Context, ForClosureWrapperArgs, ntrcr_program, Features, LocalVarDefns0, FuncDefns, Stmts0, !Info), % Insert the stuff to declare and initialize the closure. ( MaybeClosureB = yes({ClosureDefn1, InitClosure1}), LocalVarDefns1 = [ClosureDefn1 | LocalVarDefns0], Stmts1 = [InitClosure1 | Stmts0] ; MaybeClosureB = no, LocalVarDefns1 = LocalVarDefns0, Stmts1 = Stmts0 ), % For semidet code, add the declaration `MR_bool succeeded;'. ( ( CodeModel = model_det ; CodeModel = model_non ), LocalVarDefns2 = LocalVarDefns1 ; CodeModel = model_semi, SucceededVarDefn = ml_gen_succeeded_var_decl(Context), LocalVarDefns2 = [SucceededVarDefn | LocalVarDefns1] ), % Add an appropriate `return' statement. ml_append_return_statement(CodeModel, Context, WrapperCopyOutRvals, Stmts1, Stmts), % Generate code to declare and initialize the local variables % needed for accurate GC. module_info_get_globals(ModuleInfo, Globals), ( if MaybeClosureA = yes({ClosureArgType2, ClosureArgName2}), globals.get_gc_method(Globals, gc_accurate) then ml_gen_closure_wrapper_gc_decls(ClosureKind, ClosureArgName2, ClosureArgType2, PredId, ProcId, Context, GC_Defns, !Info) else GC_Defns = [] ), % Insert the local declarations of the wrapper's output arguments, % if any (this is needed for functions and for `--(non)det-copy-out'), % and the `type_params' variable used by the GC code. LocalVarDefns = GC_Defns ++ WrapperHeadVarDefns ++ LocalVarDefns2, % If the wrapper function was model_non, then pop the success continuation % that we pushed. ( CodeModel = model_det ; CodeModel = model_semi ; CodeModel = model_non, ml_gen_info_pop_success_cont(!Info) ), % Put it all together. WrapperFuncBody = ml_gen_block(LocalVarDefns, FuncDefns, Stmts, Context), ml_gen_new_func_label(yes(WrapperParams), WrapperFuncName, WrapperFuncRval, !Info), ml_gen_wrapper_func(WrapperFuncName, WrapperParams, Context, WrapperFuncBody, WrapperFuncDefn, !Info), WrapperFuncType = mlds_func_type(WrapperParams), ml_gen_info_add_closure_wrapper_defn(WrapperFuncDefn, !Info). :- pred create_for_closure_wrapper_args(list(mlds_local_var_name)::in, list(mlds_lval)::in, list(mer_type)::in, list(ml_call_arg)::out(list_skel(fcw))) is det. create_for_closure_wrapper_args(VarNames, VarLvals, VarTypes, Args) :- ( if VarNames = [], VarLvals = [], VarTypes = [] then Args = [] else if VarNames = [HeadVarName | TailVarNames], VarLvals = [HeadVarLval | TailVarLvals], VarTypes = [HeadVarType | TailVarTypes] then create_for_closure_wrapper_args(TailVarNames, TailVarLvals, TailVarTypes, TailArgs), HeadArg = arg_for_closure_wrapper(HeadVarName, HeadVarLval, HeadVarType), Args = [HeadArg | TailArgs] else unexpected($pred, "length mismatch") ). % Generate the GC trace code for `closure_arg' or `closure' % (see ml_gen_closure_wrapper above). % :- pred gen_closure_gc_statement(mlds_local_var_name::in, mer_type::in, closure_kind::in, list(mer_type)::in, purity::in, pred_or_func::in, prog_context::in, mlds_gc_statement::out, ml_gen_info::in, ml_gen_info::out) is det. gen_closure_gc_statement(ClosureName, ClosureDeclType, ClosureKind, WrapperArgTypes, Purity, PredOrFunc, Context, ClosureGCStmt, !Info) :- % We can't use WrapperArgTypes here, because we don't have type_infos % for the type variables in WrapperArgTypes; those type variables come from % the callee. But when copying closures, we don't care what the types of %% the not-yet-applied arguments are. So we can just use dummy values here. HigherOrderArgTypes = list.duplicate(list.length(WrapperArgTypes), c_pointer_type), ( ClosureKind = higher_order_proc_closure, LambdaEvalMethod = lambda_normal, construct_higher_order_type(Purity, PredOrFunc, LambdaEvalMethod, HigherOrderArgTypes, ClosureActualType) ; ClosureKind = typeclass_info_closure, ClosureActualType = sample_typeclass_info_type ; ClosureKind = special_pred_closure, unexpected($pred, "special_pred_closure") ), ml_gen_gc_statement_poly(ClosureName, ClosureDeclType, ClosureActualType, Context, ClosureGCStmt, !Info). :- pred ml_gen_wrapper_func(mlds_maybe_aux_func_id::in, mlds_func_params::in, prog_context::in, mlds_stmt::in, mlds_function_defn::out, ml_gen_info::in, ml_gen_info::out) is det. ml_gen_wrapper_func(MaybeAux, FuncParams, Context, Stmt, FunctionDefn, !Info) :- % XXX MLDS_DEFN: pass the needed flags to ml_gen_label_func ml_gen_label_func(!.Info, MaybeAux, FuncParams, Context, Stmt, FunctionDefn0), FunctionDefn0 = mlds_function_defn(Name, Ctxt, _DeclFlags0, MaybePredProcId, DefnFuncParams, Body, EnvVarNames, TailRec), DeclFlags = mlds_function_decl_flags(func_private, one_copy), FunctionDefn = mlds_function_defn(Name, Ctxt, DeclFlags, MaybePredProcId, DefnFuncParams, Body, EnvVarNames, TailRec). :- func ml_gen_wrapper_head_var_names(int, int) = list(mlds_local_var_name). ml_gen_wrapper_head_var_names(Num, Max) = VarNames :- ( if Num > Max then VarNames = [] else HeadVarName = lvn_comp_var(lvnc_wrapper_arg(Num)), TailVarNames = ml_gen_wrapper_head_var_names(Num + 1, Max), VarNames = [HeadVarName | TailVarNames] ). % ml_gen_wrapper_arg_lvals(CopyOutWhen, Context, ArgNum, ArgTuples, % LocalVarDefns, HeadVarLvals, CopyOutLvals, !Info): % % Generate lvals for the head variables specified in ArgTuples % passed in the modes specified with them. Also generate local definitions % for the output variables that will be copied out, rather than passed % by reference. % :- pred ml_gen_wrapper_arg_lvals(copy_out_when::in, prog_context::in, int::in, list(var_mvar_type_mode)::in, list(mlds_local_var_defn)::out, list(mlds_lval)::out, list(mlds_rval)::out, assoc_list(mlds_lval, mer_type)::out, ml_gen_info::in, ml_gen_info::out) is det. ml_gen_wrapper_arg_lvals(_, _, _, [], [], [], [], [], !Info). ml_gen_wrapper_arg_lvals(CopyOutWhen, Context, ArgNum, [HeadArgTuple | TailArgTuples], Defns, Lvals, CopyOutRvals, OutputLvalsTypes, !Info) :- ml_gen_wrapper_arg_lvals(CopyOutWhen, Context, ArgNum + 1, TailArgTuples, TailDefns, TailLvals, TailCopyOutRvals, TailOutputLvalsTypes, !Info), HeadArgTuple = var_mvar_type_mode(_Var, MLDSVarName, Type, TopFunctorMode), ml_gen_mlds_type(!.Info, Type, MLDS_Type), VarLval = ml_local_var(MLDSVarName, MLDS_Type), % XXX This code does ignores dummy values if they are copied outputs, % but not when they are (a) byref outputs, or (b) inputs. Why? % Is it an oversight? ( ( TopFunctorMode = top_in ; TopFunctorMode = top_unused ), HeadLval = VarLval, Defns = TailDefns, CopyOutRvals = TailCopyOutRvals, OutputLvalsTypes = TailOutputLvalsTypes ; TopFunctorMode = top_out, % Handle output variables. ml_gen_info_get_module_info(!.Info, ModuleInfo), IsDummy = is_type_a_dummy(ModuleInfo, Type), ( if ( CopyOutWhen = copy_out_only_last_arg, TailArgTuples = [], IsDummy = is_not_dummy_type ; CopyOutWhen = copy_out_always ) then % Output arguments are copied out, so we need to generate % a local declaration for them here. HeadLval = VarLval, ( IsDummy = is_dummy_type, Defns = TailDefns, CopyOutRvals = TailCopyOutRvals ; IsDummy = is_not_dummy_type, ml_gen_local_for_output_arg(MLDSVarName, Type, ArgNum, Context, HeadDefn, !Info), Defns = [HeadDefn | TailDefns], CopyOutRvals = [ml_lval(HeadLval) | TailCopyOutRvals] ) else % Output arguments are passed by reference, so we need to % dereference them. HeadLval = ml_mem_ref(ml_lval(VarLval), MLDS_Type), Defns = TailDefns, CopyOutRvals = TailCopyOutRvals ), OutputLvalsTypes = [VarLval - Type | TailOutputLvalsTypes] ), Lvals = [HeadLval | TailLvals]. % This is used for accurate GC with the MLDS->C back-end. % It generates the following variable declarations: % MR_Closure_Layout *closure_layout_ptr; % MR_TypeInfo *type_params; % and code to initialize them: either % closure_layout_ptr = % ((MR_Closure *) closure_arg)->MR_closure_layout; % type_params = MR_materialize_closure_typeinfos(closure_arg); % or % { % static const MR_Closure_Layout closure_layout = { ... } % closure_layout_ptr = &closure_layout; % } % type_params = MR_materialize_typeclass_info_typeinfos( % closure_arg, closure_layout); % :- pred ml_gen_closure_wrapper_gc_decls(closure_kind::in, mlds_local_var_name::in, mlds_type::in, pred_id::in, proc_id::in, prog_context::in, list(mlds_local_var_defn)::out, ml_gen_info::in, ml_gen_info::out) is det. ml_gen_closure_wrapper_gc_decls(ClosureKind, ClosureArgName, ClosureArgType, PredId, ProcId, Context, GC_Decls, !Info) :- ClosureArgLval = ml_local_var(ClosureArgName, ClosureArgType), ClosureLayoutPtrName = lvn_comp_var(lvnc_closure_layout_ptr), % This type is really `const MR_Closure_Layout *', but there is no easy % way to represent that in the MLDS; using MR_Box instead works fine. ClosureLayoutPtrType = mlds_generic_type, ClosureLayoutPtrLval = ml_local_var(ClosureLayoutPtrName, ClosureLayoutPtrType), TypeParamsName = lvn_comp_var(lvnc_type_params), % This type is really MR_TypeInfoParams, but there is no easy way to % represent that in the MLDS; using MR_Box instead works fine. TypeParamsType = mlds_generic_type, TypeParamsLval = ml_local_var(TypeParamsName, TypeParamsType), ( ClosureKind = higher_order_proc_closure, ClosureLayoutPtrGCInitFragments = [ target_code_output(ClosureLayoutPtrLval), raw_target_code(" = (MR_Box) ((MR_Closure *)\n"), target_code_input(ml_lval(ClosureArgLval)), raw_target_code(")->MR_closure_layout;\n") ], ClosureLayoutPtrGCInit = ml_stmt_atomic( inline_target_code(ml_target_c, ClosureLayoutPtrGCInitFragments), Context), TypeParamsGCInitFragments = [ target_code_output(TypeParamsLval), raw_target_code(" = (MR_Box) " ++ "MR_materialize_closure_type_params(\n"), target_code_input(ml_lval(ClosureArgLval)), raw_target_code(");\n") ] ; ClosureKind = typeclass_info_closure, ml_gen_closure_layout(PredId, ProcId, Context, ClosureLayoutRval, ClosureLayoutType, !Info), ClosureLayoutPtrGCInit = ml_stmt_atomic( assign(ClosureLayoutPtrLval, ml_box(ClosureLayoutType, ClosureLayoutRval)), Context), TypeParamsGCInitFragments = [ target_code_output(TypeParamsLval), raw_target_code(" = (MR_Box) " ++ "MR_materialize_typeclass_info_type_params(\n" ++ "(MR_Word) "), target_code_input(ml_lval(ClosureArgLval)), raw_target_code(", (MR_Closure_Layout *) "), target_code_input(ml_lval(ClosureLayoutPtrLval)), raw_target_code(");\n") ] ; ClosureKind = special_pred_closure, unexpected($pred, "special_pred_closure") ), TypeParamsGCInit = ml_stmt_atomic( inline_target_code(ml_target_c, TypeParamsGCInitFragments), Context), % We use 'gc_initialiser' for the garbage collection code as it is code to % initialise local variables used during garbage collection and must % run before variables are traced. ClosureLayoutPtrDecl = ml_gen_mlds_var_decl(ClosureLayoutPtrName, ClosureLayoutPtrType, gc_initialiser(ClosureLayoutPtrGCInit), Context), % We use 'gc_initialiser' for the garbage collection code as it is code to % initialise local variables used during garbage collection and must % run before variables are traced. TypeParamsDecl = ml_gen_mlds_var_decl(TypeParamsName, TypeParamsType, gc_initialiser(TypeParamsGCInit), Context), GC_Decls = [ClosureLayoutPtrDecl, TypeParamsDecl]. :- pred ml_gen_closure_field_lvals(mlds_lval::in, int::in, int::in, int::in, list(mlds_lval)::out, ml_gen_info::in, ml_gen_info::out) is det. ml_gen_closure_field_lvals(ClosureLval, Offset, ArgNum, NumClosureArgs, ClosureArgLvals, !Info) :- ( if ArgNum > NumClosureArgs then ClosureArgLvals = [] else % Generate `MR_field(MR_mktag(0), closure, )'. FieldId = ml_field_offset(ml_const(mlconst_int(ArgNum + Offset))), % XXX These types might not be right. FieldLval = ml_field(yes(ptag(0u8)), ml_lval(ClosureLval), mlds_generic_type, FieldId, mlds_generic_type), % Recursively handle the remaining fields. ml_gen_closure_field_lvals(ClosureLval, Offset, ArgNum + 1, NumClosureArgs, ClosureArgLvals0, !Info), ClosureArgLvals = [FieldLval | ClosureArgLvals0] ). %---------------------------------------------------------------------------% :- end_module ml_backend.ml_closure_gen. %---------------------------------------------------------------------------%