Skip to content

Commit

Permalink
Second attempt at block_given? optimized call site
Browse files Browse the repository at this point in the history
  • Loading branch information
headius committed Mar 27, 2024
1 parent c60310f commit 3a60714
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 4 deletions.
4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,10 @@ public static RubyBoolean block_given_p(ThreadContext context, IRubyObject recv)
return RubyBoolean.newBoolean(context, context.getCurrentFrame().getBlock().isGiven());
}

public static RubyBoolean blockGiven(ThreadContext context, IRubyObject recv, Block frameBlock) {
return RubyBoolean.newBoolean(context, frameBlock.isGiven());
}

@JRubyMethod(name = {"sprintf", "format"}, required = 1, rest = true, checkArity = false, module = true, visibility = PRIVATE)
public static IRubyObject sprintf(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
if (args.length == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,11 @@ public interface InvocationCompiler {
* Stack required: none
*/
void setCallInfo(int flags);

/**
* Invoke block_given? with awareness of any built-in methods.
*
* @param call a CallBase representing the call to block_given?
*/
void invokeBlockGiven(String file, String scopeFieldName, CallBase call);
}
16 changes: 16 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,18 @@ called frequently enough to JIT is even more uncommon (at time of writing, this
throw new NotCompilableException("ruby2_keywords can change behavior of already-compiled code");
}

// known frame-aware methods get compiled with special call sites that pass frame data through
switch (call.getName().idString()) {
case "block_given?":
if (call.getArgsCount() == 0 && !call.isPotentiallyRefined() && call.getClosureArg() == NullBlock.INSTANCE) {
/* likely a call to built-in block_given? which just checks the frame block isGiven. Only optimized
for method scopes currently. */
m.getInvocationCompiler().invokeBlockGiven(file, jvm.methodData().scopeField, call);
handleCallResult(m, call);
return;
}
}

boolean functional = call.getCallType() == CallType.FUNCTIONAL || call.getCallType() == CallType.VARIABLE;

Operand[] args = call.getCallArgs();
Expand Down Expand Up @@ -1323,6 +1335,10 @@ called frequently enough to JIT is even more uncommon (at time of writing, this
break;
}

handleCallResult(m, call);
}

private void handleCallResult(IRBytecodeAdapter m, CallBase call) {
Variable result = call.getResult();
if (result != null) {
if (!omitStoreLoad) jvmStoreLocal(result);
Expand Down
40 changes: 40 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,26 @@

package org.jruby.ir.targets.indy;

import com.headius.invokebinder.Binder;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;

import static java.lang.invoke.MethodHandles.Lookup;
import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.methodType;
import static org.jruby.runtime.Helpers.arrayOf;
import static org.jruby.util.CodegenUtils.p;
Expand Down Expand Up @@ -73,4 +80,37 @@ public static Handle getBootstrapHandle(String name, Class type, String sig) {
sig,
false);
}

public static final Handle BLOCK_GIVEN_BOOTSTRAP = getBootstrapHandle("blockGivenBootstrap", Bootstrap.class, sig(CallSite.class, Lookup.class, String.class, MethodType.class));

public static CallSite blockGivenBootstrap(Lookup lookup, String name, MethodType methodType) {
MutableCallSite blockGivenSite = new MutableCallSite(methodType);

blockGivenSite.setTarget(Binder.from(methodType).prepend(blockGivenSite, name).invokeStaticQuiet(Bootstrap.class, "blockGivenFallback"));

return blockGivenSite;
}

public static IRubyObject blockGivenFallback(MutableCallSite blockGivenSite, String name, ThreadContext context, IRubyObject self, Block block) throws Throwable {
CacheEntry entry = self.getMetaClass().searchWithCache("block_given?");
MethodHandle target;

if (entry.method.isBuiltin()) {
target = Binder.from(blockGivenSite.type())
.permute(0, 2)
.invokeStaticQuiet(Bootstrap.class, "blockGiven");
} else {
target = Binder.from(blockGivenSite.type())
.permute(0, 1)
.invoke(SelfInvokeSite.bootstrap(lookup(), name, methodType(IRubyObject.class, ThreadContext.class, IRubyObject.class), 0, 0, "", 0).dynamicInvoker());
}

blockGivenSite.setTarget(target);

return (IRubyObject) target.invokeExact(context, self, block);
}

public static IRubyObject blockGiven(ThreadContext context, Block block) {
return block.isGiven() ? context.tru : context.fals;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,26 @@ public void invokeSelf(String file, String scopeFieldName, CallBase call, int ar

String action = call.getCallType() == CallType.FUNCTIONAL ? "callFunctional" : "callVariable";
IRBytecodeAdapter.BlockPassType blockPassType = IRBytecodeAdapter.BlockPassType.fromIR(call);
String callName = constructIndyCallName(action, id);
if (blockPassType != IRBytecodeAdapter.BlockPassType.NONE) {
if (arity == -1) {
compiler.adapter.invokedynamic(action + ':' + JavaNameMangler.mangleMethodName(id), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT_ARRAY, Block.class)), SelfInvokeSite.BOOTSTRAP, blockPassType.literal(), flags, file, compiler.getLastLine());
compiler.adapter.invokedynamic(callName, sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT_ARRAY, Block.class)), SelfInvokeSite.BOOTSTRAP, blockPassType.literal(), flags, file, compiler.getLastLine());
} else {
compiler.adapter.invokedynamic(action + ':' + JavaNameMangler.mangleMethodName(id), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, arity + 1, Block.class)), SelfInvokeSite.BOOTSTRAP, blockPassType.literal(), flags, file, compiler.getLastLine());
compiler.adapter.invokedynamic(callName, sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, arity + 1, Block.class)), SelfInvokeSite.BOOTSTRAP, blockPassType.literal(), flags, file, compiler.getLastLine());
}
} else {
if (arity == -1) {
compiler.adapter.invokedynamic(action + ':' + JavaNameMangler.mangleMethodName(id), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT_ARRAY)), SelfInvokeSite.BOOTSTRAP, false, flags, file, compiler.getLastLine());
compiler.adapter.invokedynamic(callName, sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT_ARRAY)), SelfInvokeSite.BOOTSTRAP, false, flags, file, compiler.getLastLine());
} else {
compiler.adapter.invokedynamic(action + ':' + JavaNameMangler.mangleMethodName(id), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, arity)), SelfInvokeSite.BOOTSTRAP, false, flags, file, compiler.getLastLine());
compiler.adapter.invokedynamic(callName, sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, arity)), SelfInvokeSite.BOOTSTRAP, false, flags, file, compiler.getLastLine());
}
}
}

private static String constructIndyCallName(String action, String id) {
return action + ':' + JavaNameMangler.mangleMethodName(id);
}

public void invokeInstanceSuper(String file, String name, int arity, boolean hasClosure, boolean literalClosure, boolean[] splatmap, int flags) {
if (arity > IRBytecodeAdapter.MAX_ARGUMENTS)
throw new NotCompilableException("call to instance super has more than " + IRBytecodeAdapter.MAX_ARGUMENTS + " arguments");
Expand Down Expand Up @@ -215,4 +220,12 @@ public void setCallInfo(int flags) {
compiler.loadContext();
compiler.adapter.invokedynamic("callInfo", sig(void.class, ThreadContext.class), CallInfoBootstrap.CALL_INFO_BOOTSTRAP, flags);
}

@Override
public void invokeBlockGiven(String file, String scopeFieldName, CallBase call) {
compiler.loadContext();
compiler.loadSelf();
compiler.loadBlock();
compiler.adapter.invokedynamic(constructIndyCallName("callFunctional", "block_given?"), sig(IRubyObject.class, ThreadContext.class, IRubyObject.class, Block.class), Bootstrap.BLOCK_GIVEN_BOOTSTRAP);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -491,4 +491,12 @@ public void setCallInfo(int flags) {
compiler.invokeIRHelper("setCallInfo", sig(void.class, ThreadContext.class, int.class));
}
}

@Override
public void invokeBlockGiven(String file, String scopeFieldName, CallBase call) {
// just invoke normally; no magic without indy
compiler.loadContext();
compiler.loadSelf();
invokeSelf(file, scopeFieldName, call, 0);
}
}

0 comments on commit 3a60714

Please sign in to comment.