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

org.qbicc.graph.SimpleBasicBlockBuilder Maven / Gradle / Ivy

There is a newer version: 0.77.0
Show newest version
package org.qbicc.graph;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiConsumer;

import io.smallrye.common.constraint.Assert;
import org.qbicc.context.ClassContext;
import org.qbicc.context.CompilationContext;
import org.qbicc.context.Location;
import org.qbicc.graph.atomic.GlobalAccessMode;
import org.qbicc.graph.atomic.ReadAccessMode;
import org.qbicc.graph.atomic.WriteAccessMode;
import org.qbicc.graph.literal.TypeLiteral;
import org.qbicc.type.ArrayObjectType;
import org.qbicc.type.BooleanType;
import org.qbicc.type.ClassObjectType;
import org.qbicc.type.CompoundType;
import org.qbicc.type.InstanceMethodType;
import org.qbicc.type.IntegerType;
import org.qbicc.type.NullableType;
import org.qbicc.type.ObjectType;
import org.qbicc.type.PointerType;
import org.qbicc.type.PrimitiveArrayObjectType;
import org.qbicc.type.ReferenceArrayObjectType;
import org.qbicc.type.ReferenceType;
import org.qbicc.type.TypeType;
import org.qbicc.type.ValueType;
import org.qbicc.type.VoidType;
import org.qbicc.type.WordType;
import org.qbicc.type.definition.element.ExecutableElement;
import org.qbicc.type.definition.element.FieldElement;
import org.qbicc.type.definition.element.InitializerElement;
import org.qbicc.type.definition.element.InstanceFieldElement;
import org.qbicc.type.definition.element.InstanceMethodElement;
import org.qbicc.type.definition.element.LocalVariableElement;
import org.qbicc.type.descriptor.ArrayTypeDescriptor;
import org.qbicc.type.descriptor.ClassTypeDescriptor;
import org.qbicc.type.descriptor.MethodDescriptor;
import org.qbicc.type.descriptor.TypeDescriptor;

final class SimpleBasicBlockBuilder implements BasicBlockBuilder {
    private BlockLabel firstBlock;
    private int line;
    private int bci;
    private Node dependency;
    private BlockEntry blockEntry;
    private BlockLabel currentBlock;
    private BasicBlockBuilder firstBuilder;
    private ExecutableElement element;
    private final ExecutableElement rootElement;
    private Node callSite;
    private BasicBlock terminatedBlock;
    private Map> parameters;
    private final Map unique = new HashMap<>();

    SimpleBasicBlockBuilder(final ExecutableElement element) {
        this.element = element;
        this.rootElement = element;
        bci = - 1;
        parameters = new HashMap<>();
    }

    @Override
    public BlockParameter addParam(BlockLabel owner, Slot slot, ValueType type, boolean nullable) {
        Map subMap = parameters.computeIfAbsent(owner, SimpleBasicBlockBuilder::newMap);
        BlockParameter parameter = subMap.get(slot);
        if (parameter != null) {
            if (parameter.getSlot().equals(slot) && parameter.getType().equals(type) && parameter.isNullable() == nullable) {
                return parameter;
            }
            throw new IllegalArgumentException("Parameter " + slot + " already defined to " + owner);
        }
        if (nullable && ! (type instanceof NullableType)) {
            throw new IllegalArgumentException("Parameter can only be nullable if its type is nullable");
        }
        parameter = new BlockParameter(callSite, element, type, nullable, owner, slot);
        subMap.put(slot, parameter);
        return parameter;
    }

    @Override
    public BlockParameter getParam(BlockLabel owner, Slot slot) throws NoSuchElementException {
        BlockParameter parameter = parameters.getOrDefault(owner, Map.of()).get(slot);
        if (parameter == null) {
            throw new NoSuchElementException("No parameter for slot " + slot + " in " + owner);
        }
        return parameter;
    }

    public BasicBlockBuilder getFirstBuilder() {
        return firstBuilder;
    }

    public void setFirstBuilder(final BasicBlockBuilder first) {
        firstBuilder = Assert.checkNotNullParam("first", first);
    }

    public ExecutableElement getCurrentElement() {
        return element;
    }

