parrotcode: PMCs | |
Contents | Documentation |
docs/pdds/pddXX_pmc.pod - PMCs
Proposal
$Revision$
This document defines Parrot Magic Cookies (PMCs).
[[ maybe rename PMC to PO (Parrot Objects) or such to reduce confusion with Perl5's PMC (compiled .pm files). ]]
This document uses OPMC
,
when speaking of "old" PMCs of Parrot Version 0.4.6 or less.
PMC
is the new layout as proposed in this document.
An attribute
is otherwise also known as a field
or structure element,
but I'm using attribute
here because the difference of PMCs and Parrot Objects should be minimized.
PMCs are Parrot's low-level objects implemented in C.
PMCs are small non-resizable variable-sized structures.
The PMC
itself is the common part of all PMCs.
The per-PMC payload holds PMC-specific attributes.
+---------------+
| vtable | # common PMC attribute
+---------------+
| flags | # common PMC attribute
+---------------+
| attrib_1 | # "user" defined part
| ... |
+---------------+
#define THE_PMC \
VTABLE *vtable; \
UINTVAL flags
An Integer Value PMC could be defined with:
struct VInt_PMC {
THE_PMC;
INTVAL val;
};
Such PMC definitions are typically private to the .pmc files. All access to PMCs shall be through VTABLE functions or methods. OTOH some widely used PMCs might export their attributes for public use and are then part of the Parrot API.
A typical VInt
vtable function would look like this:
INTVAL get_integer()
VInt_PMC *me = (VInt_PMC*)SELF; # [1]
return me->val;
}
The OPMC
can be defined in terms of a PMC by rearranging the structure elements.
[1] The PMC compiler could provide this line automagically and define a convenience variable ME
similar to the current SELF
.
PMCs are created via VTABLE_new
or variants of _new
. It's up to the PMC to initialize it's attributes. new
is a class method, i.e. it's called with the PMC's class
as SELF
.
PMC* new() {
VInt_PMC *me = new_bufferlike_header(INTERP, sizeof(VInt_PMC));
me->val = 0;
return (PMC*)me;
}
[[ rename new_bufferlike_header
to something more meaningful ]]
The vtable can provide a pointer to the sized header pool to possibly speedup allocation.
PMCs with a non-default new
method are PMCs, The old scheme via pmc_new
and VTABLE_init
provides a fallback of creating OPMCs
.
All PMCs that refer to other PMCs have a 3rd mandatory attribute _next_for_GC
, used for garbage collection, The presence of this attribute is signaled by the flag bit PObj_is_PMC_EXT_FLAG
.
+---------------+
| vtable |
+---------------+
| flags |
+---------------+
| _next_for_GC |
+---------------+
| ... |
+---------------+
PMCs do not support properties universally, If properties are still desired, these can be implemented in one of the following ways:
[[ TODO define something canonical ]]
Each PMC that wants this extra hash can just provide an attribute for it and implement the property vtable functions.
This will be a Hash, indexed by the PMC's address, containing the property Hash. An additional flag can be provided, if such a property hash exists for a PMC. During collection of a PMC, this hash is invalidated too.
A transparent Ref
PMC can point to a structure holding the original PMC and the property Hash.
PMCs do no support locking universally. Creating sharable PMCs at runtime (from standard PMCs) is again done by transparent Refs like SharedRef
or STMRef
.
If needed, we can define shared PMCs by allocating the _Sync
structure in front of the PMC:
+---------------+
| struct |
| _Sync |
+---------------+ <--- pmc points here
| vtable |
+---------------+
| flags |
+---------------+
This works of course only, if PMCs are created as shared
in the first place. The presence of the _Sync
structure is stated by a PMC flag bit.
PMCs (like current OPMCs), which may morph themselves, and thereby change their vtable and the meaning of their attributes shall use a union of the desired attributes, e.g.:
struct Integer_PMC {
THE_PMC;
union {
INTVAL int_val;
FLOATVAL num_val;
STRING *str_val;
} u;
};
(none)
TODO pdd02_vtables.pod
TODO pddXX_interfaces.pod
TODO pddXX_classes.pod
TODO pddXX_objects.pod
TODO pddXX_cstruct.pod [2]
[2] PMCs need a class object that defines their attributes to properly allow subclassing. The attribute definition is held by a CStruct
PMC, the meta class of all Parrot PMCs. It's a list of attribute names, their types, and possibly the offsets in the PMC structure. See also the UnManangedStruct
PMC.
Current OPMCs are too rigid: mostly either too small or too big. A lot of information is hanging off secondary malloced structures like PMC_sub
in the Sub
OPMC.
But more importantly, OPMCs don't properly allow subclassing. E.g.
cl = subclass 'Hash', 'PGE::Match'
is currently done by creating a ParrotClass
. When instantiate, this is a "hidden" __value
element as first attribute, which is a pointer to the hash parent PMC. This is creating internal structures which aren't compatible, because the object attributes are differently arranged. That is, above subclassing is mainly: PGE::Match
hasa Hash
instead of isa, when it comes to attribute relationship.
This limitation prevents further implementation of already (at least partially) documented APIs, like the Compiler
one.
A Compiler
object is either a Parrot Sub
like PGE::P6Regex
or a builtin that is NCI
compiler like PIR
. But Sub
and NCI
objects are that different that even currently needed attributes aren't consistently arranged (e.g. multi_sig
or namespace_stash
). Creating proper compiler objects like PIR_Compiler
with common and needed Compiler
attributes isn't possible now.
[[ Well, with another one or two indirections all can be implemented, but that's just adding to code complexity. ]]
Please note that the mentioned Compiler
PMC ist just one of many PMCs that exhibit the same problem.
In combination with a proper metaclass for PMCs, PMCs and "real" Parrot objects should be able to work together seemlessly.
Due to reduced memory consumption and reduced allocation of secondary helper structures, this change will very likely speed up Parrot performance slightly to moderate. No negative performance impact is forseeable due to these changes.
Parrot core needs very little changes to be able to deal with differently sized PMCs. All the GC infrastructure is already there for Buffer_like
objects, which are managed in sized header pools.
TBD is:
Rearrange PObj/Buffer/PMC struct items in such away that item #1 is vtable, #2 is flags, rest is per PObj. The easierst way to achieve this ist to waste one word and give Buffers and empty vtable.
Then remove the Buffer/PMC discerns from GC code, just treat them alike, but honor the PObj_is_PMC_FLAG.
This should be all to be able to build and GC PMCs of arbitrary albeit fixed size.
Changing PMCs to the new scheme can be done as needed and isn't mandatory.
OPMCs
attribute access is currently already done through C macros, like PMC_int_val
or PMC_struct_val
. These macros can cast the passed pointer to (OPMC*)
, so that all these OPMCs
will still be working. New PMCs shall use explicit and more verbose attribute names, which don't collide with present OPMC
attributes.
There'll be no implications to existing PASM or PIR code nor to existing dynamic PMCs.
I've in another document (PMC.pod) already layed out a PMC scheme optimized for generational garbage collection. The PMC layout is using also differently but fixed sized user parts of PMCs, but these are subject of one more indirection. If we see the need for optimized GC, this PMC scheme can still be implemented. We probably could take provisions that such a change is not too intrusive by cleverly using the PMC compiler and/or C macros like the proposed ME
in [1] above.
The payload of PMCs in this scheme is hanging off a pmc_body
pointer:
+---------------+
| pmc_body | --> body (buffer) memory
+---------------+
| vtable |
+---------------+
| flags |
+---------------+
The implementation of PMC
might take into consideration that the PMC layout could change further.
|