org.qbicc.graph.SimpleBasicBlockBuilder Maven / Gradle / Ivy
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