# HG changeset patch # User Thomas Wuerthinger # Date 1427898988 -7200 # Node ID 45d46b136777b44fb66ce3c6014742e3f2aae792 # Parent 51f2e71b57aace219e6d1936b78b691ac9a5c7ac# Parent 30d6b0cd1394f0f8933364b8fb8223e25e002e50 Merge. diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/EconomyHighTier.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/EconomyHighTier.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/EconomyHighTier.java Wed Apr 01 16:36:28 2015 +0200 @@ -36,6 +36,11 @@ if (ImmutableCode.getValue()) { canonicalizer.disableReadCanonicalization(); } + + if (OptCanonicalizer.getValue()) { + appendPhase(canonicalizer); + } + appendPhase(new CleanTypeProfileProxyPhase(canonicalizer)); appendPhase(new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER)); } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java Wed Apr 01 16:36:28 2015 +0200 @@ -213,7 +213,6 @@ protected final MetaAccessProvider metaAccess; private final List registrations; - private final Thread registrationThread; /** * The minimum {@linkplain InvocationPluginIdHolder#getInvocationPluginId() id} for a method @@ -234,7 +233,6 @@ private InvocationPlugins parent; private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { - this.registrationThread = Thread.currentThread(); this.metaAccess = metaAccess; this.registrations = new ArrayList<>(INITIAL_PLUGIN_CAPACITY); InvocationPlugins p = parent; @@ -263,7 +261,6 @@ * registered for {@code method}. */ public void register(InvocationPlugin plugin, Class declaringClass, String name, Class... argumentTypes) { - assert Thread.currentThread() == registrationThread : "invocation plugin registration must be single threaded"; MethodInfo methodInfo = new MethodInfo(plugin, declaringClass, name, argumentTypes); assert Checker.check(this, methodInfo, plugin); assert plugins == null : "invocation plugin registration is closed"; diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Wed Apr 01 16:36:28 2015 +0200 @@ -31,7 +31,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.api.replacements.*; import com.oracle.graal.api.runtime.*; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.*; +import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.hotspot.word.*; @@ -171,7 +171,7 @@ wordTypes = new HotSpotWordTypes(metaAccess, target.wordKind); } try (InitTimer rt = timer("create GraphBuilderPhase plugins")) { - plugins = HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch); + plugins = createGraphBuilderPlugins(runtime, target, constantReflection, foreignCalls, metaAccess, snippetReflection, replacements, wordTypes, stampProvider); replacements.setGraphBuilderPlugins(plugins); } try (InitTimer rt = timer("create Suites provider")) { @@ -184,6 +184,12 @@ } } + protected Plugins createGraphBuilderPlugins(HotSpotGraalRuntimeProvider runtime, TargetDescription target, HotSpotConstantReflectionProvider constantReflection, + HotSpotHostForeignCallsProvider foreignCalls, HotSpotMetaAccessProvider metaAccess, HotSpotSnippetReflectionProvider snippetReflection, HotSpotReplacementsImpl replacements, + HotSpotWordTypes wordTypes, HotSpotStampProvider stampProvider) { + return HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch); + } + protected AMD64HotSpotBackend createBackend(HotSpotGraalRuntimeProvider runtime, HotSpotProviders providers) { return new AMD64HotSpotBackend(runtime, providers); } @@ -260,15 +266,15 @@ } else { /* * System V Application Binary Interface, AMD64 Architecture Processor Supplement - * + * * Draft Version 0.96 - * + * * http://www.uclibc.org/docs/psABI-x86_64.pdf - * + * * 3.2.1 - * + * * ... - * + * * This subsection discusses usage of each register. Registers %rbp, %rbx and %r12 * through %r15 "belong" to the calling function and the called function is required to * preserve their values. In other words, a called function must preserve these diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Wed Apr 01 16:36:28 2015 +0200 @@ -27,7 +27,7 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.api.runtime.*; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.*; +import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.hotspot.word.*; @@ -68,7 +68,7 @@ HotSpotReplacementsImpl replacements = new HotSpotReplacementsImpl(p, snippetReflection, runtime.getConfig(), target); HotSpotDisassemblerProvider disassembler = new HotSpotDisassemblerProvider(runtime); HotSpotWordTypes wordTypes = new HotSpotWordTypes(metaAccess, target.wordKind); - Plugins plugins = HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch); + Plugins plugins = createGraphBuilderPlugins(runtime, target, metaAccess, constantReflection, foreignCalls, stampProvider, snippetReflection, replacements, wordTypes); replacements.setGraphBuilderPlugins(plugins); HotSpotSuitesProvider suites = createSuites(runtime, plugins); HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection, @@ -77,6 +77,12 @@ return createBackend(runtime, providers); } + protected Plugins createGraphBuilderPlugins(HotSpotGraalRuntimeProvider runtime, TargetDescription target, HotSpotMetaAccessProvider metaAccess, + HotSpotConstantReflectionProvider constantReflection, HotSpotForeignCallsProvider foreignCalls, HotSpotStampProvider stampProvider, + HotSpotSnippetReflectionProvider snippetReflection, HotSpotReplacementsImpl replacements, HotSpotWordTypes wordTypes) { + return HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch); + } + protected HotSpotSuitesProvider createSuites(HotSpotGraalRuntimeProvider runtime, Plugins plugins) { return new HotSpotSuitesProvider(runtime, plugins); } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Wed Apr 01 16:36:28 2015 +0200 @@ -47,6 +47,7 @@ import com.oracle.graal.nodes.HeapAccess.BarrierType; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.util.*; import com.oracle.graal.options.*; import com.oracle.graal.replacements.*; import com.oracle.graal.word.*; @@ -124,7 +125,7 @@ r.register1(query.name(), Receiver.class, new InvocationPlugin() { public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { ValueNode javaClass = receiver.get(); - ValueNode folded = ClassQueryNode.tryFold(javaClass, query, b.getMetaAccess(), b.getConstantReflection()); + ValueNode folded = ClassQueryNode.tryFold(GraphUtil.originalValue(javaClass), query, b.getMetaAccess(), b.getConstantReflection()); if (folded != null) { b.addPush(query.returnKind, folded); } else { @@ -137,7 +138,7 @@ r.register2("cast", Receiver.class, Object.class, new InvocationPlugin() { public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) { ValueNode javaClass = receiver.get(); - ValueNode folded = ClassCastNode.tryFold(javaClass, object, b.getConstantReflection(), b.getAssumptions()); + ValueNode folded = ClassCastNode.tryFold(GraphUtil.originalValue(javaClass), object, b.getConstantReflection(), b.getAssumptions()); if (folded != null) { b.addPush(Kind.Object, folded); } else { @@ -151,7 +152,7 @@ private static void registerCallSitePlugins(InvocationPlugins plugins) { InvocationPlugin plugin = new InvocationPlugin() { public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { - ValueNode callSite = receiver.get(); + ValueNode callSite = GraphUtil.originalValue(receiver.get()); ValueNode folded = CallSiteTargetNode.tryFold(callSite, b.getMetaAccess(), b.getAssumptions()); if (folded != null) { b.addPush(Kind.Object, folded); diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaFieldImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaFieldImpl.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaFieldImpl.java Wed Apr 01 16:36:28 2015 +0200 @@ -51,7 +51,7 @@ private final LocationIdentity locationIdentity = new FieldLocationIdentity(this); public static class FieldLocationIdentity extends LocationIdentity { - HotSpotResolvedJavaFieldImpl inner; + HotSpotResolvedJavaField inner; public FieldLocationIdentity(HotSpotResolvedJavaFieldImpl inner) { super(false); @@ -78,7 +78,7 @@ @Override public String toString() { - return inner.name; + return inner.getName(); } } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassCastNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassCastNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassCastNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -29,7 +29,6 @@ import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.replacements.nodes.*; /** @@ -67,9 +66,8 @@ } public static ValueNode tryFold(ValueNode javaClass, ValueNode object, ConstantReflectionProvider constantReflection, Assumptions assumptions) { - ValueNode value = GraphUtil.originalValue(javaClass); - if (value.isConstant()) { - ResolvedJavaType type = constantReflection.asJavaType(value.asConstant()); + if (javaClass != null && javaClass.isConstant()) { + ResolvedJavaType type = constantReflection.asJavaType(javaClass.asConstant()); if (type != null && !type.isPrimitive()) { return CheckCastNode.create(type, object, null, false, assumptions); } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassQueryNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassQueryNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassQueryNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -30,7 +30,6 @@ import com.oracle.graal.nodeinfo.*; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.replacements.nodes.*; /** @@ -79,13 +78,12 @@ } public static ValueNode tryFold(ValueNode javaClass, Query query, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) { - ValueNode value = GraphUtil.originalValue(javaClass); - if (value != null && value.isConstant()) { + if (javaClass != null && javaClass.isConstant()) { if (query.returnKind == Kind.Object) { if (GraalOptions.ImmutableCode.getValue()) { return null; } - HotSpotObjectConstant c = (HotSpotObjectConstant) value.asConstant(); + HotSpotObjectConstant c = (HotSpotObjectConstant) javaClass.asConstant(); JavaConstant answer; switch (query) { case getClassLoader0: @@ -105,7 +103,7 @@ return ConstantNode.forConstant(answer, metaAccess); } } else { - ResolvedJavaType type = constantReflection.asJavaType(value.asConstant()); + ResolvedJavaType type = constantReflection.asJavaType(javaClass.asConstant()); if (type != null) { switch (query) { case isArray: diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ResolvedMethodHandleCallTargetNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ResolvedMethodHandleCallTargetNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ResolvedMethodHandleCallTargetNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.hotspot.nodes; +import static sun.misc.Version.*; + import java.lang.invoke.*; import com.oracle.graal.api.meta.*; @@ -35,24 +37,39 @@ /** * A call target that replaces itself in the graph when being lowered by restoring the original - * {@link MethodHandle} invocation target. This is required for when a {@link MethodHandle} call was - * resolved to a constant target but the target was not inlined. In that case, the original - * invocation must be restored with all of its original arguments. Why? HotSpot linkage for - * {@link MethodHandle} intrinsics (see {@code MethodHandles::generate_method_handle_dispatch}) - * expects certain implicit arguments to be on the stack such as the MemberName suffix argument for - * a call to one of the MethodHandle.linkTo* methods. An - * {@linkplain MethodHandleNode#tryResolveTargetInvoke resolved} {@link MethodHandle} invocation - * drops these arguments which means the interpreter won't find them. + * {@link MethodHandle} invocation target. Prior to + * https://bugs.openjdk.java.net/browse/JDK-8072008, this is required for when a + * {@link MethodHandle} call is resolved to a constant target but the target was not inlined. In + * that case, the original invocation must be restored with all of its original arguments. Why? + * HotSpot linkage for {@link MethodHandle} intrinsics (see + * {@code MethodHandles::generate_method_handle_dispatch}) expects certain implicit arguments to be + * on the stack such as the MemberName suffix argument for a call to one of the MethodHandle.linkTo* + * methods. An {@linkplain MethodHandleNode#tryResolveTargetInvoke resolved} {@link MethodHandle} + * invocation drops these arguments which means the interpreter won't find them. */ @NodeInfo public final class ResolvedMethodHandleCallTargetNode extends MethodCallTargetNode implements Lowerable { public static final NodeClass TYPE = NodeClass.create(ResolvedMethodHandleCallTargetNode.class); + + /** + * Creates a call target for an invocation on a direct target derived by resolving a constant + * {@link MethodHandle}. + */ + public static MethodCallTargetNode create(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, + ValueNode[] originalArguments, JavaType originalReturnType) { + if (jdkMajorVersion() >= 1 && jdkMinorVersion() >= 8 && jdkMicroVersion() >= 0 && jdkUpdateVersion() >= 60) { + // https://bugs.openjdk.java.net/browse/JDK-8072008 is targeted for 8u60 + return new MethodCallTargetNode(invokeKind, targetMethod, arguments, returnType); + } + return new ResolvedMethodHandleCallTargetNode(invokeKind, targetMethod, arguments, returnType, originalTargetMethod, originalArguments, originalReturnType); + } + protected final ResolvedJavaMethod originalTargetMethod; protected final JavaType originalReturnType; @Input NodeInputList originalArguments; - public ResolvedMethodHandleCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, + protected ResolvedMethodHandleCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, ValueNode[] originalArguments, JavaType originalReturnType) { super(TYPE, invokeKind, targetMethod, arguments, returnType); this.originalTargetMethod = originalTargetMethod; diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteTargetNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteTargetNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteTargetNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -30,7 +30,6 @@ import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.replacements.nodes.*; @NodeInfo @@ -46,9 +45,8 @@ return arguments.get(0); } - public static ConstantNode tryFold(ValueNode initialCallSite, MetaAccessProvider metaAccess, Assumptions assumptions) { - ValueNode callSite = GraphUtil.originalValue(initialCallSite); - if (callSite.isConstant() && !callSite.isNullConstant()) { + public static ConstantNode tryFold(ValueNode callSite, MetaAccessProvider metaAccess, Assumptions assumptions) { + if (callSite != null && callSite.isConstant() && !callSite.isNullConstant()) { HotSpotObjectConstant c = (HotSpotObjectConstant) callSite.asConstant(); JavaConstant target = c.getCallSiteTarget(assumptions); if (target != null) { diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -256,7 +256,7 @@ throw GraalInternalError.shouldNotReachHere(); } - MethodCallTargetNode callTarget = new ResolvedMethodHandleCallTargetNode(targetInvokeKind, target, targetArguments, targetReturnType, original, arguments, returnType); + MethodCallTargetNode callTarget = ResolvedMethodHandleCallTargetNode.create(targetInvokeKind, target, targetArguments, targetReturnType, original, arguments, returnType); // The call target can have a different return type than the invoker, // e.g. the target returns an Object but the invoker void. In this case diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java Wed Apr 01 16:36:28 2015 +0200 @@ -58,6 +58,9 @@ @Option(help = "Inlines trivial methods during bytecode parsing.", type = OptionType.Expert) public static final StableOptionValue InlineDuringParsing = new StableOptionValue<>(false); + @Option(help = "Inlines intrinsic methods during bytecode parsing.", type = OptionType.Expert) + public static final StableOptionValue InlineIntrinsicsDuringParsing = new StableOptionValue<>(true); + @Option(help = "Traces inlining performed during bytecode parsing.", type = OptionType.Debug) public static final StableOptionValue TraceInlineDuringParsing = new StableOptionValue<>(false); diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Wed Apr 01 16:36:28 2015 +0200 @@ -1745,7 +1745,7 @@ */ try (Indent indent = Debug.logAndIndent("LinearScan allocate")) { - try (Scope s = Debug.scope("LifetimeAnalysis")) { + try (Scope s = Debug.scope("LifetimeAnalysis"); DebugCloseable a = Debug.timer("LinearScan_LifetimeAnalysis").start()) { numberInstructions(); printLir("Before register allocation", true); computeLocalLiveSets(); @@ -1756,7 +1756,7 @@ throw Debug.handle(e); } - try (Scope s = Debug.scope("RegisterAllocation")) { + try (Scope s = Debug.scope("RegisterAllocation"); DebugCloseable a = Debug.timer("LinearScan_RegisterAllocation").start()) { printIntervals("Before register allocation"); allocateRegisters(); } catch (Throwable e) { @@ -1764,14 +1764,14 @@ } if (Options.LSRAOptimizeSpillPosition.getValue()) { - try (Scope s = Debug.scope("OptimizeSpillPosition")) { + try (Scope s = Debug.scope("OptimizeSpillPosition"); DebugCloseable a = Debug.timer("LinearScan_OptimizeSpillPosition").start()) { optimizeSpillPosition(); } catch (Throwable e) { throw Debug.handle(e); } } - try (Scope s = Debug.scope("ResolveDataFlow")) { + try (Scope s = Debug.scope("ResolveDataFlow"); DebugCloseable a = Debug.timer("LinearScan_ResolveDataFlow").start()) { resolveDataFlow(); } catch (Throwable e) { throw Debug.handle(e); @@ -1787,14 +1787,14 @@ verify(); } - try (Scope s1 = Debug.scope("EliminateSpillMove")) { + try (Scope s1 = Debug.scope("EliminateSpillMove"); DebugCloseable a = Debug.timer("LinearScan_EliminateSpillMove").start()) { eliminateSpillMoves(); } catch (Throwable e) { throw Debug.handle(e); } printLir("After spill move elimination", true); - try (Scope s1 = Debug.scope("AssignLocations")) { + try (Scope s1 = Debug.scope("AssignLocations"); DebugCloseable a = Debug.timer("LinearScan_AssignLocations").start()) { assignLocations(); } catch (Throwable e) { throw Debug.handle(e); diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationStage.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationStage.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationStage.java Wed Apr 01 16:36:28 2015 +0200 @@ -40,8 +40,10 @@ public static final NestedBooleanOptionValue LIROptRedundantMoveElimination = new NestedBooleanOptionValue(LIROptimization, true); @Option(help = "", type = OptionType.Debug) public static final NestedBooleanOptionValue LIROptNullCheckOptimizer = new NestedBooleanOptionValue(LIROptimization, true); - @Option(help = "", type = OptionType.Debug) - public static final OptionValue LIROptMoveProfiling = new OptionValue<>(false); + @Option(help = "Enables profiling of move types on LIR level. " + + "Move types are for example stores (register to stack), " + + "constant loads (constant to register) or copies (register to register).", type = OptionType.Debug) + public static final OptionValue LIRProfileMoves = new OptionValue<>(false); // @formatter:on } @@ -58,7 +60,7 @@ if (Options.LIROptNullCheckOptimizer.getValue()) { appendPhase(new NullCheckOptimizer()); } - if (Options.LIROptMoveProfiling.getValue()) { + if (Options.LIRProfileMoves.getValue()) { appendPhase(new MoveProfiling()); } } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingReadNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingReadNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingReadNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,4 +81,11 @@ public FixedAccessNode asFixedNode() { return graph().add(new ReadNode(object(), accessLocation(), stamp(), getGuard(), getBarrierType())); } + + @Override + public boolean verify() { + MemoryNode lla = getLastLocationAccess(); + assert lla != null || getLocationIdentity().isImmutable() : "lastLocationAccess of " + this + " shouldn't be null for mutable location identity " + getLocationIdentity(); + return super.verify(); + } } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java Wed Apr 01 16:36:28 2015 +0200 @@ -289,9 +289,8 @@ MemoryAccess access = (MemoryAccess) node; if (access.getLastLocationAccess() == anchor) { MemoryNode lastLocationAccess = state.getLastLocationAccess(access.getLocationIdentity()); - if (lastLocationAccess != null) { - access.setLastLocationAccess(lastLocationAccess); - } + assert lastLocationAccess != null; + access.setLastLocationAccess(lastLocationAccess); } } } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Wed Apr 01 16:36:28 2015 +0200 @@ -97,7 +97,7 @@ public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) { ResolvedJavaMethod subst = getMethodSubstitutionMethod(method); if (subst != null) { - if (b.parsingReplacement() || InlineDuringParsing.getValue()) { + if (b.parsingReplacement() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) { // Forced inlining of intrinsics return new InlineInfo(subst, true, true); } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -80,7 +80,7 @@ if (state1.getVirtualObject() == state2.getVirtualObject()) { // the same virtual objects will always have the same contents tool.replaceWithValue(ConstantNode.forBoolean(true, graph())); - } else if (state1.getVirtualObject().entryCount() == state2.getVirtualObject().entryCount()) { + } else if (state1.getVirtualObject().entryCount() == state2.getVirtualObject().entryCount() && state1.getState() == EscapeState.Virtual && state2.getState() == EscapeState.Virtual) { int entryCount = state1.getVirtualObject().entryCount(); boolean allEqual = true; for (int i = 0; i < entryCount; i++) { diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/AssertionNode.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/AssertionNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/AssertionNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -79,8 +79,10 @@ public void generate(NodeLIRBuilderTool generator) { assert compileTimeAssertion; - if (value.isConstant() && value.asJavaConstant().asInt() == 0) { - throw new GraalInternalError("%s: failed compile-time assertion: %s", this, message); + if (value.isConstant()) { + if (value.asJavaConstant().asInt() == 0) { + throw new GraalInternalError("%s: failed compile-time assertion: %s", this, message); + } } else { throw new GraalInternalError("%s: failed compile-time assertion (value %s): %s", this, value, message); } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java --- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Wed Apr 01 16:36:28 2015 +0200 @@ -215,11 +215,15 @@ Runnable r = new Runnable() { @Override public void run() { + boolean success = true; try (Scope s = Debug.scope("Truffle", new TruffleDebugJavaMethod(optimizedCallTarget))) { truffleCompiler.compileMethod(optimizedCallTarget); - optimizedCallTarget.notifyCompilationFinished(); } catch (Throwable e) { optimizedCallTarget.notifyCompilationFailed(e); + success = false; + } finally { + optimizedCallTarget.notifyCompilationFinished(success); + } } }; @@ -248,7 +252,10 @@ if (codeTask != null && isCompiling(optimizedCallTarget)) { this.compilations.remove(optimizedCallTarget); boolean result = codeTask.cancel(true); - getCompilationNotify().notifyCompilationDequeued(optimizedCallTarget, source, reason); + if (result) { + optimizedCallTarget.notifyCompilationFinished(false); + getCompilationNotify().notifyCompilationDequeued(optimizedCallTarget, source, reason); + } return result; } return false; diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.truffle.test/sl/TestOSR.sl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle.test/sl/TestOSR.sl Wed Apr 01 16:36:28 2015 +0200 @@ -0,0 +1,13 @@ +function test() { + i = 0; + sum = 0; + while (i < 300000) { + sum = sum + i; + i = i + 1; + } + return sum; +} + +function main() { + test(); +} diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Wed Apr 01 16:36:28 2015 +0200 @@ -205,6 +205,47 @@ } @Test + public void constantValueInertToolNodeInstrumentListener() { + FrameDescriptor fd = new FrameDescriptor(); + AbstractTestNode result = new ConstantTestNode(42); + RootTestNode root = new RootTestNode(fd, "constantValue", result); + root.adoptChildren(); + Probe probe = result.probe(); + // A listener that could insert a "tool node" into the AST, but which never does. + Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return null; + } + + }, null); + probe.attach(instrument); + + assertPartialEvalEquals("constant42", root); + } + + @Test + public void constantValueInertToolNode() { + FrameDescriptor fd = new FrameDescriptor(); + AbstractTestNode result = new ConstantTestNode(42); + RootTestNode root = new RootTestNode(fd, "constantValue", result); + root.adoptChildren(); + Probe probe = result.probe(); + // A listener that inserts a "tool node" with empty methods into the AST. + Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return new ToolNode() { + }; + } + + }, null); + probe.attach(instrument); + + assertPartialEvalEquals("constant42", root); + } + + @Test public void instrumentDeopt() { final FrameDescriptor fd = new FrameDescriptor(); final AbstractTestNode result = new ConstantTestNode(42); diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java Wed Apr 01 16:36:28 2015 +0200 @@ -29,7 +29,7 @@ public class DefaultLoopNodeFactory implements LoopNodeFactory { public LoopNode create(RepeatingNode repeatingNode) { - return new OptimizedLoopNode(repeatingNode); + return OptimizedOSRLoopNode.create(repeatingNode); } } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Wed Apr 01 16:36:28 2015 +0200 @@ -27,7 +27,6 @@ import java.io.*; import java.lang.reflect.*; import java.util.*; -import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util.stream.*; @@ -72,6 +71,7 @@ private TruffleInlining inlining; private int cachedNonTrivialNodeCount = -1; + private boolean compiling; /** * When this call target is inlined, the inlining {@link InstalledCode} registers this @@ -102,6 +102,10 @@ this.nodeRewritingAssumption = new CyclicAssumption("nodeRewritingAssumption of " + rootNode.toString()); } + public final boolean isCompiling() { + return compiling; + } + private static RootNode cloneRootNode(RootNode root) { if (root == null || !root.isCloningAllowed()) { return null; @@ -174,7 +178,7 @@ } @ExplodeLoop - private void profileArguments(Object[] args) { + void profileArguments(Object[] args) { if (profiledArgumentTypesAssumption == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); initializeProfiledArgumentTypes(args); @@ -272,7 +276,12 @@ VirtualFrame frame = createFrame(getRootNode().getFrameDescriptor(), args); Object result = callProxy(frame); - // Profile call return type + profileReturnType(result); + + return result; + } + + void profileReturnType(Object result) { if (profiledReturnTypeAssumption == null) { if (TruffleReturnTypeSpeculation.getValue()) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -286,8 +295,6 @@ profiledReturnTypeAssumption.invalidate(); } } - - return result; } @Override @@ -321,25 +328,16 @@ this.runtime.reinstallStubs(); } else { compilationProfile.reportInterpreterCall(); - if (compilationPolicy.shouldCompile(compilationProfile, getCompilerOptions())) { + if (!isCompiling() && compilationPolicy.shouldCompile(compilationProfile, getCompilerOptions())) { compile(); } } } public void compile() { - compile(TruffleBackgroundCompilation.getValue() && !TruffleCompilationExceptionsAreThrown.getValue()); - } - - public void compile(boolean mayBeAsynchronous) { - if (!runtime.isCompiling(this)) { - runtime.compile(this, mayBeAsynchronous); - } else if (!mayBeAsynchronous && runtime.isCompiling(this)) { - try { - runtime.waitForCompilation(this, 20000); - } catch (ExecutionException | TimeoutException e) { - Debug.log(3, e.getMessage()); - } + if (!isCompiling()) { + compiling = true; + runtime.compile(this, TruffleBackgroundCompilation.getValue() && !TruffleCompilationExceptionsAreThrown.getValue()); } } @@ -363,11 +361,11 @@ } } - public void notifyCompilationFinished() { - // Compilation was successful. - if (inlining != null) { + public void notifyCompilationFinished(boolean successful) { + if (successful && inlining != null) { dequeueInlinedCallSites(inlining); } + compiling = false; } private void dequeueInlinedCallSites(TruffleInlining parentDecision) { diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.truffle; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +public final class OptimizedOSRLoopNode extends LoopNode implements ReplaceObserver { + + private static final int OSR_THRESHOLD = TruffleCompilerOptions.TruffleOSRCompilationThreshold.getValue(); + + private int interpreterLoopCount; + private OptimizedCallTarget compiledTarget; + + @Child private RepeatingNode repeatableNode; + + private OptimizedOSRLoopNode(RepeatingNode repeatableNode) { + this.repeatableNode = repeatableNode; + } + + @Override + public Node copy() { + OptimizedOSRLoopNode copy = (OptimizedOSRLoopNode) super.copy(); + copy.compiledTarget = null; + copy.interpreterLoopCount = 0; + return copy; + } + + @Override + public RepeatingNode getRepeatingNode() { + return repeatableNode; + } + + @Override + public void executeLoop(VirtualFrame frame) { + if (CompilerDirectives.inInterpreter()) { + boolean done = false; + while (!done) { + if (compiledTarget == null) { + done = profilingLoop(frame); + } else { + done = compilingLoop(frame); + } + } + } else { + while (repeatableNode.executeRepeating(frame)) { + if (CompilerDirectives.inInterpreter()) { + // compiled method got invalidated. We might need OSR again. + executeLoop(frame); + return; + } + } + } + } + + private boolean profilingLoop(VirtualFrame frame) { + int overflowLoopCount = Integer.MAX_VALUE - OSR_THRESHOLD + interpreterLoopCount; + try { + while (repeatableNode.executeRepeating(frame)) { + try { + overflowLoopCount = Math.incrementExact(overflowLoopCount); + } catch (ArithmeticException e) { + compileLoop(frame); + return false; + } + } + } finally { + reportLoopCount(overflowLoopCount - Integer.MAX_VALUE + OSR_THRESHOLD - interpreterLoopCount); + } + return true; + } + + private boolean compilingLoop(VirtualFrame frame) { + int iterations = 0; + try { + do { + OptimizedCallTarget target = compiledTarget; + if (target == null) { + return false; + } else if (target.isValid()) { + Object result = target.callDirect(new Object[]{frame}); + if (result == Boolean.TRUE) { + // loop is done. No further repetitions necessary. + return true; + } else { + invalidate(this, "OSR compilation got invalidated"); + return false; + } + } else if (!target.isCompiling()) { + invalidate(this, "OSR compilation failed or cancelled"); + return false; + } + iterations++; + } while (repeatableNode.executeRepeating(frame)); + } finally { + reportLoopCount(iterations); + } + return true; + } + + private void compileLoop(VirtualFrame frame) { + atomic(new Runnable() { + public void run() { + /* + * Compilations need to run atomically as they may be scheduled by multiple threads + * at the same time. This strategy lets the first thread win. Later threads will not + * issue compiles. + */ + if (compiledTarget == null) { + compiledTarget = compileImpl(frame); + if (compiledTarget == null) { + interpreterLoopCount = 0; + } + } + } + }); + } + + private OptimizedCallTarget compileImpl(VirtualFrame frame) { + Node parent = getParent(); + OptimizedCallTarget target = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(new OSRRootNode(this)); + // to avoid a deopt on first call we provide some profiling information + target.profileReturnType(Boolean.TRUE); + target.profileReturnType(Boolean.FALSE); + target.profileArguments(new Object[]{frame}); + // let the old parent re-adopt the children + parent.adoptChildren(); + target.compile(); + return target; + } + + private void reportLoopCount(int reportIterations) { + if (reportIterations != 0) { + interpreterLoopCount += reportIterations; + getRootNode().reportLoopCount(reportIterations); + } + } + + public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { + invalidate(newNode, reason); + } + + private void invalidate(Object source, CharSequence reason) { + OptimizedCallTarget target = this.compiledTarget; + if (target != null) { + target.invalidate(source, reason); + compiledTarget = null; + interpreterLoopCount = 0; + } + } + + public static LoopNode create(RepeatingNode repeat) { + if (TruffleCompilerOptions.TruffleOSR.getValue()) { + return new OptimizedOSRLoopNode(repeat); + } else { + return new OptimizedLoopNode(repeat); + } + } + + private static class OSRRootNode extends RootNode { + + @Child private OptimizedOSRLoopNode loopNode; + + public OSRRootNode(OptimizedOSRLoopNode loop) { + super(loop.getSourceSection(), loop.getRootNode().getFrameDescriptor()); + this.loopNode = loop; + } + + @Override + public Object execute(VirtualFrame frame) { + VirtualFrame parentFrame = (VirtualFrame) frame.getArguments()[0]; + while (loopNode.getRepeatingNode().executeRepeating(parentFrame)) { + if (CompilerDirectives.inInterpreter()) { + return Boolean.FALSE; + } + } + return Boolean.TRUE; + } + + @Override + public boolean isCloningAllowed() { + return false; + } + + @Override + public String toString() { + return loopNode.getRepeatingNode().toString() + ""; + } + + } + +} diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java Wed Apr 01 16:36:28 2015 +0200 @@ -98,6 +98,12 @@ @Option(help = "Experimental. New splitting only: Split everything aggressively. ", type = OptionType.Debug) public static final OptionValue TruffleSplittingAggressive = new OptionValue<>(false); + @Option(help = "Enable on stack replacement for Truffle loops.", type = OptionType.Debug) + public static final OptionValue TruffleOSR = new OptionValue<>(true); + + @Option(help = "Number of loop iterations until on-stack-replacement compilation is triggered.", type = OptionType.Debug) + public static final OptionValue TruffleOSRCompilationThreshold = new OptionValue<>(10000); + @Option(help = "Disable call target splitting if tree size exceeds this limit", type = OptionType.Debug) public static final OptionValue TruffleSplittingMaxCalleeSize = new OptionValue<>(100); diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Wed Apr 01 16:36:28 2015 +0200 @@ -35,6 +35,11 @@ import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; /** *

