Files
mercury/compiler/notes/mutables
Julien Fischer 48a8d92585 Fix doubled-up words and spelling.
compiler/*.m:
compiler/notes/*:
    As above.
2021-06-20 20:29:50 +10:00

366 lines
9.9 KiB
Plaintext

C BACKENDS
For non-constant mutables the transformation is as follows:
:- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
===>
:- pragma foreign_decl("C", "
extern <CType> mutable_<varname>;
#ifdef MR_THREAD_SAFE
extern MercuryLock mutable_<varname>_lock;
#endif
").
:- pragma foreign_code("C", "
<CType> mutable_<varname>;
#ifdef MR_THREAD_SAFE
MercuryLock mutable_<varname>_lock;
#endif
").
NOTES:
* The name of the C global corresponding to mutable_<varname> may be
mangled.
* <CType> is chosen on a backend-specific basis. If the value stored
in the mutable is always boxed it is `MR_Word' otherwise it may
be some native type, `MR_Integer', `MR_Float' etc.
:- initialise initialise_mutable_<varname>/0.
:- impure pred initialise_mutable_<varname> is det.
initialise_mutable_<varname> :-
impure pre_initialise_mutable_<varname>,
impure X = <initval>,
impure set_<varname>(X).
:- impure pred pre_initialise_mutable_<varname> is det.
:- pragma foreign_proc("C",
pre_initialise_mutable_<varname>,
[will_not_call_mercury],
"
#ifdef MR_THREAD_SAFE
pthread_init_mutex(&mutable_<varname>_lock, MR_MUTEX_ATTR);
#endif
").
Operations on mutables are defined in terms of the following four predicates.
Note that they are all marked `thread_safe' in order to avoid having
to acquire the global lock.
:- impure pred unsafe_set_<varname>(<vartype>::in(<varinst>)) is det.
:- pragma foreign_proc("C",
unsafe_set_<varname>(X::in(<varinst>)),
[will_not_call_mercury, thread_safe],
"
mutable_<varname> = X;
").
:- semipure pred unsafe_get_<varname>(<vartype>::out(<varinst>)) is det.
:- pragma foreign_proc("C",
unsafe_get_<varname>(X::out(<varinst>)),
[promise_semipure, will_not_call_mercury, thread_safe],
"
X = mutable_<varname>;
").
:- impure lock_<varname> is det.
:- pragma foreign_proc("C",
lock_<varname>,
[will_not_call_mercury, promise_pure],
"
#ifdef MR_THREAD_SAFE
MR_LOCK(&mutable_<varname>_lock, \"lock_<varname>/0\");
#endif
").
:- impure unlock_<varname> is det.
:- pragma foreign_proc("C",
unlock_<varname>,
[will_not_call_mercury, promise_pure],
"
#ifdef MR_THREAD_SAFE
MR_UNLOCK(&mutable_<varname>_lock, \"unlock_<varname>/0\");
#endif
").
The other operations are all defined in Mercury using the above predicates:
:- impure pred set_<varname>(<vartype>::in(<varinst>)) is det.
set_<varname>(X) :-
impure lock_<varname>,
impure unsafe_set_<varname>(X),
impure unlock_<varname>.
:- semipure pred get_<varname>(<vartype>::out(<varinst>)) is det.
get_<varname>(X) :-
promise_semipure (
impure lock_<varname>
semipure unsafe_get_<varname>(X),
impure unlock_<varname>
).
etc.
For thread-local mutables the transformation is as above, with the following
differences:
:- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [thread_local]).
===>
:- pragma foreign_decl("C", "extern MR_Unsigned mutable_<varname>;").
:- pragma foreign_code("C", "MR_Unsigned mutable_<varname>;").
:- pragma foreign_proc("C",
pre_initialise_mutable_<varname>,
[will_not_call_mercury],
"
mutable_<varname> = MR_new_thread_local_mutable_index();
").
:- pragma foreign_proc("C",
unsafe_set_<varname>(X::in(<varinst>)),
[will_not_call_mercury, thread_safe],
"
MR_set_thread_local_mutable(<type>, X, mutable_<varname>);
").
:- pragma foreign_proc("C",
unsafe_get_<varname>(X::out(<varinst>)),
[promise_semipure, will_not_call_mercury, thread_safe],
"
MR_get_thread_local_mutable(<type>, X, mutable_<varname>);
").
:- pragma foreign_proc("C",
lock_<varname>,
[will_not_call_mercury, promise_pure],
"
/* blank */
").
:- pragma foreign_proc("C",
unlock_<varname>,
[will_not_call_mercury, promise_pure],
"
/* blank */
").
For constant mutables the transformation is:
:- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [constant]).
===>
:- pragma foreign_decl("C", "extern <CType> mutable_<varname>;").
:- pragma foreign_code("C", "<CType> mutable_<varname>;").
:- pred get_<varname>(<vartype>::out(<varinst>)) is det.
:- pragma foreign_proc("C",
get_<varname>(X::out(<varinst>)),
[will_not_call_mercury, promise_pure, thread_safe],
"
X = mutable_<varname>;
").
In order to initialise constant mutables we generate the following:
:- impure pred secret_initialization_only_set_<varname>(
<vartype>::in(<varinst>)) is det.
:- pragma foreign_proc("C",
secret_initialization_only_set_<varname>(X::in(<varinst>)),
[will_not_call_mercury],
"
mutable_<varname> = X;
").
:- initialise initialise_mutable_<varname>/0.
:- impure pred initialise_mutable_<varname> is det.
initialise_mutable_<varname> :-
impure X = <initval>,
impure secret_initialization_only_set_<varname>(X).
%-----------------------------------------------------------------------------%
JAVA BACKEND
For non-constant mutables the transformation is as follows:
:- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
===>
:- pragma foreign_code("Java", "
static <JType> mutable_<varname>;
").
:- initialise initialise_mutable_<varname>/0.
:- impure pred initialise_mutable_<varname> is det.
initialise_mutable_<varname> :-
impure X = <initval>,
impure set_<varname>(X).
<JType> is either `int' or `java.lang.Object' (all other types).
Operations on mutables are defined in terms of the following two predicates.
They are actually "safe": by the Java specification, 32-bit variables are
loaded/stored atomically. Doubles and longs may be treated as two 32-bit
variables, but Mercury does not expose them yet. The predicates are named so
to minimise the differences with the C backends.
:- impure pred unsafe_set_<varname>(<vartype>::in(<varinst>)) is det.
:- pragma foreign_proc("Java",
unsafe_set_<varname>(X::in(<varinst>)),
[will_not_call_mercury, thread_safe],
"
mutable_<varname> = X;
").
:- semipure pred unsafe_get_<varname>(<vartype>::out(<varinst>)) is det.
:- pragma foreign_proc("Java",
unsafe_get_<varname>(X::out(<varinst>)),
[promise_semipure, will_not_call_mercury, thread_safe],
"
X = mutable_<varname>;
").
If mutable_<varname> has the type `java.lang.Object' a cast is required
after the code above, to cast X to the correct type. This is handled by
the MLDS code generator.
For thread-local mutables the transformation is as follows:
:- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
===>
:- pragma foreign_code("Java", "
static java.lang.ThreadLocal<JType> mutable_<varname> =
new java.lang.InheritableThreadLocal<JType>();
").
:- pragma foreign_proc("Java",
unsafe_set_<varname>(X::in(<varinst>)),
[will_not_call_mercury, thread_safe],
"
mutable_<varname>.set(X);
").
:- pragma foreign_proc("Java",
unsafe_get_<varname>(X::out(<varinst>)),
[promise_semipure, will_not_call_mercury, thread_safe],
"
X = mutable_<varname>.get();
").
<JType> is `java.lang.Integer' or `java.lang.Object'.
The above predicates are called by these predicates, again to minimise
differences with the C backends:
:- impure pred set_<varname>(<vartype>::in(<varinst>)) is det.
set_<varname>(X) :-
impure unsafe_set_<varname>(X).
:- semipure pred get_<varname>(<vartype>::out(<varinst>)) is det.
get_<varname>(X) :-
semipure unsafe_get_<varname>(X).
For constant mutables the transformation is:
:- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [constant]).
===>
:- pragma foreign_code("Java", "
static <JType> mutable_<varname>;
").
:- pred get_<varname>(<vartype>::out(<varinst>)) is det.
:- pragma foreign_proc("Java",
get_<varname>(X::out(<varinst>)),
[will_not_call_mercury, promise_pure, thread_safe],
"
X = mutable_<varname>;
").
In order to initialise constant mutables we generate the following:
:- impure pred secret_initialization_only_set_<varname>(
<vartype>::in(<varinst>)) is det.
:- pragma foreign_proc("Java",
secret_initialization_only_set_<varname>(X::in(<varinst>)),
[will_not_call_mercury],
"
mutable_<varname> = X;
").
:- initialise initialise_mutable_<varname>/0.
:- impure pred initialise_mutable_<varname> is det.
initialise_mutable_<varname> :-
impure X = <initval>,
impure secret_initialization_only_set_<varname>(X).
%-----------------------------------------------------------------------------%
C# BACKEND
The C# implementation is analogous to the Java implementation, except for
thread-local mutables, which are transformed as follows:
:- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
===>
:- pragma foreign_code("C#", "
private static int mutable_<varname>;
").
:- initialise initialise_mutable_<varname>/0.
:- impure pred initialise_mutable_<varname> is det.
initialise_mutable_<varname> :-
impure pre_initialise_mutable_<varname>,
impure X = <initvalue>,
impure set_<varname>(X).
:- pragma foreign_proc("C#",
pre_initialise_mutable_<varname>,
[will_not_call_mercury],
"
mutable_<varname> = runtime.ThreadLocalMutables.new_index();
").
:- pragma foreign_proc("C#",
unsafe_set_<varname>(X::in(<varinst>)),
[will_not_call_mercury, thread_safe],
"
runtime.ThreadLocalMutables.set(mutable_<varname>, X);
").
:- pragma foreign_proc("C#",
unsafe_get_<varname>(X::out(<varinst>)),
[promise_semipure, will_not_call_mercury, thread_safe],
"
X = runtime.ThreadLocalMutables.get(mutable_<varname>);
").