    public ExecutableElement getRootElement() { return rootElement; }

    public ExecutableElement setCurrentElement(final ExecutableElement element) {
        ExecutableElement old = this.element;
        this.element = element;
        return old;
    }

    public Node getCallSite() {
        return callSite;
    }

    public Node setCallSite(final Node callSite) {
        Node old = this.callSite;
        this.callSite = callSite;
        return old;
    }

    public Location getLocation() {
        return Location.builder()
            .setElement(element)
            .setLineNumber(line)
            .setByteCodeIndex(bci)
            .build();
    }

    public int setLineNumber(final int newLineNumber) {
        try {
            return line;
        } finally {
            line = newLineNumber;
        }
    }

    public int setBytecodeIndex(final int newBytecodeIndex) {
        try {
            return bci;
        } finally {
            bci = newBytecodeIndex;
        }
    }

    public int getBytecodeIndex() {
        return bci;
    }

    public void finish() {
        if (currentBlock != null) {
            throw new IllegalStateException("Current block not terminated");
        }
        if (firstBlock != null) {
            mark(BlockLabel.getTargetOf(firstBlock), null);
            computeLoops(BlockLabel.getTargetOf(firstBlock), new ArrayList<>(), new HashSet<>(), new HashSet<>(), new HashMap<>());
            getContext().getScheduler().schedule(getFirstBlock());
        }
    }

    @Override
    public BasicBlock getFirstBlock() throws IllegalStateException {
        BlockLabel firstBlock = this.firstBlock;
        if (firstBlock != null && firstBlock.hasTarget()) {
            return BlockLabel.getTargetOf(firstBlock);
        }
        throw new IllegalStateException("First block not yet terminated");
    }

    @Override
    public BlockLabel getEntryLabel() throws IllegalStateException {
        BlockLabel firstBlock = this.firstBlock;
        if (firstBlock != null) {
            return firstBlock;
        }
        throw new IllegalStateException("First block not yet started");
    }

    private void mark(BasicBlock block, BasicBlock from) {
        if (block.setReachableFrom(from)) {
            Terminator terminator = block.getTerminator();
            int cnt = terminator.getSuccessorCount();
            for (int i = 0; i < cnt; i ++) {
                mark(terminator.getSuccessor(i), block);
            }
        }
    }

    private void computeLoops(BasicBlock block, ArrayList blocks, HashSet blocksSet, HashSet visited, Map, Map>> cache) {
        if (! visited.add(block)) {
            return;
        }
        blocks.add(block);
        blocksSet.add(block);
        Terminator terminator = block.getTerminator();
        int cnt = terminator.getSuccessorCount();
        for (int i = 0; i < cnt; i ++) {
            BasicBlock successor = terminator.getSuccessor(i);
            if (blocksSet.contains(successor)) {
                int idx = blocks.indexOf(successor);
                assert idx != -1;
                // all blocks in the span are a part of the new loop
                BasicBlock.Loop loop = new BasicBlock.Loop(successor, block);
                for (int j = idx; j < blocks.size(); j ++) {
                    BasicBlock member = blocks.get(j);
                    Set oldLoops = member.getLoops();
                    member.setLoops(cache.computeIfAbsent(oldLoops, SimpleBasicBlockBuilder::newMap).computeIfAbsent(loop, l -> setWith(oldLoops, l)));
                }
            } else {
                // a block we haven't hit yet
                computeLoops(successor, blocks, blocksSet, visited, cache);
            }
        }
        BasicBlock removed = blocks.remove(blocks.size() - 1);
        assert removed == block;
        blocksSet.remove(block);
    }

    private static  Map newMap(Object arg) {
        return new HashMap<>();
    }

    private static  Set setWith(Set set, E item) {
        if (set.contains(item)) {
            return set;
        }
        int size = set.size();
        if (size == 0) {
            return Set.of(item);
        } else if (size == 1) {
            return Set.of(set.iterator().next(), item);
        } else if (size == 2) {
            Iterator iterator = set.iterator();
            return Set.of(iterator.next(), iterator.next(), item);
        } else {
            @SuppressWarnings("unchecked")
            E[] array = set.toArray((E[]) new Object[size + 1]);
            array[size] = item;
            return Set.of(array);
        }
    }

