
org.qbicc.plugin.reachability.ReachabilityBlockBuilder Maven / Gradle / Ivy
package org.qbicc.plugin.reachability;
import java.util.HashSet;
import org.qbicc.context.CompilationContext;
import org.qbicc.facts.Facts;
import org.qbicc.graph.Action;
import org.qbicc.graph.BasicBlock;
import org.qbicc.graph.BasicBlockBuilder;
import org.qbicc.graph.ClassOf;
import org.qbicc.graph.CmpAndSwap;
import org.qbicc.graph.DelegatingBasicBlockBuilder;
import org.qbicc.graph.InitCheck;
import org.qbicc.graph.InstanceFieldOf;
import org.qbicc.graph.InterfaceMethodLookup;
import org.qbicc.graph.Load;
import org.qbicc.graph.MultiNewArray;
import org.qbicc.graph.New;
import org.qbicc.graph.NewReferenceArray;
import org.qbicc.graph.Node;
import org.qbicc.graph.NodeVisitor;
import org.qbicc.graph.OrderedNode;
import org.qbicc.graph.ReadModifyWrite;
import org.qbicc.graph.Slot;
import org.qbicc.graph.Store;
import org.qbicc.graph.Terminator;
import org.qbicc.graph.Value;
import org.qbicc.graph.VirtualMethodLookup;
import org.qbicc.graph.literal.ConstructorLiteral;
import org.qbicc.graph.literal.FunctionLiteral;
import org.qbicc.graph.literal.InitializerLiteral;
import org.qbicc.graph.literal.InstanceMethodLiteral;
import org.qbicc.graph.literal.ObjectLiteral;
import org.qbicc.graph.literal.StaticFieldLiteral;
import org.qbicc.graph.literal.StaticMethodLiteral;
import org.qbicc.graph.literal.TypeLiteral;
import org.qbicc.plugin.coreclasses.RuntimeMethodFinder;
import org.qbicc.type.ClassObjectType;
import org.qbicc.type.InterfaceObjectType;
import org.qbicc.type.ReferenceArrayObjectType;
import org.qbicc.type.definition.LoadedTypeDefinition;
import org.qbicc.type.definition.classfile.ClassFile;
import org.qbicc.type.definition.element.ConstructorElement;
import org.qbicc.type.definition.element.ExecutableElement;
import org.qbicc.type.definition.element.FieldElement;
import org.qbicc.type.definition.element.FunctionElement;
import org.qbicc.type.definition.element.InitializerElement;
import org.qbicc.type.definition.element.MethodElement;
import org.qbicc.type.definition.element.StaticFieldElement;
/**
* A block builder stage which traverses the final set of reachable Nodes
* for an ExecutableElement and invokes a ReachabilityAnalysis them.
* As a result of the analysis, new ExecutableElements may become reachable and
* this be enqueued for compilation.
*/
public class ReachabilityBlockBuilder extends DelegatingBasicBlockBuilder {
private final CompilationContext ctxt;
public ReachabilityBlockBuilder(final FactoryContext ctxt, final BasicBlockBuilder delegate) {
super(delegate);
this.ctxt = getContext();
}
@Override
public void finish() {
// finish first so that all blocks are populated
super.finish();
BasicBlock entryBlock = getFirstBlock();
entryBlock.getTerminator().accept(new ReachabilityVisitor(), new ReachabilityContext(ctxt, getDelegate().getCurrentElement()));
}
static final class ReachabilityContext {
final CompilationContext ctxt;
private final ExecutableElement currentElement;
private final ReachabilityAnalysis analysis;
final HashSet visited = new HashSet<>();
ReachabilityContext(CompilationContext ctxt, ExecutableElement currentElement) {
this.ctxt = ctxt;
this.currentElement = currentElement;
this.analysis = ReachabilityInfo.get(ctxt).getAnalysis();
}
}
static final class ReachabilityVisitor implements NodeVisitor {
@Override
public Void visitUnknown(ReachabilityContext param, Action node) {
visitUnknown(param, (Node) node);
return null;
}
@Override
public Void visitUnknown(ReachabilityContext param, Value node) {
visitUnknown(param, (Node) node);
return null;
}
@Override
public Void visitUnknown(ReachabilityContext param, Terminator node) {
if (visitUnknown(param, (Node) node)) {
// process reachable successors
int cnt = node.getSuccessorCount();
for (int i = 0; i < cnt; i ++) {
node.getSuccessor(i).getTerminator().accept(this, param);
}
for (Slot slot : node.getOutboundArgumentNames()) {
node.getOutboundArgument(slot).accept(this, param);
}
}
return null;
}
boolean visitUnknown(ReachabilityContext param, Node node) {
if (param.visited.add(node)) {
int cnt = node.getValueDependencyCount();
for (int i = 0; i < cnt; i ++) {
node.getValueDependency(i).accept(this, param);
}
if (node instanceof OrderedNode on) {
Node dependency =on.getDependency();
if (dependency instanceof Action a) {
a.accept(this, param);
} else if (dependency instanceof Value v) {
v.accept(this, param);
} else if (dependency instanceof Terminator t) {
t.accept(this, param);
}
}
return true;
}
return false;
}
@Override
public Void visit(ReachabilityContext param, ObjectLiteral value) {
param.analysis.processReachableObject(value.getValue(), param.currentElement);
return null;
}
@Override
public Void visit(ReachabilityContext param, TypeLiteral value) {
if (value.getValue() instanceof ClassObjectType cot) {
param.analysis.processReachableType(cot.getDefinition().load(), param.currentElement);
} else if (value.getValue() instanceof InterfaceObjectType iot) {
param.analysis.processReachableType(iot.getDefinition().load(), param.currentElement);
} else if (value.getValue() instanceof ReferenceArrayObjectType aot) {
param.analysis.processArrayElementType(aot.getLeafElementType());
}
return null;
}
@Override
public Void visit(ReachabilityContext param, ConstructorLiteral node) {
if (visitUnknown(param, (Node)node)) {
ConstructorElement target = node.getExecutable();
param.analysis.processReachableExactInvocation(target, param.currentElement);
// The lambda meta factory and other reflective machinery in the JDK use Unsafe.allocateInstance
// instead of a "real" New to instantiate a class. To compensate, reachability analysis assumes
// that if a ConstructorElementHandle is invoked and the invocation isn't the super. that starts
// all non-Object constructors, then the declaring type of the constructor _must_ have been instantiated somehow.
if (!target.getEnclosingType().load().isAbstract()) {
if (!(param.currentElement instanceof ConstructorElement) ||
(param.currentElement instanceof ConstructorElement &&
(param.currentElement.getEnclosingType().load().getSuperClass() == null ||
!param.currentElement.getEnclosingType().load().getSuperClass().equals(target.getEnclosingType())))) {
param.analysis.processInstantiatedClass(target.getEnclosingType().load(), false, param.currentElement);
}
}
}
return null;
}
@Override
public Void visit(ReachabilityContext param, InitializerLiteral literal) {
final InitializerElement init = literal.getExecutable();
if (init.hasAllModifiersOf(ClassFile.I_ACC_RUN_TIME)) {
param.analysis.processReachableRuntimeInitializer(init, param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, FunctionLiteral node) {
if (visitUnknown(param, (Node)node)) {
FunctionElement target = node.getExecutable();
param.analysis.processReachableExactInvocation(target, param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, InstanceMethodLiteral node) {
if (visitUnknown(param, (Node)node)) {
param.analysis.processReachableExactInvocation(node.getExecutable(), param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, VirtualMethodLookup node) {
if (visitUnknown(param, (Node)node)) {
param.analysis.processReachableDispatchedInvocation(node.getMethod(), param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, InterfaceMethodLookup node) {
if (visitUnknown(param, (Node)node)) {
param.analysis.processReachableDispatchedInvocation(node.getMethod(), param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, StaticMethodLiteral node) {
if (visitUnknown(param, (Node)node)) {
MethodElement target = node.getExecutable();
param.analysis.processReachableExactInvocation(target, param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, New node) {
if (visitUnknown(param, (Node)node)) {
LoadedTypeDefinition ltd = node.getClassObjectType().getDefinition().load();
param.analysis.processInstantiatedClass(ltd, false, param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, NewReferenceArray node) {
if (visitUnknown(param, (Node)node)) {
// Force the array's leaf element type to be reachable (and thus assigned a typeId).
param.analysis.processArrayElementType(node.getArrayType().getLeafElementType());
}
return null;
}
@Override
public Void visit(ReachabilityContext param, MultiNewArray node) {
if (visitUnknown(param, (Node)node)) {
if (node.getArrayType() instanceof ReferenceArrayObjectType at) {
// Force the array's leaf element type to be reachable (and thus assigned a typeId).
param.analysis.processArrayElementType(at.getLeafElementType());
}
}
return null;
}
@Override
public Void visit(ReachabilityContext param, StaticFieldLiteral node) {
if (visitUnknown(param, (Node)node)) {
StaticFieldElement f = node.getVariableElement();
param.analysis.processReachableStaticFieldAccess(f, param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, InitCheck node) {
if (visitUnknown(param, (Node)node)) {
param.analysis.processReachableRuntimeInitializer(node.getInitializerElement(), param.currentElement);
MethodElement run = RuntimeMethodFinder.get(param.ctxt).getMethod("org/qbicc/runtime/main/Once", "run");
param.analysis.processReachableDispatchedInvocation(run, param.currentElement);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, ClassOf node) {
if (visitUnknown(param, (Node)node)) {
MethodElement methodElement = RuntimeMethodFinder.get(param.ctxt).getMethod("getClassFromTypeId");
param.analysis.processReachableExactInvocation(methodElement, param.currentElement);
}
return null;
}
// Field reachability
@Override
public Void visit(ReachabilityContext param, Load node) {
if (visitUnknown(param, (Node)node)) {
FieldElement field;
if (node.getPointer() instanceof StaticFieldLiteral sfl) {
field = sfl.getVariableElement();
} else if (node.getPointer() instanceof InstanceFieldOf ifo) {
field = ifo.getVariableElement();
} else {
return null;
}
Facts.get(param.ctxt).discover(field, FieldReachabilityFacts.IS_READ);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, Store node) {
if (visitUnknown(param, (Node)node)) {
FieldElement field;
if (node.getPointer() instanceof StaticFieldLiteral sfl) {
field = sfl.getVariableElement();
} else if (node.getPointer() instanceof InstanceFieldOf ifo) {
field = ifo.getVariableElement();
} else {
return null;
}
// todo: exclude writes of the field's original constant value (usually null/zero but might be something else)
Facts.get(param.ctxt).discover(field, FieldReachabilityFacts.IS_WRITTEN);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, CmpAndSwap node) {
if (visitUnknown(param, (Node)node)) {
FieldElement field;
if (node.getPointer() instanceof StaticFieldLiteral sfl) {
field = sfl.getVariableElement();
} else if (node.getPointer() instanceof InstanceFieldOf ifo) {
field = ifo.getVariableElement();
} else {
return null;
}
// todo: if `expect` is not equal to the field's original constant value, then only mark a read here
Facts.get(param.ctxt).discover(field, FieldReachabilityFacts.IS_READ, FieldReachabilityFacts.IS_WRITTEN);
}
return null;
}
@Override
public Void visit(ReachabilityContext param, ReadModifyWrite node) {
if (visitUnknown(param, (Node)node)) {
FieldElement field;
if (node.getPointer() instanceof StaticFieldLiteral sfl) {
field = sfl.getVariableElement();
} else if (node.getPointer() instanceof InstanceFieldOf ifo) {
field = ifo.getVariableElement();
} else {
return null;
}
Facts.get(param.ctxt).discover(field, FieldReachabilityFacts.IS_READ, FieldReachabilityFacts.IS_WRITTEN);
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy