Permalink
Browse files

Send $foo."$bar"() calls through indy also.

Don't do much optimization here, this just simplifies code-gen and
brings it inline with that of normal method calls.
  • Loading branch information...
1 parent 20547c3 commit a84eedf683751c1ca75adc334313ad8f9f2620c6 @jnthn jnthn committed Apr 27, 2013
Showing with 96 additions and 36 deletions.
  1. +34 −36 src/vm/jvm/QAST/Compiler.nqp
  2. +62 −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, :$inv_first) {
+sub process_args_onto_stack($qastcomp, @children, $il, :$obj_first, :$inv_first, :$name_first, :$obj_second) {
# Make sure we do positionals before nameds.
my @pos;
my @named;
@@ -1129,9 +1129,16 @@ sub process_args_onto_stack($qastcomp, @children, $il, :$obj_first, :$inv_first)
my @argnames;
my int $i := 0;
while $i < +@order {
- my $arg_res := $i == 0 && ($obj_first || $inv_first)
- ?? $qastcomp.as_jast(@order[$i], :want($RT_OBJ))
- !! $qastcomp.as_jast(@order[$i]);
+ my $arg_res;
+ if $i == 0 && ($obj_first || $inv_first) || $i == 1 && $obj_second {
+ $arg_res := $qastcomp.as_jast(@order[$i], :want($RT_OBJ));
+ }
+ elsif $i == 0 && $name_first {
+ $arg_res := $qastcomp.as_jast(@order[$i], :want($RT_STR));
+ }
+ else {
+ $arg_res := $qastcomp.as_jast(@order[$i]);
+ }
$il.append($arg_res.jast);
nqp::push(@arg_results, $arg_res);
@@ -1146,7 +1153,7 @@ sub process_args_onto_stack($qastcomp, @children, $il, :$obj_first, :$inv_first)
nqp::push(@arg_jtypes, jtype($arg_res.type));
}
- unless $i == 0 && $inv_first {
+ unless $i == 0 && ($inv_first || $name_first) {
my int $flags := 0;
if @order[$i].flat {
$flags := @order[$i].named ?? 24 !! 16;
@@ -1235,8 +1242,8 @@ QAST::OperationsJAST.add_core_op('callmethod', -> $qastcomp, $node {
}
my @children := nqp::clone(@($node));
- # If it's a direct call, we can go through invokedynamic to optimize the
- # calling.
+ # If it's a direct call, we can get invokedynamic to do something smart
+ # with guard clauses for us.
if $node.name ne '' {
# Process arguments and force them into locals.
my @argstuff := process_args_onto_stack($qastcomp, @children, $il, :obj_first);
@@ -1259,42 +1266,33 @@ QAST::OperationsJAST.add_core_op('callmethod', -> $qastcomp, $node {
}
# Otherwise, it's indirect, and we need to resolve the method each and
- # every call.
+ # every call. Still wire it through invokedynamic, but it can't do quite
+ # so much for us.
else {
- # Process the name.
+ # Ensure we have a name, and re-arrange it to come first.
if +@children == 1 {
nqp::die("Method call must either supply a name or have a child node that evaluates to the name");
}
- my $inv := @children.shift();
- my $name_tmp := $*TA.fresh_s();
- my $name_res := $qastcomp.as_jast(@children.shift(), :want($RT_STR));
- $il.append($name_res.jast);
- $*STACK.obtain($il, $name_res);
- $il.append(JAST::Instruction.new( :op('astore'), $name_tmp ));
- @children.unshift($inv);
-
- # Process arguments, stashing the invocant.
- my $inv_temp := $*TA.fresh_o();
- my @argstuff := process_args($qastcomp, @children, $il, 0, :$inv_temp);
+ my $inv := nqp::shift(@children);
+ my $name := nqp::shift(@children);
+ nqp::unshift(@children, $inv);
+ nqp::unshift(@children, $name);
+
+ # Process arguments and force them into locals.
+ my @argstuff := process_args_onto_stack($qastcomp, @children, $il, :name_first, :obj_second);
my $cs_idx := @argstuff[0];
+ $*STACK.spill_to_locals($il);
- # Look up method.
- $il.append(JAST::Instruction.new( :op('aload_1') ));
- $il.append(JAST::Instruction.new( :op('aload'), $inv_temp ));
- if $name_tmp {
- $il.append(JAST::Instruction.new( :op('aload'), $name_tmp ));
- }
- else {
- $il.append(JAST::PushSVal.new( :value($node.name) ));
- }
- $il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS, 'findmethod', $TYPE_SMO, $TYPE_TC, $TYPE_SMO, $TYPE_STR ));
-
- # Emit call.
- $il.append(JAST::PushIndex.new( :value($cs_idx) ));
- $il.append(JAST::Instruction.new( :op('aload'), @argstuff[1] ));
+ # Emit the call.
$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(
+ 'indmethcall', 'V', @argstuff[2],
+ 'org/perl6/nqp/runtime/IndyBootstrap', 'indmethcall',
+ [
+ JAST::PushIndex.new( :value($cs_idx) )
+ ]
+ ));
}
# Load result onto the stack, unless in void context.
@@ -385,4 +385,66 @@ public static void methcallResolve(Lookup caller, MutableCallSite cs, String nam
public static boolean stGuard(STable expected, ThreadContext _, SixModelObject obj) {
return obj.st == expected;
}
+
+ public static CallSite indmethcall(Lookup caller, String _, MethodType type, int csIdx) {
+ try {
+ /* Look up methcall invoker method. */
+ MethodType resType = MethodType.methodType(void.class,
+ MutableCallSite.class, int.class,
+ ThreadContext.class, String.class, Object[].class);
+ MethodHandle res = caller.findStatic(IndyBootstrap.class, "indmethcallInvoker", resType);
+
+ /* Create a mutable callsite, and curry the resolver with it and
+ * the method 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) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void indmethcallInvoker(MutableCallSite cs, int csIdx,
+ ThreadContext tc, String name, Object... args) {
+ /* Resolve callsite descriptor. */
+ CallSiteDescriptor csd = csIdx >= 0
+ ? tc.curFrame.codeRef.staticInfo.compUnit.callSites[csIdx]
+ : Ops.emptyCallSite;
+
+ /* Try to resolve method to a coderef. */
+ SixModelObject invocant = (SixModelObject)args[0];
+ SixModelObject invokee = invocant.st.MethodCache.get(name);
+ if (invokee == null)
+ throw ExceptionHandling.dieInternal(tc, "Method '" + name + "' not found");
+ 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) {
+ ExceptionHandling.dieInternal(tc, e);
+ }
+ }
}

0 comments on commit a84eedf

Please sign in to comment.