    public Value add(final Value v1, final Value v2) {
        return unique(new Add(callSite, element, line, bci, v1, v2));
    }

    public Value multiply(final Value v1, final Value v2) {
        return unique(new Multiply(callSite, element, line, bci, v1, v2));
    }

    public Value and(final Value v1, final Value v2) {
        return unique(new And(callSite, element, line, bci, v1, v2));
    }

    public Value or(final Value v1, final Value v2) {
        return unique(new Or(callSite, element, line, bci, v1, v2));
    }

    public Value xor(final Value v1, final Value v2) {
        return unique(new Xor(callSite, element, line, bci, v1, v2));
    }

    public Value isEq(final Value v1, final Value v2) {
        return unique(new IsEq(callSite, element, line, bci, v1, v2, getTypeSystem().getBooleanType()));
    }

    public Value isNe(final Value v1, final Value v2) {
        return unique(new IsNe(callSite, element, line, bci, v1, v2, getTypeSystem().getBooleanType()));
    }

    public Value shr(final Value v1, final Value v2) {
        return unique(new Shr(callSite, element, line, bci, v1, v2));
    }

    public Value shl(final Value v1, final Value v2) {
        return unique(new Shl(callSite, element, line, bci, v1, v2));
    }

    public Value sub(final Value v1, final Value v2) {
        return unique(new Sub(callSite, element, line, bci, v1, v2));
    }

    public Value divide(final Value v1, final Value v2) {
        return unique(new Div(callSite, element, line, bci, v1, v2));
    }

    public Value remainder(final Value v1, final Value v2) {
        return unique(new Mod(callSite, element, line, bci, v1, v2));
    }

    public Value min(final Value v1, final Value v2) {
        return unique(new Min(callSite, element, line, bci, v1, v2));
    }

    public Value max(final Value v1, final Value v2) {
        return unique(new Max(callSite, element, line, bci, v1, v2));
    }

    public Value isLt(final Value v1, final Value v2) {
        return unique(new IsLt(callSite, element, line, bci, v1, v2, getTypeSystem().getBooleanType()));
    }

    public Value isGt(final Value v1, final Value v2) {
        return unique(new IsGt(callSite, element, line, bci, v1, v2, getTypeSystem().getBooleanType()));
    }

    public Value isLe(final Value v1, final Value v2) {
        return unique(new IsLe(callSite, element, line, bci, v1, v2, getTypeSystem().getBooleanType()));
    }

    public Value isGe(final Value v1, final Value v2) {
        return unique(new IsGe(callSite, element, line, bci, v1, v2, getTypeSystem().getBooleanType()));
    }

    public Value rol(final Value v1, final Value v2) {
        return unique(new Rol(callSite, element, line, bci, v1, v2));
    }

    public Value ror(final Value v1, final Value v2) {
        return unique(new Ror(callSite, element, line, bci, v1, v2));
    }

    public Value cmp(Value v1, Value v2) {
        return unique(new Cmp(callSite, element, line, bci, v1, v2, getTypeSystem().getSignedInteger32Type()));
    }

    public Value cmpG(Value v1, Value v2) {
        return unique(new CmpG(callSite, element, line, bci, v1, v2, getTypeSystem().getSignedInteger32Type()));
    }

    public Value cmpL(Value v1, Value v2) {
        return unique(new CmpL(callSite, element, line, bci, v1, v2, getTypeSystem().getSignedInteger32Type()));
    }

    public Value notNull(Value v) {
        return v.isNullable() ? unique(new NotNull(callSite, element, line, bci, v)) : v;
    }

    public Value negate(final Value v) {
        return unique(new Neg(callSite, element, line, bci, v));
    }

    public Value complement(Value v) {
        Assert.checkNotNullParam("v", v);
        if (! (v.getType() instanceof IntegerType || v.getType() instanceof BooleanType)) {
            throw new IllegalArgumentException("Invalid input type");
        }
        return unique(new Comp(callSite, element, line, bci, v));
    }

    public Value byteSwap(final Value v) {
        return unique(new ByteSwap(callSite, element, line, bci, v));
    }

    public Value bitReverse(final Value v) {
        return unique(new BitReverse(callSite, element, line, bci, v));
    }

