Name: chromatic
Member since: 2000-03-20 04:52:20
Last Login: 2008-05-07 05:43:30
Homepage: http://wgz.org/chromatic/
Notes:
chromatic @ wgz . org
I'm the Open Source and Free Software editor at the O'Reilly Network. I'd like to discuss good software development practices more frequently; we'll see if it happens.
Perl 6 Design Minutes for 07 May 2008
<summary type="xhtml">The Perl 6 design team met by phone on 07 May 2008. Larry, Allison, Patrick, Will, Jerry, Nicholas, Jesse, and chromatic attended.
Allison:
c:
Allison:
c:
Patrick:
c:
Jesse:
Larry:
nofat rulePatrick:
Jerry:
Patrick:
Jerry:
c:
Nicholas:
state implementation of any languageJesse:
Patrick:
Jesse:
Nicholas:
Jesse:
Will:
Jerry:
Patrick:
Nicholas:
Jerry:
Jesse:
Nicholas:
Allison:
Jesse:
Allison:
Will:
Patrick:
Allison:
Will:
Allison:
Jerry:
Patrick:
Will:
Patrick:
Jerry:
Patrick:
Larry:
Patrick:
Larry:
c:
Will:
Jesse:
Good Error Messages are Important
Parrot r27355 was fun to write.
One of the persistent error messages Parrot emits for compiler writers is
Null PMC access in invoke(). If you've had your hands deep in the
guts of Parrot, you know what that means -- you tried to call a Sub PMC when
you don't have a Sub PMC, you have no PMC. (If you don't know what that means,
this entry is for you.)
Sometimes this means that there's a problem in Parrot. We've fixed almost all of those problems though, so the error usually comes from elsewhere. If you're writing a compiler, or running a compiler built on Parrot, the error usually means "You tried to call a function that doesn't exist."
Parrot's optimizer does something interesting at the end of compilation time. You've probably heard that Parrot's compiler, IMCC, translates PIR into PBC. That is, it turns source code into bytecode, which Parrot can either serialize t to disk or execute immediately. That bytecode is just a chunk of linear data in memory. It's not really a data structure. (Okay, it's a C array, but that doesn't make it a data structure.)
After IMCC has finished building a standalone chunk of bytecode, it performs a constant fixup phase. The notable part of this phase is that it edits the bytecode in place to replace all named invocations of functions known at fixup time with offset invocations.
The previous code looks something like:
invoke known_function
null # padding
null # padding
If IMCC has already seen known_function by this time, the
direct invocation of known_function can continue. There's no
runtime lookup necessary; all functions already compiled and ready are
available in the bytecode.
If IMCC hasn't seen that function, runtime lookup is necessary, and so this function replaces the bytecode earlier with the equivalent of:
<nobr> <wbr></nobr>.local pmc func
func = find_name 'unknown_function'
invoke func
(I've simplified what actually happens slightly, because the concepts are more important than the details. Hopefully you see why the padding is necessary. If not, just imagine trying to splice additional opcodes into what may presumably be a lengthy C array -- like I said, barely a data structure.)
The problem with this second form occurs when find_name returns
a NULL PMC, which it can legitimately do. In that case, the
invoke opcode tries to invoke a NULL PMC and fails, and Parrot
throws an exception saying "There's nothing here to invoke." There's the error
message.
It's clear why that happens, but it's not useful. It would be more
useful to see the name of the function you tried to invoke in the
error message. Unfortunately, by the time Parrot calls the invoke
op, that name is long gone.
My first idea was to rewrite the dynamic lookup form into something resembling:
<nobr> <wbr></nobr>.local pmc func
func = find_name 'unknown_function'
if defined func goto call_it
die "Can't invoke 'unknown_function'"
call_it:
invoke func
Unfortunately, I didn't have the space in the bytecode stream to insert that many ops, and I had no desire to move chunks of memory around in that C array. I could have added more padding after an invocation, but to be fair I'm only mostly sure that it exists there in the first place.
I had room for one op with a destination PMC and a string constant argument.
I added an experimental op called find_sub_not_null which does the
same thing as find_name but throws an exception which includes
the requested name if Parrot can't find a PMC of that name.
This isn't entirely an ideal situation. It's a special case op, and I prefer to remove ops where possible. It's also nearly code duplication, though it's effectively three lines of code in an op, which isn't awful. I still want to be able to perform these kinds of transformations in PBC itself, but we need a different way to generate PBC and perform op-level transformations in PIR before we can do this effectively.
There are always tradeoffs, though. Doing this check in C is slightly faster than doing it in PIR. The standard Perl 5 rule of optimization applies even in Parrot -- the fewer ops, the mostly faster you can go. As well, I was able to improve the warning message today, rather than at some point in the future when we have better PBC optimization possibilities.
After all, I can always remove this op in the future.
What are You Going to Do, SMS at Me?
<summary type="xhtml">For years, I've thought that the only thing sillier than complaining about disaffection and how the world really should work differently in IRC was to sign silly Internet petitions about it. Now I realize that people who feel compelled to register their righteous indignation in 141 characters of chatspeak matter least.
Here's a quarter, kid. Go buy a postcard.
What a GC Bug Looks Like in Parrot
<summary type="xhtml">Every so often someone reports weird behavior in #parrot, and someone says "Hey, that looks like a GC bug!"
Most of them aren't. (Most of them lately seem to be that we're changing the way bytecode works, and we don't have all of the dependencies for all of the generated PBC files correct, so you have to run make realclean and rebuild.)
While adding the vtable override cache the other day, I did create a forehead-slapper of GC bug, but I caught it before I checked in the code. How did I know it was a GC bug? Easy.
The Class PMC itself contains pointers to several other PMCs and GC entities, including the name of the class and its corresponding namespace. I added a pointer to a Hash PMC which maps the names of vtable overrides to Sub PMCs.
I remember thinking at the time "Hey, it's just a cache. I don't have to mark it during the mark phase explicitly. All of the Subs it refers to will stay alive as long as their namespaces live. That's easy."
When I ran the tests, I saw a weird error about not being able to perform a
keyed index PMC lookup on a Key PMC. I set a breakpoint on the
real_exception function (which reports these kinds of errors) in
the debugger, and the backtrace showed that the cause of the call was my cache
lookup function.
"That's weird," I thought. Then I realized what I had done.
My line of thinking was correct in that I don't have to mark all of the PMCs contained in the cache PMC. They're already reachable from the rootset through the namespace. The GC won't collect them.
The problem is that the cache itself -- the Hash PMC -- is only reachable through the Class PMC. Unless it gets marked as live, the GC will reclaim its header and put it on the free list again.
The Class PMC still has a pointer to that header, but the next PMC allocated from the GC which uses that header will overwrite the PMC's information, effectively morphing my lovely cache into something else. In this case, my Hash PMC turned into a Key PMC.
Usually they're not this obvious, but I've gone through all of the PMCs in Parrot to make sure they mark their contained GCable entities appropriately.
Perl 6 Design Minutes for 23 April 2008
The Perl 6 design team met by phone on 23 April 2008. Larry, Allison, Patrick, Jerry, Will, Jesse, Nicholas, and chromatic attended.
Jerry:
Patrick:
Jerry:
Nicholas:
Jesse:
Jerry:
Larry:
Jerry:
Larry:
Allison:
Patrick:
Allison:
Patrick:
Will:
Allison:
Will:
c:
Patrick:
c:
Jesse:
Patrick:
c:
Patrick:
Will:
c:
Patrick:
Will:
Nicholas:
c:
Jesse:
c:
Jesse:
c:
Jerry:
return
Allison:
Will:
return, break, and continue
Allison:
Patrick:
return type exception, it grabs the arguments, does what it has to, and then does a Parrot return
Allison:
Patrick:
handled
Allison:
Patrick:
return in for the April 15 milestone we're behind on
Jerry:
Allison:
c:
Allison:
chromatic certified others as follows:
Others have certified chromatic as follows:
[ Certification disabled because you're not logged in. ]
FOAF updates: Trust rankings are now exported, making the data available to other users and websites. An external FOAF URI has been added, allowing users to link to an additional FOAF file.
Keep up with the latest Advogato features by reading the Advogato status blog.
If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!