MemoryAccess added as separate package in VMMaker SqueakSource project

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

MemoryAccess added as separate package in VMMaker SqueakSource project

David T. Lewis
 
I made a separate package for MemoryAccess (Slang implementation of
sqMemoryAccess.h) and uploaded it to the VMMaker project on SqueakSource.
In the VMMaker project, VMMaker-dtl.110.mcz contains all prerequisites
for MemoryAccess-dtl.1.mcz.

Some support code patches are required to use this for Unix VM generation
(patches attached). See also http://wiki.squeak.org/squeak/6081 for an
overview that may be useful if merging into another VMMaker code base.

MemoryAccess makes no changes to generated VM code until it is enabled
("MemoryAccess enable"), so it can be safely loaded into an image and
used as needed.

I find that using MemoryAccess helps the C compiler find type casting
errors, and produces code that is easier to follow in a debugger. On my
computer, there is no performance difference between the MemoryAccess
slang versus CPP macros in sqMemoryAccess.h.

Dave


MemoryAccessPlatformDiffs.zip (4K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: MemoryAccess added as separate package in VMMaker SqueakSource project

Eliot Miranda-2
 
David,

    can you hum a few bars on how the Smalltalk code looks using this and not using this?

One of the things I hate most about Slang is the exposed bit twiddlng for object headers.  i.e. there is nothing that maps to the level of a C struct with bit fields.  In my Cog Stack interpreter I made a class InterpreterStackPage map to a struct.  this class is only a container.  I'd like to do the same for the object header so in Smalltalk one would have a proper object type that one could send messages like classFormatField and markBit to and have that map down onto a struct bitfield dereference which would improve readability in both Smalltalk and C.  Any thoughts on that?

On Sat, Dec 13, 2008 at 2:24 PM, David T. Lewis <[hidden email]> wrote:
 
I made a separate package for MemoryAccess (Slang implementation of
sqMemoryAccess.h) and uploaded it to the VMMaker project on SqueakSource.
In the VMMaker project, VMMaker-dtl.110.mcz contains all prerequisites
for MemoryAccess-dtl.1.mcz.

Some support code patches are required to use this for Unix VM generation
(patches attached). See also http://wiki.squeak.org/squeak/6081 for an
overview that may be useful if merging into another VMMaker code base.

MemoryAccess makes no changes to generated VM code until it is enabled
("MemoryAccess enable"), so it can be safely loaded into an image and
used as needed.

I find that using MemoryAccess helps the C compiler find type casting
errors, and produces code that is easier to follow in a debugger. On my
computer, there is no performance difference between the MemoryAccess
slang versus CPP macros in sqMemoryAccess.h.

Dave



Reply | Threaded
Open this post in threaded view
|

Re: MemoryAccess added as separate package in VMMaker SqueakSource project

David T. Lewis
 
On Sat, Dec 13, 2008 at 03:07:28PM -0800, Eliot Miranda wrote:
>  
> David,
>     can you hum a few bars on how the Smalltalk code looks using this and
> not using this?

There is no difference at all in the Smalltalk code. Only the generated C
is affected by changing from CPP macros to inlined Slang. See below for an
example.

> One of the things I hate most about Slang is the exposed bit twiddlng for
> object headers.  i.e. there is nothing that maps to the level of a C struct
> with bit fields.  In my Cog Stack interpreter I made a class
> InterpreterStackPage map to a struct.  this class is only a container.  I'd
> like to do the same for the object header so in Smalltalk one would have a
> proper object type that one could send messages like classFormatField and
> markBit to and have that map down onto a struct bitfield dereference which
> would improve readability in both Smalltalk and C.  Any thoughts on that?

Excellent idea. I'm afraid that MemoryAccess does nothing to address it, but
I see no reason that it can't be done, and it would probably be helpful in
migrating object headers from one format to another.

For an example of the effect of memory access macros implemented in Slang,
take as an example ObjectMemory>>accessibleObjectAfter: showing the effect
of longAt() implemented first as a macro, and then as as C code generated
from a method in class MemoryAccess.

=== Smalltalk (slang) source code ===
accessibleObjectAfter: oop
        "Return the accessible object following the given object or
        free chunk in the heap. Return nil when heap is exhausted."
        | obj |
        self inline: false.
        obj := self objectAfter: oop.
        [self oop: obj isLessThan: endOfMemory]
                whileTrue: [(self isFreeObject: obj) ifFalse: [^ obj].
                        obj := self objectAfter: obj].
        ^ nil

=== C code after normal code generation, relying on sqMemoryAccess.h macros ===

/* Return the accessible object following the given object or
        free chunk in the heap. Return nil when heap is exhausted. */

sqInt accessibleObjectAfter(sqInt oop) {
register struct foo * foo = &fum;
    sqInt obj;
    sqInt sz;
    sqInt header;
    sqInt sz1;
    sqInt header1;

        /* begin objectAfter: */
        if (DoAssertionChecks) {
                if ((((usqInt) oop)) >= (((usqInt) foo->endOfMemory))) {
                        error("no objects after the end of memory");
                }
        }
        if (((longAt(oop)) & TypeMask) == HeaderTypeFree) {
                sz1 = (longAt(oop)) & AllButTypeMask;
        } else {
                /* begin sizeBitsOf: */
                header1 = longAt(oop);
                if ((header1 & TypeMask) == HeaderTypeSizeAndClass) {
                        sz1 = (longAt(oop - (BytesPerWord * 2))) & LongSizeMask;
                        goto l2;
                } else {
                        sz1 = header1 & SizeMask;
                        goto l2;
                }
        l2: /* end sizeBitsOf: */;
        }
        obj = (oop + sz1) + (foo->headerTypeBytes[(longAt(oop + sz1)) & TypeMask]);
        while ((((usqInt) obj)) < (((usqInt) foo->endOfMemory))) {
                if (!(((longAt(obj)) & TypeMask) == HeaderTypeFree)) {
                        return obj;
                }
                /* begin objectAfter: */
                if (DoAssertionChecks) {
                        if ((((usqInt) obj)) >= (((usqInt) foo->endOfMemory))) {
                                error("no objects after the end of memory");
                        }
                }
                if (((longAt(obj)) & TypeMask) == HeaderTypeFree) {
                        sz = (longAt(obj)) & AllButTypeMask;
                } else {
                        /* begin sizeBitsOf: */
                        header = longAt(obj);
                        if ((header & TypeMask) == HeaderTypeSizeAndClass) {
                                sz = (longAt(obj - (BytesPerWord * 2))) & LongSizeMask;
                                goto l1;
                        } else {
                                sz = header & SizeMask;
                                goto l1;
                        }
                l1: /* end sizeBitsOf: */;
                }
                obj = (obj + sz) + (foo->headerTypeBytes[(longAt(obj + sz)) & TypeMask]);
        }
        return null;
}

=== C code generated with class MemoryAccess replacing macros in sqMemoryAccess.h ===

/* Return the accessible object following the given object or
        free chunk in the heap. Return nil when heap is exhausted. */

sqInt accessibleObjectAfter(sqInt oop) {
register struct foo * foo = &fum;
    sqInt obj;
    sqInt sz;
    sqInt header;
    sqInt sz1;
    sqInt header1;

        /* begin objectAfter: */
        if (DoAssertionChecks) {
                if ((((usqInt) oop)) >= (((usqInt) foo->endOfMemory))) {
                        error("no objects after the end of memory");
                }
        }
        if (((((sqInt) ((((sqInt *) ((sqMemoryBase) + oop)))[0]))) & TypeMask) == HeaderTypeFree) {
                sz1 = (((sqInt) ((((sqInt *) ((sqMemoryBase) + oop)))[0]))) & AllButTypeMask;
        } else {
                /* begin sizeBitsOf: */
                header1 = ((sqInt) ((((sqInt *) ((sqMemoryBase) + oop)))[0]));
                if ((header1 & TypeMask) == HeaderTypeSizeAndClass) {
                        sz1 = (((sqInt) ((((sqInt *) ((sqMemoryBase) + (oop - (BytesPerWord * 2)))))[0]))) & LongSizeMask;
                        goto l2;
                } else {
                        sz1 = header1 & SizeMask;
                        goto l2;
                }
        l2: /* end sizeBitsOf: */;
        }
        obj = (oop + sz1) + (foo->headerTypeBytes[(((sqInt) ((((sqInt *) ((sqMemoryBase) + (oop + sz1))))[0]))) & TypeMask]);
        while ((((usqInt) obj)) < (((usqInt) foo->endOfMemory))) {
                if (!(((((sqInt) ((((sqInt *) ((sqMemoryBase) + obj)))[0]))) & TypeMask) == HeaderTypeFree)) {
                        return obj;
                }
                /* begin objectAfter: */
                if (DoAssertionChecks) {
                        if ((((usqInt) obj)) >= (((usqInt) foo->endOfMemory))) {
                                error("no objects after the end of memory");
                        }
                }
                if (((((sqInt) ((((sqInt *) ((sqMemoryBase) + obj)))[0]))) & TypeMask) == HeaderTypeFree) {
                        sz = (((sqInt) ((((sqInt *) ((sqMemoryBase) + obj)))[0]))) & AllButTypeMask;
                } else {
                        /* begin sizeBitsOf: */
                        header = ((sqInt) ((((sqInt *) ((sqMemoryBase) + obj)))[0]));
                        if ((header & TypeMask) == HeaderTypeSizeAndClass) {
                                sz = (((sqInt) ((((sqInt *) ((sqMemoryBase) + (obj - (BytesPerWord * 2)))))[0]))) & LongSizeMask;
                                goto l1;
                        } else {
                                sz = header & SizeMask;
                                goto l1;
                        }
                l1: /* end sizeBitsOf: */;
                }
                obj = (obj + sz) + (foo->headerTypeBytes[(((sqInt) ((((sqInt *) ((sqMemoryBase) + (obj + sz))))[0]))) & TypeMask]);
        }
        return null;
}

Reply | Threaded
Open this post in threaded view
|

Object header access (was: MemoryAccess added as separate package in VMMaker SqueakSource project)

David T. Lewis
 
On Sat, Dec 13, 2008 at 10:09:33PM -0500, David T. Lewis wrote:

>  
> On Sat, Dec 13, 2008 at 03:07:28PM -0800, Eliot Miranda wrote:
>
> > One of the things I hate most about Slang is the exposed bit twiddlng for
> > object headers.  i.e. there is nothing that maps to the level of a C struct
> > with bit fields.  In my Cog Stack interpreter I made a class
> > InterpreterStackPage map to a struct.  this class is only a container.  I'd
> > like to do the same for the object header so in Smalltalk one would have a
> > proper object type that one could send messages like classFormatField and
> > markBit to and have that map down onto a struct bitfield dereference which
> > would improve readability in both Smalltalk and C.  Any thoughts on that?
>
> Excellent idea. I'm afraid that MemoryAccess does nothing to address it, but
> I see no reason that it can't be done, and it would probably be helpful in
> migrating object headers from one format to another.

On second thought I'm not so sure this is a such good idea, at least
not with the current approach for slang code generation. When code is
generated from a class outside of the ObjectMemory hierarchy, the mechanism
is hidden and not easily browsed. If there was a new class ObjectHeader,
it would need to be outside of the ObjectMemory or InterpreterPlugin
hierarchies, so the translation would be awkward.

I guess that what's missing is a better notion of class A and class B being
able to generate code for a method in class A without class B being in the
same hierarchy. To some extent, that is done now with the translation
dictionary in CCodeGenerator. That approach could be extended to do code
generation for e.g. a new class ObjectHeader but I'm not sure it would be
an improvement over the current bit twiddling.

Maybe an idiom such as "anObject objectHeader classFormatField" would make
sense if the code generator could be convinced to find the TParseNode parse
tree from class B and include it in the parse tree for a method in class A.

For MemoryAccess, I just slipped the MemoryAccess class in as a superclass
of ObjectMemory and InterpreterPlugin (or remove it from the hierarchy to
"disable" it). It's something of a hack to be sure, but it seems reasonable
to think of ObjectMemory as being implemented "on top of" a memory access
layer (either MemoryAccess or sqMemoryAccess.h as the case may be) in the
same sense that the Interpreter is implemented "on top of" ObjectMemory.

Dave