Browse files

Move back to an atomic generation ID and install some simple type/mod…

…ification guards for dynopt calls.
  • Loading branch information...
1 parent 71aa55d commit a5503a49df27a4f661b86c282c64a4ca2045cb32 @headius headius committed Jun 10, 2010
View
12 src/org/jruby/Ruby.java
@@ -3743,6 +3743,15 @@ public long getNextDynamicMethodSerial() {
}
/**
+ * Get a new generation number for a module or class.
+ *
+ * @return a new generation number
+ */
+ public int getNextModuleGeneration() {
+ return moduleGeneration.incrementAndGet();
+ }
+
+ /**
* Get the global object used to synchronize class-hierarchy modifications like
* cache invalidation, subclass sets, and included hierarchy sets.
*
@@ -3936,4 +3945,7 @@ public Object getHierarchyLock() {
// An atomic long for generating DynamicMethod serial numbers
private final AtomicLong dynamicMethodSerial = new AtomicLong(0);
+
+ // An atomic int for generating class generation numbers
+ private final AtomicInteger moduleGeneration = new AtomicInteger(1);
}
View
19 src/org/jruby/RubyModule.java
@@ -205,7 +205,7 @@ protected RubyModule(Ruby runtime, RubyClass metaClass, boolean objectSpace) {
runtime.addModule(this);
// if (parent == null) parent = runtime.getObject();
setFlag(USER7_F, !isClass());
- generation = new Object();
+ generation = runtime.getNextModuleGeneration();
}
/** used by MODULE_ALLOCATOR and RubyClass constructors
@@ -851,7 +851,7 @@ public CacheEntry searchWithCache(String name) {
// we grab serial number first; the worst that will happen is we cache a later
// update with an earlier serial number, which would just flush anyway
- Object token = getCacheToken();
+ int token = getCacheToken();
DynamicMethod method = searchMethodInner(name);
if (method instanceof DefaultMethod) {
@@ -861,7 +861,7 @@ public CacheEntry searchWithCache(String name) {
return method != null ? addToCache(name, method, token) : addToCache(name, UndefinedMethod.getInstance(), token);
}
- public final Object getCacheToken() {
+ public final int getCacheToken() {
return generation;
}
@@ -889,17 +889,17 @@ private CacheEntry cacheHit(String name) {
}
protected static abstract class CacheEntryFactory {
- public abstract CacheEntry newCacheEntry(DynamicMethod method, Object token);
+ public abstract CacheEntry newCacheEntry(DynamicMethod method, int token);
}
protected static final CacheEntryFactory NormalCacheEntryFactory = new CacheEntryFactory() {
- public CacheEntry newCacheEntry(DynamicMethod method, Object token) {
+ public CacheEntry newCacheEntry(DynamicMethod method, int token) {
return new CacheEntry(method, token);
}
};
protected static final CacheEntryFactory SynchronizedCacheEntryFactory = new CacheEntryFactory() {
- public CacheEntry newCacheEntry(DynamicMethod method, Object token) {
+ public CacheEntry newCacheEntry(DynamicMethod method, int token) {
return new CacheEntry(new SynchronizedDynamicMethod(method), token);
}
};
@@ -915,7 +915,7 @@ public boolean isSynchronized() {
return cacheEntryFactory == SynchronizedCacheEntryFactory;
}
- private CacheEntry addToCache(String name, DynamicMethod method, Object token) {
+ private CacheEntry addToCache(String name, DynamicMethod method, int token) {
CacheEntry entry = cacheEntryFactory.newCacheEntry(method, token);
getCachedMethodsForWrite().put(name, entry);
@@ -931,7 +931,8 @@ protected DynamicMethod searchMethodInner(String name) {
}
public void invalidateCacheDescendants() {
- generation = new Object();
+ System.out.println("invalidating descendants: " + classId);
+ generation = getRuntime().getNextModuleGeneration();
// update all hierarchies into which this module has been included
synchronized (getRuntime().getHierarchyLock()) {
for (RubyClass includingHierarchy : includingHierarchies) {
@@ -3163,7 +3164,7 @@ private static void define(RubyModule module, JavaMethodDescriptor desc, Dynamic
private volatile Map<String, IRubyObject> constants = Collections.EMPTY_MAP;
private volatile Map<String, DynamicMethod> methods = Collections.EMPTY_MAP;
private Map<String, CacheEntry> cachedMethods = Collections.EMPTY_MAP;
- protected Object generation;
+ protected int generation;
protected volatile Set<RubyClass> includingHierarchies = Collections.EMPTY_SET;
View
27 src/org/jruby/compiler/ASTCompiler.java
@@ -816,12 +816,15 @@ public void call(BodyCompiler context) {
// only do direct calls for specific arity
if (argsCallback == null || argsCallback.getArity() >= 0 && argsCallback.getArity() <= 3) {
- if (compileIntrinsic(context, callNode, cacheSite.methodName, entry.method, receiverCallback, argsCallback, closureArg)) {
+ if (compileIntrinsic(context, callNode, cacheSite.methodName, entry.token, entry.method, receiverCallback, argsCallback, closureArg)) {
// intrinsic compilation worked, hooray!
return;
} else {
// otherwise, normal straight-through native call
- context.getInvocationCompiler().invokeNative(nativeCall, receiverCallback, argsCallback, closureArg);
+ context.getInvocationCompiler().invokeNative(
+ name, nativeCall, entry.token, receiverCallback,
+ argsCallback, closureArg, CallType.NORMAL,
+ callNode.getIterNode() instanceof IterNode);
return;
}
}
@@ -830,7 +833,7 @@ public void call(BodyCompiler context) {
// check for a recursive call
if (callNode.getReceiverNode() instanceof SelfNode) {
// recursive calls
- if (compileRecursiveCall(entry.method, context, argsCallback, expr)) return;
+ if (compileRecursiveCall(callNode.getName(), entry.token, CallType.NORMAL, callNode.getIterNode() instanceof IterNode, entry.method, context, argsCallback, closureArg, expr)) return;
}
}
}
@@ -920,20 +923,20 @@ public void call(BodyCompiler context) {
floatDoubleIntrinsics.put("<=>", "op_cmp");
}
- private boolean compileRecursiveCall(DynamicMethod method, BodyCompiler context, ArgumentsCallback argsCallback, boolean expr) {
+ private boolean compileRecursiveCall(String name, int generation, CallType callType, boolean iterator, DynamicMethod method, BodyCompiler context, ArgumentsCallback argsCallback, CompilerCallback closure, boolean expr) {
if (currentBodyNode != null) {
if (method instanceof InterpretedMethod) {
InterpretedMethod target = (InterpretedMethod)method;
if (target.getBodyNode() == currentBodyNode) {
- context.getInvocationCompiler().invokeRecursive(argsCallback);
+ context.getInvocationCompiler().invokeRecursive(name, generation, argsCallback, closure, callType, iterator);
if (!expr) context.consumeCurrentValue();
return true;
}
}
if (method instanceof DefaultMethod) {
DefaultMethod target = (DefaultMethod)method;
if (target.getBodyNode() == currentBodyNode) {
- context.getInvocationCompiler().invokeRecursive(argsCallback);
+ context.getInvocationCompiler().invokeRecursive(name, generation, argsCallback, closure, callType, iterator);
if (!expr) context.consumeCurrentValue();
return true;
}
@@ -942,7 +945,7 @@ private boolean compileRecursiveCall(DynamicMethod method, BodyCompiler context,
if (method instanceof JittedMethod) {
DefaultMethod target = (DefaultMethod)((JittedMethod)method).getRealMethod();
if (target.getBodyNode() == currentBodyNode) {
- context.getInvocationCompiler().invokeRecursive(argsCallback);
+ context.getInvocationCompiler().invokeRecursive(name, generation, argsCallback, closure, callType, iterator);
if (!expr) context.consumeCurrentValue();
return true;
}
@@ -991,7 +994,7 @@ private boolean compileTrivialCall(DynamicMethod method, BodyCompiler context, b
return false;
}
- private boolean compileIntrinsic(BodyCompiler context, CallNode callNode, String name, DynamicMethod method, CompilerCallback receiverCallback, ArgumentsCallback argsCallback, CompilerCallback closureCallback) {
+ private boolean compileIntrinsic(BodyCompiler context, CallNode callNode, String name, int generation, DynamicMethod method, CompilerCallback receiverCallback, ArgumentsCallback argsCallback, CompilerCallback closureCallback) {
if (!(method.getImplementationClass() instanceof RubyClass)) return false;
RubyClass implClass = (RubyClass)method.getImplementationClass();
@@ -1002,14 +1005,14 @@ private boolean compileIntrinsic(BodyCompiler context, CallNode callNode, String
if (argument instanceof FixnumNode) {
Map<String, String> typeLongIntrinsics = typeIntrinsics.get(FixnumNode.class);
if (typeLongIntrinsics.containsKey(name)) {
- context.getInvocationCompiler().invokeFixnumLong(receiverCallback, typeLongIntrinsics.get(name), ((FixnumNode)argument).getValue());
+ context.getInvocationCompiler().invokeFixnumLong(name, generation, receiverCallback, typeLongIntrinsics.get(name), ((FixnumNode)argument).getValue());
return true;
}
}
if (argument instanceof FloatNode) {
Map<String, String> typeDoubleIntrinsics = typeIntrinsics.get(FloatNode.class);
if (typeDoubleIntrinsics.containsKey(name)) {
- context.getInvocationCompiler().invokeFloatDouble(receiverCallback, typeDoubleIntrinsics.get(name), ((FloatNode)argument).getValue());
+ context.getInvocationCompiler().invokeFloatDouble(name, generation, receiverCallback, typeDoubleIntrinsics.get(name), ((FloatNode)argument).getValue());
return true;
}
}
@@ -2409,7 +2412,7 @@ public void compileFCall(Node node, BodyCompiler context, boolean expr) {
if (closureArg == null && (argsCallback == null || (argsCallback.getArity() >= 0 && argsCallback.getArity() <= 3))) {
// recursive calls
- if (compileRecursiveCall(entry.method, context, argsCallback, expr)) return;
+ if (compileRecursiveCall(fcallNode.getName(), entry.token, CallType.FUNCTIONAL, fcallNode.getIterNode() instanceof IterNode, entry.method, context, argsCallback, closureArg, expr)) return;
// peephole inlining for trivial targets
if (compileTrivialCall(entry.method, context, expr)) return;
@@ -3879,7 +3882,7 @@ public void compileVCall(Node node, BodyCompiler context, boolean expr) {
CacheEntry entry = cacheSite.getCache();
// recursive calls
- if (compileRecursiveCall(entry.method, context, null, expr)) return;
+ if (compileRecursiveCall(vcallNode.getName(), entry.token, CallType.VARIABLE, false, entry.method, context, null, null, expr)) return;
// peephole inlining for trivial targets
if (compileTrivialCall(entry.method, context, expr)) return;
View
9 src/org/jruby/compiler/InvocationCompiler.java
@@ -91,11 +91,12 @@
public void invokeEqq(ArgumentsCallback receivers, CompilerCallback argument);
public void invokeBinaryFixnumRHS(String name, CompilerCallback receiverCallback, long fixnum);
+ public void invokeBinaryFloatRHS(String name, CompilerCallback receiverCallback, double flote);
- public void invokeFixnumLong(CompilerCallback receiverCallback, String method, long fixnum);
- public void invokeFloatDouble(CompilerCallback receiverCallback, String method, double flote);
+ public void invokeFixnumLong(String rubyName, int moduleGeneration, CompilerCallback receiverCallback, String methodName, long fixnum);
+ public void invokeFloatDouble(String rubyName, int moduleGeneration, CompilerCallback receiverCallback, String methodName, double flote);
- public void invokeRecursive(ArgumentsCallback callback);
+ public void invokeRecursive(String name, int moduleGeneration, ArgumentsCallback argsCallback, CompilerCallback closure, CallType callType, boolean iterator);
- public void invokeNative(NativeCall nativeCall, CompilerCallback receiver, ArgumentsCallback args, CompilerCallback closure);
+ public void invokeNative(String name, NativeCall nativeCall, int generation, CompilerCallback receiver, ArgumentsCallback args, CompilerCallback closure, CallType callType, boolean iterator);
}
View
10 src/org/jruby/compiler/impl/StandardASMCompiler.java
@@ -136,12 +136,10 @@ public static String getClosure19Signature() {
public static final int CLOSURE_OFFSET = 0;
public static final int DYNAMIC_SCOPE_OFFSET = 1;
- public static final int RUNTIME_OFFSET = 2;
- public static final int VARS_ARRAY_OFFSET = 3;
- public static final int NIL_OFFSET = 4;
- public static final int EXCEPTION_OFFSET = 5;
- public static final int PREVIOUS_EXCEPTION_OFFSET = 6;
- public static final int FIRST_TEMP_OFFSET = 7;
+ public static final int VARS_ARRAY_OFFSET = 2;
+ public static final int EXCEPTION_OFFSET = 3;
+ public static final int PREVIOUS_EXCEPTION_OFFSET = 4;
+ public static final int FIRST_TEMP_OFFSET = 5;
public static final int STARTING_DSTR_SIZE = 20;
View
136 src/org/jruby/compiler/impl/StandardInvocationCompiler.java
@@ -401,25 +401,101 @@ public void invokeBinaryFixnumRHS(String name, CompilerCallback receiverCallback
method.invokevirtual(p(CallSite.class), callSiteMethod, signature);
}
- public void invokeFixnumLong(CompilerCallback receiverCallback, String methodName, long other) {
+ public void invokeBinaryFloatRHS(String name, CompilerCallback receiverCallback, double flote) {
+ methodCompiler.getScriptCompiler().getCacheCompiler().cacheCallSite(methodCompiler, name, CallType.NORMAL);
+ methodCompiler.loadThreadContext(); // [adapter, tc]
+
+ // for visibility checking without requiring frame self
+ // TODO: don't bother passing when fcall or vcall, and adjust callsite appropriately
+ methodCompiler.loadSelf();
+
+ if (receiverCallback != null) {
+ receiverCallback.call(methodCompiler);
+ } else {
+ methodCompiler.loadSelf();
+ }
+
+ method.ldc(flote);
+
+ String signature = sig(IRubyObject.class, params(ThreadContext.class, IRubyObject.class, IRubyObject.class, double.class));
+ String callSiteMethod = "call";
+
+ method.invokevirtual(p(CallSite.class), callSiteMethod, signature);
+ }
+
+ public void invokeFixnumLong(String rubyName, int moduleGeneration, CompilerCallback receiverCallback, String methodName, long fixnum) {
receiverCallback.call(methodCompiler);
+ final int tmp = methodCompiler.getVariableCompiler().grabTempLocal();
+ method.astore(tmp);
+
+ method.aload(tmp);
+ method.ldc(moduleGeneration);
+ methodCompiler.invokeUtilityMethod("isGenerationEqual", sig(boolean.class, IRubyObject.class, int.class));
+
+ Label slow = new Label();
+ Label after = new Label();
+ method.ifne(slow);
+
+ method.aload(tmp);
method.checkcast(p(RubyFixnum.class));
methodCompiler.loadThreadContext();
- method.ldc(other);
+ method.ldc(fixnum);
method.invokevirtual(p(RubyFixnum.class), methodName, sig(IRubyObject.class, ThreadContext.class, long.class));
+
+ method.go_to(after);
+ method.label(slow);
+
+ invokeBinaryFixnumRHS(rubyName, new CompilerCallback() {
+ public void call(BodyCompiler context) {
+ method.aload(tmp);
+ }
+ }, fixnum);
+
+ method.label(after);
}
- public void invokeFloatDouble(CompilerCallback receiverCallback, String methodName, double flote) {
+ public void invokeFloatDouble(String rubyName, int moduleGeneration, CompilerCallback receiverCallback, String methodName, double flote) {
receiverCallback.call(methodCompiler);
+ final int tmp = methodCompiler.getVariableCompiler().grabTempLocal();
+ method.astore(tmp);
+
+ method.aload(tmp);
+ method.ldc(moduleGeneration);
+ methodCompiler.invokeUtilityMethod("isGenerationEqual", sig(boolean.class, IRubyObject.class, int.class));
+
+ Label slow = new Label();
+ Label after = new Label();
+ method.ifne(slow);
+
+ method.aload(tmp);
method.checkcast(p(RubyFloat.class));
methodCompiler.loadThreadContext();
method.ldc(flote);
method.invokevirtual(p(RubyFloat.class), methodName, sig(IRubyObject.class, ThreadContext.class, double.class));
+
+ method.go_to(after);
+ method.label(slow);
+
+ invokeBinaryFloatRHS(rubyName, new CompilerCallback() {
+ public void call(BodyCompiler context) {
+ method.aload(tmp);
+ }
+ }, flote);
+
+ method.label(after);
}
- public void invokeRecursive(ArgumentsCallback argsCallback) {
+ public void invokeRecursive(String name, int moduleGeneration, ArgumentsCallback argsCallback, CompilerCallback closure, CallType callType, boolean iterator) {
+ methodCompiler.loadSelf();
+ method.ldc(moduleGeneration);
+ methodCompiler.invokeUtilityMethod("isGenerationEqual", sig(boolean.class, IRubyObject.class, int.class));
+
+ Label slow = new Label();
+ Label after = new Label();
+ method.ifne(slow);
+
method.aload(0);
methodCompiler.loadThreadContext();
methodCompiler.loadSelf();
@@ -429,21 +505,43 @@ public void invokeRecursive(ArgumentsCallback argsCallback) {
method.aconst_null();
method.invokestatic(methodCompiler.getScriptCompiler().getClassname(), methodCompiler.getNativeMethodName(), methodCompiler.getSignature());
+
+ method.go_to(after);
+ method.label(slow);
+
+ invokeDynamic(name, null, argsCallback, callType, closure, iterator);
+
+ method.label(after);
}
- public void invokeNative(DynamicMethod.NativeCall nativeCall, CompilerCallback receiver, ArgumentsCallback args, CompilerCallback closure) {
+ public void invokeNative(String name, DynamicMethod.NativeCall nativeCall,
+ int moduleGeneration, CompilerCallback receiver, ArgumentsCallback args,
+ CompilerCallback closure, CallType callType, boolean iterator) {
Class[] nativeSignature = nativeCall.getNativeSignature();
int leadingArgs = 0;
+ receiver.call(methodCompiler);
+ final int tmp = methodCompiler.getVariableCompiler().grabTempLocal();
+ method.astore(tmp);
+
+ // validate generation
+ method.aload(tmp);
+ method.ldc(moduleGeneration);
+ methodCompiler.invokeUtilityMethod("isGenerationEqual", sig(boolean.class, IRubyObject.class, int.class));
+
+ Label slow = new Label();
+ Label after = new Label();
+ method.ifne(slow);
+
if (nativeCall.isStatic()) {
if (nativeSignature.length > 0 && nativeSignature[0] == ThreadContext.class) {
methodCompiler.loadThreadContext();
leadingArgs++;
}
- receiver.call(methodCompiler);
+ method.aload(tmp);
leadingArgs++;
} else {
- receiver.call(methodCompiler);
+ method.aload(tmp);
method.checkcast(p(nativeCall.getNativeTarget()));
if (nativeSignature.length > 0 && nativeSignature[0] == ThreadContext.class) {
methodCompiler.loadThreadContext();
@@ -461,16 +559,38 @@ public void invokeNative(DynamicMethod.NativeCall nativeCall, CompilerCallback r
if (nativeSignature.length == leadingArgs + 1 && nativeSignature[leadingArgs] == Block.class) {
// ok, pass the block
} else {
- // doesn't receive block, dump it
+ // doesn't receive block, drop it
+ // note: have to evaluate it because it could be a & block arg
+ // with side effects
method.pop();
}
+ } else {
+ if (nativeSignature.length == leadingArgs + 1 && nativeSignature[leadingArgs] == Block.class) {
+ // needs a block
+ method.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class));
+ } else {
+ // ok with no block
+ }
}
if (nativeCall.isStatic()) {
method.invokestatic(p(nativeCall.getNativeTarget()), nativeCall.getNativeName(), sig(nativeCall.getNativeReturn(), nativeSignature));
} else {
method.invokevirtual(p(nativeCall.getNativeTarget()), nativeCall.getNativeName(), sig(nativeCall.getNativeReturn(), nativeSignature));
}
+
+ method.go_to(after);
+ method.label(slow);
+
+ invokeDynamic(name, new CompilerCallback() {
+ public void call(BodyCompiler context) {
+ method.aload(tmp);
+ }
+ }, args, callType, closure, iterator);
+
+ method.label(after);
+
+ methodCompiler.getVariableCompiler().releaseTempLocal();
}
public void invokeDynamic(String name, CompilerCallback receiverCallback, ArgumentsCallback argsCallback, CallType callType, CompilerCallback closureArg, boolean iterator) {
View
4 src/org/jruby/javasupport/util/RuntimeHelpers.java
@@ -1858,4 +1858,8 @@ public static RubyModule getSuperClassForDefined(Ruby runtime, RubyModule klazz)
return superklazz;
}
+
+ public static boolean isGenerationEqual(IRubyObject object, int generation) {
+ return object.getMetaClass().getCacheToken() == generation;
+ }
}
View
1 src/org/jruby/runtime/CallSite.java
@@ -41,6 +41,7 @@ public CallSite(String methodName, CallType callType) {
// binary typed calls
public abstract IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, long fixnum);
+ public abstract IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, double flote);
// no block
public abstract IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self);
View
6 src/org/jruby/runtime/callsite/CacheEntry.java
@@ -5,11 +5,11 @@
import org.jruby.internal.runtime.methods.UndefinedMethod;
public class CacheEntry {
- public static final CacheEntry NULL_CACHE = new CacheEntry(UndefinedMethod.INSTANCE, new Object());
+ public static final CacheEntry NULL_CACHE = new CacheEntry(UndefinedMethod.INSTANCE, 0);
public final DynamicMethod method;
- public final Object token;
+ public final int token;
- public CacheEntry(DynamicMethod method, Object token) {
+ public CacheEntry(DynamicMethod method, int token) {
this.method = method;
this.token = token;
}
View
5 src/org/jruby/runtime/callsite/CachingCallSite.java
@@ -3,6 +3,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
+import org.jruby.RubyFloat;
import org.jruby.RubyLocalJumpError;
import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.JumpException.BreakJump;
@@ -58,6 +59,10 @@ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject s
return call(context, caller, self, RubyFixnum.newFixnum(context.getRuntime(), fixnum));
}
+ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, double flote) {
+ return call(context, caller, self, RubyFloat.newFloat(context.getRuntime(), flote));
+ }
+
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject... args) {
RubyClass selfType = pollAndGetClass(context, self);
CacheEntry myCache = cache;
View
6 src/org/jruby/runtime/callsite/SuperCallSite.java
@@ -2,6 +2,7 @@
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
+import org.jruby.RubyFloat;
import org.jruby.RubyLocalJumpError;
import org.jruby.RubyModule;
import org.jruby.exceptions.JumpException;
@@ -39,6 +40,11 @@ public SuperCallSite() {
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, long fixnum) {
return call(context, caller, self, RubyFixnum.newFixnum(context.getRuntime(), fixnum));
}
+
+ @Override
+ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, double flote) {
+ return call(context, caller, self, RubyFloat.newFloat(context.getRuntime(), flote));
+ }
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject... args) {
RubyModule klazz = context.getFrameKlazz();

0 comments on commit a5503a4

Please sign in to comment.