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 |
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:
|
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; } |
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 |
Free forum by Nabble | Edit this page |