I've been reviewing the PMC proposals on hand to finish off the PMC PDD. Here are a few thoughts on the matter, for mailing list discussion. Overall, the current structure of PMCs is sound, and allows a good bit of flexibility.

I'd like to do away with the PMC_DATA_IN_EXT flag so the "DPOINTER *data;" struct element is always in the PMC struct instead of being sometimes in the PMC struct and sometimes in the PMC_EXT struct. Access is all hidden behind macros anyway, so the change shouldn't affect anything outside of include/parrot/pobj.h. And, PMC_DATA_IN_EXT is currently set to 1 in include/parrot/pobj.h, and never unset anywhere in the repository.

On variable-sized PMCs, I generally agree with the goals the proposal is trying to accomplish, but I want to tackle these goals in a simpler way. One of the goals mentioned for the proposal was to shrink down the size of PMCs, but it's hard to get much smaller than a struct containing 1 struct, 1 pointer to a struct, and a pointer to the vtable, where both of the 2 contained structs can be null.

So, rather than making the core PMC struct variable sized, I want to make one of its contained structs variable sized. This is already a common practice, storing a structure pointer in PMC_data. I also want to eliminate the union value from the pobj_t struct. The concept that a PMC contains either a buffer, 2 pointers, 2 ints, a float val, or a string val, and that these all must overlap in storage is shortsighted. One thing that would convince me otherwise is the argument that it's good to have some core PMC data accessible without a pointer dereference. Standing against that is the fact that we're currently allocating (a small piece of) memory for every PMC that may never be used.

This is the current implementation:

    struct PMC {
        pobj_t obj;
        VTABLE *vtable;
 #if ! PMC_DATA_IN_EXT
        DPOINTER *data;
 #endif
        struct PMC_EXT *pmc_ext;
    };

---
    typedef struct pobj_t {
        UnionVal u;
        Parrot_UInt flags;
    } pobj_t;

---
    struct PMC_EXT {
 #if PMC_DATA_IN_EXT
        DPOINTER *data;
 #endif
        PMC *_metadata;
        struct _Sync *_synchronize;
        PMC *_next_for_GC;
    };

This is an alternate structure I'm considering:

    struct PMC {
        Parrot_UInt flags;
        DPOINTER *data;          /* Modifiable for each PMC */
        VTABLE *vtable;
        struct PMC_EXT *pmc_ext; /* auxiliary data */
    };

---
    struct PMC_EXT {
        PMC *_roles;                /* runtime roles */
        PMC *_metadata;             /* properties, replaced by a role */
        struct _Sync *_synchronize; /* [to be deprecated, see STM] */
        PMC *_next_for_GC;          /* [GC hack, may be replaced] */
    };


(flags could move into the PMC_EXT struct, depending on whether we think it will be frequently accessed. It's a question of speed of access versus size of the core PMC allocation.)

Combining this with the proposal to give PMCs a vtable function responsible for constructing the PMC: calling the C<new> opcode constructs the core PMC struct for all PMCs, and then calls the C<construct> (or C<create>) vtable function. Each PMC that overrides C<construct> creates its own struct to store in C<data>. The C<default> PMC's C<construct> does nothing. C<construct> is separated from C<init> so they can be overridden separately.

To accommodate low-level role composition, the C<data> struct for a PMC isn't defined directly in C, it's defined with a bit of Pmc2c syntactic sugar. Pmc2c takes all the struct members defined in the PMC and in any compile-time roles and builds them into one struct for the PMC. Also included are any struct members defined in inherited PMCs. (A PMC is not permitted to redefine any struct members defined in parents, and roles may not contain struct members of the same name. Yes, this is rather strict, but it is C.)

For backward compatibility, some of the first defined roles will provide the simple structures that the union of the current implementation can contain without being a union type (a buffer, 2 pointers, 2 ints, a float val, or a string val).

Low-level PMCs can also have roles composed at runtime, but these are entirely different creatures. They are added through the C<add_role> vtable function and live in a PMC data structure that hangs off the PMC_EXT struct. Runtime composed roles are Role PMCs or subclasses of the Role PMC.

Thoughts and comments welcome.

Allison

Reply via email to