mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-23 00:24:11 -08:00
Add optimization that eliminates repeated access to fields
This commit is contained in:
parent
511160935f
commit
81cc3c156e
244
core/src/main/java/org/teavm/model/analysis/AliasAnalysis.java
Normal file
244
core/src/main/java/org/teavm/model/analysis/AliasAnalysis.java
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.analysis;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntArrayDeque;
|
||||||
|
import com.carrotsearch.hppc.IntDeque;
|
||||||
|
import com.carrotsearch.hppc.IntHashSet;
|
||||||
|
import com.carrotsearch.hppc.IntSet;
|
||||||
|
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.common.GraphBuilder;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Incoming;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.Phi;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.ArrayElementType;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
|
|
||||||
|
public class AliasAnalysis {
|
||||||
|
private Graph interferenceGraph;
|
||||||
|
private boolean[] variablesWithExternalObject;
|
||||||
|
private int[] arrayOfVariablesWithExternalObject;
|
||||||
|
|
||||||
|
public void analyze(Program program, MethodDescriptor methodDescriptor) {
|
||||||
|
DfgBuildVisitor visitor = prepare(program, methodDescriptor);
|
||||||
|
IntSet[] instances = propagate(visitor, program.variableCount());
|
||||||
|
buildInterferenceGraph(instances, visitor.constructedObjectCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] affectedVariables(int variable) {
|
||||||
|
return interferenceGraph.outgoingEdges(variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean affectsEverything(int variable) {
|
||||||
|
return variablesWithExternalObject[variable];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getExternalObjects() {
|
||||||
|
return arrayOfVariablesWithExternalObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DfgBuildVisitor prepare(Program program, MethodDescriptor methodDescriptor) {
|
||||||
|
DfgBuildVisitor visitor = new DfgBuildVisitor(program.variableCount());
|
||||||
|
|
||||||
|
for (int i = 1; i <= methodDescriptor.parameterCount(); ++i) {
|
||||||
|
if (methodDescriptor.parameterType(i - 1) instanceof ValueType.Object) {
|
||||||
|
visitor.queue.addLast(i);
|
||||||
|
visitor.queue.addLast(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Phi phi : block.getPhis()) {
|
||||||
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
|
visitor.builder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block.getExceptionVariable() != null) {
|
||||||
|
visitor.queue.addLast(block.getExceptionVariable().getIndex());
|
||||||
|
visitor.queue.addLast(0);
|
||||||
|
}
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instruction.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return visitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntSet[] propagate(DfgBuildVisitor visitor, int variableCount) {
|
||||||
|
Graph dfg = visitor.builder.build();
|
||||||
|
IntDeque queue = visitor.queue;
|
||||||
|
IntSet[] instances = new IntSet[variableCount];
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
int v = queue.removeFirst();
|
||||||
|
int instance = queue.removeFirst();
|
||||||
|
|
||||||
|
IntSet instancesByVar = instances[v];
|
||||||
|
if (instancesByVar == null) {
|
||||||
|
instancesByVar = new IntHashSet();
|
||||||
|
instances[v] = instancesByVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instancesByVar.contains(instance) || instancesByVar.contains(0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance == 0) {
|
||||||
|
instancesByVar.clear();
|
||||||
|
}
|
||||||
|
instancesByVar.add(instance);
|
||||||
|
|
||||||
|
for (int successor : dfg.outgoingEdges(v)) {
|
||||||
|
if (instances[successor] == null
|
||||||
|
|| (!instances[successor].contains(instance) && !instances[successor].contains(0))) {
|
||||||
|
queue.addLast(successor);
|
||||||
|
queue.addLast(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildInterferenceGraph(IntSet[] instances, int instanceCount) {
|
||||||
|
GraphBuilder builder = new GraphBuilder(instances.length);
|
||||||
|
variablesWithExternalObject = new boolean[instances.length];
|
||||||
|
IntSet setOfVariablesWithExternalObject = new IntHashSet();
|
||||||
|
|
||||||
|
IntSet[] instanceBackMap = new IntSet[instanceCount];
|
||||||
|
for (int i = 0; i < instances.length; i++) {
|
||||||
|
IntSet instancesByVar = instances[i];
|
||||||
|
if (instancesByVar == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (IntCursor cursor : instancesByVar) {
|
||||||
|
int instance = cursor.value;
|
||||||
|
if (instance == 0) {
|
||||||
|
variablesWithExternalObject[i] = true;
|
||||||
|
setOfVariablesWithExternalObject.add(i);
|
||||||
|
} else {
|
||||||
|
IntSet variables = instanceBackMap[instance];
|
||||||
|
if (variables == null) {
|
||||||
|
variables = new IntHashSet();
|
||||||
|
instanceBackMap[instance] = variables;
|
||||||
|
}
|
||||||
|
variables.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int v = 0; v < instances.length; v++) {
|
||||||
|
builder.addEdge(v, v);
|
||||||
|
|
||||||
|
IntSet instancesByVar = instances[v];
|
||||||
|
if (instancesByVar == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntSet set;
|
||||||
|
if (instancesByVar.size() == 1) {
|
||||||
|
set = instanceBackMap[instancesByVar.iterator().next().value];
|
||||||
|
} else {
|
||||||
|
IntHashSet hashSet = new IntHashSet();
|
||||||
|
for (IntCursor cursor : instancesByVar) {
|
||||||
|
hashSet.addAll(instanceBackMap[cursor.value]);
|
||||||
|
}
|
||||||
|
set = hashSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] array = set.toArray();
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
for (int j = i + 1; j < array.length; ++j) {
|
||||||
|
builder.addEdge(array[i], array[j]);
|
||||||
|
builder.addEdge(array[j], array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interferenceGraph = builder.build();
|
||||||
|
arrayOfVariablesWithExternalObject = setOfVariablesWithExternalObject.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DfgBuildVisitor extends AbstractInstructionVisitor {
|
||||||
|
GraphBuilder builder;
|
||||||
|
int constructedObjectCounter = 1;
|
||||||
|
IntDeque queue = new IntArrayDeque();
|
||||||
|
|
||||||
|
DfgBuildVisitor(int variableCount) {
|
||||||
|
builder = new GraphBuilder(variableCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CastInstruction insn) {
|
||||||
|
builder.addEdge(insn.getValue().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(AssignInstruction insn) {
|
||||||
|
builder.addEdge(insn.getAssignee().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NullCheckInstruction insn) {
|
||||||
|
builder.addEdge(insn.getValue().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(constructedObjectCounter++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
if (insn.getReceiver() != null && insn.getMethod().getReturnType() instanceof ValueType.Object) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetFieldInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetElementInstruction insn) {
|
||||||
|
if (insn.getType() == ArrayElementType.OBJECT) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,10 +16,13 @@
|
||||||
package org.teavm.model.optimization;
|
package org.teavm.model.optimization;
|
||||||
|
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
|
||||||
public interface MethodOptimizationContext {
|
public interface MethodOptimizationContext {
|
||||||
MethodReader getMethod();
|
MethodReader getMethod();
|
||||||
|
|
||||||
DependencyInfo getDependencyInfo();
|
DependencyInfo getDependencyInfo();
|
||||||
|
|
||||||
|
ClassReaderSource getClassSource();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,404 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.optimization;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntArrayDeque;
|
||||||
|
import com.carrotsearch.hppc.IntDeque;
|
||||||
|
import com.carrotsearch.hppc.IntObjectHashMap;
|
||||||
|
import com.carrotsearch.hppc.IntObjectMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
|
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
||||||
|
import com.carrotsearch.hppc.cursors.ObjectIntCursor;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.common.DominatorTree;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.common.GraphUtils;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldReader;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.analysis.AliasAnalysis;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
|
public class RepeatedFieldReadElimination implements MethodOptimization {
|
||||||
|
private static final int ENTER = 0;
|
||||||
|
private static final int EXIT = 1;
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
private boolean[] everythingInvalid;
|
||||||
|
private List<IntObjectHashMap<Set<FieldReference>>> invalidFields = new ArrayList<>();
|
||||||
|
private AliasAnalysis aliasAnalysis;
|
||||||
|
private boolean changed;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean optimize(MethodOptimizationContext context, Program program) {
|
||||||
|
classSource = context.getClassSource();
|
||||||
|
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||||
|
DominatorTree dom = GraphUtils.buildDominatorTree(cfg);
|
||||||
|
aliasAnalysis = new AliasAnalysis();
|
||||||
|
aliasAnalysis.analyze(program, context.getMethod().getDescriptor());
|
||||||
|
|
||||||
|
insertInvalidationPoints(cfg, dom, program);
|
||||||
|
new Traversal(program, dom, cfg).perform();
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Traversal {
|
||||||
|
Program program;
|
||||||
|
IntDeque worklist = new IntArrayDeque();
|
||||||
|
IntObjectMap<ObjectIntMap<FieldReference>> cacheVars = new IntObjectHashMap<>();
|
||||||
|
Deque<State> stateStack = new ArrayDeque<>();
|
||||||
|
Graph domGraph;
|
||||||
|
InstructionAnalyzer instructionAnalyzer = new InstructionAnalyzer();
|
||||||
|
|
||||||
|
Traversal(Program program, DominatorTree dom, Graph cfg) {
|
||||||
|
this.program = program;
|
||||||
|
worklist.addLast(0);
|
||||||
|
worklist.addLast(ENTER);
|
||||||
|
domGraph = GraphUtils.buildDominatorGraph(dom, cfg.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void perform() {
|
||||||
|
while (!worklist.isEmpty()) {
|
||||||
|
int operation = worklist.removeLast();
|
||||||
|
int blockIndex = worklist.removeLast();
|
||||||
|
BasicBlock block = program.basicBlockAt(blockIndex);
|
||||||
|
switch (operation) {
|
||||||
|
case ENTER:
|
||||||
|
enterBlock(block);
|
||||||
|
break;
|
||||||
|
case EXIT:
|
||||||
|
exitBlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enterBlock(BasicBlock block) {
|
||||||
|
stateStack.addLast(new State());
|
||||||
|
invalidatePreparedFields(block);
|
||||||
|
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
if (instruction instanceof GetFieldInstruction) {
|
||||||
|
handleGetField((GetFieldInstruction) instruction);
|
||||||
|
} else {
|
||||||
|
instructionAnalyzer.reset();
|
||||||
|
instruction.acceptVisitor(instructionAnalyzer);
|
||||||
|
if (instructionAnalyzer.invalidatesAll) {
|
||||||
|
invalidateAllFields();
|
||||||
|
} else if (instructionAnalyzer.invalidatedField != null) {
|
||||||
|
FieldReference field = instructionAnalyzer.invalidatedField;
|
||||||
|
int instance = instructionAnalyzer.instance;
|
||||||
|
if (!isVolatile(field)) {
|
||||||
|
invalidateField(instance, field);
|
||||||
|
storeIntoCache(instance, field, instructionAnalyzer.newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
worklist.addLast(block.getIndex());
|
||||||
|
worklist.addLast(EXIT);
|
||||||
|
for (int successor : domGraph.outgoingEdges(block.getIndex())) {
|
||||||
|
worklist.addLast(successor);
|
||||||
|
worklist.addLast(ENTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidatePreparedFields(BasicBlock block) {
|
||||||
|
if (everythingInvalid[block.getIndex()]) {
|
||||||
|
invalidateAllFields();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntObjectHashMap<Set<FieldReference>> invalidFieldsByBlock = invalidFields.get(block.getIndex());
|
||||||
|
if (invalidFieldsByBlock != null) {
|
||||||
|
for (IntObjectCursor<Set<FieldReference>> cursor : invalidFieldsByBlock) {
|
||||||
|
int instance = cursor.key;
|
||||||
|
for (FieldReference field : cursor.value) {
|
||||||
|
invalidateField(instance, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleGetField(GetFieldInstruction instruction) {
|
||||||
|
FieldReference field = instruction.getField();
|
||||||
|
if (isVolatile(field)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int instanceIndex = instruction.getInstance() != null ? instruction.getInstance().getIndex() : -1;
|
||||||
|
ObjectIntMap<FieldReference> cacheVarsByInstance = cacheVars.get(instanceIndex);
|
||||||
|
if (cacheVarsByInstance == null) {
|
||||||
|
cacheVarsByInstance = new ObjectIntHashMap<>();
|
||||||
|
cacheVars.put(instanceIndex, cacheVarsByInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cachedVar = cacheVarsByInstance.getOrDefault(field, -1);
|
||||||
|
if (cachedVar >= 0) {
|
||||||
|
AssignInstruction assign = new AssignInstruction();
|
||||||
|
assign.setReceiver(instruction.getReceiver());
|
||||||
|
assign.setAssignee(program.variableAt(cachedVar));
|
||||||
|
assign.setLocation(instruction.getLocation());
|
||||||
|
instruction.replace(assign);
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
cachedVar = instruction.getReceiver().getIndex();
|
||||||
|
cacheVarsByInstance.put(field, cachedVar);
|
||||||
|
markFieldAsAdded(instanceIndex, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeIntoCache(int instance, FieldReference field, int value) {
|
||||||
|
ObjectIntMap<FieldReference> cacheVarsByInstance = cacheVars.get(instance);
|
||||||
|
if (cacheVarsByInstance == null) {
|
||||||
|
cacheVarsByInstance = new ObjectIntHashMap<>();
|
||||||
|
cacheVars.put(instance, cacheVarsByInstance);
|
||||||
|
}
|
||||||
|
cacheVarsByInstance.put(field, value);
|
||||||
|
markFieldAsAdded(instance, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markFieldAsAdded(int instance, FieldReference field) {
|
||||||
|
State state = currentState();
|
||||||
|
ObjectIntMap<FieldReference> removedFieldsByInstance = state.removedCacheFields.get(instance);
|
||||||
|
if (removedFieldsByInstance == null || !removedFieldsByInstance.containsKey(field)) {
|
||||||
|
Set<FieldReference> fields = state.addedCacheFields.get(instance);
|
||||||
|
if (fields == null) {
|
||||||
|
fields = new HashSet<>();
|
||||||
|
state.addedCacheFields.put(instance, fields);
|
||||||
|
}
|
||||||
|
fields.add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateAllFields() {
|
||||||
|
State state = currentState();
|
||||||
|
|
||||||
|
for (IntObjectCursor<ObjectIntMap<FieldReference>> instanceCursor : cacheVars) {
|
||||||
|
int instance = instanceCursor.key;
|
||||||
|
ObjectIntMap<FieldReference> cacheVarsByInstance = instanceCursor.value;
|
||||||
|
for (ObjectIntCursor<FieldReference> fieldCursor : cacheVarsByInstance) {
|
||||||
|
FieldReference field = fieldCursor.key;
|
||||||
|
int value = fieldCursor.value;
|
||||||
|
markFieldAsRemoved(state, instance, field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheVars.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateField(int instance, FieldReference field) {
|
||||||
|
if (instance == -1) {
|
||||||
|
invalidateSingleField(instance, field);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aliasAnalysis.affectsEverything(instance)) {
|
||||||
|
invalidateFieldOnAllInstances(field);
|
||||||
|
} else {
|
||||||
|
for (int affectedVar : aliasAnalysis.affectedVariables(instance)) {
|
||||||
|
invalidateSingleField(affectedVar, field);
|
||||||
|
}
|
||||||
|
for (int affectedVar : aliasAnalysis.getExternalObjects()) {
|
||||||
|
invalidateSingleField(affectedVar, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateSingleField(int instance, FieldReference field) {
|
||||||
|
ObjectIntMap<FieldReference> cacheVarsByInstance = cacheVars.get(instance);
|
||||||
|
if (cacheVarsByInstance == null || !cacheVarsByInstance.containsKey(field)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int value = cacheVarsByInstance.remove(field);
|
||||||
|
State state = currentState();
|
||||||
|
markFieldAsRemoved(state, instance, field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateFieldOnAllInstances(FieldReference field) {
|
||||||
|
for (IntObjectCursor<ObjectIntMap<FieldReference>> instanceCursor : cacheVars) {
|
||||||
|
int instance = instanceCursor.key;
|
||||||
|
int value = instanceCursor.value.getOrDefault(field, -1);
|
||||||
|
if (value >= 0) {
|
||||||
|
instanceCursor.value.remove(field);
|
||||||
|
State state = currentState();
|
||||||
|
markFieldAsRemoved(state, instance, field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markFieldAsRemoved(State state, int instance, FieldReference field, int value) {
|
||||||
|
Set<FieldReference> addedFieldsByInstance = state.addedCacheFields.get(instance);
|
||||||
|
if (addedFieldsByInstance == null || !addedFieldsByInstance.contains(field)) {
|
||||||
|
ObjectIntMap<FieldReference> removedFieldsByInstance = state.removedCacheFields.get(instance);
|
||||||
|
if (removedFieldsByInstance == null) {
|
||||||
|
removedFieldsByInstance = new ObjectIntHashMap<>();
|
||||||
|
state.removedCacheFields.put(instance, removedFieldsByInstance);
|
||||||
|
}
|
||||||
|
if (!removedFieldsByInstance.containsKey(field)) {
|
||||||
|
removedFieldsByInstance.put(field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exitBlock() {
|
||||||
|
State state = stateStack.removeLast();
|
||||||
|
|
||||||
|
for (IntObjectCursor<Set<FieldReference>> instanceCursor : state.addedCacheFields) {
|
||||||
|
int instance = instanceCursor.key;
|
||||||
|
ObjectIntMap<FieldReference> cachedFieldsByInstances = cacheVars.get(instance);
|
||||||
|
if (cachedFieldsByInstances != null) {
|
||||||
|
for (FieldReference field : instanceCursor.value) {
|
||||||
|
cachedFieldsByInstances.remove(field);
|
||||||
|
}
|
||||||
|
if (cachedFieldsByInstances.isEmpty()) {
|
||||||
|
cacheVars.remove(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IntObjectCursor<ObjectIntMap<FieldReference>> instanceCursor : state.removedCacheFields) {
|
||||||
|
int instance = instanceCursor.key;
|
||||||
|
ObjectIntMap<FieldReference> cacheVarsByInstance = cacheVars.get(instance);
|
||||||
|
if (cacheVarsByInstance == null) {
|
||||||
|
cacheVarsByInstance = new ObjectIntHashMap<>();
|
||||||
|
cacheVars.put(instance, cacheVarsByInstance);
|
||||||
|
}
|
||||||
|
for (ObjectIntCursor<FieldReference> fieldCursor : instanceCursor.value) {
|
||||||
|
FieldReference field = fieldCursor.key;
|
||||||
|
int value = fieldCursor.value;
|
||||||
|
cacheVarsByInstance.put(field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private State currentState() {
|
||||||
|
return stateStack.getLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class State {
|
||||||
|
IntObjectMap<ObjectIntMap<FieldReference>> removedCacheFields = new IntObjectHashMap<>();
|
||||||
|
IntObjectMap<Set<FieldReference>> addedCacheFields = new IntObjectHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertInvalidationPoints(Graph cfg, DominatorTree dom, Program program) {
|
||||||
|
int[][] domFrontiers = GraphUtils.findDominanceFrontiers(cfg, dom);
|
||||||
|
everythingInvalid = new boolean[program.basicBlockCount()];
|
||||||
|
invalidFields.addAll(Collections.nCopies(program.basicBlockCount(), null));
|
||||||
|
|
||||||
|
IntArrayDeque worklist = new IntArrayDeque();
|
||||||
|
InstructionAnalyzer instructionAnalyzer = new InstructionAnalyzer();
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
int[] frontiers = domFrontiers[block.getIndex()];
|
||||||
|
if (frontiers.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instructionAnalyzer.reset();
|
||||||
|
instruction.acceptVisitor(instructionAnalyzer);
|
||||||
|
|
||||||
|
if (instructionAnalyzer.invalidatesAll) {
|
||||||
|
worklist.addLast(frontiers);
|
||||||
|
while (!worklist.isEmpty()) {
|
||||||
|
int target = worklist.removeFirst();
|
||||||
|
if (!everythingInvalid[target]) {
|
||||||
|
everythingInvalid[target] = true;
|
||||||
|
invalidFields.set(target, null);
|
||||||
|
worklist.addLast(domFrontiers[target]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (instructionAnalyzer.invalidatedField != null
|
||||||
|
&& !isVolatile(instructionAnalyzer.invalidatedField)) {
|
||||||
|
worklist.addLast(frontiers);
|
||||||
|
|
||||||
|
int instance = instructionAnalyzer.instance;
|
||||||
|
FieldReference field = instructionAnalyzer.invalidatedField;
|
||||||
|
|
||||||
|
while (!worklist.isEmpty()) {
|
||||||
|
int target = worklist.removeFirst();
|
||||||
|
if (everythingInvalid[target]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntObjectHashMap<Set<FieldReference>> invalidFieldsByBlock = invalidFields.get(target);
|
||||||
|
if (invalidFieldsByBlock == null) {
|
||||||
|
invalidFieldsByBlock = new IntObjectHashMap<>();
|
||||||
|
invalidFields.set(target, invalidFieldsByBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<FieldReference> invalidFieldsByVar = invalidFieldsByBlock.get(instance);
|
||||||
|
if (invalidFieldsByVar == null) {
|
||||||
|
invalidFieldsByVar = new HashSet<>();
|
||||||
|
invalidFieldsByBlock.put(instance, invalidFieldsByVar);
|
||||||
|
}
|
||||||
|
if (invalidFieldsByVar.add(field)) {
|
||||||
|
worklist.addLast(domFrontiers[target]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isVolatile(FieldReference fieldRef) {
|
||||||
|
FieldReader field = classSource.resolve(fieldRef);
|
||||||
|
return field == null || field.hasModifier(ElementModifier.VOLATILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InstructionAnalyzer extends AbstractInstructionVisitor {
|
||||||
|
boolean invalidatesAll;
|
||||||
|
FieldReference invalidatedField;
|
||||||
|
int instance;
|
||||||
|
int newValue;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
invalidatesAll = false;
|
||||||
|
invalidatedField = null;
|
||||||
|
instance = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PutFieldInstruction insn) {
|
||||||
|
invalidatedField = insn.getField();
|
||||||
|
instance = insn.getInstance() != null ? insn.getInstance().getIndex() : -1;
|
||||||
|
newValue = insn.getValue().getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
invalidatesAll = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,6 +89,7 @@ import org.teavm.model.optimization.MethodOptimization;
|
||||||
import org.teavm.model.optimization.MethodOptimizationContext;
|
import org.teavm.model.optimization.MethodOptimizationContext;
|
||||||
import org.teavm.model.optimization.RedundantJumpElimination;
|
import org.teavm.model.optimization.RedundantJumpElimination;
|
||||||
import org.teavm.model.optimization.RedundantNullCheckElimination;
|
import org.teavm.model.optimization.RedundantNullCheckElimination;
|
||||||
|
import org.teavm.model.optimization.RepeatedFieldReadElimination;
|
||||||
import org.teavm.model.optimization.ScalarReplacement;
|
import org.teavm.model.optimization.ScalarReplacement;
|
||||||
import org.teavm.model.optimization.UnreachableBasicBlockElimination;
|
import org.teavm.model.optimization.UnreachableBasicBlockElimination;
|
||||||
import org.teavm.model.optimization.UnusedVariableElimination;
|
import org.teavm.model.optimization.UnusedVariableElimination;
|
||||||
|
@ -622,7 +623,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
|
|
||||||
InliningStrategy inliningStrategy;
|
InliningStrategy inliningStrategy;
|
||||||
if (optimizationLevel == TeaVMOptimizationLevel.FULL) {
|
if (optimizationLevel == TeaVMOptimizationLevel.FULL) {
|
||||||
inliningStrategy = new DefaultInliningStrategy(14, 7, 300, false);
|
inliningStrategy = new DefaultInliningStrategy(20, 7, 300, false);
|
||||||
} else {
|
} else {
|
||||||
inliningStrategy = new DefaultInliningStrategy(100, 7, 300, true);
|
inliningStrategy = new DefaultInliningStrategy(100, 7, 300, true);
|
||||||
}
|
}
|
||||||
|
@ -750,6 +751,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
public DependencyInfo getDependencyInfo() {
|
public DependencyInfo getDependencyInfo() {
|
||||||
return dependencyAnalyzer;
|
return dependencyAnalyzer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassReaderSource getClassSource() {
|
||||||
|
return dependencyAnalyzer.getClassSource();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MethodOptimization> getOptimizations() {
|
private List<MethodOptimization> getOptimizations() {
|
||||||
|
@ -761,6 +767,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
//optimizations.add(new LoopInversion());
|
//optimizations.add(new LoopInversion());
|
||||||
optimizations.add(new LoopInvariantMotion());
|
optimizations.add(new LoopInvariantMotion());
|
||||||
}
|
}
|
||||||
|
if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.FULL.ordinal()) {
|
||||||
|
optimizations.add(new RepeatedFieldReadElimination());
|
||||||
|
}
|
||||||
optimizations.add(new GlobalValueNumbering(optimizationLevel == TeaVMOptimizationLevel.SIMPLE));
|
optimizations.add(new GlobalValueNumbering(optimizationLevel == TeaVMOptimizationLevel.SIMPLE));
|
||||||
optimizations.add(new RedundantNullCheckElimination());
|
optimizations.add(new RedundantNullCheckElimination());
|
||||||
if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.ADVANCED.ordinal()) {
|
if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.ADVANCED.ordinal()) {
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.optimization.test;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TestName;
|
||||||
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
import org.teavm.model.AccessLevel;
|
||||||
|
import org.teavm.model.ClassHolder;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldHolder;
|
||||||
|
import org.teavm.model.ListingParseUtils;
|
||||||
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MutableClassHolderSource;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.optimization.MethodOptimizationContext;
|
||||||
|
import org.teavm.model.optimization.RepeatedFieldReadElimination;
|
||||||
|
import org.teavm.model.text.ListingBuilder;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
|
public class RepeatedFieldReadEliminationTest {
|
||||||
|
private static final String PREFIX = "model/optimization/repeated-field-read-elimination/";
|
||||||
|
@Rule
|
||||||
|
public TestName name = new TestName();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void simple() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void volatileField() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fieldStoreInvalidates() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fieldStoreInDifferentObjects() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalidateInOneBranch() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invocationInvalidates() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void alwaysInvalidateExternalObject() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updatingExternalObjectInvalidatesAll() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergeInAliasAnalysis() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTest() {
|
||||||
|
String originalPath = PREFIX + name.getMethodName() + ".original.txt";
|
||||||
|
String expectedPath = PREFIX + name.getMethodName() + ".expected.txt";
|
||||||
|
Program original = ListingParseUtils.parseFromResource(originalPath);
|
||||||
|
Program expected = ListingParseUtils.parseFromResource(expectedPath);
|
||||||
|
|
||||||
|
performOptimization(original);
|
||||||
|
|
||||||
|
String originalText = new ListingBuilder().buildListing(original, "");
|
||||||
|
String expectedText = new ListingBuilder().buildListing(expected, "");
|
||||||
|
Assert.assertEquals(expectedText, originalText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performOptimization(Program program) {
|
||||||
|
MutableClassHolderSource classSource = new MutableClassHolderSource();
|
||||||
|
|
||||||
|
ClassHolder testClass = new ClassHolder("TestClass");
|
||||||
|
MethodHolder testMethod = new MethodHolder("testMethod", ValueType.VOID);
|
||||||
|
testMethod.setProgram(ProgramUtils.copy(program));
|
||||||
|
testClass.addMethod(testMethod);
|
||||||
|
|
||||||
|
classSource.putClassHolder(testClass);
|
||||||
|
|
||||||
|
ClassHolder foo = new ClassHolder("Foo");
|
||||||
|
|
||||||
|
FieldHolder intField = new FieldHolder("intField");
|
||||||
|
intField.setLevel(AccessLevel.PUBLIC);
|
||||||
|
intField.setType(ValueType.INTEGER);
|
||||||
|
foo.addField(intField);
|
||||||
|
|
||||||
|
FieldHolder volatileField = new FieldHolder("volatileField");
|
||||||
|
volatileField.setLevel(AccessLevel.PUBLIC);
|
||||||
|
volatileField.setType(ValueType.INTEGER);
|
||||||
|
volatileField.getModifiers().add(ElementModifier.VOLATILE);
|
||||||
|
foo.addField(volatileField);
|
||||||
|
|
||||||
|
MethodHolder getFoo = new MethodHolder("getFoo", ValueType.object("Foo"));
|
||||||
|
getFoo.getModifiers().add(ElementModifier.STATIC);
|
||||||
|
getFoo.getModifiers().add(ElementModifier.NATIVE);
|
||||||
|
foo.addMethod(getFoo);
|
||||||
|
|
||||||
|
classSource.putClassHolder(foo);
|
||||||
|
|
||||||
|
MethodOptimizationContext context = new MethodOptimizationContext() {
|
||||||
|
@Override
|
||||||
|
public MethodReader getMethod() {
|
||||||
|
return testMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DependencyInfo getDependencyInfo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassReaderSource getClassSource() {
|
||||||
|
return classSource;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new RepeatedFieldReadElimination().optimize(context, program);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import org.junit.Test;
|
||||||
import org.junit.rules.TestName;
|
import org.junit.rules.TestName;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ListingParseUtils;
|
import org.teavm.model.ListingParseUtils;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
@ -110,6 +111,11 @@ public class ScalarReplacementTest {
|
||||||
public DependencyInfo getDependencyInfo() {
|
public DependencyInfo getDependencyInfo() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassReaderSource getClassSource() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
new ScalarReplacement().optimize(context, program);
|
new ScalarReplacement().optimize(context, program);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@q := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @p as I
|
||||||
|
@b := field Foo.intField @q as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @o := @v as I
|
||||||
|
@a1 := @a
|
||||||
|
@b1 := field Foo.intField @q as I
|
||||||
|
return
|
|
@ -0,0 +1,13 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@q := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @p as I
|
||||||
|
@b := field Foo.intField @q as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @o := @v as I
|
||||||
|
@a1 := field Foo.intField @p as I
|
||||||
|
@b1 := field Foo.intField @q as I
|
||||||
|
return
|
|
@ -0,0 +1,10 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @p := @v as I
|
||||||
|
@b := @a
|
||||||
|
return
|
|
@ -0,0 +1,10 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @p := @v as I
|
||||||
|
@b := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,10 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@p := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @p := @v as I
|
||||||
|
@b := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,10 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@p := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @p := @v as I
|
||||||
|
@b := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,18 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@p := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
if @a == 0 then goto $zero else goto $nonzero
|
||||||
|
$zero
|
||||||
|
@b := @a
|
||||||
|
goto $join
|
||||||
|
$nonzero
|
||||||
|
@c := @a
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @p := @v as I
|
||||||
|
goto $join
|
||||||
|
$join
|
||||||
|
@d := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,18 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@p := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
if @a == 0 then goto $zero else goto $nonzero
|
||||||
|
$zero
|
||||||
|
@b := field Foo.intField @o as I
|
||||||
|
goto $join
|
||||||
|
$nonzero
|
||||||
|
@c := field Foo.intField @o as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @p := @v as I
|
||||||
|
goto $join
|
||||||
|
$join
|
||||||
|
@d := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,8 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@b := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,8 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@b := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,22 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@q := new Foo
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@b := field Foo.intField @p as I
|
||||||
|
@c := field Foo.intField @q as I
|
||||||
|
if @a == 0 then goto $zero else goto $nonzero
|
||||||
|
$zero
|
||||||
|
goto $join
|
||||||
|
$nonzero
|
||||||
|
goto $join
|
||||||
|
$join
|
||||||
|
@j := phi @o from $zero, @p from $nonzero
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @j := @v as I
|
||||||
|
@a1 := field Foo.intField @o as I
|
||||||
|
@b1 := field Foo.intField @p as I
|
||||||
|
@c1 := @c
|
||||||
|
return
|
|
@ -0,0 +1,22 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@q := new Foo
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@b := field Foo.intField @p as I
|
||||||
|
@c := field Foo.intField @q as I
|
||||||
|
if @a == 0 then goto $zero else goto $nonzero
|
||||||
|
$zero
|
||||||
|
goto $join
|
||||||
|
$nonzero
|
||||||
|
goto $join
|
||||||
|
$join
|
||||||
|
@j := phi @o from $zero, @p from $nonzero
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @j := @v as I
|
||||||
|
@a1 := field Foo.intField @o as I
|
||||||
|
@b1 := field Foo.intField @p as I
|
||||||
|
@c1 := field Foo.intField @q as I
|
||||||
|
return
|
|
@ -0,0 +1,10 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@b := @a
|
||||||
|
@c := 23
|
||||||
|
field Foo.intField @o := @c as I
|
||||||
|
@d := @c
|
||||||
|
return
|
|
@ -0,0 +1,10 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@b := field Foo.intField @o as I
|
||||||
|
@c := 23
|
||||||
|
field Foo.intField @o := @c as I
|
||||||
|
@d := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,15 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@q := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@b := field Foo.intField @p as I
|
||||||
|
@c := field Foo.intField @q as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @q := @v as I
|
||||||
|
@a1 := field Foo.intField @o as I
|
||||||
|
@b1 := field Foo.intField @p as I
|
||||||
|
@c1 := @v
|
||||||
|
return
|
|
@ -0,0 +1,15 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@p := new Foo
|
||||||
|
@q := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@a := field Foo.intField @o as I
|
||||||
|
@b := field Foo.intField @p as I
|
||||||
|
@c := field Foo.intField @q as I
|
||||||
|
@v := 23
|
||||||
|
field Foo.intField @q := @v as I
|
||||||
|
@a1 := field Foo.intField @o as I
|
||||||
|
@b1 := field Foo.intField @p as I
|
||||||
|
@c1 := field Foo.intField @q as I
|
||||||
|
return
|
|
@ -0,0 +1,7 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@a := field Foo.volatileField @o as I
|
||||||
|
@b := field Foo.volatileField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,7 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := new Foo
|
||||||
|
@a := field Foo.volatileField @o as I
|
||||||
|
@b := field Foo.volatileField @o as I
|
||||||
|
return
|
Loading…
Reference in New Issue
Block a user