This is the mail archive of the
kawa@sources.redhat.com
mailing list for the Kawa project.
Re: Handling the Perl VM's "list" op with Kawa IR
- To: "Bradley M. Kuhn" <bkuhn at ebb dot org>
- Subject: Re: Handling the Perl VM's "list" op with Kawa IR
- From: Per Bothner <per at bothner dot com>
- Date: 15 Dec 2000 16:20:35 -0800
- Cc: kawa at sources dot redhat dot com
- References: <20001215170447.G12393@ebb.org>
"Bradley M. Kuhn" <bkuhn@ebb.org> writes:
> Perl's "virtual machine" has basically two components:
>
> * a syntax tree of OP codes
> * a temporary array, used to hold values between one operation to the
> next.
The high-level question is: Can you and do you want to "compile away"
the temporay array? Compare the Java expression stack. To compile
Java bytecodes you have two options: (1) Compile to a environment that
preserves the stack, and does stack operations at run-time; or (2)
compile away the stack, by simulating pushes and pops at compile-time.
Better JVM compilers do (2). So the question is: Can you do something
similar with the Perl temporary array - an dif so, should you? Have
people worked on compiling Perl IR to C code? If so, what did they do?
If the temporary array is predictable way, only within a given statement,
and you know when "marks" are pushed/popped, then presumably you can
compile away the temporary array. I assume you can, since there is
nothing in the Perl *semantics* that uses this temporary array - right?
Of course it doesn't follow that you *should* compile away the
temporary array, even if you can. The reason is that if this array
(stack) persists for a while, you may save on object allocation.
An idea:
Consider a builtin operation Op that can be called in both scalar and
vector context. We can implement this using a class like:
public class Op extends PerlOp
{
public Object applyN(...) { return scalar result; }
public void apply(..., java.util.Stack result)
{ evaluate, leave results on result; }
}
Then print something that takes takes a list argument would be
implemented as:
public class Print extends PerlCmd
{
public void apply(java.util.Stack input)
{
print results from stack;
}
}
But how do we compile this? The secret is to use Kawa's Target
classes. Look at gnu.expr.ConsumerTarget, which does something similar.
You basically need a new class:
public class PerlListTarget extends Target
{
Declaration temporaryArray;
public void compileFromStack(Compilation comp, Type stackType)
{
// Argument of type stackType has been evaluated as scalar.
Generate code to push argument on temporaryArray;
}
}
Then for something like the qw or listoperation:
public class ListOp extends PerlOp implements Inlineable
{
public void applyN(...);
public void compile (ApplyExp exp, Compilation comp, Target target)
{
if (target instanceof PerlListTarget)
{
// Generate code to push each operand of target's temporaryArray:
for each operand
operand.compile(comp, target);
}
else
{
// Generate code to evaluate list in scalar context.
}
}
}
For print, you could do:
public class Print extends PerlCmd implements Inlineable
{
public void apply(...) { }
public void compile (ApplyExp exp, Compilation comp, Target target)
{
Declaration tempArray = ...; // Perhaps a parameter? or allocate new?
PerlListTarget subTarget = new PerlListTarget(tempArray);
for each sub-expression
subExp.compile(comp, subTarget); // Leave on tempArray
Generate code to call Print.apply at runtime, passing it the
Vector corresponding to tempArray;
}
}
This is pretty dense, I admit. I've left a bit vague the actual calling
convention to be used at run-time. For that, I suggest taking a look
at the Consumer and ConsumeProc classes in gnu.kawa.util. It seems
plausible to define a PerlConsumer class that would implement Consumer.
It would manage the temporary array,and the distinction between scalar
and list context. Then Perl builtins and sub-rotines may be extensions
of ConsumeProc.
--
--Per Bothner
per@bothner.com http://www.bothner.com/~per/