AST Instrumentation

@@ -339,142 +344,6 @@ } - private abstract class TestLanguageNode extends Node { - public abstract Object execute(VirtualFrame vFrame); - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new TestLanguageWrapperNode(this); - } - } - - @NodeInfo(cost = NodeCost.NONE) - private class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode { - @Child private TestLanguageNode child; - @Child private ProbeNode probeNode; - - public TestLanguageWrapperNode(TestLanguageNode child) { - assert !(child instanceof TestLanguageWrapperNode); - this.child = child; - } - - @Override - public String instrumentationInfo() { - return "Wrapper node for testing"; - } - - @Override - public boolean isInstrumentable() { - return false; - } - - @Override - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; - } - - @Override - public Probe getProbe() { - return probeNode.getProbe(); - } - - @Override - public Node getChild() { - return child; - } - - @Override - public Object execute(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); - Object result; - - try { - result = child.execute(vFrame); - probeNode.returnValue(child, vFrame, result); - } catch (KillException e) { - throw (e); - } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); - throw (e); - } - - return result; - } - } - - /** - * A simple node for our test language to store a value. - */ - private class TestValueNode extends TestLanguageNode { - private final int value; - - public TestValueNode(int value) { - this.value = value; - } - - @Override - public Object execute(VirtualFrame vFrame) { - return new Integer(this.value); - } - } - - /** - * A node for our test language that adds up two {@link TestValueNode}s. - */ - private class TestAdditionNode extends TestLanguageNode { - @Child private TestLanguageNode leftChild; - @Child private TestLanguageNode rightChild; - - public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) { - this.leftChild = insert(leftChild); - this.rightChild = insert(rightChild); - } - - @Override - public Object execute(VirtualFrame vFrame) { - return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue()); - } - } - - /** - * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST - * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle - * completes an AST. The root nodes serves as our entry point into a program. - */ - private class TestRootNode extends RootNode { - @Child private TestLanguageNode body; - - /** - * This constructor emulates the global machinery that applies registered probers to every - * newly created AST. Global registry is not used, since that would interfere with other - * tests run in the same environment. - */ - public TestRootNode(TestLanguageNode body) { - super(null); - this.body = body; - } - - @Override - public Object execute(VirtualFrame vFrame) { - return body.execute(vFrame); - } - - @Override - public boolean isCloningAllowed() { - return true; - } - - @Override - public void applyInstrumentation() { - Probe.applyASTProbers(body); - } - } - private interface TestCounter { int enterCount(); diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Wed Apr 01 16:36:28 2015 +0200 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.instrument; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.nodes.*; + +/** + * Tests instrumentation where a client can attach a node that gets attached into the AST. + */ +class InstrumentationTestNodes { + + abstract static class TestLanguageNode extends Node { + public abstract Object execute(VirtualFrame vFrame); + + @Override + public boolean isInstrumentable() { + return true; + } + + @Override + public WrapperNode createWrapperNode() { + return new TestLanguageWrapperNode(this); + } + } + + @NodeInfo(cost = NodeCost.NONE) + static class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode { + @Child private TestLanguageNode child; + @Child private ProbeNode probeNode; + + public TestLanguageWrapperNode(TestLanguageNode child) { + assert !(child instanceof TestLanguageWrapperNode); + this.child = child; + } + + @Override + public String instrumentationInfo() { + return "Wrapper node for testing"; + } + + @Override + public boolean isInstrumentable() { + return false; + } + + @Override + public void insertProbe(ProbeNode newProbeNode) { + this.probeNode = newProbeNode; + } + + @Override + public Probe getProbe() { + return probeNode.getProbe(); + } + + @Override + public Node getChild() { + return child; + } + + @Override + public Object execute(VirtualFrame vFrame) { + probeNode.enter(child, vFrame); + Object result; + + try { + result = child.execute(vFrame); + probeNode.returnValue(child, vFrame, result); + } catch (KillException e) { + throw (e); + } catch (Exception e) { + probeNode.returnExceptional(child, vFrame, e); + throw (e); + } + + return result; + } + } + + /** + * A simple node for our test language to store a value. + */ + static class TestValueNode extends TestLanguageNode { + private final int value; + + public TestValueNode(int value) { + this.value = value; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return new Integer(this.value); + } + } + + /** + * A node for our test language that adds up two {@link TestValueNode}s. + */ + static class TestAdditionNode extends TestLanguageNode { + @Child private TestLanguageNode leftChild; + @Child private TestLanguageNode rightChild; + + public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) { + this.leftChild = insert(leftChild); + this.rightChild = insert(rightChild); + } + + @Override + public Object execute(VirtualFrame vFrame) { + return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue()); + } + } + + /** + * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST + * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle + * completes an AST. The root nodes serves as our entry point into a program. + */ + static class TestRootNode extends RootNode { + @Child private TestLanguageNode body; + + /** + * This constructor emulates the global machinery that applies registered probers to every + * newly created AST. Global registry is not used, since that would interfere with other + * tests run in the same environment. + */ + public TestRootNode(TestLanguageNode body) { + super(null); + this.body = body; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + + @Override + public void applyInstrumentation() { + Probe.applyASTProbers(body); + } + } + +} diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java Wed Apr 01 16:36:28 2015 +0200 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.instrument; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; + +/** + * Tests instrumentation where a client can attach a node that gets attached into the AST. + */ +public class ToolNodeInstrumentationTest { + + @Test + public void testToolNodeListener() { + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final TestRootNode rootNode = new TestRootNode(addNode); + final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + + // Ensure it executes correctly + assertEquals(13, callTarget1.call()); + + // Probe the addition node + final Probe probe = addNode.probe(); + + assertEquals(13, callTarget1.call()); + + // Attach a listener that never actually attaches a node. + final Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return null; + } + + }, null); + probe.attach(instrument); + + assertEquals(13, callTarget1.call()); + + final int[] count = new int[1]; + + // Attach a listener that never actually attaches a node. + probe.attach(Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return new ToolNode() { + + @Override + public void enter(Node node, VirtualFrame vFrame) { + count[0] = count[0] + 1; + } + }; + } + + }, null)); + assertEquals(0, count[0]); + + assertEquals(13, callTarget1.call()); + + assertEquals(1, count[0]); + + } + +} diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TestNodes.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TestNodes.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TestNodes.java Wed Apr 01 16:36:28 2015 +0200 @@ -32,7 +32,7 @@ /** * Nodes and an {@linkplain CallTarget executable ASTs} for testing. */ -public class TestNodes { +class TestNodes { /** * A fake source used for testing: empty line 1, expression on line 2. @@ -44,7 +44,7 @@ /** * An executable addition expression that evaluates to 13. */ - public static CallTarget createExpr13TestCallTarget() { + static CallTarget createExpr13TestCallTarget() { final RootNode rootNode = createExpr13TestRootNode(); return Truffle.getRuntime().createCallTarget(rootNode); } @@ -52,7 +52,7 @@ /** * Root holding an addition expression that evaluates to 13. */ - public static RootNode createExpr13TestRootNode() { + static RootNode createExpr13TestRootNode() { final TestLanguageNode ast = createExpr13AST(); final TestRootNode rootNode = new TestRootNode(ast); rootNode.adoptChildren(); @@ -62,7 +62,7 @@ /** * Addition expression that evaluates to 13, with faked source attribution. */ - public static TestLanguageNode createExpr13AST() { + static TestLanguageNode createExpr13AST() { final SourceSection leftSourceSection = expr13Source.createSection("left", 1, 1); final TestValueNode leftValueNode = new TestValueNode(6, leftSourceSection); final SourceSection rightSourceSection = expr13Source.createSection("right", 3, 1); @@ -71,7 +71,7 @@ return new TestAddNode(leftValueNode, rightValueNode, exprSourceSection); } - public abstract static class TestLanguageNode extends Node { + abstract static class TestLanguageNode extends Node { public abstract Object execute(VirtualFrame frame); public TestLanguageNode() { @@ -93,7 +93,7 @@ } @NodeInfo(cost = NodeCost.NONE) - public static class TestWrapperNode extends TestLanguageNode implements WrapperNode { + static class TestWrapperNode extends TestLanguageNode implements WrapperNode { @Child private TestLanguageNode child; @Child private ProbeNode probeNode; @@ -151,7 +151,7 @@ * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle * completes an AST. The root nodes serves as our entry point into a program. */ - public static class TestRootNode extends RootNode { + static class TestRootNode extends RootNode { @Child private TestLanguageNode body; /** @@ -180,7 +180,7 @@ } } - public static class TestValueNode extends TestLanguageNode { + static class TestValueNode extends TestLanguageNode { private final int value; public TestValueNode(int value) { @@ -198,7 +198,7 @@ } } - public static class TestAddNode extends TestLanguageNode { + static class TestAddNode extends TestLanguageNode { @Child private TestLanguageNode leftChild; @Child private TestLanguageNode rightChild; diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Wed Apr 01 16:36:28 2015 +0200 @@ -194,6 +194,19 @@ return new ASTInstrument(astListener, instrumentInfo); } + /** + * Creates an instrument that, when executed the first time in any particular AST location, + * invites the tool to provide an AST fragment for attachment/adoption into the running AST. + * + * @param toolNodeListener a listener for the tool that can request an AST fragment + * @param instrumentInfo instrumentInfo optional description of the instrument's role, useful + * for debugging. + * @return a new instrument, ready for attachment at a probe. + */ + public static Instrument create(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { + return new ToolNodeInstrument(toolNodeListener, instrumentInfo); + } + // TODO (mlvdv) experimental /** * For implementation testing. @@ -278,7 +291,7 @@ if (instrumentNode != null) { if (instrumentNode.getInstrument() == this) { // Found the match at the head of the chain - return instrumentNode.nextInstrument; + return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. found = instrumentNode.removeFromChain(BasicInstrument.this); @@ -298,29 +311,29 @@ public void enter(Node node, VirtualFrame vFrame) { BasicInstrument.this.instrumentListener.enter(BasicInstrument.this.probe); - if (nextInstrument != null) { - nextInstrument.enter(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { BasicInstrument.this.instrumentListener.returnVoid(BasicInstrument.this.probe); - if (nextInstrument != null) { - nextInstrument.returnVoid(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { BasicInstrument.this.instrumentListener.returnValue(BasicInstrument.this.probe, result); - if (nextInstrument != null) { - nextInstrument.returnValue(node, vFrame, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { BasicInstrument.this.instrumentListener.returnExceptional(BasicInstrument.this.probe, exception); - if (nextInstrument != null) { - nextInstrument.returnExceptional(node, vFrame, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); } } @@ -362,7 +375,7 @@ if (instrumentNode != null) { if (instrumentNode.getInstrument() == this) { // Found the match at the head of the chain - return instrumentNode.nextInstrument; + return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. found = instrumentNode.removeFromChain(ASTInstrument.this); @@ -382,29 +395,29 @@ public void enter(Node node, VirtualFrame vFrame) { ASTInstrument.this.astListener.enter(ASTInstrument.this.probe, node, vFrame); - if (nextInstrument != null) { - nextInstrument.enter(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { ASTInstrument.this.astListener.returnVoid(ASTInstrument.this.probe, node, vFrame); - if (nextInstrument != null) { - nextInstrument.returnVoid(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { ASTInstrument.this.astListener.returnValue(ASTInstrument.this.probe, node, vFrame, result); - if (nextInstrument != null) { - nextInstrument.returnValue(node, vFrame, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { ASTInstrument.this.astListener.returnExceptional(ASTInstrument.this.probe, node, vFrame, exception); - if (nextInstrument != null) { - nextInstrument.returnExceptional(node, vFrame, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); } } @@ -416,6 +429,104 @@ } + /** + * An instrument that propagates events to an instance of {@link ASTInstrumentListener}. + */ + private static final class ToolNodeInstrument extends Instrument { + + /** + * Tool-supplied listener for AST events. + */ + private final ToolNodeInstrumentListener toolNodeListener; + + private ToolNodeInstrument(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { + super(instrumentInfo); + this.toolNodeListener = toolNodeListener; + } + + @Override + AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { + return new ToolInstrumentNode(nextNode); + } + + @Override + AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) { + boolean found = false; + if (instrumentNode != null) { + if (instrumentNode.getInstrument() == this) { + // Found the match at the head of the chain + return instrumentNode.nextInstrumentNode; + } + // Match not at the head of the chain; remove it. + found = instrumentNode.removeFromChain(ToolNodeInstrument.this); + } + if (!found) { + throw new IllegalStateException("Couldn't find instrument node to remove: " + this); + } + return instrumentNode; + } + + @NodeInfo(cost = NodeCost.NONE) + private final class ToolInstrumentNode extends AbstractInstrumentNode { + + @Child ToolNode toolNode; + + private ToolInstrumentNode(AbstractInstrumentNode nextNode) { + super(nextNode); + } + + public void enter(Node node, VirtualFrame vFrame) { + if (toolNode == null) { + final ToolNode newToolNode = ToolNodeInstrument.this.toolNodeListener.getToolNode(ToolNodeInstrument.this.probe); + if (newToolNode != null) { + toolNode = newToolNode; + adoptChildren(); + ToolNodeInstrument.this.probe.invalidateProbeUnchanged(); + } + } + if (toolNode != null) { + toolNode.enter(node, vFrame); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); + } + } + + public void returnVoid(Node node, VirtualFrame vFrame) { + if (toolNode != null) { + toolNode.returnVoid(node, vFrame); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); + } + } + + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + if (toolNode != null) { + toolNode.returnValue(node, vFrame, result); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); + } + } + + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + if (toolNode != null) { + toolNode.returnExceptional(node, vFrame, exception); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); + } + } + + public String instrumentationInfo() { + final String info = getInstrumentInfo(); + return info != null ? info : toolNodeListener.getClass().getSimpleName(); + } + } + + } + public interface TruffleOptListener { void notifyIsCompiled(boolean isCompiled); } @@ -440,7 +551,7 @@ if (instrumentNode != null) { if (instrumentNode.getInstrument() == this) { // Found the match at the head of the chain - return instrumentNode.nextInstrument; + return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. found = instrumentNode.removeFromChain(TruffleOptInstrument.this); @@ -466,26 +577,26 @@ this.isCompiled = CompilerDirectives.inCompiledCode(); TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled); } - if (nextInstrument != null) { - nextInstrument.enter(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { - if (nextInstrument != null) { - nextInstrument.returnVoid(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { - if (nextInstrument != null) { - nextInstrument.returnValue(node, vFrame, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - if (nextInstrument != null) { - nextInstrument.returnExceptional(node, vFrame, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); } } @@ -500,10 +611,10 @@ @NodeInfo(cost = NodeCost.NONE) abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode { - @Child protected AbstractInstrumentNode nextInstrument; + @Child protected AbstractInstrumentNode nextInstrumentNode; protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) { - this.nextInstrument = nextNode; + this.nextInstrumentNode = nextNode; } @Override @@ -527,21 +638,21 @@ */ private boolean removeFromChain(Instrument instrument) { assert getInstrument() != instrument; - if (nextInstrument == null) { + if (nextInstrumentNode == null) { return false; } - if (nextInstrument.getInstrument() == instrument) { + if (nextInstrumentNode.getInstrument() == instrument) { // Next is the one to remove - if (nextInstrument.nextInstrument == null) { + if (nextInstrumentNode.nextInstrumentNode == null) { // Next is at the tail; just forget - nextInstrument = null; + nextInstrumentNode = null; } else { // Replace next with its successor - nextInstrument.replace(nextInstrument.nextInstrument); + nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode); } return true; } - return nextInstrument.removeFromChain(instrument); + return nextInstrumentNode.removeFromChain(instrument); } protected String getInstrumentInfo() { diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Wed Apr 01 16:36:28 2015 +0200 @@ -336,7 +336,7 @@ } } - private void invalidateProbeUnchanged() { + void invalidateProbeUnchanged() { probeStateUnchangedCyclic.invalidate(); } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -129,7 +129,7 @@ /** * First {@link AbstractInstrumentNode} node in chain; {@code null} of no instruments in chain. */ - @Child protected AbstractInstrumentNode firstInstrument; + @Child protected AbstractInstrumentNode firstInstrumentNode; @Override public boolean isInstrumentable() { @@ -156,29 +156,29 @@ if (trap != null) { trap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize()); } - if (firstInstrument != null) { - firstInstrument.enter(node, vFrame); + if (firstInstrumentNode != null) { + firstInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { this.probe.checkProbeUnchanged(); - if (firstInstrument != null) { - firstInstrument.returnVoid(node, vFrame); + if (firstInstrumentNode != null) { + firstInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { this.probe.checkProbeUnchanged(); - if (firstInstrument != null) { - firstInstrument.returnValue(node, vFrame, result); + if (firstInstrumentNode != null) { + firstInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { this.probe.checkProbeUnchanged(); - if (firstInstrument != null) { - firstInstrument.returnExceptional(node, vFrame, exception); + if (firstInstrumentNode != null) { + firstInstrumentNode.returnExceptional(node, vFrame, exception); } } @@ -194,7 +194,7 @@ assert instrument.getProbe() == probe; // The existing chain of nodes may be empty // Attach the modified chain. - firstInstrument = insert(instrument.addToChain(firstInstrument)); + firstInstrumentNode = insert(instrument.addToChain(firstInstrumentNode)); } /** @@ -205,11 +205,11 @@ @TruffleBoundary void removeInstrument(Instrument instrument) { assert instrument.getProbe() == probe; - final AbstractInstrumentNode modifiedChain = instrument.removeFromChain(firstInstrument); + final AbstractInstrumentNode modifiedChain = instrument.removeFromChain(firstInstrumentNode); if (modifiedChain == null) { - firstInstrument = null; + firstInstrumentNode = null; } else { - firstInstrument = insert(modifiedChain); + firstInstrumentNode = insert(modifiedChain); } } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * Root of a tool-provided AST fragment that can be attached directly into an executing AST via + * {@link Instrument#create(ToolNodeInstrumentListener, String)}. + *

+ * Note: Instances of this class will in some situations be cloned by the + * instrumentation platform for attachment at equivalent locations in cloned parent ASTs. + */ +public abstract class ToolNode extends Node implements InstrumentationNode.TruffleEvents, InstrumentationNode { + + public void enter(Node node, VirtualFrame vFrame) { + } + + public void returnVoid(Node node, VirtualFrame vFrame) { + } + + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + } + + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + } + + public String instrumentationInfo() { + return null; + } + +} diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java Wed Apr 01 16:36:28 2015 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument; + +/** + * Instrument listener for a tool that works by providing an AST to be attached/adopted directly + * into the AST. + */ +public interface ToolNodeInstrumentListener { + + /** + * Receive notification that a probed AST node to which the {@link Instrument} is attached is + * about to be executed for the first time. This is a lazy opportunity for the tool to + * optionally add the root of a newly created AST fragment that will be attached/adopted + * directly into the executing AST. The new AST fragment will immediately begin receiving + * {@link InstrumentationNode.TruffleEvents}, beginning with the current execution event. + *

+ * AST fragments must be written to Truffle conventions. Some of these conventions are + * especially important if the fragment is to be fully optimized along with it's new parent AST. + *

+ * If this method returns {@code null} then it will be called again the next time the probed + * node is about to be executed. + *

+ * In some situations, this method will be called more than once for a particular Probe, and a + * new instance must be supplied each time. Each instance will be attached at the equivalent + * location in clones of the AST, and so should be behave as if equivalent for most purposes. + *

+ * In some situations the AST fragment supplied by this method maybe cloned for attachment to + * equivalent locations in cloned AST, so care should be taken about any state local to each + * instance of the AST fragment. + * + * @see Instrument + */ + ToolNode getToolNode(Probe probe); + +} diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -64,7 +64,7 @@ * @param node the node to format. * @return a formatted source section string */ - private static String formatSourceSection(Node node) { + public static String formatSourceSection(Node node) { if (node == null) { return ""; } diff -r 51f2e71b57aa -r 45d46b136777 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLRepeatingNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLRepeatingNode.java Wed Apr 01 16:36:15 2015 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLRepeatingNode.java Wed Apr 01 16:36:28 2015 +0200 @@ -93,4 +93,9 @@ } } + @Override + public String toString() { + return SLStatementNode.formatSourceSection(this); + } + } diff -r 51f2e71b57aa -r 45d46b136777 mx/mx_graal.py --- a/mx/mx_graal.py Wed Apr 01 16:36:15 2015 +0200 +++ b/mx/mx_graal.py Wed Apr 01 16:36:28 2015 +0200 @@ -83,6 +83,9 @@ _minVersion = mx.VersionSpec('1.8') +# max version (first _unsupported_ version) +_untilVersion = mx.VersionSpec('1.8.0_40') + class JDKDeployedDist: def __init__(self, name, isExtension): self.name = name @@ -2527,7 +2530,9 @@ def mx_post_parse_cmd_line(opts): # # TODO _minVersion check could probably be part of a Suite in mx? if mx.java().version < _minVersion: - mx.abort('Requires Java version ' + str(_minVersion) + ' or greater, got version ' + str(mx.java().version)) + mx.abort('Requires Java version ' + str(_minVersion) + ' or greater for JAVA_HOME, got version ' + str(mx.java().version)) + if mx.java().version >= _untilVersion: + mx.abort('Requires Java version strictly before ' + str(_untilVersion) + ' for JAVA_HOME, got version ' + str(mx.java().version)) if _vmSourcesAvailable: if hasattr(opts, 'vm') and opts.vm is not None: diff -r 51f2e71b57aa -r 45d46b136777 mxtool/mx.py --- a/mxtool/mx.py Wed Apr 01 16:36:15 2015 +0200 +++ b/mxtool/mx.py Wed Apr 01 16:36:28 2015 +0200 @@ -1660,6 +1660,8 @@ elif get_os() == 'linux': base = '/usr/lib/jvm' candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))] + base = '/usr/java' + candidateJdks += [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))] elif get_os() == 'solaris': base = '/usr/jdk/instances' candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))] @@ -5331,7 +5333,7 @@ if opts.extra_java_homes: for java_home in opts.extra_java_homes.split(os.pathsep): extraJdk = JavaConfig(java_home, opts.java_dbg_port) - if extraJdk > defaultJdk: + if extraJdk.javaCompliance > defaultJdk.javaCompliance: abort('Secondary JDK ' + extraJdk.jdk + ' has higher compliance level than default JDK ' + defaultJdk.jdk) _java_homes.append(extraJdk)