[m-rev.] for review: float registers for higher order calls

Julien Fischer juliensf at csse.unimelb.edu.au
Mon Jan 30 16:09:41 AEDT 2012



Hi Peter,

Here are some initial review comments for this.

On Fri, 14 Oct 2011, Peter Wang wrote:

> Branches: main
>
> Allow the use of Mercury abstract machine float registers for passing
> double-precision float arguments in higher order calls.
>
> In of itself this is not so useful for typical Mercury code.  However, as
> all non-local procedures are potentially the targets of higher order calls,
> without this change first order calls to non-local procedures could not use
> float registers either.  That is the actual motivation for this change.
>
> The basic mechanism is straightforward.  As before, do_call_closure_* is
> invoked to place the closure's hidden arguments into r1, ..., rN, and extra
> input arguments shifted into rN+1, etc.  With float registers, extra input
> arguments may also be in f1, f2, etc. and the closure may also have hidden
> float arguments.  Optimising for calls, we order the closure's hidden
> arguments so that all float register arguments come after all regular
> register arguments in the vector.  Having the arguments out of order does
> complicate code which needs to deconstruct closures, but that is not so
> important.
>
> Polymorphism complicates things.  A closure with type pred(float) may be
> passed to a procedure expecting pred(T).  Due to the `float' argument type,
> the closure expects its argument in a float register.  But when passed to the
> procedure, the polymorphic argument type means it would be called with the
> argument in a regular register.
>
> Higher-order insts already contain information about the calling convention,
> without which a higher-order term cannot be called.  We extend higher-order
> insts to include information about the register class required for each
> argument.  For example, we can distinguish between:
>
> 	pred(in) is semidet /* arg regs: [reg_f] */
> and
> 	pred(in) is semidet /* arg regs: [reg_r] */
>
> Using this information, we can create a wrapper around a higher-order
> variable if it appears in a context requiring a different calling convention.
> We do this in a new HLDS pass, called float_regs.m.
>
> Note: Mercury code has a tendency to lose insts for higher-order terms, then
> "recover" them by hacky means.  The float_regs pass depends on higher-order
> insts; it is impossible to create a wrapper for a procedure without knowing
> how to call it.  The float_regs pass will report errors which we otherwise
> accepted, due to higher-order insts being unavailable.  It should be possible
> for the user to adjust the code to satify the pass, though the user may not

s/satify/satisfy/

> understand why it should be necessary.  In most cases, it probably really
> *is* unnecessary.  We may be able to make the float_regs pass more tolerant
> of missing higher-order insts in the future.


...

> compiler/options.m:
> compiler/handle_options.m:
> 	Always enable float registers on low-level C grades when floats are
> 	wider than a word.

s/on low-level/in low-level/

>
> compiler/make_hlds_passes.m:
> 	Always allow double word floats to be stored unboxed in cells on C
> 	grades.
>
> compiler/hlds_goal.m:
> 	Add an extra field to `generic_call' which gives the register class
> 	to use for each argument.  This is set by the float_regs pass.
>
> compiler/prog_data.m:
> 	Add an extra field to `pred_inst_info' which records the register class
> 	to use for each argument.  This is set by the float_regs pass.
>
> compiler/hlds_pred.m:
> 	Add a field to `proc_sub_info' which lists the headvars which must be
> 	passed via regular registers despite their types.
>
> 	Add a field to `pred_sub_info' to record the original unsubstituted
> 	argument types for instance method predicates.
>
> compiler/check_typeclass.m:
> 	In the pred_info of an instance method predicate, record the original
> 	argument types before substiting the type variables for the instance.

s/substiting/substituting/

...

> compiler/notes/todo.html:
> TODO:
> 	Delete one hundred billion year old todos.

Dr. Evil?

...

> diff --git a/compiler/float_regs.m b/compiler/float_regs.m
> new file mode 100644
> index 0000000..2779c6d
> --- /dev/null
> +++ b/compiler/float_regs.m

...

> +:- pred add_arg_regs_in_pred_inst_info(module_info::in, set(inst_name)::in,
> +    list(mer_type)::in, pred_inst_info::in, pred_inst_info::out) is det.
> +
> +add_arg_regs_in_pred_inst_info(ModuleInfo, Seen, ArgTypes, PredInstInfo0,
> +        PredInstInfo) :-
> +    PredInstInfo0 = pred_inst_info(PredOrFunc, Modes0, _, Det),
> +    list.map_corresponding(add_arg_regs_in_mode_2(ModuleInfo, Seen),
> +        ArgTypes, Modes0, Modes),
> +    list.map(ho_arg_reg_for_type, ArgTypes, ArgRegs),
> +    PredInstInfo = pred_inst_info(PredOrFunc, Modes, yes(ArgRegs), Det).
> +
> +:- pred add_arg_regs_in_bound_inst(module_info::in, set(inst_name)::in,
> +    mer_type::in, bound_inst::in, bound_inst::out) is det.
> +
> +add_arg_regs_in_bound_inst(ModuleInfo, Seen, Type, BoundInst0, BoundInst) :-
> +    BoundInst0 = bound_functor(ConsId, ArgInsts0),
> +    (
> +        get_cons_id_non_existential_arg_types(ModuleInfo, Type, ConsId,
> +            ArgTypes)
> +    ->
> +        (
> +            ArgTypes = [],
> +            % When a foreign type overrides a d.u. type, the inst may have
> +            % arguments but the foreign type does not.
> +            ArgInsts = ArgInsts0
> +        ;
> +            ArgTypes = [_ | _],
> +            list.map_corresponding(add_arg_regs_in_inst(ModuleInfo, Seen),
> +                ArgTypes, ArgInsts0, ArgInsts)
> +        )
> +    ;
> +        % XXX handle existentially typed cons_ids
> +        ArgInsts = ArgInsts0

Doesn't that XXX need to be fixed?  (Or is this also an issue with the XXX in
the test case below?)

...

> +insert_reg_wrappers_goal_2(Goal0, Goal, !InstMap, !Info, !Specs) :-
> +    Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
> +    (
> +        GoalExpr0 = unify(_LHS, _RHS, _Mode, _Unification, _Context),
> +        insert_reg_wrappers_unify_goal(GoalExpr0, GoalInfo0, Goal, !InstMap,
> +            !Info, !Specs)
> +    ;
> +        GoalExpr0 = conj(ConjType, Goals0),
> +        (
> +            ConjType = plain_conj,
> +            insert_reg_wrappers_conj(Goals0, Goals, !InstMap, !Info, !Specs)
> +        ;
> +            ConjType = parallel_conj,
> +            list.map_foldl3(insert_reg_wrappers_goal, Goals0, Goals,
> +                !InstMap, !Info, !Specs)
> +        ),
> +        GoalExpr = conj(ConjType, Goals),
> +        Goal = hlds_goal(GoalExpr, GoalInfo0),
> +        % For some reason, the instmap delta for the conjunction as a whole may
> +        % be unreachable even though all of the conjuncts are reachable.
> +        % If we leave the instmap reachable that leads to problems when we
> +        % try to fix up at the end of branching goals.
> +        % XXX figure out why

Do you have an example where this occurs?

...

> +:- pred insert_reg_wrappers_unify_goal(hlds_goal_expr::in(goal_expr_unify),
> +    hlds_goal_info::in, hlds_goal::out, instmap::in, instmap::out,
> +    lambda_info::in, lambda_info::out,
> +    list(error_spec)::in, list(error_spec)::out) is det.
> +
> +insert_reg_wrappers_unify_goal(GoalExpr0, GoalInfo0, Goal, !InstMap, !Info,
> +        !Specs) :-
> +    GoalExpr0 = unify(LHS, RHS0, Mode, Unification0, Context),
> +    (
> +        Unification0 = construct(CellVar, ConsId, Args0, UniModes0,
> +            HowToConstruct, IsUnique, SubInfo),
> +        (
> +            RHS0 = rhs_functor(_, IsExistConstruct, _)
> +        ;
> +            RHS0 = rhs_var(_),
> +            unexpected($module, $pred, "construct rhs_var")
> +        ;
> +            RHS0 = rhs_lambda_goal(_, _, _, _, _, _, _, _, _),
> +            unexpected($module, $pred, "construct rhs_lambda_goal")
> +        ),
> +        (
> +            Args0 = [],
> +            ModuleInfo = !.Info ^ li_module_info,
> +            update_construct_goal_instmap_delta(ModuleInfo, CellVar, ConsId,
> +                Args0, GoalInfo0, GoalInfo, !InstMap),
> +            Goal = hlds_goal(GoalExpr0, GoalInfo)
> +        ;
> +            Args0 = [_ | _],
> +            GoalContext = goal_info_get_context(GoalInfo0),
> +            insert_reg_wrappers_construct(CellVar, ConsId, Args0, Args,
> +                UniModes0, UniModes, Maybe, !.InstMap, GoalContext,
> +                !Info, !Specs),

s/Maybe/MaybeWrappedGoals/ there and below (and in the body of
insert_reg_wrappers_construct).

> +            (
> +                Maybe = yes(WrapGoals),
> +                list.foldl(update_instmap, WrapGoals, !InstMap),
> +                ModuleInfo = !.Info ^ li_module_info,
> +                update_construct_goal_instmap_delta(ModuleInfo, CellVar,
> +                    ConsId, Args, GoalInfo0, GoalInfo1, !InstMap),
> +                RHS = rhs_functor(ConsId, IsExistConstruct, Args),
> +                Unification = construct(CellVar, ConsId, Args, UniModes,
> +                    HowToConstruct, IsUnique, SubInfo),
> +                GoalExpr1 = unify(LHS, RHS, Mode, Unification, Context),
> +                Goal1 = hlds_goal(GoalExpr1, GoalInfo1),
> +                conj_list_to_goal(WrapGoals ++ [Goal1], GoalInfo1, Goal)
> +            ;
> +                Maybe = no,
> +                ModuleInfo = !.Info ^ li_module_info,
> +                update_construct_goal_instmap_delta(ModuleInfo, CellVar,
> +                    ConsId, Args0, GoalInfo0, GoalInfo, !InstMap),
> +                Goal = hlds_goal(GoalExpr0, GoalInfo)
> +            )
> +        )
> +    ;
> +        Unification0 = deconstruct(CellVar, ConsId, Args, UniModes0,
> +            CanFail, CanCGC),
> +        % Update the uni_modes of the deconstruction using the current inst of
> +        % the deconstructed var. Recompute the instmap delta from the new
> +        % uni_modes if changed.
> +        ModuleInfo = !.Info ^ li_module_info,
> +        list.length(Args, Arity),
> +        instmap_lookup_var(!.InstMap, CellVar, CellVarInst0),
> +        inst_expand(ModuleInfo, CellVarInst0, CellVarInst),
> +        (
> +            get_arg_insts(CellVarInst, ConsId, Arity, ArgInsts),
> +            list.map_corresponding(uni_mode_set_rhs_final_inst,
> +                ArgInsts, UniModes0, UniModes),
> +            UniModes \= UniModes0
> +        ->
> +            Unification = deconstruct(CellVar, ConsId, Args, UniModes,
> +                CanFail, CanCGC),
> +            GoalExpr1 = unify(LHS, RHS0, Mode, Unification, Context),
> +            Goal1 = hlds_goal(GoalExpr1, GoalInfo0),
> +            do_recompute_atomic_instmap_delta(Goal1, Goal, !.InstMap, !Info)
> +        ;
> +            Goal = hlds_goal(GoalExpr0, GoalInfo0)
> +        ),
> +        update_instmap(Goal, !InstMap)
> +    ;
> +        Unification0 = assign(ToVar, FromVar),
> +        Delta0 = goal_info_get_instmap_delta(GoalInfo0),
> +        instmap_lookup_var(!.InstMap, FromVar, Inst),
> +        instmap_delta_set_var(ToVar, Inst, Delta0, Delta),
> +        goal_info_set_instmap_delta(Delta, GoalInfo0, GoalInfo1),
> +        Goal = hlds_goal(GoalExpr0, GoalInfo1),
> +        update_instmap(Goal, !InstMap)
> +    ;
> +        Unification0 = simple_test(_, _),
> +        Goal = hlds_goal(GoalExpr0, GoalInfo0),
> +        update_instmap(Goal, !InstMap)
> +    ;
> +        Unification0 = complicated_unify(_, _, _),
> +        unexpected($module, $pred, "complicated_unify")
> +    ).
> +

...

> +:- pred rebuild_cell_inst(module_info::in, instmap::in, cons_id::in,
> +    list(prog_var)::in, mer_inst::in, mer_inst::out) is det.
> +
> +rebuild_cell_inst(ModuleInfo, InstMap, ConsId, Args, Inst0, Inst) :-
> +    (
> +        Inst0 = bound(Uniq, BoundInsts0),
> +        list.map(rebuild_cell_bound_inst(InstMap, ConsId, Args),
> +            BoundInsts0, BoundInsts),
> +        Inst = bound(Uniq, BoundInsts)
> +    ;
> +        Inst0 = ground(Uniq, higher_order(PredInstInfo0)),
> +        PredInstInfo0 = pred_inst_info(PredOrFunc, Modes, _, Determinism),
> +        ( ConsId = closure_cons(ShroudedPredProcId, _EvalMethod) ->
> +            proc(PredId, _) = unshroud_pred_proc_id(ShroudedPredProcId),
> +            module_info_pred_info(ModuleInfo, PredId, PredInfo),
> +            pred_info_get_arg_types(PredInfo, ArgTypes),
> +            list.length(Args, NumArgs),
> +            list.det_drop(NumArgs, ArgTypes, MissingArgTypes),
> +            list.map(ho_arg_reg_for_type, MissingArgTypes, ArgRegs),
> +            PredInstInfo = pred_inst_info(PredOrFunc, Modes, yes(ArgRegs),
> +                Determinism),
> +            Inst = ground(Uniq, higher_order(PredInstInfo))
> +        ;
> +            Inst = Inst0
> +        )
> +    ;
> +        % XXX do we need to handle any of these other cases?

Yes, you also need to apply it to the any(Uniq, higher_order(_)) case.
It should do the same thing as the ground case.

I think you also need to handle insts nested inside the constrained_inst_vars/2
case.  (And maybe elsewhere as well.)

> +        ( Inst0 = free
> +        ; Inst0 = free(_)
> +        ; Inst0 = any(_, _)
> +        ; Inst0 = ground(_, none)
> +        ; Inst0 = not_reached
> +        ; Inst0 = constrained_inst_vars(_, _)
> +        ; Inst0 = defined_inst(_)
> +        ),
> +        Inst = Inst0
> +    ;
> +        Inst0 = abstract_inst(_, _),
> +        unexpected($module, $pred, "abstract_inst")
> +    ;
> +        Inst0 = inst_var(_),
> +        unexpected($module, $pred, "inst_var")
> +    ).
> +

...

> diff --git a/compiler/hlds_goal.m b/compiler/hlds_goal.m
> index bc07c7f..a1ee238 100644
> --- a/compiler/hlds_goal.m
> +++ b/compiler/hlds_goal.m
> @@ -123,6 +123,12 @@
>                 % this field is junk until after mode analysis.
>                 gcall_modes         :: list(mer_mode),
>
> +                gcall_reg_types     :: maybe(list(ho_arg_reg)),
> +                                    % The register type to use for each of the
> +                                    % arguments. This is only needed when float
> +                                    % registers exist, and is only set after
> +                                    % the float reg wrappers pass.
> +
>                 % The determinism of the call.
>                 gcall_detism        :: determinism


I suggest defining a purpose-specific type rather than using maybe(list(ho_arg_regs)
there, something like:

:- type reg_type_info
 	---> 	reg_types_not_needed
 	;	reg_types(list(ho_arg_reg)).

You should also use the purpose specific type in prog_data.m.

...

> diff --git a/compiler/hlds_pred.m b/compiler/hlds_pred.m
> index 1d533fc..e5a873c 100644
> --- a/compiler/hlds_pred.m
> +++ b/compiler/hlds_pred.m

...

> @@ -2335,6 +2353,11 @@ attribute_list_to_attributes(Attributes, Attributes).
>                 % Allocation of variables to stack slots.
>                 stack_slots                 :: stack_slots,
>
> +                % The head variables which must be forced to use regular
> +                % registers in the calling convention, despite having type

  ... by the calling convention

> +                % float. This is only meaningful with float registers.
> +                reg_r_headvars              :: set_of_progvar,
> +
>                 % The calling convention of each argument: information computed
>                 % by arg_info.m (based on the modes etc.) and used by code
>                 % generation to determine how each argument should be passed.

...

> diff --git a/compiler/inst_match.m b/compiler/inst_match.m
> index 998526a..d57f479 100644
> --- a/compiler/inst_match.m
> +++ b/compiler/inst_match.m
> @@ -799,7 +799,7 @@ ho_inst_info_matches_initial(HOInstInfoA, none, _, _, !Info) :-
>     \+ ho_inst_info_is_nonstandard_func_mode(!.Info ^ imi_module_info,
>         HOInstInfoA).
> ho_inst_info_matches_initial(none, higher_order(PredInstB), _, Type, !Info) :-
> -    PredInstB = pred_inst_info(pf_function, ArgModes, _Det),
> +    PredInstB = pred_inst_info(pf_function, ArgModes, _, _Det),
>     Arity = list.length(ArgModes),
>     PredInstA = pred_inst_info_standard_func_mode(Arity),
>     pred_inst_matches_2(PredInstA, PredInstB, Type, !Info).
> @@ -827,11 +827,13 @@ pred_inst_matches_1(PredInstA, PredInstB, MaybeType, ModuleInfo) :-
> :- pred pred_inst_matches_2(pred_inst_info::in, pred_inst_info::in,
>     maybe(mer_type)::in, inst_match_info::in, inst_match_info::out) is semidet.
>
> -pred_inst_matches_2(pred_inst_info(PredOrFunc, ModesA, Det),
> -        pred_inst_info(PredOrFunc, ModesB, Det), MaybeType, !Info) :-
> +pred_inst_matches_2(PredInstA, PredInstB, MaybeType, !Info) :-
> +    PredInstA = pred_inst_info(PredOrFunc, ModesA, _MaybeArgRegsA, Det),
> +    PredInstB = pred_inst_info(PredOrFunc, ModesB, _MaybeArgRegsB, Det),
>     maybe_get_higher_order_arg_types(MaybeType, length(ModesA),
>         MaybeTypes),
>     pred_inst_argmodes_matches(ModesA, ModesB, MaybeTypes, !Info).
> +    % XXX do we need to match the arg reg lists? I don't know how.

Is this ever going to be called after stage 213?  (If not, then you can certainly
ignore them.)

...

> diff --git a/compiler/lambda.m b/compiler/lambda.m
> index adac13d..cf3691c 100644
> --- a/compiler/lambda.m
> +++ b/compiler/lambda.m
> @@ -69,8 +69,16 @@
> :- module transform_hlds.lambda.
> :- interface.
>
> +:- import_module hlds.hlds_goal.
> :- import_module hlds.hlds_module.
> :- import_module hlds.hlds_pred.
> +:- import_module hlds.hlds_rtti.
> +:- import_module mdbcomp.prim_data.
> +:- import_module parse_tree.prog_data.
> +:- import_module parse_tree.set_of_var.
> +
> +:- import_module bool.
> +:- import_module list.
>
> %-----------------------------------------------------------------------------%
>
> @@ -80,6 +88,39 @@
>     is det.
>
> %-----------------------------------------------------------------------------%
> +
> +:- interface.
> +
> +% The following is exported for float_reg.m

Is it really necessary to export it all?  Can lambda_info/0 not be abstract exported,
along with any access predicates required?

> +:- type lambda_info
> +    --->    lambda_info(
> +                li_varset               :: prog_varset,
> +                li_vartypes             :: vartypes,
> +                li_tvarset              :: tvarset,
> +                li_inst_varset          :: inst_varset,
> +                li_rtti_varmaps         :: rtti_varmaps,
> +                li_has_parallel_conj    :: bool,
> +                li_pred_info            :: pred_info,
> +                li_module_info          :: module_info,
> +                % True iff we need to recompute the nonlocals.
> +                li_recompute_nonlocals  :: bool,
> +                % True if we expanded some lambda expressions.
> +                li_have_expanded_lambda :: bool
> +            ).
> +
> +:- type reg_wrapper_proc
> +    --->    reg_wrapper_proc(set_of_progvar)
> +    ;       not_reg_wrapper_proc.
> +
> +:- pred expand_lambda(purity::in, ho_groundness::in,
> +    pred_or_func::in, lambda_eval_method::in, reg_wrapper_proc::in,
> +    list(prog_var)::in, list(mer_mode)::in, determinism::in,
> +    list(prog_var)::in, hlds_goal::in, unification::in,
> +    unify_rhs::out, unification::out,
> +    lambda_info::in, lambda_info::out) is det.
> +

...

> @@ -385,15 +397,12 @@ expand_lambda(Purity, _Groundness, PredOrFunc, EvalMethod, Vars, Modes,
>
>     (
>         Unification0 = construct(Var, _, OrigNonLocals1, UniModes0, _, _, _),
> -        % We use to use OrigVars = OrigNonLocals0 (from rhs_lambda_goal) but
> -        % the order of the variables does not necessarily match UniModes0.
> +        % We use to use OrigVars = OrigNonLocals0 (from the outer
> +        % rhs_lambda_goal) but OrigNonLocals0 may be a reordered version of
> +        % OrigNonLocals1.

The comment should explain *why* OrigNonLocals0 may be reordered, i.e. because
the use of float registers requires that.

>         OrigVars = OrigNonLocals1,
> -        trace [compiletime(flag("lambda_var_order"))] (
> -            list.sort(OrigNonLocals0, SortedOrigNonLocals0),
> -            list.sort(OrigNonLocals1, SortedOrigNonLocals1),
> -            expect(unify(SortedOrigNonLocals0, SortedOrigNonLocals1),
> -                $module, $pred, "OrigNonLocals0 != OrigNonLocals1")
> -        )
> +        expect(unify(sort(OrigNonLocals0), sort(OrigNonLocals1) : list(_)),
> +            $module, $pred, "ConstructArgs != OrigVars")
>     ;
>         ( Unification0 = deconstruct(_, _, _, _, _, _)
>         ; Unification0 = assign(_, _)

...


> diff --git a/compiler/mercury_compile_middle_passes.m b/compiler/mercury_compile_middle_passes.m
> index 3062904..78e7815 100644
> --- a/compiler/mercury_compile_middle_passes.m
> +++ b/compiler/mercury_compile_middle_passes.m
> @@ -70,6 +70,7 @@
> :- import_module transform_hlds.distance_granularity.
> :- import_module transform_hlds.equiv_type_hlds.
> :- import_module transform_hlds.exception_analysis.
> +:- import_module transform_hlds.float_regs.
> :- import_module transform_hlds.granularity.
> :- import_module transform_hlds.higher_order.
> :- import_module transform_hlds.implicit_parallelism.
> @@ -240,6 +241,9 @@ middle_pass(ModuleName, !HLDS, !DumpInfo, !IO) :-
>     maybe_lco(Verbose, Stats, !HLDS, !IO),
>     maybe_dump_hlds(!.HLDS, 210, "lco", !DumpInfo, !IO),
>
> +    maybe_float_reg_wrapper(Verbose, Stats, !HLDS, !IO),
> +    maybe_dump_hlds(!.HLDS, 213, "float_reg_wrapper", !DumpInfo, !IO),

Does this have to be done here, or could it be done later, for example
around stages 300-310?  (Doing it later would mean that the passes between now
and 300 don't need to deal with the extra information you are attaching to insts.)

If there are constraints on when the float_regs pass must occur then it would be
as well to document them here so that the pass isn't accidently shifted around.

...


> diff --git a/compiler/prog_data.m b/compiler/prog_data.m
> index d8033cf..d935f43 100644
> --- a/compiler/prog_data.m
> +++ b/compiler/prog_data.m
> @@ -2073,10 +2073,21 @@ get_type_kind(kinded_type(_, Kind)) = Kind.
>                                     % of the return value as the last element
>                                     % of the list.
>
> +                maybe(list(ho_arg_reg)),
> +                                    % The register type to use for each of the
> +                                    % additional arguments of the pred. This
> +                                    % field is only needed when float registers
> +                                    % exist, and is only set after the float
> +                                    % reg wrappers pass.
> +
>                 determinism         % The determinism of the predicate or
>                                     % function.
>             ).

See my comments in hlds_goal.m.

...

> diff --git a/compiler/prog_type.m b/compiler/prog_type.m
> index a310df6..db27c6e 100644
> --- a/compiler/prog_type.m
> +++ b/compiler/prog_type.m
> @@ -66,6 +66,10 @@
>     pred_or_func::out, lambda_eval_method::out, list(mer_type)::out)
>     is semidet.
>
> +:- pred type_is_higher_order_details_det(mer_type::in, purity::out,
> +    pred_or_func::out, lambda_eval_method::out, list(mer_type)::out)
> +    is det.

I suggest following the convention used in the stdlib and calling that
predicate: det_type_is_higher_order_details.

> +
>     % Succeed if the given type is a tuple type, returning
>     % the argument types.
>     %
> @@ -440,6 +444,17 @@ type_is_higher_order_details(Type, Purity, PredOrFunc, EvalMethod,
>         PredArgTypes = ArgTypes
>     ).
>
> +type_is_higher_order_details_det(Type, !:Purity, !:PredOrFunc, !:EvalMethod,
> +        !:PredArgTypes) :-
> +    (
> +        type_is_higher_order_details(Type, !:Purity, !:PredOrFunc,
> +            !:EvalMethod, !:PredArgTypes)
> +    ->
> +        true
> +    ;
> +        unexpected($module, $pred, "type is not higher-order")
> +    ).
> +
> type_is_tuple(Type, ArgTypes) :-
>     strip_kind_annotation(Type) = tuple_type(ArgTypes, _).

...


> diff --git a/runtime/mercury_ho_call.c b/runtime/mercury_ho_call.c
> index 3cb8e63..a2e517e 100644
> --- a/runtime/mercury_ho_call.c
> +++ b/runtime/mercury_ho_call.c
> @@ -334,8 +334,12 @@ static  MR_Word MR_compare_closures_representation(MR_Closure *x,
> ** returned in registers MR_r1, MR_r2, etc for det and nondet calls or
> ** registers MR_r2, MR_r3, etc for semidet calls.
> **
> -** The placement of the extra input arguments into MR_r3, MR_r4 etc is done by
> -** the code generator, as is the movement of the output arguments to their
> +** When float registers are in effect, float input arguments are passed

s/in effect/enabled/

> +** in MR_f1, MR_f2, etc. Float output arguments are returned in registers
> +** MR_f1, MR_f2, etc.
> +**
> +** The placement of the extra input arguments into MR_rN and MR_fN etc. is done
> +** by the code generator, as is the movement of the output arguments to their
> ** eventual destinations.
> **
> ** Each do_call_closure_N variant is specialized for the case where the number

...

> +%-----------------------------------------------------------------------------%
> +
> +:- type existstruct(T)
> +    --->    some [U] existstruct(pred(T, T), U).
> +
> +:- inst existstruct
> +    --->    existstruct(pred(in, out) is det, ground).
> +
> +% XXX mode checking fails
> +%
> +% :- pred docall_existstruct(existstruct(T)::in(existstruct), T::in, T::out)
> +%     is det.
> +% :- pragma no_inline(docall_existstruct/3).
> +%
> +% docall_existstruct(existstruct(P, _), X, Y) :-
> +%     P(X, Y).

Presumably, because of bug #89 or is this something else?

Julien.
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list