Skip to content

Commit

Permalink
Use static callInfo handling for better inlining
Browse files Browse the repository at this point in the history
Most JVM JITs can trivially inline simple static methods, and
these calls are made frequently, so we flip them to static
versions.
  • Loading branch information
headius committed Feb 8, 2024
1 parent 492d539 commit 4769248
Show file tree
Hide file tree
Showing 14 changed files with 48 additions and 36 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyEnumerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public static IRubyObject __from(ThreadContext context, IRubyObject klass, IRuby
int argc = Arity.checkArgumentCount(context, args, 2, 4);

boolean keywords = (context.callInfo & CALL_KEYWORD) != 0 && (context.callInfo & ThreadContext.CALL_KEYWORD_EMPTY) == 0;
context.resetCallInfo();
ThreadContext.resetCallInfo(context);

// Lazy.__from(enum, method, *args, size)
IRubyObject object = args[0];
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public static IRubyObject open19(ThreadContext context, IRubyObject recv, IRubyO
public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
int argc = Arity.checkArgumentCount(context, args, 1, 4);

boolean keywords = hasKeywords(context.resetCallInfo());
boolean keywords = hasKeywords(ThreadContext.resetCallInfo(context));
Ruby runtime = context.runtime;
// symbol to_open = 0;
boolean redirect = false;
Expand Down Expand Up @@ -2166,7 +2166,7 @@ public static IRubyObject obj_to_enum(final ThreadContext context, IRubyObject s

boolean keywords = (callInfo & ThreadContext.CALL_KEYWORD) != 0 && (callInfo & ThreadContext.CALL_KEYWORD_EMPTY) == 0;

context.resetCallInfo();
ThreadContext.resetCallInfo(context);
return enumeratorizeWithSize(context, self, method, args, sizeFn, keywords);
}

Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/RubyStruct.java
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ private void checkSize(int length) {
}

private void checkForKeywords(ThreadContext context, boolean keywordInit) {
if (hasKeywords(context.resetCallInfo()) && !keywordInit) {
if (hasKeywords(ThreadContext.resetCallInfo(context)) && !keywordInit) {
context.runtime.getWarnings().warn("Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3.2. Please use a Hash literal like .new({k: v}) instead of .new(k: v).");
}
}
Expand All @@ -423,7 +423,7 @@ private void checkForKeywords(ThreadContext context, boolean keywordInit) {
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
IRubyObject keywordInit = RubyStruct.getInternalVariable(classOf(), KEYWORD_INIT_VAR);
checkForKeywords(context, !keywordInit.isNil());
context.resetCallInfo();
ThreadContext.resetCallInfo(context);
modify();
checkSize(args.length);

Expand Down Expand Up @@ -467,7 +467,7 @@ public IRubyObject initialize(ThreadContext context) {
public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
IRubyObject keywordInit = RubyStruct.getInternalVariable(classOf(), KEYWORD_INIT_VAR);
checkForKeywords(context, !keywordInit.isNil());
context.resetCallInfo();
ThreadContext.resetCallInfo(context);
Ruby runtime = context.runtime;

if (keywordInit.isTrue()) {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ private static RubyThread adoptThread(final Ruby runtime, final ThreadService se

@JRubyMethod(rest = true, visibility = PRIVATE, keywords = true)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) {
int callInfo = context.resetCallInfo();
int callInfo = ThreadContext.resetCallInfo(context);
if (!block.isGiven()) throw context.runtime.newThreadError("must be called with a block");
if (threadImpl != ThreadLike.DUMMY) throw context.runtime.newThreadError("already initialized thread");

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -1717,7 +1717,7 @@ private IRubyObject initializeNow(ThreadContext context, IRubyObject zone) {

@JRubyMethod(name = "initialize", optional = 7, checkArity = false, visibility = PRIVATE, keywords = true)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
boolean keywords = hasKeywords(context.resetCallInfo());
boolean keywords = hasKeywords(ThreadContext.resetCallInfo(context));
IRubyObject zone = null;
IRubyObject nil = context.nil;

Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/ext/coverage/CoverageModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static IRubyObject setup(ThreadContext context, IRubyObject self, IRubyOb
}

if (argc != 0) {
boolean keyword = hasKeywords(context.resetCallInfo());
boolean keyword = hasKeywords(ThreadContext.resetCallInfo(context));

if (keyword) {
RubyHash keywords = (RubyHash) TypeConverter.convertToType(args[0], runtime.getHash(), "to_hash");
Expand Down Expand Up @@ -151,7 +151,7 @@ public static IRubyObject result(ThreadContext context, IRubyObject self, IRubyO
boolean stop = true;
boolean clear = true;

if (argc > 0 && hasKeywords(context.resetCallInfo())) {
if (argc > 0 && hasKeywords(ThreadContext.resetCallInfo(context))) {
RubyHash keywords = (RubyHash) TypeConverter.convertToType(args[0], runtime.getHash(), "to_hash");
stop = ArgsUtil.extractKeywordArg(context, "stop", keywords).isTrue();
clear = ArgsUtil.extractKeywordArg(context, "clear", keywords).isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public AttrReaderMethod(RubyModule implementationClass, Visibility visibility, S
}

public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
context.resetCallInfo();
ThreadContext.resetCallInfo(context);
IRubyObject variable = (IRubyObject) verifyAccessor(self.getMetaClass().getRealClass()).get(self);
return variable == null ? context.nil : variable;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public AttrWriterMethod(RubyModule implementationClass, Visibility visibility, S
}

public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg1) {
context.resetCallInfo();
ThreadContext.resetCallInfo(context);
verifyAccessor(self.getMetaClass().getRealClass()).set(self, arg1);
return arg1;
}
Expand Down
23 changes: 6 additions & 17 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -704,15 +704,15 @@ public static IRubyObject undefined() {
@JIT // Only used for specificArity JITted methods with at least one parameter
public static IRubyObject receiveSpecificArityKeywords(ThreadContext context, IRubyObject last) {
if (!(last instanceof RubyHash)) {
context.resetCallInfo();
ThreadContext.clearCallInfo(context);
return last;
}

return receiveSpecificArityHashKeywords(context, last);
}

private static IRubyObject receiveSpecificArityHashKeywords(ThreadContext context, IRubyObject last) {
int callInfo = context.resetCallInfo();
int callInfo = ThreadContext.resetCallInfo(context);
boolean isKwarg = (callInfo & CALL_KEYWORD) != 0;

return receiverSpecificArityKwargsCommon(context, last, callInfo, isKwarg);
Expand All @@ -722,15 +722,15 @@ private static IRubyObject receiveSpecificArityHashKeywords(ThreadContext contex
@JIT // Only used for specificArity JITted methods with at least one parameter
public static IRubyObject receiveSpecificArityRuby2Keywords(ThreadContext context, IRubyObject last) {
if (!(last instanceof RubyHash)) {
context.resetCallInfo();
ThreadContext.clearCallInfo(context);
return last;
}

return receiveSpecificArityRuby2HashKeywords(context, last);
}

private static IRubyObject receiveSpecificArityRuby2HashKeywords(ThreadContext context, IRubyObject last) {
int callInfo = context.resetCallInfo();
int callInfo = ThreadContext.resetCallInfo(context);
boolean isKwarg = (callInfo & CALL_KEYWORD) != 0;

// ruby2_keywords only get unmarked if it enters a method which accepts keywords.
Expand Down Expand Up @@ -780,7 +780,7 @@ public static IRubyObject receiveKeywords(ThreadContext context, StaticScope sta
@Interp
public static IRubyObject receiveKeywords(ThreadContext context, IRubyObject[] args, boolean hasRestArgs,
boolean acceptsKeywords, boolean ruby2_keywords_method) {
int callInfo = context.resetCallInfo();
int callInfo = ThreadContext.resetCallInfo(context);

if ((callInfo & CALL_KEYWORD_EMPTY) != 0) return UNDEFINED;
if (args.length < 1) return UNDEFINED;
Expand Down Expand Up @@ -858,17 +858,6 @@ public static void setCallInfo(ThreadContext context, int flags) {
context.callInfo = (context.callInfo & CALL_KEYWORD_EMPTY) | flags;
}

// specific args of arity 0 does not receive kwargs so we have to reset this.
@JIT
public static void resetCallInfo(ThreadContext context) {
context.resetCallInfo();
}

@JIT
public static void clearCallInfo(ThreadContext context) {
context.clearCallInfo();
}

public static void checkForExtraUnwantedKeywordArgs(ThreadContext context, final StaticScope scope, RubyHash keywordArgs) {
// we do an inexpensive non-gathering scan first to see if there's a bad keyword
try {
Expand Down Expand Up @@ -1986,7 +1975,7 @@ public static RubyRegexp newLiteralRegexp(ThreadContext context, ByteList source
@JIT
public static RubyArray irSplat(ThreadContext context, IRubyObject ary) {
Ruby runtime = context.runtime;
int callInfo = context.resetCallInfo();
int callInfo = ThreadContext.resetCallInfo(context);
IRubyObject tmp = TypeConverter.convertToTypeWithCheck(context, ary, runtime.getArray(), sites(context).to_a_checked);
if (tmp.isNil()) {
tmp = runtime.newArray(ary);
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -2161,7 +2161,7 @@ public void ReceiveKeywordsInstr(ReceiveKeywordsInstr instr) {
jvmAdapter().astore(3 + argsLength - 1); // 3 - 0-2 are not args // FIXME: This should get abstracted
} else {
jvmMethod().loadContext();
jvmMethod().invokeIRHelper("resetCallInfo", sig(void.class, ThreadContext.class));
jvmMethod().adapter.invokestatic(p(ThreadContext.class), "resetCallInfo", sig(void.class, ThreadContext.class));
}
jvmMethod().invokeIRHelper("undefined", sig(IRubyObject.class));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class CallInfoBootstrap {
public static CallSite callInfoBootstrap(MethodHandles.Lookup lookup, String name, MethodType type, int callInfo) throws Throwable {
MethodHandle handle;
if (callInfo == 0) {
handle = lookup.findVirtual(ThreadContext.class, "clearCallInfo", methodType(void.class));
handle = lookup.findStatic(ThreadContext.class, "clearCallInfo", methodType(void.class, ThreadContext.class));
} else {
handle = lookup.findStatic(IRRuntimeHelpers.class, "setCallInfo", methodType(void.class, ThreadContext.class, int.class));
handle = insertArguments(handle, 1, callInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ private void finishBinding(CacheEntry entry, MethodHandle mh, IRubyObject self,
SmartHandle callInfoWrapper;
SmartBinder baseBinder = SmartBinder.from(signature.changeReturn(void.class)).permute("context");
if (flags == 0) {
callInfoWrapper = baseBinder.invokeVirtualQuiet(LOOKUP, "clearCallInfo");
callInfoWrapper = baseBinder.invokeStaticQuiet(LOOKUP, ThreadContext.class, "clearCallInfo");
} else {
callInfoWrapper = baseBinder.append("flags", flags).invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "setCallInfo");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ public void asString(AsStringInstr call, String scopeFieldName, String file) {
public void setCallInfo(int flags) {
compiler.loadContext();
if (flags == 0) {
compiler.invokeIRHelper("clearCallInfo", sig(void.class, ThreadContext.class));
compiler.adapter.invokestatic(p(ThreadContext.class), "clearCallInfo", sig(void.class, ThreadContext.class));
} else {
compiler.adapter.ldc(flags);
compiler.invokeIRHelper("setCallInfo", sig(void.class, ThreadContext.class, int.class));
Expand Down
29 changes: 26 additions & 3 deletions core/src/main/java/org/jruby/runtime/ThreadContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -1519,18 +1519,41 @@ public IRubyObject getLocalMatchOrNil() {
* Reset call info state and return the value of call info right before
* it is reset.
* @return the old call info
* @deprecated use the trivially-inlinable static version
*/
@Deprecated
public int resetCallInfo() {
int callInfo = this.callInfo;
this.callInfo = 0;
return resetCallInfo(this);
}

/**
* Reset call info state and return the value of call info right before
* it is reset. This method is static to make it trivially inlinable on
* most JVM JITs
*
* @return the old call info
*/
public static int resetCallInfo(ThreadContext context) {
int callInfo = context.callInfo;
context.callInfo = 0;
return callInfo;
}

/**
* Clear call info state (set to 0).
* @deprecated use the trivially-inlinable static version
*/
@Deprecated
public void clearCallInfo() {
this.callInfo = 0;
clearCallInfo(this);
}

/**
* Clear call info state (set to 0). This method is static to make it trivially
* inlinable on most JVM JITs
*/
public static void clearCallInfo(ThreadContext context) {
context.callInfo = 0;
}

public static boolean hasKeywords(int callInfo) {
Expand Down

0 comments on commit 4769248

Please sign in to comment.