Permalink
Browse files

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...
1 parent 1cc2d3e commit 20547c318f13b02876eb44a615545626c0acadc2 @jnthn jnthn committed Apr 27, 2013
Showing with 89 additions and 24 deletions.
  1. +29 −24 src/vm/jvm/QAST/Compiler.nqp
  2. +60 −0 src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java
@@ -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;
@@ -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);
@@ -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++;
}
@@ -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.
@@ -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);
@@ -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. */

0 comments on commit 20547c3

Please sign in to comment.