    public Value countLeadingZeros(final Value v) {
        return unique(new CountLeadingZeros(callSite, element, line, bci, v, getTypeSystem().getSignedInteger32Type()));
    }

    public Value countTrailingZeros(final Value v) {
        return unique(new CountTrailingZeros(callSite, element, line, bci, v, getTypeSystem().getSignedInteger32Type()));
    }

    public Value populationCount(final Value v) {
        throw Assert.unsupported();
    }

    public Value loadLength(final Value arrayPointer) {
        throw new IllegalStateException("loadLength not converted");
    }

    public Value loadTypeId(final Value objectPointer) {
        throw new IllegalStateException("loadTypeId not converted");
    }

    public Value truncate(final Value value, final WordType toType) {
        return unique(new Truncate(callSite, element, line, bci, value, toType));
    }

    public Value extend(final Value value, final WordType toType) {
        return unique(new Extend(callSite, element, line, bci, value, toType));
    }

    public Value bitCast(final Value value, final WordType toType) {
        return unique(new BitCast(callSite, element, line, bci, value, toType));
    }

    public Value valueConvert(final Value value, final WordType toType) {
        return unique(new Convert(callSite, element, line, bci, value, toType));
    }

    public Value decodeReference(Value refVal, PointerType pointerType) {
        // not asDependency() because the dependency may precede the required dependency
        return unique(new DecodeReference(callSite, element, line, bci, requireDependency(), refVal, pointerType));
    }

