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

com.oracle.truffle.runtime.OptimizedBlockNode Maven / Gradle / Ivy

/*
 * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.runtime;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.ReplaceObserver;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.BlockNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeUtil;
import com.oracle.truffle.api.nodes.NodeVisitor;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.source.SourceSection;

public final class OptimizedBlockNode extends BlockNode implements ReplaceObserver {

    @CompilationFinal private volatile PartialBlocks partialBlocks;
    private final ElementExecutor executor;
    @CompilationFinal private volatile Assumption alwaysNoArgument;

    OptimizedBlockNode(T[] elements, ElementExecutor executor) {
        super(elements);
        this.executor = executor;
    }

    @Override
    @ExplodeLoop
    public Object executeGeneric(VirtualFrame frame, int argument) {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.execute(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeGeneric(frame, e[last], last, arg);
    }

    private int profileArg(int arg) {
        Assumption a = alwaysNoArgument;
        if (a == null) {
            if (CompilerDirectives.inInterpreter()) {
                // no need to deoptimize if the block was never executed
                alwaysNoArgument = makeAlwaysZeroAssumption(arg == NO_ARGUMENT);
            }
        } else if (a.isValid()) {
            if (arg == NO_ARGUMENT) {
                return NO_ARGUMENT;
            } else {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                a.invalidate();
            }
        }
        return arg;
    }

    private static Assumption makeAlwaysZeroAssumption(boolean valid) {
        if (valid) {
            return Truffle.getRuntime().createAssumption("Always zero block node argument.");
        } else {
            return Assumption.NEVER_VALID;
        }
    }

    @Override
    @ExplodeLoop
    public void executeVoid(VirtualFrame frame, int argument) {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                g.execute(frame, arg);
                return;
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        for (int i = 0; i < e.length; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
    }

    @Override
    @ExplodeLoop
    public byte executeByte(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeByte(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeByte(frame, e[last], last, arg);
    }

    @Override
    @ExplodeLoop
    public short executeShort(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeShort(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeShort(frame, e[last], last, arg);
    }

    @Override
    @ExplodeLoop
    public char executeChar(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeChar(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeChar(frame, e[last], last, arg);
    }

    @Override
    @ExplodeLoop
    public int executeInt(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeInt(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeInt(frame, e[last], last, arg);
    }

    @Override
    @ExplodeLoop
    public long executeLong(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeLong(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeLong(frame, e[last], last, arg);
    }

    @Override
    @ExplodeLoop
    public float executeFloat(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeFloat(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeFloat(frame, e[last], last, arg);
    }

    @Override
    @ExplodeLoop
    public double executeDouble(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeDouble(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeDouble(frame, e[last], last, arg);
    }

    @Override
    @ExplodeLoop
    public boolean executeBoolean(VirtualFrame frame, int argument) throws UnexpectedResultException {
        int arg = profileArg(argument);
        if (CompilerDirectives.inCompiledCode()) {
            PartialBlocks g = this.partialBlocks;
            if (g != null) {
                return g.executeBoolean(frame, arg);
            }
        }
        ElementExecutor ex = this.executor;
        T[] e = getElements();
        int last = e.length - 1;
        for (int i = 0; i < last; ++i) {
            ex.executeVoid(frame, e[i], i, arg);
        }
        return ex.executeBoolean(frame, e[last], last, arg);
    }

    static List preparePartialBlockCompilations(OptimizedCallTarget rootCompilation) {
        if (rootCompilation.getOptionValue(OptimizedRuntimeOptions.PartialBlockCompilation)) {
            int nonTrivialNodeCount = rootCompilation.getNonTrivialNodeCount();
            int maxBlockSize = rootCompilation.getOptionValue(OptimizedRuntimeOptions.PartialBlockCompilationSize);
            if (nonTrivialNodeCount > maxBlockSize) {
                BlockVisitor visitor = new BlockVisitor(rootCompilation, maxBlockSize);
                NodeUtil.forEachChild(rootCompilation.getRootNode(), visitor);
                return visitor.blockTargets;
            }
        }
        return Collections.emptyList();
    }

    /*
     * The partial blocks are computed depth first. The deepest block is computed first and if that
     * block computes partial blocks then these nodes will be subtracted from the parent block
     * computation. A block might be split if it has at least two elements. A set of block elements
     * is split if it reaches the maxBlockSize limit. If the total number of child nodes of a block
     * is smaller than the maxBlockSize limit, then the block will not be split. A parent block
     * might still split in that case.
     */
    private static  PartialBlocks computePartialBlocks(OptimizedCallTarget rootCompilation, OptimizedBlockNode currentBlock, BlockVisitor visitor, Object[] array,
                    int maxBlockSize) {
        int currentBlockSize = 0;
        int currentBlockIndex = 0;
        int totalCount = 0;
        int[] blockRanges = null;
        int[] blockSizes = null;
        for (int i = 0; i < array.length; i++) {
            Object child = array[i];
            if (child != null) {
                Node childNode = (Node) child;
                int nodeCountBefore = visitor.count;
                visitor.visit(childNode);
                int childCount = visitor.count - nodeCountBefore;
                totalCount += childCount;
                int newBlockSize = currentBlockSize + childCount;
                if (newBlockSize <= maxBlockSize) {
                    currentBlockSize = newBlockSize;
                } else {
                    /*
                     * If the first child already exceeds the limit, there are no previous elements
                     * to create a partial block from.
                     */
                    if (i > 0) {
                        if (blockRanges == null) {
                            blockRanges = new int[8];
                            /*
                             * blockSizes array needs one more slot than blockRanges because of the
                             * assignment below the for-loop.
                             */
                            blockSizes = new int[blockRanges.length + 1];
                        } else if (currentBlockIndex >= blockRanges.length) {
                            blockRanges = Arrays.copyOf(blockRanges, blockRanges.length * 2);
                            /*
                             * blockSizes array needs one more slot than blockRanges because of the
                             * assignment below the for-loop.
                             */
                            blockSizes = Arrays.copyOf(blockSizes, blockRanges.length + 1);
                        }
                        blockSizes[currentBlockIndex] = currentBlockSize;
                        blockRanges[currentBlockIndex++] = i;
                    }
                    currentBlockSize = childCount;
                }
            }
        }

        if (blockRanges != null) {
            blockSizes[currentBlockIndex] = currentBlockSize;
            /*
             * parent blocks should not count partial child blocks. they can hardly do much better.
             */
            visitor.count -= totalCount;

            // trim block ranges
            blockRanges = blockRanges.length != currentBlockIndex ? Arrays.copyOf(blockRanges, currentBlockIndex) : blockRanges;
            return new PartialBlocks<>(rootCompilation, currentBlock, blockRanges, blockSizes, visitor.blockIndex++);
        }
        return null;
    }

    public PartialBlocks getPartialBlocks() {
        return partialBlocks;
    }

    @Override
    public boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
        PartialBlocks blocks = this.partialBlocks;
        if (blocks == null) {
            /* No partial compilation units compiled -> common case */
            return false;
        }

        /*
         * Find block element for which the child node was replaced. Maybe be the replaced node
         * itself. No replaces may happen during this event as the nodeReplaced event has acquired
         * an AST lock.
         */
        Node element = newNode;
        Node elementParent = element.getParent();
        while (elementParent != this && elementParent != null) {
            element = elementParent;
            elementParent = elementParent.getParent();
        }
        if (elementParent != this) {
            // could not find element, should not happen
            assert false;
            return false;
        }

        /*
         * Search the element using a slow iterative search. This could probably be improved with a
         * second child array that allows to binary search using the hashCode. At this point replace
         * events like this seem rare enough to not justify the cost.
         */
        int elementIndex = -1;
        T[] elements = getElements();
        for (int i = 0; i < elements.length; i++) {
            if (element == elements[i]) {
                elementIndex = i;
                break;
            }
        }
        if (elementIndex == -1) {
            // could not find element, should not happen
            assert false;
            return false;
        }
        assert elementIndex < getElements().length;
        assert elementIndex >= 0;
        /*
         * Block ranges are always ascending so we can use a binary search to find the block index.
         */
        int foundBlockIndex = Arrays.binarySearch(blocks.blockRanges, elementIndex);
        int callTargetIndex;
        if (foundBlockIndex < 0) {
            // insertion match
            callTargetIndex = -foundBlockIndex - 1;
        } else {
            // direct match
            callTargetIndex = foundBlockIndex + 1;
        }
        blocks.blockTargets[callTargetIndex].nodeReplaced(oldNode, newNode, reason);
        // Also report the replace to parent block compilations and root call targets.
        return false;
    }

    private void reportBlocksInstalled(CharSequence reason) {
        // no need to report for current node.
        Node node = getParent();
        while (node != null) {
            boolean consumed = false;
            if (node instanceof ReplaceObserver) {
                consumed = ((ReplaceObserver) node).nodeReplaced(this, this, reason);
            } else if (node instanceof RootNode) {
                CallTarget target = ((RootNode) node).getCallTarget();
                if (target instanceof ReplaceObserver) {
                    consumed = ((ReplaceObserver) target).nodeReplaced(this, this, reason);
                }
            }
            if (consumed) {
                break;
            }
            node = node.getParent();
        }
    }

    static final class BlockVisitor implements NodeVisitor {

        final List blockTargets = new ArrayList<>();
        final OptimizedCallTarget rootCompilation;
        final int maxBlockSize;
        int blockIndex;
        int count;

        BlockVisitor(OptimizedCallTarget rootCompilation, int maxBlockSize) {
            this.rootCompilation = rootCompilation;
            this.maxBlockSize = maxBlockSize;
        }

        @Override
        public boolean visit(Node node) {
            if (!node.getCost().isTrivial()) {
                count++;
            }
            if (node instanceof BlockNode) {
                computeBlock((OptimizedBlockNode) node);
            } else {
                NodeUtil.forEachChild(node, this);
            }
            return true;
        }

        private  void computeBlock(OptimizedBlockNode blockNode) {
            Object[] children = blockNode.getElements();
            PartialBlocks oldBlocks = blockNode.getPartialBlocks();
            PartialBlocks newBlocks;
            if (oldBlocks == null) {
                newBlocks = computePartialBlocks(rootCompilation, blockNode, this, children, maxBlockSize);
            } else {
                newBlocks = oldBlocks;
            }
            if (oldBlocks == null) {
                blockNode.atomic(new Runnable() {
                    @Override
                    public void run() {
                        PartialBlocks otherOldBlocks = blockNode.getPartialBlocks();
                        if (otherOldBlocks == null) {
                            blockNode.partialBlocks = newBlocks;
                            blockNode.reportBlocksInstalled("Partial blocks installed");
                        }
                    }
                });
            }
            if (newBlocks != null) {
                blockTargets.addAll(Arrays.asList(newBlocks.blockTargets));
            }
        }
    }

    static final class PartialBlockRootNode extends RootNode {

        private final OptimizedBlockNode block;
        private final int startIndex;
        private final int endIndex;
        private final int blockIndex;
        private SourceSection cachedSourceSection;

        PartialBlockRootNode(FrameDescriptor descriptor, OptimizedBlockNode block, int startIndex, int endIndex, int blockIndex) {
            super(null, descriptor);
            this.block = block;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.blockIndex = blockIndex;
            assert startIndex != endIndex : "no empty blocks allowed";
        }

        @Override
        public boolean isCloningAllowed() {
            return false;
        }

        @Override
        public String getName() {
            return computeName(block.getRootNode().getName());
        }

        @Override
        public boolean isInternal() {
            return true;
        }

        @Override
        @TruffleBoundary
        public SourceSection getSourceSection() {
            SourceSection section = this.cachedSourceSection;
            if (section == null) {
                T[] elements = block.getElements();
                SourceSection startSection = elements[startIndex].getSourceSection();
                SourceSection endSection = elements[endIndex - 1].getSourceSection();
                if (startSection != null && endSection != null && startSection.getSource().equals(endSection.getSource())) {
                    if (startSection.getCharIndex() <= endSection.getCharEndIndex()) {
                        section = startSection.getSource().createSection(startSection.getStartLine(), startSection.getStartColumn(), endSection.getEndLine(), endSection.getEndColumn());
                    } else {
                        section = startSection;
                    }
                } else if (startSection != null) {
                    section = startSection;
                } else {
                    section = endSection;
                }
                this.cachedSourceSection = section;
            }
            return section;
        }

        @Override
        public String getQualifiedName() {
            return computeName(block.getRootNode().getQualifiedName());
        }

        private String computeName(String name) {
            StringBuilder blockIndices = new StringBuilder();
            Node parent = block.getParent();
            while (parent != null) {
                if (parent instanceof OptimizedBlockNode) {
                    PartialBlocks blocks = ((OptimizedBlockNode) parent).getPartialBlocks();
                    if (blocks != null) {
                        blockIndices.append(((PartialBlockRootNode) blocks.getBlockTargets()[0].getRootNode()).blockIndex);
                        blockIndices.append(":");
                    }
                }
                parent = parent.getParent();
            }
            blockIndices.append(blockIndex);

            String suffix = "";
            if (name == null) {
                return suffix;
            } else {
                return name + suffix;
            }
        }

        @Override
        @ExplodeLoop
        public Object execute(VirtualFrame frame) {
            Object[] arguments = frame.getArguments();
            MaterializedFrame outerFrame = (MaterializedFrame) arguments[0];
            int arg = readAndProfileArg(arguments);
            ElementExecutor ex = block.executor;
            T[] e = block.getElements();
            int last = endIndex - 1;
            for (int i = startIndex; i < last; ++i) {
                ex.executeVoid(outerFrame, e[i], i, arg);
            }
            if (last == block.getElements().length - 1) {
                // last element of the block -> return a value
                return ex.executeGeneric(outerFrame, e[last], last, arg);
            } else {
                ex.executeVoid(outerFrame, e[last], last, arg);
                return null;
            }
        }

        private int readAndProfileArg(Object[] arguments) {
            Assumption alwaysNoArgument = block.alwaysNoArgument;
            if (alwaysNoArgument != null && alwaysNoArgument.isValid()) {
                return NO_ARGUMENT;
            } else {
                return (int) arguments[1];
            }
        }

        @Override
        public String toString() {
            return getQualifiedName();
        }
    }

    public static final class PartialBlocks {

        final OptimizedBlockNode block;
        /*
         * We do not use direct call nodes to avoid inlining.
         */
        @CompilationFinal(dimensions = 1) final OptimizedCallTarget[] blockTargets;
        @CompilationFinal(dimensions = 1) final int[] blockRanges;

        PartialBlocks(OptimizedCallTarget rootCompilation, OptimizedBlockNode block, int[] blockRanges, int[] blockSizes, int blockIndex) {
            assert blockRanges.length > 0;
            this.block = block;
            this.blockRanges = blockRanges;

            RootNode rootNode = rootCompilation.getRootNode();
            assert rootNode == block.getRootNode();
            OptimizedTruffleRuntime runtime = OptimizedTruffleRuntime.getRuntime();
            Class materializedFrameClass = runtime.createMaterializedFrame(new Object[0]).getClass();
            FrameDescriptor descriptor = rootNode.getFrameDescriptor();
            runtime.markFrameMaterializeCalled(descriptor);
            int startIndex = 0;
            OptimizedCallTarget[] targets = new OptimizedCallTarget[blockRanges.length + 1];
            for (int i = 0; i < targets.length; i++) {
                int endIndex;
                if (i < blockRanges.length) {
                    endIndex = blockRanges[i];
                } else {
                    endIndex = block.getElements().length;
                }

                PartialBlockRootNode partialRootNode = new PartialBlockRootNode<>(new FrameDescriptor(), block, startIndex, endIndex, blockIndex);
                OptimizedRuntimeAccessor.NODES.applySharingLayer(rootNode, partialRootNode);

                targets[i] = (OptimizedCallTarget) partialRootNode.getCallTarget();
                targets[i].setNonTrivialNodeCount(blockSizes[i]);
                // we know the parameter types for block compilations. No need to check, lets cast
                // them unsafely.
                targets[i].initializeUnsafeArgumentTypes(new Class[]{materializedFrameClass, Integer.class});
                // All block compilations share the speculation log of the root compilation.
                targets[i].setSpeculationLog(rootCompilation.getSpeculationLog());
                startIndex = endIndex;
            }
            this.blockTargets = targets;
        }

        public OptimizedCallTarget[] getBlockTargets() {
            return blockTargets;
        }

        public int[] getBlockRanges() {
            return blockRanges;
        }

        int executeInt(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Integer) {
                return (int) result;
            }
            throw new UnexpectedResultException(result);
        }

        byte executeByte(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Byte) {
                return (byte) result;
            }
            throw new UnexpectedResultException(result);
        }

        short executeShort(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Short) {
                return (short) result;
            }
            throw new UnexpectedResultException(result);
        }

        long executeLong(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Long) {
                return (long) result;
            }
            throw new UnexpectedResultException(result);
        }

        char executeChar(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Character) {
                return (char) result;
            }
            throw new UnexpectedResultException(result);
        }

        float executeFloat(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Float) {
                return (float) result;
            }
            throw new UnexpectedResultException(result);
        }

        double executeDouble(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Double) {
                return (double) result;
            }
            throw new UnexpectedResultException(result);
        }

        boolean executeBoolean(VirtualFrame frame, int arg) throws UnexpectedResultException {
            Object result = execute(frame, arg);
            if (result instanceof Boolean) {
                return (boolean) result;
            }
            throw new UnexpectedResultException(result);
        }

        @ExplodeLoop
        Object execute(VirtualFrame frame, int arg) {
            Object[] arguments = new Object[]{frame.materialize(), arg};
            int[] ranges = blockRanges;
            OptimizedCallTarget[] targets = this.blockTargets;
            for (int i = 0; i < ranges.length; i++) {
                targets[i].doInvoke(arguments);
            }
            return targets[ranges.length].doInvoke(arguments);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy