[m-rev.] for review: implement mutables for erlang

Julien Fischer juliensf at csse.unimelb.edu.au
Tue Jun 12 15:31:51 AEST 2007


On Tue, 12 Jun 2007, Peter Wang wrote:

> Estimated hours taken: 15
> Branches: main
>
> Add support for mutables in the Erlang backend.
>
> compiler/make_hlds_passes.m:
> 	Refactor code that inserts mutable-related items for C backend.
>
> 	Insert items for mutable-related predicates for Erlang.
>
> compiler/prog_item.m:
> 	Add item_mutable inst.
>
> compiler/prog_mutable.m:
> 	Document the mutable transformation for Erlang.
>
> library/erlang.m:
> 	New module.  This initially contains a server process that will run
> 	in the background to handle messages relating to mutables in Erlang.
> 	In future it may hold other things for the Erlang backend.
>
> library/library.m:
> 	Add `erlang' module to the standard library.

Discussed in person.

> compiler/elds_to_erlang.m:
> 	Make the Erlang main wrapper start and stop the Erlang global server.
>
> tests/hard_coded/Mmakefile:
> tests/hard_coded/float_gv.m:
> tests/hard_coded/sub-modules/non_word_mutable.m:
> 	Make these test cases work in Erlang.

Those test cases are for specific regressions.  For historical reasons
the basic mutable tests are for trailed mutables.  You should probably
add another one for untrailed mutables.

Also, the mutable section of the reference manual probably needs
updating.  (At least, add a commented out version of the documentation
for Erlang.)

...

> +%-----------------------------------------------------------------------------%
> +%
> +% Erlang mutables
> +%
> +
> +:- pred add_erlang_mutable_preds(item::in(item_mutable), string::in,
> +    import_status::in, import_status::out, prog_context::in,
> +    module_info::in, module_info::out, qual_info::in, qual_info::out,
> +    list(error_spec)::in, list(error_spec)::out) is det.
> +
> +add_erlang_mutable_preds(Item, TargetMutableName,
> +        !Status, Context, !ModuleInfo, !QualInfo, !Specs) :-
> +    module_info_get_name(!.ModuleInfo, ModuleName),
> +    Item = item_mutable(MutableName, _Type, InitTerm, Inst,
> +        MutAttrs, MutVarset),
> +    IsConstant = mutable_var_constant(MutAttrs),
> +    (
> +        IsConstant = yes,
> +        InitSetPredName = mutable_secret_set_pred_sym_name(ModuleName,
> +            MutableName),
> +        add_erlang_constant_mutable_access_preds(TargetMutableName,
> +            ModuleName, MutableName, Inst,
> +            !Status, Context, !ModuleInfo, !QualInfo, !Specs)
> +    ;
> +        IsConstant = no,
> +        InitSetPredName = mutable_set_pred_sym_name(ModuleName,
> +            MutableName),
> +        add_erlang_mutable_user_access_preds(TargetMutableName,
> +            ModuleName, MutableName, MutAttrs, Inst,
> +            !Status, Context, !ModuleInfo, !QualInfo, !Specs)
> +    ),
> +    add_erlang_mutable_initialisation(ModuleName, MutableName,
> +        MutVarset, InitSetPredName, InitTerm,
> +        !Status, Context, !ModuleInfo, !QualInfo, !Specs).
> +
> +    % Add the access predicates for constant mutables.
>     %
> -:- pred get_mutable_global_foreign_decl_defn(module_info::in, mer_type::in,
> -    string::in, bool::in, mutable_thread_local::in, item::out, item::out)
> -    is det.
> +:- pred add_erlang_constant_mutable_access_preds(string::in,
> +    module_name::in, string::in, mer_inst::in,
> +    import_status::in, import_status::out, prog_context::in,
> +    module_info::in, module_info::out, qual_info::in, qual_info::out,
> +    list(error_spec)::in, list(error_spec)::out) is det.
>
> -get_mutable_global_foreign_decl_defn(ModuleInfo, Type, TargetMutableName,
> -        IsConstant, IsThreadLocal, Decl, Defn) :-
> -    module_info_get_globals(ModuleInfo, Globals),
> -    globals.lookup_bool_option(Globals, mutable_always_boxed, AlwaysBoxed),
> -    globals.get_target(Globals, Backend),
> +add_erlang_constant_mutable_access_preds(TargetMutableName,
> +        ModuleName, MutableName, Inst,
> +        !Status, Context, !ModuleInfo, !QualInfo, !Specs) :-
> +    varset.new_named_var(varset.init, "X", X, ProgVarSet),
> +    InstVarSet = varset.init,
> +    Attrs = default_attributes(lang_erlang),
> +    set_purity(purity_pure, Attrs, ConstantGetAttrs0),
> +    set_thread_safe(proc_thread_safe, ConstantGetAttrs0, ConstantGetAttrs),
> +
> +    % Getter.
> +    GetCode = erlang_mutable_get_code(TargetMutableName),
> +    ConstantGetForeignProc = pragma_foreign_proc(
> +        ConstantGetAttrs,
> +        mutable_get_pred_sym_name(ModuleName, MutableName),
> +        pf_predicate,
> +        [pragma_var(X, "X", out_mode(Inst), native_if_possible)],
> +        ProgVarSet,

I suggest adding a comment to prog_data.box_policy mentioning that
the distinction it makes is only applicable to the C backends.
It's probably also worth mentioning here that for Erlang it doesn't
matter what the box policy is.

> +        InstVarSet,
> +        fc_impl_ordinary(GetCode, yes(Context))
> +    ),
> +    ConstantGetClause = item_pragma(compiler(mutable_decl),
> +        ConstantGetForeignProc),
> +    add_item_clause(ConstantGetClause, !Status, Context, !ModuleInfo,
> +        !QualInfo, !Specs),
> +
> +    % Secret setter.
> +    SetCode = erlang_mutable_set_code(TargetMutableName),
> +    ConstantSetForeignProc = pragma_foreign_proc(Attrs,
> +        mutable_secret_set_pred_sym_name(ModuleName, MutableName),
> +        pf_predicate,
> +        [pragma_var(X, "X", in_mode(Inst), native_if_possible)],
> +        ProgVarSet,
> +        InstVarSet,
> +        fc_impl_ordinary(SetCode, yes(Context))
> +    ),
> +    ConstantSetClause = item_pragma(compiler(mutable_decl),
> +        ConstantSetForeignProc),
> +    add_item_clause(ConstantSetClause, !Status, Context, !ModuleInfo,
> +        !QualInfo, !Specs).
> +
> +    % Add the access predicates for a non-constant mutable.
> +    % If the mutable has the `attach_to_io_state' attribute then add the
> +    % versions of the access preds that take the I/O state as well.
> +    %
> +:- pred add_erlang_mutable_user_access_preds(string::in,
> +    module_name::in, string::in, mutable_var_attributes::in, mer_inst::in,
> +    import_status::in, import_status::out, prog_context::in,
> +    module_info::in, module_info::out, qual_info::in, qual_info::out,
> +    list(error_spec)::in, list(error_spec)::out) is det.
> +
> +add_erlang_mutable_user_access_preds(TargetMutableName,
> +        ModuleName, MutableName, MutAttrs, Inst,
> +        !Status, Context, !ModuleInfo, !QualInfo, !Specs) :-
> +    IsThreadLocal = mutable_var_thread_local(MutAttrs),
> +    Attrs = default_attributes(lang_erlang),
> +    varset.new_named_var(varset.init, "X", X, ProgVarSet0),
> +
> +    %
> +    % Construct the semipure get predicate.
> +    %
> +    set_purity(purity_semipure, Attrs, GetAttrs0),
> +    set_thread_safe(proc_thread_safe, GetAttrs0, GetAttrs),
>     (
> -        Backend = target_c,
> -        (
> -            IsThreadLocal = mutable_not_thread_local,
> -            TypeName = global_foreign_type_name(AlwaysBoxed, lang_c,
> -                ModuleInfo, Type)
> -        ;
> -            IsThreadLocal = mutable_thread_local,
> -            %
> -            % For thread-local mutables, the variable holds an index into an
> -            % array.
> -            %
> -            TypeName = "MR_Unsigned"
> +        IsThreadLocal = mutable_not_thread_local,
> +        GetCode = erlang_mutable_get_code(TargetMutableName)
> +    ;
> +        IsThreadLocal = mutable_thread_local,
> +        % XXX this will need to change

because ...

> +        GetCode = "X = get({'MR_thread_local_mutable', " ++
> +            TargetMutableName ++ "})"
> +    ),
> +    GetPredName = mutable_get_pred_sym_name(ModuleName, MutableName),
> +    GetForeignProc = pragma_foreign_proc(GetAttrs,
> +        GetPredName,
> +        pf_predicate,
> +        [pragma_var(X, "X", out_mode(Inst), native_if_possible)],
> +        ProgVarSet0,
> +        varset.init, % Inst varset.
> +        fc_impl_ordinary(GetCode, yes(Context))
> +    ),
> +    GetClause = item_pragma(compiler(mutable_decl), GetForeignProc),
> +    add_item_clause(GetClause, !Status, Context, !ModuleInfo, !QualInfo,
> +        !Specs),
> +
> +    %
> +    % Construct the impure set predicate.
> +    %
> +    set_purity(purity_impure, Attrs, SetAttrs0),
> +    set_thread_safe(proc_thread_safe, SetAttrs0, SetAttrs),
> +    (
> +        IsThreadLocal = mutable_not_thread_local,
> +        SetCode = erlang_mutable_set_code(TargetMutableName)
> +    ;
> +        IsThreadLocal = mutable_thread_local,
> +        % XXX this will need to change

because ... (I imagine for the same reason as above, so just a pointer
to that.)

> +        SetCode = "put({'MR_thread_local_mutable', " ++
> +            TargetMutableName ++ "}, X)"
> +    ),
> +    SetPredName = mutable_set_pred_sym_name(ModuleName, MutableName),
> +    SetForeignProc = pragma_foreign_proc(SetAttrs,
> +        SetPredName,
> +        pf_predicate,
> +        [pragma_var(X, "X", in_mode(Inst), native_if_possible)],
> +        ProgVarSet0,
> +        varset.init, % Inst varset.
> +        fc_impl_ordinary(SetCode, yes(Context))
> +    ),
> +    SetClause = item_pragma(compiler(mutable_decl), SetForeignProc),
> +    add_item_clause(SetClause, !Status, Context, !ModuleInfo, !QualInfo,
> +        !Specs),
> +
> +    IOStateInterface = mutable_var_attach_to_io_state(MutAttrs),
> +    (
> +        IOStateInterface = yes,
> +        varset.new_named_var(ProgVarSet0, "IO", IO, ProgVarSet),
> +        Ctxt = context_init,
> +
> +        % Construct the pure get predicate.
> +        % This just calls the semipure get predicate with a promise_pure
> +        % around it.
> +        CallSemipureGet = call_expr(GetPredName, [variable(X, Context)],
> +            purity_semipure) - Context,
> +        IOGetBody = promise_purity_expr(dont_make_implicit_promises,
> +            purity_pure, CallSemipureGet) - Context,
> +
> +        IOGetClause = item_clause(
> +            compiler(mutable_decl),
> +            ProgVarSet,
> +            pf_predicate,
> +            GetPredName,
> +            [variable(X, Ctxt), variable(IO, Ctxt), variable(IO, Ctxt)],
> +            IOGetBody
>         ),
> +
> +        add_item_clause(IOGetClause, !Status, Context, !ModuleInfo, !QualInfo,
> +            !Specs),
> +
> +        % Construct the pure set predicate.
>         %

...

> +        % We just call the impure version and attach a promise_pure
> +        % pragma to the predicate.  (The purity pragma was added during
> +        % stage 2.)
> +        CallImpureSet = call_expr(SetPredName, [variable(X, Context)],
> +            purity_impure) - Context,
> +        IOSetClause = item_clause(
> +            compiler(mutable_decl),
> +            ProgVarSet,
> +            pf_predicate,
> +            SetPredName,
> +            [variable(X, Ctxt), variable(IO, Ctxt), variable(IO, Ctxt)],
> +            CallImpureSet
>         ),
> -
> -        DeclBody = string.append_list([
> -            "extern ", TypeName, " ", TargetMutableName, ";\n" | LockDecl]),
> -        Decl = item_pragma(compiler(mutable_decl),
> -            pragma_foreign_decl(lang_c, foreign_decl_is_exported, DeclBody)),
>
> -        DefnBody = string.append_list([
> -            TypeName, " ", TargetMutableName, ";\n" | LockDefn]),
> -        Defn = item_pragma(compiler(mutable_decl),
> -            pragma_foreign_code(lang_c, DefnBody))
> -    ;
> -        ( Backend = target_il
> -        ; Backend = target_java
> -        ; Backend = target_asm
> -        ; Backend = target_x86_64
> -        ; Backend = target_erlang
> -        ),
> -        sorry(this_file, "we don't yet support mutables for non-C backends")
> +        add_item_clause(IOSetClause, !Status, Context, !ModuleInfo, !QualInfo,
> +            !Specs)
> +    ;
> +        IOStateInterface = no
>     ).
> -
> -:- func global_foreign_type_name(bool, foreign_language, module_info, mer_type)

...

> +% The transformation for thread_local mutables has not been decided (we need a
> +% way for spawned processes to inherit all the thread-local mutable values of
> +% its parent process, but a child process in Erlang does not automatically
> +% inherit its parent process's process dictionary).
> +%

and (presumably) the erlang grade does not support trailed mutables at
at all.

The changes to the compiler look fine - Pete may want to check the
Erlang bits.

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