Blog Closed

This blog has moved to Github. This page will not be updated and is not open for comments. Please go to the new site for updated content.

Friday, February 19, 2010

Argument Passing Refactors

On tuesday it was decided that the next round of PCC refactors should start this sprint. Allison created a branch for the task, after having created a detailed tasklist for it in the previous weeks. To understand what the point of the refactor is I first need to describe the system as it is now.

When we make a function or method call in Parrot, we use fancy-schmance PIR that looks like this:

($P0, $I0) = foo(1, 2.0, $S0)

This looks all well and good, and certainly makes the programmers happy to see familiar syntax. Internally, this call is anything but pretty. In PIR, we can construct a call using a more verbose syntax with some compiler directives:

.const 'Sub' foo = 'foo'
.begin_call
.set_arg 1
.set_arg 2.0
.set_arg $S0
.call foo
.get_result $P0
.get_result $I0
.end_call

This is much worse in terms of syntax and verbosity, but at least it makes good explicit sense: We find the sub object, we get the arguments, we call the function, then we get the result values. This seems all well and good, but this isn't the bottom layer of the cake. These things above are IMCC compiler directives, not actual bytecode. The actual bytecode of the file looks much more like this:

$P97 = find_name "foo"
$P98 = new ['String']
$P98 = "0x0010,0x0013,0x0001"
set_args $P98, 1, 2.0, $S0
$P99 = new ['FixedIntegerArray']
$P99[0] = 0x02
$P99[1] = 0x00
get_results $P99, $P0, $I0
invokecc $P97

There are a few things we can immediately see about this code listing that are a little bit obnoxious. I'll list them out in no particular order:
  1. get_results is called before invokecc. This means we are preparing to retrieve results before we've even called the function. The actual process of copying returns from the callee into the caller happens inside the callee. This creates a fundamental disconnect in a system that is supposed to be continuation-based.
  2. set_params takes a string PMC containing a string of hex values containing flags corresponding to each argment. Inside set_params, that string needs to be painstakingly parsed to get a proper array of flags.
  3. set_params and get_results opcodes both take variadic argument lists. It's impossible for something like a bytecode disassembler to figure out how much memory the opcode takes up without reading the first argument and determining how many flags are specified.
Allison's current branch is intending to address #1. She's going to reverse the logic so that results are collected after the returns are passed. This will allow us to unify the code paths that handle function calls and returns into a single function. Hopefully this will lead to a few optimizations.

#2 and #3 above are a little disconcerting for a variety of reasons. First, we have all the necessary information about the call at compile time. We have the number and types of the arguments, and all the associated flags that govern what they are and how they are used. All this information is passed directly to set_args, which uses it to built a CallContext PMC.

To recap, we have all the information we need to build the CallContext PMC at compile time.

So let's ignore for a second how stupid it is to iterate character-by-character over a String PMC to get the flags, when it's obvious that the results mechanism uses a much better suited integer array for the same purpose. The question isn't how we store the flags in the bytecode, it's why we're bothering to store them separately at all? Why don't we create a CallContext PMC constant, or maybe some new kind of "CallArguments" PMC constant at compile time, cache it in the bytecode in exactly the form we need the data to be in, and use that when performing calls?

The question is a rhetorical one, and I've opened a ticket to suggest we bring a little bit of sanity to this code and maybe see some serious performance wins as well. Since Allison is already working on this code, it should be pretty easy to build on that momentum and fix the last major wart that the calling code has.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.