From a84eedf683751c1ca75adc334313ad8f9f2620c6 Mon Sep 17 00:00:00 2001 From: jnthn Date: Sat, 27 Apr 2013 15:19:28 +0200 Subject: [PATCH] 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. --- src/vm/jvm/QAST/Compiler.nqp | 70 +++++++++---------- .../org/perl6/nqp/runtime/IndyBootstrap.java | 62 ++++++++++++++++ 2 files changed, 96 insertions(+), 36 deletions(-) diff --git a/src/vm/jvm/QAST/Compiler.nqp b/src/vm/jvm/QAST/Compiler.nqp index c5535fa299..5b58d76a6b 100644 --- a/src/vm/jvm/QAST/Compiler.nqp +++ b/src/vm/jvm/QAST/Compiler.nqp @@ -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. diff --git a/src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java b/src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java index 7e06f7deeb..6682e82631 100644 --- a/src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java +++ b/src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java @@ -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); + } + } }