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

org.teavm.model.analysis.EscapeAnalysis Maven / Gradle / Ivy

There is a newer version: 0.2.8
Show newest version
/*
 *  Copyright 2017 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 java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.teavm.common.DisjointSet;
import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder;
import org.teavm.model.BasicBlock;
import org.teavm.model.FieldReference;
import org.teavm.model.Incoming;
import org.teavm.model.Instruction;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.AbstractInstructionVisitor;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.MonitorEnterInstruction;
import org.teavm.model.instructions.MonitorExitInstruction;
import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.instructions.RaiseInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.instructions.UnwrapArrayInstruction;
import org.teavm.model.util.DefinitionExtractor;
import org.teavm.model.util.LivenessAnalyzer;
import org.teavm.model.util.UsageExtractor;

public class EscapeAnalysis {
    private int[] definitionClasses;
    private boolean[] escapingVars;
    private FieldReference[][] fields;
    private Map fieldTypes;

    public void analyze(Program program, MethodReference methodReference) {
        InstructionEscapeVisitor visitor = new InstructionEscapeVisitor(program.variableCount());
        for (int i = 0; i <= methodReference.parameterCount(); ++i) {
            visitor.escapingVars[i] = true;
        }

        for (BasicBlock block : program.getBasicBlocks()) {
            for (Instruction insn : block) {
                insn.acceptVisitor(visitor);
            }
            if (block.getExceptionVariable() != null) {
                visitor.escapingVars[block.getExceptionVariable().getIndex()] = true;
            }
        }

        definitionClasses = visitor.definitionClasses.pack(program.variableCount());
        escapingVars = new boolean[program.variableCount()];
        fieldTypes = visitor.fieldTypes;
        for (int i = 0; i < program.variableCount(); ++i) {
            if (visitor.escapingVars[i]) {
                escapingVars[definitionClasses[i]] = true;
            }
        }
        analyzePhis(program, methodReference.getDescriptor());

        propagateFields(program, visitor.fields);
        fields = packFields(visitor.fields);
    }

    public boolean escapes(int var) {
        return escapingVars[definitionClasses[var]];
    }

    public ValueType getFieldType(FieldReference field) {
        return fieldTypes.get(field);
    }

    public FieldReference[] getFields(int var) {
        FieldReference[] varFields = fields[definitionClasses[var]];
        return varFields != null ? varFields.clone() : null;
    }

    private void analyzePhis(Program program, MethodDescriptor methodDescriptor) {
        LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer();
        livenessAnalyzer.analyze(program, methodDescriptor);

        GraphBuilder graphBuilder = new GraphBuilder(program.variableCount());
        IntDeque queue = new IntArrayDeque();
        for (BasicBlock block : program.getBasicBlocks()) {
            BitSet usedVars = getUsedVarsInBlock(livenessAnalyzer, block);

            // For instructions like A = B if B lives after instruction, mark both A and B as escaping
            UsageExtractor useExtractor = new UsageExtractor();
            DefinitionExtractor defExtractor = new DefinitionExtractor();
            for (Instruction insn = block.getLastInstruction(); insn != null; insn = insn.getPrevious()) {
                if (insn instanceof AssignInstruction) {
                    AssignInstruction assign = (AssignInstruction) insn;
                    if (usedVars.get(assign.getAssignee().getIndex())) {
                        queue.addLast(definitionClasses[assign.getAssignee().getIndex()]);
                    }
                } else if (insn instanceof NullCheckInstruction) {
                    NullCheckInstruction nullCheck = (NullCheckInstruction) insn;
                    if (usedVars.get(nullCheck.getValue().getIndex())) {
                        queue.addLast(definitionClasses[nullCheck.getValue().getIndex()]);
                    }
                } else if (insn instanceof UnwrapArrayInstruction) {
                    UnwrapArrayInstruction unwrapArray = (UnwrapArrayInstruction) insn;
                    if (usedVars.get(unwrapArray.getArray().getIndex())) {
                        queue.addLast(definitionClasses[unwrapArray.getArray().getIndex()]);
                    }
                }

                insn.acceptVisitor(useExtractor);
                insn.acceptVisitor(defExtractor);
                for (Variable var : useExtractor.getUsedVariables()) {
                    usedVars.set(var.getIndex());
                }
                for (Variable var : defExtractor.getDefinedVariables()) {
                    usedVars.clear(var.getIndex());
                }
            }

            // If incoming variables of phi functions live after phi, mark them as escaping
            IntSet sharedIncomingVars = new IntHashSet();
            for (Phi phi : block.getPhis()) {
                if (escapes(phi.getReceiver().getIndex())) {
                    queue.addLast(definitionClasses[phi.getReceiver().getIndex()]);
                }
                for (Incoming incoming : phi.getIncomings()) {
                    int var = incoming.getValue().getIndex();
                    graphBuilder.addEdge(definitionClasses[var], definitionClasses[phi.getReceiver().getIndex()]);
                    if (escapes(var) || !sharedIncomingVars.add(var) || usedVars.get(var)) {
                        queue.addLast(definitionClasses[var]);
                    }
                }
            }
        }
        Graph graph = graphBuilder.build();

        IntSet visited = new IntHashSet();
        while (!queue.isEmpty()) {
            int var = queue.removeFirst();
            if (visited.add(var)) {
                escapingVars[var] = true;
                for (int successor : graph.outgoingEdges(var)) {
                    queue.addLast(successor);
                }
                for (int predecessor : graph.incomingEdges(var)) {
                    queue.addLast(predecessor);
                }
            }
        }
    }

    private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) {
        return liveness.liveOut(block.getIndex());
    }

    private void propagateFields(Program program, List> fields) {
        class Task {
            int index;
            FieldReference field;
            Task(int index, FieldReference field) {
                this.index = index;
                this.field = field;
            }
        }
        Queue queue = new ArrayDeque<>();

        GraphBuilder graphBuilder = new GraphBuilder(program.variableCount());
        for (BasicBlock block : program.getBasicBlocks()) {
            for (Instruction insn : block) {
                if (insn instanceof AssignInstruction) {
                    AssignInstruction assign = (AssignInstruction) insn;
                    graphBuilder.addEdge(assign.getReceiver().getIndex(), assign.getAssignee().getIndex());
                } else if (insn instanceof NullCheckInstruction) {
                    NullCheckInstruction nullCheck = (NullCheckInstruction) insn;
                    graphBuilder.addEdge(nullCheck.getReceiver().getIndex(), nullCheck.getValue().getIndex());
                } else if (insn instanceof UnwrapArrayInstruction) {
                    UnwrapArrayInstruction unwrapArray = (UnwrapArrayInstruction) insn;
                    graphBuilder.addEdge(unwrapArray.getReceiver().getIndex(), unwrapArray.getArray().getIndex());
                }
            }
            for (Phi phi : block.getPhis()) {
                for (Incoming incoming : phi.getIncomings()) {
                    graphBuilder.addEdge(phi.getReceiver().getIndex(), incoming.getValue().getIndex());
                }
            }
        }
        Graph graph = graphBuilder.build();

        for (int i = 0; i < program.variableCount(); ++i) {
            Set receiverFields = fields.get(i);
            if (receiverFields != null) {
                for (FieldReference field : receiverFields) {
                    queue.add(new Task(i, field));
                }
                receiverFields.clear();
            }
        }

        while (!queue.isEmpty()) {
            Task task = queue.remove();

            Set taskFields = fields.get(task.index);
            if (taskFields == null) {
                taskFields = new LinkedHashSet<>();
                fields.set(task.index, taskFields);
            }
            if (!taskFields.add(task.field)) {
                continue;
            }

            for (int successor : graph.outgoingEdges(task.index)) {
                queue.add(new Task(successor, task.field));
            }
        }
    }

    private FieldReference[][] packFields(List> fields) {
        List> joinedFields = new ArrayList<>(Collections.nCopies(fields.size(), null));

        for (int i = 0; i < fields.size(); ++i) {
            if (fields.get(i) == null) {
                continue;
            }
            int j = definitionClasses[i];
            Set fieldSet = joinedFields.get(j);
            if (fieldSet == null) {
                fieldSet = new LinkedHashSet<>();
                joinedFields.set(j, fieldSet);
            }
            fieldSet.addAll(fields.get(i));
        }

        FieldReference[][] packedFields = new FieldReference[fields.size()][];
        for (int i = 0; i < packedFields.length; ++i) {
            if (joinedFields.get(i) != null) {
                packedFields[i] = joinedFields.get(i).toArray(new FieldReference[0]);
            }
        }
        return packedFields;
    }

    static class InstructionEscapeVisitor extends AbstractInstructionVisitor {
        DisjointSet definitionClasses;
        boolean[] escapingVars;
        List> fields;
        Map fieldTypes = new HashMap<>();

        InstructionEscapeVisitor(int variableCount) {
            fields = new ArrayList<>(Collections.nCopies(variableCount, null));
            definitionClasses = new DisjointSet();
            for (int i = 0; i < variableCount; ++i) {
                definitionClasses.create();
            }
            escapingVars = new boolean[variableCount];
        }

        @Override
        public void visit(NullConstantInstruction insn) {
            escapingVars[insn.getReceiver().getIndex()] = true;
        }

        @Override
        public void visit(ClassConstantInstruction insn) {
            escapingVars[insn.getReceiver().getIndex()] = true;
        }

        @Override
        public void visit(StringConstantInstruction insn) {
            escapingVars[insn.getReceiver().getIndex()] = true;
        }

        @Override
        public void visit(CloneArrayInstruction insn) {
            escapingVars[insn.getArray().getIndex()] = true;
            escapingVars[insn.getReceiver().getIndex()] = true;
        }

        @Override
        public void visit(UnwrapArrayInstruction insn) {
            definitionClasses.union(insn.getReceiver().getIndex(), insn.getArray().getIndex());
        }

        @Override
        public void visit(AssignInstruction insn) {
            definitionClasses.union(insn.getReceiver().getIndex(), insn.getAssignee().getIndex());
        }

        @Override
        public void visit(CastInstruction insn) {
            escapingVars[insn.getReceiver().getIndex()] = true;
            escapingVars[insn.getValue().getIndex()] = true;
        }

        @Override
        public void visit(ExitInstruction insn) {
            if (insn.getValueToReturn() != null) {
                escapingVars[insn.getValueToReturn().getIndex()] = true;
            }
        }

        @Override
        public void visit(RaiseInstruction insn) {
            escapingVars[insn.getException().getIndex()] = true;
        }

        @Override
        public void visit(GetFieldInstruction insn) {
            escapingVars[insn.getReceiver().getIndex()] = true;
            addField(insn.getInstance(), insn.getField(), insn.getFieldType());
        }

        @Override
        public void visit(PutFieldInstruction insn) {
            escapingVars[insn.getValue().getIndex()] = true;
            addField(insn.getInstance(), insn.getField(), insn.getFieldType());
        }

        private void addField(Variable instance, FieldReference field, ValueType fieldType) {
            if (instance == null) {
                return;
            }
            Set fieldSet = fields.get(instance.getIndex());
            if (fieldSet == null) {
                fieldSet = new LinkedHashSet<>();
                fields.set(instance.getIndex(), fieldSet);
            }
            fieldSet.add(field);
            fieldTypes.put(field, fieldType);
        }

        @Override
        public void visit(GetElementInstruction insn) {
            escapingVars[insn.getReceiver().getIndex()] = true;
        }

        @Override
        public void visit(PutElementInstruction insn) {
            escapingVars[insn.getValue().getIndex()] = true;
        }

        @Override
        public void visit(InvokeInstruction insn) {
            if (insn.getInstance() != null) {
                escapingVars[insn.getInstance().getIndex()] = true;
            }
            for (Variable arg : insn.getArguments()) {
                escapingVars[arg.getIndex()] = true;
            }
            if (insn.getReceiver() != null) {
                escapingVars[insn.getReceiver().getIndex()] = true;
            }
        }

        @Override
        public void visit(IsInstanceInstruction insn) {
            escapingVars[insn.getValue().getIndex()] = true;
        }

        @Override
        public void visit(NullCheckInstruction insn) {
            definitionClasses.union(insn.getValue().getIndex(), insn.getReceiver().getIndex());
        }

        @Override
        public void visit(MonitorEnterInstruction insn) {
            escapingVars[insn.getObjectRef().getIndex()] = true;
        }

        @Override
        public void visit(MonitorExitInstruction insn) {
            escapingVars[insn.getObjectRef().getIndex()] = true;
        }

        @Override
        public void visit(BranchingInstruction insn) {
            switch (insn.getCondition()) {
                case NULL:
                case NOT_NULL:
                    escapingVars[insn.getOperand().getIndex()] = true;
                    break;
                default:
                    break;
            }
        }

        @Override
        public void visit(BinaryBranchingInstruction insn) {
            switch (insn.getCondition()) {
                case REFERENCE_EQUAL:
                case REFERENCE_NOT_EQUAL:
                    escapingVars[insn.getFirstOperand().getIndex()] = true;
                    escapingVars[insn.getSecondOperand().getIndex()] = true;
                    break;
                default:
                    break;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy