Smuggling Data in Pointers
undiscoveredfeatures.com
undiscoveredfeatures.com
The base level of the tree wasn't really a node DOM but rather a stream of document events (start element, text, end element, etc). We put these in a binary tree that was balanced as a splay tree. To drive memory usage down I reduced the {parent, left, right} pointers down to {child, next, left_child_bit, next_is_parent_bit}. 'child' pointed to the the first child. I knew if it was the left or the right child based on the left_child_bit. 'next' pointed either to the sibling or the parent, depending on the 'next_is_parent_bit'. However, to really realize the savings, I needed to hide these bits in the pointers themselves.
Other tricks that we used to save memory:
1) Put rare data into a hash table indexed off of the 'this' pointer of the object. Have a bit to indicate if it was there or not. We called these lookaside pointers.
2) Embed objects in other objects to save pointers back and forth. The embedded object would do math on its this pointer (based on bits for which embedded member it is) to get the this pointer of the parent object.
I'm not sure, but I suspect that one of the reasons I think that current IE is slower now than it should be is that a lot of these optimizations are no longer necessary and cause more problems than they solve.
Crashes back in Windows 95 days suddenly make a lot more sense to me...
For example, when tearing down a page, we would go ahead and release the laster of the references to the ActiveMovie COM objects. That component was using a separate thread and wanted to shut it down cleanly during clean up. It used windows messaging to communicate to the thread and had to run a message loop. That message loop would also dispatch messages to our top level window. If the wrong message came in at the wrong time in this situation we would have code dealing with a semi-destructed data structure and it would crash. I dealt with a lot of these issues by being very defensive when calling out to anything. Things like lots of null checks, saving references to be released until the end, etc. Of course there was a perf cost for each of these that we monitored closely.
I don't think that any of these were the root of security problems. Those were more due to the impedance mismatches at the level of the shell/browser host and the URL deliver services.
http://en.wikipedia.org/wiki/Tagged_pointer
They are commonly used in interpreter implementations to distinguish between pointers and integers (if the LSB is 0 then the value is a pointer, otherwise (v >> 1) is an integer value)
Furthermore, for thunks of algebraic data types with only a few alternatives (e.g. booleans, the Maybe type) the tag will encode which of the alternatives the pointer actually points to. So if you are case scrutinising a boolean you can decide which branch to jump to by inspecting the pointer only with no memory access.
This last trick doesn't work if the number of alternatives is > 3 (for 2 byte alignment) because there aren't enough bits available.
Further, on the Amiga, any sensible pointers were generally 4-aligned anyway (you couldn't read words or longs from odd addresses and I think reading longs from 2-aligned addresses was slower, too, so nobody wanted to do that) so, theoretically, if you were to abuse this to the maximum effect you could've stashed 10 bits of excess payload data into an address: 8 bits in the bits 23-31 and 2 bits in 0-1 of an address. Any pointer was internally just a 32-bit address register value so you certainly had bits to spare!
I almost mentioned the fact that pointers were pretty much always aligned on the 68k, but I never really head of anyone using those bits to store anything. The top-8 had the "nice" property that the hardware did the masking for you.
I do remember that certain parts of the Amiga OS (particularly in the file system, I believe) used "BPTRs" which were essentially pointers shifted down 2 bits -- in other words, the word index instead of the individual byte index. Normal pointers were called "APTRs" by the code that used BPTRs.
I'm sure this was borrowed from a lisp implementation.
00 = Integer 01 = Pointer
With 00 as the integer tag, integer addition and subtraction can be done directly without masking off the tags. Multiplication and division need the tags masked, but are slow anyway. The tag can also be stripped off for free on any architecture that offers BASE + OFFSET addressing (just subtract 1 from the desired constant offset).
Chakra uses the bottom most bit to distinguish SMIs from pointers.
Quoting LWN:
"The anticipatory, deadline, and CFQ I/O schedulers all employ rbtrees to track requests; the packet CD/DVD driver does the same. The high-resolution timer code uses an rbtree to organize outstanding timer requests. The ext3 filesystem tracks directory entries in a red-black tree. Virtual memory areas (VMAs) are tracked with red-black trees, as are epoll file descriptors, cryptographic keys, and network packets in the "hierarchical token bucket" scheduler."
http://en.wikipedia.org/wiki/Mac_OS_memory_management#32-bit...
https://secure.wikimedia.org/wikipedia/en/wiki/X86-64#Virtua...
But yes, not for ahead of time compiled code...
Even excluding large ram increases, presumably the current (effective) 48bit address space limitation on x86_64 is likely to go away, and then ASLR is going to make it hard to guarantee that your high bits are clear.
Atomic lock free data structures is one example. Tagging the pointers can be used notify that a node is going to be deleted in order to avoid the ABA problem (as mentioned in the article). Tagging pointers is absolutely essential for more complex lock free data structures, like doubly linked lists.
This can also be used in a garbage collector to add a few bits of metadata. In a GC you can (and probably should) decide to use memory objects with proper alignment, which leaves you with a few bits to store data in. A perfect solution to implement something like Lisp cons cells.
Search for OBJC_IS_TAGGED_PTR in http://opensource.apple.com/source/objc4/objc4-493.9/runtime...