    public Value instanceOf(final Value input, final ObjectType expectedType, final int expectedDimensions) {
        final BasicBlockBuilder fb = getFirstBuilder();
        ObjectType ifTrueExpectedType = expectedType;
        for (int i=0; i dimensions) {
        return asDependency(new MultiNewArray(callSite, element, line, bci, requireDependency(), arrayType, dimensions));
    }

    public Value multiNewArray(final ArrayTypeDescriptor desc, final List dimensions) {
        throw new IllegalStateException("New of unresolved array type");
    }

    public Value load(final Value pointer, final ReadAccessMode mode) {
        return asDependency(new Load(callSite, element, line, bci, requireDependency(), pointer, mode));
    }

    public Value readModifyWrite(Value pointer, ReadModifyWrite.Op op, Value update, ReadAccessMode readMode, WriteAccessMode writeMode) {
        return asDependency(new ReadModifyWrite(callSite, element, line, bci, requireDependency(), pointer, op, update, readMode, writeMode));
    }

    public Value cmpAndSwap(Value pointer, Value expect, Value update, ReadAccessMode readMode, WriteAccessMode writeMode, CmpAndSwap.Strength strength) {
        CompilationContext ctxt = getCurrentElement().getEnclosingType().getContext().getCompilationContext();
        return asDependency(new CmpAndSwap(callSite, element, line, bci, CmpAndSwap.getResultType(ctxt, pointer.getPointeeType()), requireDependency(), pointer, expect, update, readMode, writeMode, strength));
    }

    public Node store(Value handle, Value value, WriteAccessMode mode) {
        return asDependency(new Store(callSite, element, line, bci, requireDependency(), handle, value, mode));
    }

    public Node initCheck(InitializerElement initializer, Value initThunk) {
        return asDependency(new InitCheck(callSite, element, line, bci, requireDependency(), initializer, initThunk));
    }

    public Node fence(final GlobalAccessMode fenceType) {
        return asDependency(new Fence(callSite, element, line, bci, requireDependency(), fenceType));
    }

    public Node monitorEnter(final Value obj) {
        return asDependency(new MonitorEnter(callSite, element, line, bci, requireDependency(), Assert.checkNotNullParam("obj", obj)));
    }

    public Node monitorExit(final Value obj) {
        return asDependency(new MonitorExit(callSite, element, line, bci, requireDependency(), Assert.checkNotNullParam("obj", obj)));
    }

    public Value call(Value targetPtr, Value receiver, List arguments) {
        if (receiver.getType() instanceof VoidType && targetPtr.getPointeeType() instanceof InstanceMethodType) {
            getContext().error(getLocation(), "Call to instance method without receiver");
        }
        return asDependency(new Call(callSite, element, line, bci, requireDependency(), targetPtr, receiver, arguments));
    }

    public Value callNoSideEffects(Value targetPtr, Value receiver, List arguments) {
        if (receiver.getType() instanceof VoidType && targetPtr.getPointeeType() instanceof InstanceMethodType) {
            getContext().error(getLocation(), "Call to instance method without receiver");
        }
        return unique(new CallNoSideEffects(callSite, element, line, bci, targetPtr, receiver, arguments));
    }

    public Node nop() {
        return requireDependency();
    }

    private  N asDependency(N node) {
        this.dependency = node;
        return node;
    }

    private  V unique(V value) {
        Value existing = unique.putIfAbsent(value, value);
        //noinspection unchecked
        return existing != null ? (V) existing : value;
    }

    public Node begin(final BlockLabel blockLabel) {
        Assert.checkNotNullParam("blockLabel", blockLabel);
        if (blockLabel.hasTarget()) {
            throw new IllegalStateException("Block already terminated");
        }
        if (currentBlock != null) {
            throw new IllegalStateException("Block already in progress");
        }
        currentBlock = blockLabel;
        if (firstBlock == null) {
            firstBlock = blockLabel;
        }
        return dependency = blockEntry = new BlockEntry(callSite, element, blockLabel);
    }

    @Override
    public  BasicBlock begin(BlockLabel blockLabel, T arg, BiConsumer maker) {
        Assert.checkNotNullParam("blockLabel", blockLabel);
        Assert.checkNotNullParam("maker", maker);
        if (blockLabel.hasTarget()) {
            throw new IllegalStateException("Block already terminated");
        }
        // save all state on the stack
        final int oldLine = line;
        final int oldBci = bci;
        final Node oldDependency = dependency;
        final BlockEntry oldBlockEntry = blockEntry;
        final BlockLabel oldCurrentBlock = currentBlock;
        final BasicBlock oldTerminatedBlock = terminatedBlock;
        final ExecutableElement oldElement = element;
        final Node oldCallSite = callSite;
        final Map> oldParameters = new HashMap<>(parameters);
        try {
            return doBegin(blockLabel, arg, maker);
        } finally {
            // restore all state
            parameters = oldParameters;
            callSite = oldCallSite;
            element = oldElement;
            terminatedBlock = oldTerminatedBlock;
            currentBlock = oldCurrentBlock;
            blockEntry = oldBlockEntry;
            dependency = oldDependency;
            bci = oldBci;
            line = oldLine;
        }
    }

    private  BasicBlock doBegin(final BlockLabel blockLabel, final T arg, final BiConsumer maker) {
        try {
            currentBlock = blockLabel;
            if (firstBlock == null) {
                firstBlock = blockLabel;
            }
            dependency = blockEntry = new BlockEntry(callSite, element, blockLabel);
            parameters = new HashMap<>();
            maker.accept(arg, firstBuilder);
            if (currentBlock != null) {
                getContext().error(getLocation(), "Block not terminated");
                firstBuilder.unreachable();
            }
        } catch (BlockEarlyTermination ignored) {
        }
        return BlockLabel.getTargetOf(blockLabel);
    }

    public Node reachable(final Value value) {
        return asDependency(new Reachable(callSite, element, line, bci, requireDependency(), value));
    }

    public Node safePoint() {
        return asDependency(new SafePoint(callSite, element, line, bci, requireDependency()));
    }

    public BasicBlock callNoReturn(Value targetPtr, Value receiver, List arguments) {
        if (receiver.getType() instanceof VoidType && targetPtr.getPointeeType() instanceof InstanceMethodType) {
            getContext().error(getLocation(), "Call to instance method without receiver");
        }
        return terminate(requireCurrentBlock(), new CallNoReturn(callSite, element, line, bci, blockEntry, dependency, targetPtr, receiver, arguments));
    }

    public BasicBlock invokeNoReturn(Value targetPtr, Value receiver, List arguments, BlockLabel catchLabel, Map targetArguments) {
        if (receiver.getType() instanceof VoidType && targetPtr.getPointeeType() instanceof InstanceMethodType) {
            getContext().error(getLocation(), "Call to instance method without receiver");
        }
        return terminate(requireCurrentBlock(), new InvokeNoReturn(callSite, element, line, bci, blockEntry, dependency, targetPtr, receiver, arguments, catchLabel, targetArguments));
    }

    public BasicBlock tailCall(Value targetPtr, Value receiver, List arguments) {
        if (receiver.getType() instanceof VoidType && targetPtr.getPointeeType() instanceof InstanceMethodType) {
            getContext().error(getLocation(), "Call to instance method without receiver");
        }
        return terminate(requireCurrentBlock(), new TailCall(callSite, element, line, bci, blockEntry, dependency, targetPtr, receiver, arguments));
    }

    public Value invoke(Value targetPtr, Value receiver, List arguments, BlockLabel catchLabel, BlockLabel resumeLabel, Map targetArguments) {
        if (receiver.getType() instanceof VoidType && targetPtr.getPointeeType() instanceof InstanceMethodType) {
            getContext().error(getLocation(), "Call to instance method without receiver");
        }
        final BlockLabel currentBlock = requireCurrentBlock();
        Invoke invoke = new Invoke(callSite, element, line, bci, blockEntry, dependency, targetPtr, receiver, arguments, catchLabel, resumeLabel, targetArguments);
        terminate(currentBlock, invoke);
        // todo: addParam(resumeLabel, Slot.result(), targetPtr.getReturnType())
        return invoke.getReturnValue();
    }

    public BasicBlock goto_(final BlockLabel resumeLabel, final Map targetArguments) {
        return terminate(requireCurrentBlock(), new Goto(callSite, element, line, bci, blockEntry, dependency, resumeLabel, targetArguments));
    }

    public BasicBlock if_(final Value condition, final BlockLabel trueTarget, final BlockLabel falseTarget, final Map targetArguments) {
        return terminate(requireCurrentBlock(), new If(callSite, element, line, bci, blockEntry, dependency, condition, trueTarget, falseTarget, targetArguments));
    }

    public BasicBlock return_(final Value value) {
        if (value == null) {
            return return_(emptyVoid());
        }
        return terminate(requireCurrentBlock(), new Return(callSite, element, line, bci, blockEntry, dependency, value));
    }

    public BasicBlock unreachable() {
        return terminate(requireCurrentBlock(), new Unreachable(callSite, element, line, bci, blockEntry, dependency));
    }

    public BasicBlock throw_(final Value value) {
        return terminate(requireCurrentBlock(), new Throw(callSite, element, line, bci, blockEntry, dependency, value));
    }

    public BasicBlock ret(final Value address, Map targetArguments) {
        return terminate(requireCurrentBlock(), new Ret(callSite, element, line, bci, blockEntry, dependency, address, targetArguments));
    }

    public BlockEntry getBlockEntry() {
        requireCurrentBlock();
        return blockEntry;
    }

    public BasicBlock getTerminatedBlock() {
        BasicBlock block = terminatedBlock;
        if (block == null) {
            throw new IllegalStateException("No block terminated yet");
        }
        return block;
    }

    public BasicBlock switch_(final Value value, final int[] checkValues, final BlockLabel[] targets, final BlockLabel defaultTarget, final Map targetArguments) {
        return terminate(requireCurrentBlock(), new Switch(callSite, element, line, bci, blockEntry, dependency, defaultTarget, checkValues, targets, value, targetArguments));
    }

    private BasicBlock terminate(final BlockLabel block, final Terminator op) {
        BasicBlock realBlock = op.getTerminatedBlock();
        terminatedBlock = realBlock;
        block.setTarget(realBlock);
        blockEntry = null;
        currentBlock = null;
        dependency = null;
        return realBlock;
    }

    private BlockLabel requireCurrentBlock() {
        BlockLabel block = this.currentBlock;
        if (block == null) {
            assert dependency == null;
            throw noBlock();
        }
        assert dependency != null;
        return block;
    }

    private Node requireDependency() {
        Node dependency = this.dependency;
        if (dependency == null) {
            assert currentBlock == null;
            throw noBlock();
        }
        assert currentBlock != null;
        return dependency;
    }

    private IllegalStateException noBlock() {
        return new IllegalStateException("No block in progress");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy