diff --git a/core/src/main/java/org/jruby/RubyModule.java b/core/src/main/java/org/jruby/RubyModule.java index 0e4d06c9ff91..1e2b5d929104 100644 --- a/core/src/main/java/org/jruby/RubyModule.java +++ b/core/src/main/java/org/jruby/RubyModule.java @@ -5108,6 +5108,10 @@ private RaiseException cannotRemoveError(String id) { return getRuntime().newNameError(str(runtime, "cannot remove ", ids(runtime, id), " for ", types(runtime, this)), id); } + public static boolean testModuleMatch(ThreadContext context, IRubyObject arg0, int id) { + return arg0 instanceof RubyModule && ((RubyModule) arg0).id == id; + } + // ////////////////// INTERNAL MODULE VARIABLE API METHODS //////////////// diff --git a/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java b/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java index 1f59fffbc3e0..2118c6d18bb3 100644 --- a/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java +++ b/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java @@ -48,8 +48,9 @@ import org.jruby.ir.representations.BasicBlock; import org.jruby.ir.runtime.IRRuntimeHelpers; import org.jruby.ir.targets.IRBytecodeAdapter.BlockPassType; -import org.jruby.ir.targets.indy.Bootstrap; import org.jruby.ir.targets.indy.CallTraceSite; +import org.jruby.ir.targets.indy.CoverageSite; +import org.jruby.ir.targets.indy.MetaClassBootstrap; import org.jruby.parser.StaticScope; import org.jruby.runtime.ArgumentDescriptor; import org.jruby.runtime.Block; @@ -57,7 +58,6 @@ import org.jruby.runtime.DynamicScope; import org.jruby.runtime.Frame; import org.jruby.runtime.Helpers; -import org.jruby.runtime.RubyEvent; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; @@ -1515,7 +1515,7 @@ public void DefineMetaClassInstr(DefineMetaClassInstr definemetaclassinstr) { jvmAdapter().invokedynamic( "openMetaClass", sig(DynamicMethod.class, ThreadContext.class, IRubyObject.class, String.class, StaticScope.class), - Bootstrap.OPEN_META_CLASS, + MetaClassBootstrap.OPEN_META_CLASS, bodyHandle, scopeHandle, setScopeHandle, @@ -1738,7 +1738,7 @@ public void LineNumberInstr(LineNumberInstr linenumberinstr) { jvmAdapter().invokedynamic( "coverLine", sig(void.class, ThreadContext.class), - Bootstrap.coverLineHandle(), + CoverageSite.COVER_LINE_BOOTSTRAP, jvm.methodData().scope.getFile(), linenumberinstr.getLineNumber(), linenumberinstr.oneshot ? 1 : 0); diff --git a/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java b/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java index 1aac20c8cd83..2c20d13e35d0 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java @@ -26,47 +26,19 @@ package org.jruby.ir.targets.indy; -import com.headius.invokebinder.Binder; -import org.jruby.Ruby; -import org.jruby.RubyModule; import org.jruby.internal.runtime.methods.DynamicMethod; -import org.jruby.ir.JIT; -import org.jruby.ir.runtime.IRRuntimeHelpers; -import org.jruby.parser.StaticScope; -import org.jruby.runtime.Binding; import org.jruby.runtime.Block; -import org.jruby.runtime.CallType; -import org.jruby.runtime.CompiledIRBlockBody; -import org.jruby.runtime.DynamicScope; -import org.jruby.runtime.Frame; -import org.jruby.runtime.Helpers; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.callsite.CachingCallSite; -import org.jruby.runtime.callsite.FunctionalCachingCallSite; -import org.jruby.runtime.callsite.MonomorphicCallSite; -import org.jruby.runtime.callsite.VariableCachingCallSite; -import org.jruby.runtime.invokedynamic.MathLinker; -import org.jruby.runtime.opto.Invalidator; -import org.jruby.runtime.scope.DynamicScopeGenerator; 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.ConstantCallSite; -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.invoke.MutableCallSite; -import java.lang.invoke.SwitchPoint; import static java.lang.invoke.MethodHandles.Lookup; -import static java.lang.invoke.MethodHandles.constant; import static java.lang.invoke.MethodHandles.dropArguments; -import static java.lang.invoke.MethodHandles.insertArguments; -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; @@ -81,95 +53,12 @@ public class Bootstrap { private static final Logger LOG = LoggerFactory.getLogger(Bootstrap.class); private static final Lookup LOOKUP = MethodHandles.lookup(); - public static final Handle CALLSITE = new Handle( - Opcodes.H_INVOKESTATIC, - p(Bootstrap.class), - "callSite", - sig(CallSite.class, Lookup.class, String.class, MethodType.class, String.class, int.class), - false); - - public static CallSite callSite(Lookup lookup, String name, MethodType type, String id, int callType) { - return new ConstantCallSite(constant(CachingCallSite.class, callSite(id, callType))); - } - - private static CachingCallSite callSite(String id, int callType) { - switch (CallType.fromOrdinal(callType)) { - case NORMAL: - return new MonomorphicCallSite(id); - case FUNCTIONAL: - return new FunctionalCachingCallSite(id); - case VARIABLE: - return new VariableCachingCallSite(id); - default: - throw new RuntimeException("BUG: Unexpected call type " + callType + " in JVM6 invoke logic"); - } - } - - public static final Handle OPEN_META_CLASS = new Handle( - Opcodes.H_INVOKESTATIC, - p(Bootstrap.class), - "openMetaClass", - sig(CallSite.class, Lookup.class, String.class, MethodType.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, int.class, int.class, int.class), - false); - - @JIT - public static CallSite openMetaClass(Lookup lookup, String name, MethodType type, MethodHandle body, MethodHandle scope, MethodHandle setScope, int line, int dynscopeEliminated, int refinements) { - try { - StaticScope staticScope = (StaticScope) scope.invokeExact(); - return new ConstantCallSite(insertArguments(OPEN_META_CLASS_HANDLE, 4, body, staticScope, setScope, line, dynscopeEliminated == 1 ? true : false, refinements == 1 ? true : false)); - } catch (Throwable t) { - Helpers.throwException(t); - return null; - } - } - - private static final MethodHandle OPEN_META_CLASS_HANDLE = - Binder - .from(DynamicMethod.class, ThreadContext.class, IRubyObject.class, String.class, StaticScope.class, MethodHandle.class, StaticScope.class, MethodHandle.class, int.class, boolean.class, boolean.class) - .invokeStaticQuiet(LOOKUP, Bootstrap.class, "openMetaClass"); - - @JIT - public static DynamicMethod openMetaClass(ThreadContext context, IRubyObject object, String descriptor, StaticScope parent, MethodHandle body, StaticScope scope, MethodHandle setScope, int line, boolean dynscopeEliminated, boolean refinements) throws Throwable { - if (scope == null) { - scope = Helpers.restoreScope(descriptor, parent); - setScope.invokeExact(scope); - } - return IRRuntimeHelpers.newCompiledMetaClass(context, body, scope, object, line, dynscopeEliminated, refinements); - } - - /////////////////////////////////////////////////////////////////////////// - // Fixnum binding - - public static boolean testModuleMatch(ThreadContext context, IRubyObject arg0, int id) { - return arg0 instanceof RubyModule && ((RubyModule)arg0).id == id; - } - - public static Handle getFixnumOperatorHandle() { - return getBootstrapHandle("fixnumOperatorBootstrap", MathLinker.class, BOOTSTRAP_LONG_STRING_INT_SIG); - } - - public static Handle getFloatOperatorHandle() { - return getBootstrapHandle("floatOperatorBootstrap", MathLinker.class, BOOTSTRAP_DOUBLE_STRING_INT_SIG); - } - - public static Handle checkpointHandle() { - return getBootstrapHandle("checkpointBootstrap", BOOTSTRAP_BARE_SIG); - } - - public static Handle callInfoHandle() { - return getBootstrapHandle("callInfoBootstrap", BOOTSTRAP_INT_SIG); - } - - public static Handle coverLineHandle() { - return getBootstrapHandle("coverLineBootstrap", sig(CallSite.class, Lookup.class, String.class, MethodType.class, String.class, int.class, int.class)); - } - - public static Handle getHeapLocalHandle() { - return getBootstrapHandle("getHeapLocalBootstrap", BOOTSTRAP_INT_INT_SIG); + static String logMethod(DynamicMethod method) { + return "[#" + method.getSerialNumber() + " " + method.getImplementationClass().getMethodLocation() + "]"; } - public static Handle getHeapLocalOrNilHandle() { - return getBootstrapHandle("getHeapLocalOrNilBootstrap", BOOTSTRAP_INT_INT_SIG); + static String logBlock(Block block) { + return "[" + block.getBody().getFile() + ":" + block.getBody().getLine() + "]"; } public static Handle getBootstrapHandle(String name, String sig) { @@ -184,200 +73,4 @@ public static Handle getBootstrapHandle(String name, Class type, String sig) { sig, false); } - - public static CallSite checkpointBootstrap(Lookup lookup, String name, MethodType type) throws Throwable { - MutableCallSite site = new MutableCallSite(type); - MethodHandle handle = lookup.findStatic(Bootstrap.class, "checkpointFallback", methodType(void.class, MutableCallSite.class, ThreadContext.class)); - - handle = handle.bindTo(site); - site.setTarget(handle); - - return site; - } - - public static void checkpointFallback(MutableCallSite site, ThreadContext context) throws Throwable { - Ruby runtime = context.runtime; - Invalidator invalidator = runtime.getCheckpointInvalidator(); - - MethodHandle target = Binder - .from(void.class, ThreadContext.class) - .nop(); - MethodHandle fallback = lookup().findStatic(Bootstrap.class, "checkpointFallback", methodType(void.class, MutableCallSite.class, ThreadContext.class)); - fallback = fallback.bindTo(site); - - target = ((SwitchPoint)invalidator.getData()).guardWithTest(target, fallback); - - site.setTarget(target); - - // poll for events once since we've ended up back in fallback - context.pollThreadEvents(); - } - - public static CallSite callInfoBootstrap(Lookup lookup, String name, MethodType type, int callInfo) throws Throwable { - MethodHandle handle; - if (callInfo == 0) { - handle = lookup.findVirtual(ThreadContext.class, "clearCallInfo", methodType(void.class)); - } else { - handle = lookup.findStatic(IRRuntimeHelpers.class, "setCallInfo", methodType(void.class, ThreadContext.class, int.class)); - handle = insertArguments(handle, 1, callInfo); - } - - return new ConstantCallSite(handle); - } - - public static CallSite coverLineBootstrap(Lookup lookup, String name, MethodType type, String filename, int line, int oneshot) throws Throwable { - MutableCallSite site = new MutableCallSite(type); - MethodHandle handle = lookup.findStatic(Bootstrap.class, "coverLineFallback", methodType(void.class, MutableCallSite.class, ThreadContext.class, String.class, int.class, boolean.class)); - - handle = handle.bindTo(site); - handle = insertArguments(handle, 1, filename, line, oneshot != 0); - site.setTarget(handle); - - return site; - } - - public static void coverLineFallback(MutableCallSite site, ThreadContext context, String filename, int line, boolean oneshot) throws Throwable { - IRRuntimeHelpers.updateCoverage(context, filename, line); - - if (oneshot) site.setTarget(Binder.from(void.class, ThreadContext.class).dropAll().nop()); - } - - public static CallSite getHeapLocalBootstrap(Lookup lookup, String name, MethodType type, int depth, int location) throws Throwable { - // no null checking needed for method bodies - MethodHandle getter; - Binder binder = Binder - .from(type); - - if (depth == 0) { - if (location < DynamicScopeGenerator.SPECIALIZED_GETS.size()) { - getter = binder.invokeVirtualQuiet(LOOKUP, DynamicScopeGenerator.SPECIALIZED_GETS.get(location)); - } else { - getter = binder - .insert(1, location) - .invokeVirtualQuiet(LOOKUP, "getValueDepthZero"); - } - } else { - getter = binder - .insert(1, arrayOf(int.class, int.class), location, depth) - .invokeVirtualQuiet(LOOKUP, "getValue"); - } - - ConstantCallSite site = new ConstantCallSite(getter); - - return site; - } - - public static CallSite getHeapLocalOrNilBootstrap(Lookup lookup, String name, MethodType type, int depth, int location) throws Throwable { - MethodHandle getter; - Binder binder = Binder - .from(type) - .filter(1, LiteralValueBootstrap.contextValue(lookup, "nil", methodType(IRubyObject.class, ThreadContext.class)).dynamicInvoker()); - - if (depth == 0) { - if (location < DynamicScopeGenerator.SPECIALIZED_GETS_OR_NIL.size()) { - getter = binder.invokeVirtualQuiet(LOOKUP, DynamicScopeGenerator.SPECIALIZED_GETS_OR_NIL.get(location)); - } else { - getter = binder - .insert(1, location) - .invokeVirtualQuiet(LOOKUP, "getValueDepthZeroOrNil"); - } - } else { - getter = binder - .insert(1, arrayOf(int.class, int.class), location, depth) - .invokeVirtualQuiet(LOOKUP, "getValueOrNil"); - } - - ConstantCallSite site = new ConstantCallSite(getter); - - return site; - } - - public static Handle prepareBlock() { - return new Handle( - Opcodes.H_INVOKESTATIC, - p(Bootstrap.class), - "prepareBlock", - sig(CallSite.class, Lookup.class, String.class, - MethodType.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String.class, long.class, - String.class, int.class, String.class), - false); - } - - public static CallSite prepareBlock(Lookup lookup, String name, MethodType type, - MethodHandle bodyHandle, MethodHandle scopeHandle, MethodHandle setScopeHandle, MethodHandle parentHandle, String scopeDescriptor, long encodedSignature, - String file, int line, String encodedArgumentDescriptors - ) throws Throwable { - StaticScope staticScope = (StaticScope) scopeHandle.invokeExact(); - - if (staticScope == null) { - staticScope = Helpers.restoreScope(scopeDescriptor, (StaticScope) parentHandle.invokeExact()); - setScopeHandle.invokeExact(staticScope); - } - - CompiledIRBlockBody body = new CompiledIRBlockBody(bodyHandle, staticScope, file, line, encodedArgumentDescriptors, encodedSignature); - - Binder binder = Binder.from(type); - - binder = binder.fold(FRAME_SCOPE_BINDING); - - // This optimization can't happen until we can see into the method we're calling to know if it reifies the block - if (false) { - /* - if (needsBinding) { - if (needsFrame) { - FullInterpreterContext fic = scope.getExecutionContext(); - if (fic.needsBinding()) { - if (fic.needsFrame()) { - binder = binder.fold(FRAME_SCOPE_BINDING); - } else { - binder = binder.fold(SCOPE_BINDING); - } - } else { - if (needsFrame) { - binder = binder.fold(FRAME_BINDING); - } else { - binder = binder.fold(SELF_BINDING); - } - }*/ - } - - MethodHandle blockMaker = binder.drop(1, 3) - .append(body) - .invoke(CONSTRUCT_BLOCK); - - return new ConstantCallSite(blockMaker); - } - - static String logMethod(DynamicMethod method) { - return "[#" + method.getSerialNumber() + " " + method.getImplementationClass().getMethodLocation() + "]"; - } - - static String logBlock(Block block) { - return "[" + block.getBody().getFile() + ":" + block.getBody().getLine() + "]"; - } - - private static final Binder BINDING_MAKER_BINDER = Binder.from(Binding.class, ThreadContext.class, IRubyObject.class, DynamicScope.class); - - private static final MethodHandle FRAME_SCOPE_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "newFrameScopeBinding"); - - private static final MethodHandle FRAME_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "frameBinding"); - public static Binding frameBinding(ThreadContext context, IRubyObject self, DynamicScope scope) { - Frame frame = context.getCurrentFrame().capture(); - return new Binding(self, frame, frame.getVisibility()); - } - - private static final MethodHandle SCOPE_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "scopeBinding"); - public static Binding scopeBinding(ThreadContext context, IRubyObject self, DynamicScope scope) { - return new Binding(self, scope); - } - - private static final MethodHandle SELF_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "selfBinding"); - public static Binding selfBinding(ThreadContext context, IRubyObject self, DynamicScope scope) { - return new Binding(self); - } - - private static final MethodHandle CONSTRUCT_BLOCK = Binder.from(Block.class, Binding.class, CompiledIRBlockBody.class).invokeStaticQuiet(LOOKUP, Bootstrap.class, "constructBlock"); - public static Block constructBlock(Binding binding, CompiledIRBlockBody body) throws Throwable { - return new Block(body, binding); - } } diff --git a/core/src/main/java/org/jruby/ir/targets/indy/CallInfoBootstrap.java b/core/src/main/java/org/jruby/ir/targets/indy/CallInfoBootstrap.java new file mode 100644 index 000000000000..787ff6606ae2 --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/CallInfoBootstrap.java @@ -0,0 +1,30 @@ +package org.jruby.ir.targets.indy; + +import org.jruby.ir.runtime.IRRuntimeHelpers; +import org.jruby.runtime.ThreadContext; +import org.objectweb.asm.Handle; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static java.lang.invoke.MethodHandles.insertArguments; +import static java.lang.invoke.MethodType.methodType; + +public class CallInfoBootstrap { + public static final Handle CALL_INFO_BOOTSTRAP = Bootstrap.getBootstrapHandle("callInfoBootstrap", Bootstrap.BOOTSTRAP_INT_SIG); + + 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)); + } else { + handle = lookup.findStatic(IRRuntimeHelpers.class, "setCallInfo", methodType(void.class, ThreadContext.class, int.class)); + handle = insertArguments(handle, 1, callInfo); + } + + return new ConstantCallSite(handle); + } +} diff --git a/core/src/main/java/org/jruby/ir/targets/indy/CallSiteCacheBootstrap.java b/core/src/main/java/org/jruby/ir/targets/indy/CallSiteCacheBootstrap.java new file mode 100644 index 000000000000..195d94112791 --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/CallSiteCacheBootstrap.java @@ -0,0 +1,44 @@ +package org.jruby.ir.targets.indy; + +import org.jruby.runtime.CallType; +import org.jruby.runtime.callsite.CachingCallSite; +import org.jruby.runtime.callsite.FunctionalCachingCallSite; +import org.jruby.runtime.callsite.MonomorphicCallSite; +import org.jruby.runtime.callsite.VariableCachingCallSite; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static java.lang.invoke.MethodHandles.constant; +import static org.jruby.util.CodegenUtils.p; +import static org.jruby.util.CodegenUtils.sig; + +public class CallSiteCacheBootstrap { + public static final Handle CALLSITE = new Handle( + Opcodes.H_INVOKESTATIC, + p(Bootstrap.class), + "callSite", + sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class, int.class), + false); + + public static CallSite callSite(MethodHandles.Lookup lookup, String name, MethodType type, String id, int callType) { + return new ConstantCallSite(constant(CachingCallSite.class, callSite(id, callType))); + } + + private static CachingCallSite callSite(String id, int callType) { + switch (CallType.fromOrdinal(callType)) { + case NORMAL: + return new MonomorphicCallSite(id); + case FUNCTIONAL: + return new FunctionalCachingCallSite(id); + case VARIABLE: + return new VariableCachingCallSite(id); + default: + throw new RuntimeException("BUG: Unexpected call type " + callType + " in JVM6 invoke logic"); + } + } +} diff --git a/core/src/main/java/org/jruby/ir/targets/indy/CheckpointSite.java b/core/src/main/java/org/jruby/ir/targets/indy/CheckpointSite.java new file mode 100644 index 000000000000..9f54620bb609 --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/CheckpointSite.java @@ -0,0 +1,53 @@ +package org.jruby.ir.targets.indy; + +import com.headius.invokebinder.Binder; +import org.jruby.Ruby; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.opto.Invalidator; +import org.objectweb.asm.Handle; + +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 java.lang.invoke.SwitchPoint; + +import static java.lang.invoke.MethodHandles.lookup; +import static java.lang.invoke.MethodType.methodType; + +public class CheckpointSite extends MutableCallSite { + public static final Handle CHECKPOINT_BOOTSTRAP = Bootstrap.getBootstrapHandle("checkpointBootstrap", Bootstrap.BOOTSTRAP_BARE_SIG); + + public CheckpointSite(MethodType type) { + super(type); + } + + public static CallSite checkpointBootstrap(MethodHandles.Lookup lookup, String name, MethodType type) throws Throwable { + CheckpointSite site = new CheckpointSite(type); + MethodHandle handle = lookup.findVirtual(CheckpointSite.class, "checkpointFallback", methodType(void.class, ThreadContext.class)); + + handle = handle.bindTo(site); + site.setTarget(handle); + + return site; + } + + public void checkpointFallback(ThreadContext context) throws Throwable { + Ruby runtime = context.runtime; + Invalidator invalidator = runtime.getCheckpointInvalidator(); + + MethodHandle target = Binder + .from(void.class, ThreadContext.class) + .nop(); + MethodHandle fallback = lookup().findStatic(Bootstrap.class, "checkpointFallback", methodType(void.class, MutableCallSite.class, ThreadContext.class)); + fallback = fallback.bindTo(this); + + target = ((SwitchPoint)invalidator.getData()).guardWithTest(target, fallback); + + this.setTarget(target); + + // poll for events once since we've ended up back in fallback + context.pollThreadEvents(); + } +} diff --git a/core/src/main/java/org/jruby/ir/targets/indy/ConstructBlockBootstrap.java b/core/src/main/java/org/jruby/ir/targets/indy/ConstructBlockBootstrap.java new file mode 100644 index 000000000000..89341fb0fff3 --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/ConstructBlockBootstrap.java @@ -0,0 +1,107 @@ +package org.jruby.ir.targets.indy; + +import com.headius.invokebinder.Binder; +import org.jruby.ir.runtime.IRRuntimeHelpers; +import org.jruby.parser.StaticScope; +import org.jruby.runtime.Binding; +import org.jruby.runtime.Block; +import org.jruby.runtime.CompiledIRBlockBody; +import org.jruby.runtime.DynamicScope; +import org.jruby.runtime.Frame; +import org.jruby.runtime.Helpers; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static org.jruby.util.CodegenUtils.p; +import static org.jruby.util.CodegenUtils.sig; + +public class ConstructBlockBootstrap { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + private static final Binder BINDING_MAKER_BINDER = Binder.from(Binding.class, ThreadContext.class, IRubyObject.class, DynamicScope.class); + private static final MethodHandle SELF_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "selfBinding"); + private static final MethodHandle SCOPE_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "scopeBinding"); + private static final MethodHandle FRAME_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "frameBinding"); + private static final MethodHandle FRAME_SCOPE_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "newFrameScopeBinding"); + private static final MethodHandle CONSTRUCT_BLOCK = Binder.from(Block.class, Binding.class, CompiledIRBlockBody.class).invokeStaticQuiet(LOOKUP, Bootstrap.class, "constructBlock"); + + public static Handle prepareBlock() { + return new Handle( + Opcodes.H_INVOKESTATIC, + p(Bootstrap.class), + "prepareBlock", + sig(CallSite.class, MethodHandles.Lookup.class, String.class, + MethodType.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String.class, long.class, + String.class, int.class, String.class), + false); + } + + public static CallSite prepareBlock(MethodHandles.Lookup lookup, String name, MethodType type, + MethodHandle bodyHandle, MethodHandle scopeHandle, MethodHandle setScopeHandle, MethodHandle parentHandle, String scopeDescriptor, long encodedSignature, + String file, int line, String encodedArgumentDescriptors + ) throws Throwable { + StaticScope staticScope = (StaticScope) scopeHandle.invokeExact(); + + if (staticScope == null) { + staticScope = Helpers.restoreScope(scopeDescriptor, (StaticScope) parentHandle.invokeExact()); + setScopeHandle.invokeExact(staticScope); + } + + CompiledIRBlockBody body = new CompiledIRBlockBody(bodyHandle, staticScope, file, line, encodedArgumentDescriptors, encodedSignature); + + Binder binder = Binder.from(type); + + binder = binder.fold(FRAME_SCOPE_BINDING); + + // This optimization can't happen until we can see into the method we're calling to know if it reifies the block + if (false) { + /* + if (needsBinding) { + if (needsFrame) { + FullInterpreterContext fic = scope.getExecutionContext(); + if (fic.needsBinding()) { + if (fic.needsFrame()) { + binder = binder.fold(FRAME_SCOPE_BINDING); + } else { + binder = binder.fold(SCOPE_BINDING); + } + } else { + if (needsFrame) { + binder = binder.fold(FRAME_BINDING); + } else { + binder = binder.fold(SELF_BINDING); + } + }*/ + } + + MethodHandle blockMaker = binder.drop(1, 3) + .append(body) + .invoke(CONSTRUCT_BLOCK); + + return new ConstantCallSite(blockMaker); + } + + public static Binding frameBinding(ThreadContext context, IRubyObject self, DynamicScope scope) { + Frame frame = context.getCurrentFrame().capture(); + return new Binding(self, frame, frame.getVisibility()); + } + + public static Binding scopeBinding(ThreadContext context, IRubyObject self, DynamicScope scope) { + return new Binding(self, scope); + } + + public static Binding selfBinding(ThreadContext context, IRubyObject self, DynamicScope scope) { + return new Binding(self); + } + + public static Block constructBlock(Binding binding, CompiledIRBlockBody body) throws Throwable { + return new Block(body, binding); + } +} diff --git a/core/src/main/java/org/jruby/ir/targets/indy/CoverageSite.java b/core/src/main/java/org/jruby/ir/targets/indy/CoverageSite.java new file mode 100644 index 000000000000..cd80577072db --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/CoverageSite.java @@ -0,0 +1,37 @@ +package org.jruby.ir.targets.indy; + +import com.headius.invokebinder.Binder; +import org.jruby.ir.runtime.IRRuntimeHelpers; +import org.jruby.runtime.ThreadContext; +import org.objectweb.asm.Handle; + +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.insertArguments; +import static java.lang.invoke.MethodType.methodType; +import static org.jruby.util.CodegenUtils.sig; + +public class CoverageSite { + public static final Handle COVER_LINE_BOOTSTRAP = Bootstrap.getBootstrapHandle("coverLineBootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class, int.class, int.class)); + + public static CallSite coverLineBootstrap(MethodHandles.Lookup lookup, String name, MethodType type, String filename, int line, int oneshot) throws Throwable { + MutableCallSite site = new MutableCallSite(type); + MethodHandle handle = lookup.findStatic(Bootstrap.class, "coverLineFallback", methodType(void.class, MutableCallSite.class, ThreadContext.class, String.class, int.class, boolean.class)); + + handle = handle.bindTo(site); + handle = insertArguments(handle, 1, filename, line, oneshot != 0); + site.setTarget(handle); + + return site; + } + + public static void coverLineFallback(MutableCallSite site, ThreadContext context, String filename, int line, boolean oneshot) throws Throwable { + IRRuntimeHelpers.updateCoverage(context, filename, line); + + if (oneshot) site.setTarget(Binder.from(void.class, ThreadContext.class).dropAll().nop()); + } +} diff --git a/core/src/main/java/org/jruby/ir/targets/indy/HeapVariableBootstrap.java b/core/src/main/java/org/jruby/ir/targets/indy/HeapVariableBootstrap.java new file mode 100644 index 000000000000..1dc8123fcf2a --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/HeapVariableBootstrap.java @@ -0,0 +1,72 @@ +package org.jruby.ir.targets.indy; + +import com.headius.invokebinder.Binder; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.scope.DynamicScopeGenerator; +import org.objectweb.asm.Handle; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static java.lang.invoke.MethodType.methodType; +import static org.jruby.runtime.Helpers.arrayOf; + +public class HeapVariableBootstrap { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + public static final Handle GET_HEAP_LOCAL_OR_NIL_BOOTSTRAP = Bootstrap.getBootstrapHandle("getHeapLocalOrNilBootstrap", Bootstrap.BOOTSTRAP_INT_INT_SIG); + public static final Handle GET_HEAP_LOCAL_BOOTSTRAP = Bootstrap.getBootstrapHandle("getHeapLocalBootstrap", Bootstrap.BOOTSTRAP_INT_INT_SIG); + + public static CallSite getHeapLocalBootstrap(MethodHandles.Lookup lookup, String name, MethodType type, int depth, int location) throws Throwable { + // no null checking needed for method bodies + MethodHandle getter; + Binder binder = Binder + .from(type); + + if (depth == 0) { + if (location < DynamicScopeGenerator.SPECIALIZED_GETS.size()) { + getter = binder.invokeVirtualQuiet(LOOKUP, DynamicScopeGenerator.SPECIALIZED_GETS.get(location)); + } else { + getter = binder + .insert(1, location) + .invokeVirtualQuiet(LOOKUP, "getValueDepthZero"); + } + } else { + getter = binder + .insert(1, arrayOf(int.class, int.class), location, depth) + .invokeVirtualQuiet(LOOKUP, "getValue"); + } + + ConstantCallSite site = new ConstantCallSite(getter); + + return site; + } + + public static CallSite getHeapLocalOrNilBootstrap(MethodHandles.Lookup lookup, String name, MethodType type, int depth, int location) throws Throwable { + MethodHandle getter; + Binder binder = Binder + .from(type) + .filter(1, LiteralValueBootstrap.contextValue(lookup, "nil", methodType(IRubyObject.class, ThreadContext.class)).dynamicInvoker()); + + if (depth == 0) { + if (location < DynamicScopeGenerator.SPECIALIZED_GETS_OR_NIL.size()) { + getter = binder.invokeVirtualQuiet(LOOKUP, DynamicScopeGenerator.SPECIALIZED_GETS_OR_NIL.get(location)); + } else { + getter = binder + .insert(1, location) + .invokeVirtualQuiet(LOOKUP, "getValueDepthZeroOrNil"); + } + } else { + getter = binder + .insert(1, arrayOf(int.class, int.class), location, depth) + .invokeVirtualQuiet(LOOKUP, "getValueOrNil"); + } + + ConstantCallSite site = new ConstantCallSite(getter); + + return site; + } +} diff --git a/core/src/main/java/org/jruby/ir/targets/indy/IndyBlockCompiler.java b/core/src/main/java/org/jruby/ir/targets/indy/IndyBlockCompiler.java index a5d8662f8a04..704fc5e57e45 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/IndyBlockCompiler.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/IndyBlockCompiler.java @@ -49,6 +49,6 @@ public void prepareBlock(IRClosure closure, String parentScopeField, Handle hand long encodedSignature = signature.encode(); compiler.adapter.invokedynamic(handle.getName(), sig(Block.class, ThreadContext.class, IRubyObject.class, DynamicScope.class), - Bootstrap.prepareBlock(), handle, scopeHandle, setScopeHandle, parentScopeHandle, scopeDescriptor, encodedSignature, file, line, encodedArgumentDescriptors); + ConstructBlockBootstrap.prepareBlock(), handle, scopeHandle, setScopeHandle, parentScopeHandle, scopeDescriptor, encodedSignature, file, line, encodedArgumentDescriptors); } } diff --git a/core/src/main/java/org/jruby/ir/targets/indy/IndyCheckpointCompiler.java b/core/src/main/java/org/jruby/ir/targets/indy/IndyCheckpointCompiler.java index 86c42a211b9b..2b375de36ce5 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/IndyCheckpointCompiler.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/IndyCheckpointCompiler.java @@ -18,6 +18,6 @@ public void checkpoint() { compiler.adapter.invokedynamic( "checkpoint", sig(void.class, ThreadContext.class), - Bootstrap.checkpointHandle()); + CheckpointSite.CHECKPOINT_BOOTSTRAP); } } diff --git a/core/src/main/java/org/jruby/ir/targets/indy/IndyInvocationCompiler.java b/core/src/main/java/org/jruby/ir/targets/indy/IndyInvocationCompiler.java index 3b22b36698c9..3c980ff2719c 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/IndyInvocationCompiler.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/IndyInvocationCompiler.java @@ -16,6 +16,7 @@ import org.jruby.runtime.MethodIndex; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.invokedynamic.MathLinker; import org.jruby.util.CodegenUtils; import org.jruby.util.JavaNameMangler; @@ -78,7 +79,7 @@ public void invokeOtherOneFixnum(String file, CallBase call, long fixnum) { compiler.adapter.invokedynamic( "fixnumOperator:" + JavaNameMangler.mangleMethodName(id), signature, - Bootstrap.getFixnumOperatorHandle(), + MathLinker.FIXNUM_OPERATOR_BOOTSTRAP, fixnum, call.getCallType().ordinal(), file, @@ -99,7 +100,7 @@ public void invokeOtherOneFloat(String file, CallBase call, double flote) { compiler.adapter.invokedynamic( "floatOperator:" + JavaNameMangler.mangleMethodName(id), signature, - Bootstrap.getFloatOperatorHandle(), + MathLinker.FLOAT_OPERATOR_BOOTSTRAP, flote, call.getCallType().ordinal(), file, @@ -212,6 +213,6 @@ public void asString(AsStringInstr call, String scopeFieldName, String file) { @Override public void setCallInfo(int flags) { compiler.loadContext(); - compiler.adapter.invokedynamic("callInfo", sig(void.class, ThreadContext.class), Bootstrap.callInfoHandle(), flags); + compiler.adapter.invokedynamic("callInfo", sig(void.class, ThreadContext.class), CallInfoBootstrap.CALL_INFO_BOOTSTRAP, flags); } } diff --git a/core/src/main/java/org/jruby/ir/targets/indy/IndyLocalVariableCompiler.java b/core/src/main/java/org/jruby/ir/targets/indy/IndyLocalVariableCompiler.java index eb5fa3632047..47aa232d5b54 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/IndyLocalVariableCompiler.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/IndyLocalVariableCompiler.java @@ -26,12 +26,12 @@ public void getHeapLocal(int depth, int location) { normalLocalVariableCompiler.getHeapLocal(depth, location); return; } - compiler.adapter.invokedynamic("getHeapLocal", sig(IRubyObject.class, DynamicScope.class), Bootstrap.getHeapLocalHandle(), depth, location); + compiler.adapter.invokedynamic("getHeapLocal", sig(IRubyObject.class, DynamicScope.class), HeapVariableBootstrap.GET_HEAP_LOCAL_BOOTSTRAP, depth, location); } @Override public void getHeapLocalOrNil(int depth, int location) { compiler.loadContext(); - compiler.adapter.invokedynamic("getHeapLocalOrNil", sig(IRubyObject.class, DynamicScope.class, ThreadContext.class), Bootstrap.getHeapLocalOrNilHandle(), depth, location); + compiler.adapter.invokedynamic("getHeapLocalOrNil", sig(IRubyObject.class, DynamicScope.class, ThreadContext.class), HeapVariableBootstrap.GET_HEAP_LOCAL_OR_NIL_BOOTSTRAP, depth, location); } } diff --git a/core/src/main/java/org/jruby/ir/targets/indy/IndyValueCompiler.java b/core/src/main/java/org/jruby/ir/targets/indy/IndyValueCompiler.java index 014076e9641a..08e414b99862 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/IndyValueCompiler.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/IndyValueCompiler.java @@ -155,7 +155,7 @@ public void pushCallSite(String className, String siteName, String scopeFieldNam if (!specialSite) { // use indy to cache the site object - method.invokedynamic("callSite", sig(CachingCallSite.class), Bootstrap.CALLSITE, call.getId(), callType.ordinal()); + method.invokedynamic("callSite", sig(CachingCallSite.class), CallSiteCacheBootstrap.CALLSITE, call.getId(), callType.ordinal()); return; } diff --git a/core/src/main/java/org/jruby/ir/targets/indy/MetaClassBootstrap.java b/core/src/main/java/org/jruby/ir/targets/indy/MetaClassBootstrap.java new file mode 100644 index 000000000000..45e4f6dd4177 --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/MetaClassBootstrap.java @@ -0,0 +1,57 @@ +package org.jruby.ir.targets.indy; + +import com.headius.invokebinder.Binder; +import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.ir.JIT; +import org.jruby.ir.runtime.IRRuntimeHelpers; +import org.jruby.parser.StaticScope; +import org.jruby.runtime.Helpers; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static java.lang.invoke.MethodHandles.insertArguments; +import static org.jruby.util.CodegenUtils.p; +import static org.jruby.util.CodegenUtils.sig; + +public class MetaClassBootstrap { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + + public static final Handle OPEN_META_CLASS = new Handle( + Opcodes.H_INVOKESTATIC, + p(MetaClassBootstrap.class), + "openMetaClass", + sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, int.class, int.class, int.class), + false); + private static final MethodHandle OPEN_META_CLASS_HANDLE = + Binder + .from(DynamicMethod.class, ThreadContext.class, IRubyObject.class, String.class, StaticScope.class, MethodHandle.class, StaticScope.class, MethodHandle.class, int.class, boolean.class, boolean.class) + .invokeStaticQuiet(LOOKUP, MetaClassBootstrap.class, "openMetaClass"); + + @JIT + public static CallSite openMetaClass(MethodHandles.Lookup lookup, String name, MethodType type, MethodHandle body, MethodHandle scope, MethodHandle setScope, int line, int dynscopeEliminated, int refinements) { + try { + StaticScope staticScope = (StaticScope) scope.invokeExact(); + return new ConstantCallSite(insertArguments(OPEN_META_CLASS_HANDLE, 4, body, staticScope, setScope, line, dynscopeEliminated == 1 ? true : false, refinements == 1 ? true : false)); + } catch (Throwable t) { + Helpers.throwException(t); + return null; + } + } + + @JIT + public static DynamicMethod openMetaClass(ThreadContext context, IRubyObject object, String descriptor, StaticScope parent, MethodHandle body, StaticScope scope, MethodHandle setScope, int line, boolean dynscopeEliminated, boolean refinements) throws Throwable { + if (scope == null) { + scope = Helpers.restoreScope(descriptor, parent); + setScope.invokeExact(scope); + } + return IRRuntimeHelpers.newCompiledMetaClass(context, body, scope, object, line, dynscopeEliminated, refinements); + } +} diff --git a/core/src/main/java/org/jruby/runtime/invokedynamic/MathLinker.java b/core/src/main/java/org/jruby/runtime/invokedynamic/MathLinker.java index 5c02c89e8ace..1c6e1b3e0bff 100644 --- a/core/src/main/java/org/jruby/runtime/invokedynamic/MathLinker.java +++ b/core/src/main/java/org/jruby/runtime/invokedynamic/MathLinker.java @@ -29,10 +29,8 @@ import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.SwitchPoint; -import java.util.List; import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; @@ -43,6 +41,7 @@ import org.jruby.RubyFixnum; import org.jruby.RubyFloat; import org.jruby.RubyNumeric; +import org.jruby.ir.targets.indy.Bootstrap; import org.jruby.ir.targets.simple.NormalInvokeSite; import org.jruby.runtime.CallType; import org.jruby.runtime.MethodIndex; @@ -54,11 +53,14 @@ import org.jruby.util.cli.Options; import org.jruby.util.log.Logger; import org.jruby.util.log.LoggerFactory; +import org.objectweb.asm.Handle; public class MathLinker { private static final Logger LOG = LoggerFactory.getLogger(MathLinker.class); public static final Lookup LOOKUP = lookup(); + public static final Handle FIXNUM_OPERATOR_BOOTSTRAP = Bootstrap.getBootstrapHandle("fixnumOperatorBootstrap", MathLinker.class, Bootstrap.BOOTSTRAP_LONG_STRING_INT_SIG); + public static final Handle FLOAT_OPERATOR_BOOTSTRAP = Bootstrap.getBootstrapHandle("floatOperatorBootstrap", MathLinker.class, Bootstrap.BOOTSTRAP_DOUBLE_STRING_INT_SIG); static { // enable DEBUG output if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.setDebugEnable(true); @@ -372,5 +374,4 @@ private static CacheEntry searchWithCache(final String operator, IRubyObject cal } return entry; } - }