All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.truffleruby.extra.TruffleGraalNodes Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved. This
 * code is released under a tri EPL/GPL/LGPL license. You can use it,
 * redistribute it and/or modify it under the terms of the:
 *
 * Eclipse Public License version 2.0, or
 * GNU General Public License version 2, or
 * GNU Lesser General Public License version 2.1.
 */
package org.truffleruby.extra;

import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.nodes.Node;
import org.truffleruby.annotations.CoreMethod;
import org.truffleruby.annotations.CoreModule;
import org.truffleruby.annotations.Primitive;
import org.truffleruby.annotations.SuppressFBWarnings;
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
import org.truffleruby.builtins.PrimitiveNode;
import org.truffleruby.core.cast.ToCallTargetNode;
import org.truffleruby.core.proc.ProcCallTargets;
import org.truffleruby.core.proc.ProcType;
import org.truffleruby.core.proc.RubyProc;
import org.truffleruby.interop.ToJavaStringNode;
import org.truffleruby.language.RubyLambdaRootNode;
import org.truffleruby.language.RubyNode;
import org.truffleruby.language.RubyRootNode;
import org.truffleruby.language.arguments.NoKeywordArgumentsDescriptor;
import org.truffleruby.language.library.RubyStringLibrary;
import org.truffleruby.annotations.Split;
import org.truffleruby.language.threadlocal.SpecialVariableStorage;
import org.truffleruby.language.arguments.RubyArguments;
import org.truffleruby.language.control.RaiseException;
import org.truffleruby.language.literal.ObjectLiteralNode;
import org.truffleruby.language.locals.ReadDeclarationVariableNode;
import org.truffleruby.language.locals.WriteDeclarationVariableNode;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.nodes.NodeUtil;

import java.lang.invoke.VarHandle;
import java.lang.management.CompilationMXBean;
import java.lang.management.ManagementFactory;

@CoreModule("Truffle::Graal")
public abstract class TruffleGraalNodes {

    @CoreMethod(names = "always_split", onSingleton = true, required = 1)
    public abstract static class AlwaysSplitNode extends CoreMethodArrayArgumentsNode {
        @TruffleBoundary
        @Specialization
        Object alwaysSplit(Object executable,
                @Cached ToCallTargetNode toCallTargetNode) {
            final RootCallTarget callTarget = toCallTargetNode.execute(this, executable);
            if (getContext().getOptions().ALWAYS_SPLIT_HONOR) {
                RubyRootNode.of(callTarget).setSplit(Split.ALWAYS);
            }
            return executable;
        }
    }

    @CoreMethod(names = "never_split", onSingleton = true, required = 1)
    public abstract static class NeverSplitNode extends CoreMethodArrayArgumentsNode {
        @TruffleBoundary
        @Specialization
        Object neverSplit(Object executable,
                @Cached ToCallTargetNode toCallTargetNode) {
            final RootCallTarget callTarget = toCallTargetNode.execute(this, executable);
            if (getContext().getOptions().NEVER_SPLIT_HONOR) {
                RubyRootNode.of(callTarget).setSplit(Split.NEVER);
            }
            return executable;
        }
    }

