Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Send indirect calls through invokedynamic.
Not expecting a performance change from this, but it makes code gen a
bit simpler/better/uniformer.
  • Loading branch information
jnthn committed Apr 27, 2013
1 parent 1cc2d3e commit 20547c3
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 24 deletions.
53 changes: 29 additions & 24 deletions src/vm/jvm/QAST/Compiler.nqp
Expand Up @@ -1111,7 +1111,7 @@ sub process_args($qastcomp, @children, $il, $first, :$inv_temp) {
# Return callsite index (which may create it if needed).
return [$*CODEREFS.get_callsite_idx(@callsite, @argnames), $arg_array];
}
sub process_args_onto_stack($qastcomp, @children, $il, :$obj_first) {
sub process_args_onto_stack($qastcomp, @children, $il, :$obj_first, :$inv_first) {
# Make sure we do positionals before nameds.
my @pos;
my @named;
Expand All @@ -1129,7 +1129,7 @@ sub process_args_onto_stack($qastcomp, @children, $il, :$obj_first) {
my @argnames;
my int $i := 0;
while $i < +@order {
my $arg_res := $i == 0 && $obj_first
my $arg_res := $i == 0 && ($obj_first || $inv_first)
?? $qastcomp.as_jast(@order[$i], :want($RT_OBJ))
!! $qastcomp.as_jast(@order[$i]);
$il.append($arg_res.jast);
Expand All @@ -1146,15 +1146,17 @@ sub process_args_onto_stack($qastcomp, @children, $il, :$obj_first) {
nqp::push(@arg_jtypes, jtype($arg_res.type));
}

my int $flags := 0;
if @order[$i].flat {
$flags := @order[$i].named ?? 24 !! 16;
}
elsif @order[$i].named -> $name {
$flags := 8;
nqp::push(@argnames, $name);
unless $i == 0 && $inv_first {
my int $flags := 0;
if @order[$i].flat {
$flags := @order[$i].named ?? 24 !! 16;
}
elsif @order[$i].named -> $name {
$flags := 8;
nqp::push(@argnames, $name);
}
nqp::push(@callsite, arg_type($type) + $flags);
}
nqp::push(@callsite, arg_type($type) + $flags);

$i++;
}
Expand Down Expand Up @@ -1190,23 +1192,26 @@ QAST::OperationsJAST.add_core_op('call', sub ($qastcomp, $node) {

# Otherwise, it's an indirect call.
else {
# Compile the thing to invoke.
# Ensure we have a thing to invoke.
nqp::die("A 'call' node must have a name or at least one child") unless +@($node) >= 1;
my $invokee := $qastcomp.as_jast($node[0], :want($RT_OBJ));
$il.append($invokee.jast);

# Process arguments.
my @argstuff := process_args($qastcomp, @($node), $il, $node.name eq "" ?? 1 !! 0);

# Proces arguments, making sure first one is an object (since that is
# the thing to invoke).
my @argstuff := process_args_onto_stack($qastcomp, @($node), $il, :inv_first);
my $cs_idx := @argstuff[0];
$*STACK.spill_to_locals($il);

# Emit the call.
$*STACK.obtain($il, $invokee);
$il.append(JAST::PushIndex.new( :value($cs_idx) ));
$il.append(JAST::Instruction.new( :op('aload'), @argstuff[1] ));

# Emit the call, using the same thread context trick. The first thing
# will be invoked.
$il.append(JAST::Instruction.new( :op('aload_1') ));
$il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS, 'invoke',
'Void', $TYPE_SMO, 'Integer', "[$TYPE_OBJ", $TYPE_TC ));
$*STACK.obtain($il, |@argstuff[1]) if @argstuff[1];
$il.append(JAST::InvokeDynamic.new(
'indcall', 'V', @argstuff[2],
'org/perl6/nqp/runtime/IndyBootstrap', 'indcall',
[
JAST::PushIndex.new( :value($cs_idx) )
]
));
}

# Load result onto the stack, unless in void context.
Expand Down Expand Up @@ -1234,7 +1239,7 @@ QAST::OperationsJAST.add_core_op('callmethod', -> $qastcomp, $node {
# calling.
if $node.name ne '' {
# Process arguments and force them into locals.
my @argstuff := process_args_onto_stack($qastcomp, @children, $il);
my @argstuff := process_args_onto_stack($qastcomp, @children, $il, :obj_first);
my $cs_idx := @argstuff[0];
$*STACK.spill_to_locals($il);

Expand Down
60 changes: 60 additions & 0 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java
Expand Up @@ -240,6 +240,66 @@ public static void lexotic_s(long target, SixModelObject boxType, ThreadContext
throw throwee;
}

public static CallSite indcall(Lookup caller, String _, MethodType type, int csIdx) {
try {
/* Look up indirect call invoker method. */
MethodType resType = MethodType.methodType(void.class,
MutableCallSite.class, int.class, ThreadContext.class,
SixModelObject.class, Object[].class);
MethodHandle res = caller.findStatic(IndyBootstrap.class, "indcallInvoker", resType);

/* Create a mutable callsite, and curry the resolver with it and
* the sub name. */
MutableCallSite cs = new MutableCallSite(type);
cs.setTarget(MethodHandles
.insertArguments(res, 0, cs, csIdx)
.asCollector(Object[].class, type.parameterCount() - 2)
.asType(type));

/* Produce callsite. */
return cs;
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

public static void indcallInvoker(MutableCallSite cs, int csIdx,
ThreadContext tc, SixModelObject invokee, Object... args) {
/* Resolve callsite descriptor. */
CallSiteDescriptor csd = csIdx >= 0
? tc.curFrame.codeRef.staticInfo.compUnit.callSites[csIdx]
: Ops.emptyCallSite;

/* Get the code ref. */
CodeRef cr;
if (invokee instanceof CodeRef) {
cr = (CodeRef)invokee;
}
else {
InvocationSpec is = invokee.st.InvocationSpec;
if (is == null)
throw ExceptionHandling.dieInternal(tc, "Can not invoke this object");
if (is.ClassHandle != null)
cr = (CodeRef)invokee.get_attribute_boxed(tc, is.ClassHandle, is.AttrName, is.Hint);
else
cr = (CodeRef)is.InvocationHandler;
}

/* Make the call. */
try {
cr.staticInfo.mh.invokeExact(tc, cr, csd, args);
}
catch (ControlException e) {
throw e;
}
catch (Throwable e) {
e.printStackTrace();
ExceptionHandling.dieInternal(tc, e);
}
}

public static CallSite methcall(Lookup caller, String _, MethodType type, String name, int csIdx) {
try {
/* Look up methcall resolver method. */
Expand Down

0 comments on commit 20547c3

Please sign in to comment.