    /** This method creates a new Proc for an existing lambda proc with a copy of the captured variables' values,
     * which is correct if these variables are not changed in the parent scope later on. It works by replacing
     * {@link ReadDeclarationVariableNode} with the captured variables' values. This avoids constantly reading from the
     * declaration frame (which always escapes in a define_method) and folds many checks on these captured variables
     * since their values become compilation constants.
     * 

* Similar to Smalltalk's fixTemps, but not mutating the Proc. */ @CoreMethod(names = "copy_captured_locals", onSingleton = true, required = 1) public abstract static class CopyCapturedLocalsNode extends CoreMethodArrayArgumentsNode { @TruffleBoundary @Specialization(guards = "proc.isLambda()") RubyProc copyCapturedLocals(RubyProc proc) { final RubyLambdaRootNode rootNode = RubyLambdaRootNode.of(proc.callTarget); final RubyNode newBody = rootNode.copyBody(); assert NodeUtil.findAllNodeInstances(newBody, WriteDeclarationVariableNode.class).isEmpty(); for (ReadDeclarationVariableNode readNode : NodeUtil .findAllNodeInstances(newBody, ReadDeclarationVariableNode.class)) { final MaterializedFrame frame = RubyArguments .getDeclarationFrame(proc.declarationFrame, readNode.getFrameDepth() - 1); final Object value = frame.getValue(readNode.getFrameSlot()); readNode.replace(new ObjectLiteralNode(value)); } final RubyLambdaRootNode newRootNode = rootNode.copyRootNode(rootNode.getSharedMethodInfo(), newBody); final RootCallTarget newCallTarget = newRootNode.getCallTarget(); final SpecialVariableStorage variables = proc.declarationVariables; final Object[] args = RubyArguments .pack( null, null, RubyArguments.getMethod(proc.declarationFrame), null, nil, nil, NoKeywordArgumentsDescriptor.INSTANCE, EMPTY_ARGUMENTS); // The Proc no longer needs the original declaration frame. However, all procs must have a // declaration frame (to allow Proc#binding) so we shall create an empty one. final MaterializedFrame newDeclarationFrame = getLanguage().createEmptyDeclarationFrame(args, variables); return new RubyProc( coreLibrary().procClass, getLanguage().procShape, ProcType.LAMBDA, proc.arity, proc.argumentDescriptors, new ProcCallTargets(newCallTarget), newCallTarget, newDeclarationFrame, variables, proc.declaringMethod, proc.frameOnStackMarker, proc.declarationContext); // TODO(norswap): trace allocation? } } @Primitive(name = "assert_compilation_constant") public abstract static class AssertCompilationConstantNode extends PrimitiveArrayArgumentsNode { @Specialization Object assertCompilationConstant(Object value) { if (!CompilerDirectives.isCompilationConstant(value)) { notConstantBoundary(); } return value; } @TruffleBoundary private void notConstantBoundary() { throw new RaiseException(getContext(), coreExceptions().graalErrorAssertConstantNotConstant(this)); } } @Primitive(name = "assert_not_compiled") public abstract static class AssertNotCompiledNode extends PrimitiveNode { @Specialization Object assertNotCompiled() { if (CompilerDirectives.inCompiledCode()) { compiledBoundary(); } return nil; } @TruffleBoundary private void compiledBoundary() { throw new RaiseException(getContext(), coreExceptions().graalErrorAssertNotCompiledCompiled(this)); } } @Primitive(name = "compiler_bailout") public abstract static class BailoutNode extends PrimitiveArrayArgumentsNode { @Specialization(guards = "strings.isRubyString(message)", limit = "1") static Object bailout(Object message, @Cached RubyStringLibrary strings, @Cached ToJavaStringNode toJavaStringNode, @Bind("this") Node node) { CompilerDirectives.bailout(toJavaStringNode.execute(node, message)); return nil; } } @Primitive(name = "blackhole") public abstract static class BlackholeNode extends PrimitiveArrayArgumentsNode { @Specialization Object blackhole(boolean value) { CompilerDirectives.blackhole(value); return nil; } @Specialization Object blackhole(int value) { CompilerDirectives.blackhole(value); return nil; } @Specialization Object blackhole(long value) { CompilerDirectives.blackhole(value); return nil; } @Specialization Object blackhole(double value) { CompilerDirectives.blackhole(value); return nil; } @Specialization Object blackhole(Object value) { CompilerDirectives.blackhole(value); return nil; } } @CoreMethod(names = "total_compilation_time", onSingleton = true) public abstract static class TotalCompilationTimeNode extends CoreMethodArrayArgumentsNode { private static CompilationMXBean bean; @SuppressFBWarnings("LI_LAZY_INIT_STATIC") @TruffleBoundary @Specialization final long totalCompilationTime() { if (bean == null) { var compilationMXBean = ManagementFactory.getCompilationMXBean(); VarHandle.storeStoreFence(); bean = compilationMXBean; } return bean.getTotalCompilationTime(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy