diff --git a/core/src/main/java/org/teavm/model/analysis/NullnessInformation.java b/core/src/main/java/org/teavm/model/analysis/NullnessInformation.java new file mode 100644 index 000000000..a2f522308 --- /dev/null +++ b/core/src/main/java/org/teavm/model/analysis/NullnessInformation.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 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 org.teavm.model.Program; + +public class NullnessInformation { + NullnessInformation() { + } + + public void dispose() { + } + + public static NullnessInformation build(Program program) { + NullnessInformation instance = new NullnessInformation(); + return instance; + } +} diff --git a/core/src/main/java/org/teavm/model/analysis/NullnessInformationBuilder.java b/core/src/main/java/org/teavm/model/analysis/NullnessInformationBuilder.java new file mode 100644 index 000000000..ec4a04bcb --- /dev/null +++ b/core/src/main/java/org/teavm/model/analysis/NullnessInformationBuilder.java @@ -0,0 +1,289 @@ +/* + * Copyright 2016 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 java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +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.TryCatchBlock; +import org.teavm.model.TryCatchJoint; +import org.teavm.model.Variable; +import org.teavm.model.instructions.AbstractInstructionVisitor; +import org.teavm.model.instructions.ArrayLengthInstruction; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.BranchingCondition; +import org.teavm.model.instructions.BranchingInstruction; +import org.teavm.model.instructions.ClassConstantInstruction; +import org.teavm.model.instructions.CloneArrayInstruction; +import org.teavm.model.instructions.ConstructArrayInstruction; +import org.teavm.model.instructions.ConstructInstruction; +import org.teavm.model.instructions.ConstructMultiArrayInstruction; +import org.teavm.model.instructions.GetFieldInstruction; +import org.teavm.model.instructions.InstructionVisitor; +import org.teavm.model.instructions.InvokeInstruction; +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.PutFieldInstruction; +import org.teavm.model.instructions.StringConstantInstruction; +import org.teavm.model.instructions.UnwrapArrayInstruction; +import org.teavm.model.util.PhiUpdater; + +class NullnessInformationBuilder { + private Program program; + private MethodDescriptor methodDescriptor; + private BitSet notNullVariables = new BitSet(); + private BitSet nullVariables = new BitSet(); + private BitSet synthesizedVariables = new BitSet(); + private PhiUpdater phiUpdater; + private List nullInstructions = new ArrayList<>(); + private List notNullInstructions = new ArrayList<>(); + private List> additionalInstructionsByBlock = new ArrayList<>(); + private Graph assignmentGraph; + private int[] nullPredecessorsLeft; + private int[] notNullPredecessorsLeft; + + NullnessInformationBuilder(Program program, MethodDescriptor methodDescriptor) { + this.program = program; + this.methodDescriptor = methodDescriptor; + } + + void build() { + extendProgram(); + buildAssignmentGraph(); + findKnownNullness(); + } + + private void extendProgram() { + insertAdditionalVariables(); + + Variable[] parameters = new Variable[methodDescriptor.parameterCount() + 1]; + for (int i = 0; i < parameters.length; ++i) { + parameters[i] = program.variableAt(i); + } + phiUpdater = new PhiUpdater(); + phiUpdater.updatePhis(program, parameters); + + collectAdditionalVariables(); + } + + private void insertAdditionalVariables() { + for (int i = 0; i < program.basicBlockCount(); ++i) { + additionalInstructionsByBlock.add(new ArrayList<>()); + } + + NullExtensionVisitor ev = new NullExtensionVisitor(); + for (BasicBlock block : program.getBasicBlocks()) { + ev.currentBasicBlock = block; + + Instruction lastInstruction = block.getLastInstruction(); + if (lastInstruction instanceof BranchingInstruction) { + BranchingInstruction branching = (BranchingInstruction) lastInstruction; + if (branching.getCondition() == BranchingCondition.NULL) { + insertNullAndNotNull(branching.getOperand(), branching.getConsequent(), branching.getAlternative()); + } else if (branching.getCondition() == BranchingCondition.NOT_NULL) { + insertNullAndNotNull(branching.getOperand(), branching.getAlternative(), branching.getConsequent()); + } + } + + for (ev.index = 0; ev.index < block.getInstructions().size(); ++ev.index) { + Instruction instruction = block.getInstructions().get(ev.index); + instruction.acceptVisitor(ev); + } + } + + for (int i = 0; i < program.basicBlockCount(); ++i) { + List additionalInstructions = additionalInstructionsByBlock.get(i); + program.basicBlockAt(i).getInstructions().addAll(0, additionalInstructions); + } + additionalInstructionsByBlock.clear(); + } + + private void collectAdditionalVariables() { + for (NullConstantInstruction nullInstruction : nullInstructions) { + nullVariables.set(nullInstruction.getReceiver().getIndex()); + } + for (NullCheckInstruction notNullInstruction : notNullInstructions) { + notNullVariables.set(notNullInstruction.getReceiver().getIndex()); + } + synthesizedVariables.or(nullVariables); + synthesizedVariables.or(notNullVariables); + + nullInstructions.clear(); + notNullInstructions.clear(); + } + + private void insertNullAndNotNull(Variable variable, BasicBlock nullBlock, BasicBlock notNullBlock) { + NullCheckInstruction notNullInstruction = new NullCheckInstruction(); + notNullInstruction.setValue(variable); + notNullInstruction.setReceiver(variable); + additionalInstructionsByBlock.get(notNullBlock.getIndex()).add(notNullInstruction); + notNullInstructions.add(notNullInstruction); + + NullConstantInstruction nullInstruction = new NullConstantInstruction(); + nullInstruction.setReceiver(variable); + additionalInstructionsByBlock.get(nullBlock.getIndex()).add(nullInstruction); + nullInstructions.add(nullInstruction); + } + + private void buildAssignmentGraph() { + GraphBuilder builder = new GraphBuilder(); + for (BasicBlock block : program.getBasicBlocks()) { + for (Phi phi : block.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + builder.addEdge(incoming.getSource().getIndex(), phi.getReceiver().getIndex()); + } + } + for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { + for (TryCatchJoint joint : tryCatch.getJoints()) { + for (Variable sourceVar : joint.getSourceVariables()) { + builder.addEdge(sourceVar.getIndex(), joint.getReceiver().getIndex()); + } + } + } + + for (Instruction instruction : block.getInstructions()) { + if (instruction instanceof AssignInstruction) { + AssignInstruction assignment = (AssignInstruction) instruction; + builder.addEdge(assignment.getAssignee().getIndex(), assignment.getReceiver().getIndex()); + } + } + } + assignmentGraph = builder.build(); + + // TODO: handle SCCs + + nullPredecessorsLeft = new int[assignmentGraph.size()]; + notNullPredecessorsLeft = new int[assignmentGraph.size()]; + for (int i = 0; i < assignmentGraph.size(); ++i) { + nullPredecessorsLeft[i] = assignmentGraph.incomingEdgesCount(i); + notNullPredecessorsLeft[i] = assignmentGraph.incomingEdgesCount(i); + } + } + + private void findKnownNullness() { + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block.getInstructions()) { + instruction.acceptVisitor(nullnessVisitor); + } + } + } + + class NullExtensionVisitor extends AbstractInstructionVisitor { + int index; + BasicBlock currentBasicBlock; + + @Override + public void visit(GetFieldInstruction insn) { + if (insn.getInstance() != null) { + insertNotNullInstruction(insn.getInstance()); + } + } + + @Override + public void visit(PutFieldInstruction insn) { + if (insn.getInstance() != null) { + insertNotNullInstruction(insn.getInstance()); + } + } + + @Override + public void visit(ArrayLengthInstruction insn) { + insertNotNullInstruction(insn.getArray()); + } + + @Override + public void visit(CloneArrayInstruction insn) { + insertNotNullInstruction(insn.getArray()); + } + + @Override + public void visit(UnwrapArrayInstruction insn) { + insertNotNullInstruction(insn.getArray()); + } + + @Override + public void visit(InvokeInstruction insn) { + if (insn.getInstance() != null) { + insertNotNullInstruction(insn.getInstance()); + } + } + + @Override + public void visit(MonitorEnterInstruction insn) { + insertNotNullInstruction(insn.getObjectRef()); + } + + @Override + public void visit(MonitorExitInstruction insn) { + insertNotNullInstruction(insn.getObjectRef()); + } + + private void insertNotNullInstruction(Variable var) { + NullCheckInstruction insn = new NullCheckInstruction(); + insn.setReceiver(var); + insn.setValue(var); + notNullInstructions.add(insn); + currentBasicBlock.getInstructions().add(++index, insn); + } + } + + private InstructionVisitor nullnessVisitor = new AbstractInstructionVisitor() { + @Override + public void visit(ClassConstantInstruction insn) { + notNullVariables.set(insn.getReceiver().getIndex()); + } + + @Override + public void visit(NullConstantInstruction insn) { + nullVariables.set(insn.getReceiver().getIndex()); + } + + @Override + public void visit(StringConstantInstruction insn) { + notNullVariables.set(insn.getReceiver().getIndex()); + } + + @Override + public void visit(ConstructArrayInstruction insn) { + notNullVariables.set(insn.getReceiver().getIndex()); + } + + @Override + public void visit(ConstructInstruction insn) { + notNullVariables.set(insn.getReceiver().getIndex()); + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + notNullVariables.set(insn.getReceiver().getIndex()); + } + + @Override + public void visit(NullCheckInstruction insn) { + super.visit(insn); + } + }; +} diff --git a/core/src/main/java/org/teavm/model/instructions/AbstractInstructionVisitor.java b/core/src/main/java/org/teavm/model/instructions/AbstractInstructionVisitor.java new file mode 100644 index 000000000..340ebb0e1 --- /dev/null +++ b/core/src/main/java/org/teavm/model/instructions/AbstractInstructionVisitor.java @@ -0,0 +1,168 @@ +/* + * Copyright 2016 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.instructions; + +import org.teavm.model.InvokeDynamicInstruction; + +public abstract class AbstractInstructionVisitor implements InstructionVisitor { + @Override + public void visit(EmptyInstruction insn) { + } + + @Override + public void visit(ClassConstantInstruction insn) { + } + + @Override + public void visit(NullConstantInstruction insn) { + } + + @Override + public void visit(IntegerConstantInstruction insn) { + } + + @Override + public void visit(LongConstantInstruction insn) { + } + + @Override + public void visit(FloatConstantInstruction insn) { + } + + @Override + public void visit(DoubleConstantInstruction insn) { + } + + @Override + public void visit(StringConstantInstruction insn) { + } + + @Override + public void visit(BinaryInstruction insn) { + } + + @Override + public void visit(NegateInstruction insn) { + } + + @Override + public void visit(AssignInstruction insn) { + } + + @Override + public void visit(CastInstruction insn) { + } + + @Override + public void visit(CastNumberInstruction insn) { + } + + @Override + public void visit(CastIntegerInstruction insn) { + } + + @Override + public void visit(BranchingInstruction insn) { + } + + @Override + public void visit(BinaryBranchingInstruction insn) { + } + + @Override + public void visit(JumpInstruction insn) { + } + + @Override + public void visit(SwitchInstruction insn) { + } + + @Override + public void visit(ExitInstruction insn) { + } + + @Override + public void visit(RaiseInstruction insn) { + } + + @Override + public void visit(ConstructArrayInstruction insn) { + } + + @Override + public void visit(ConstructInstruction insn) { + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + } + + @Override + public void visit(GetFieldInstruction insn) { + } + + @Override + public void visit(PutFieldInstruction insn) { + } + + @Override + public void visit(ArrayLengthInstruction insn) { + } + + @Override + public void visit(CloneArrayInstruction insn) { + } + + @Override + public void visit(UnwrapArrayInstruction insn) { + } + + @Override + public void visit(GetElementInstruction insn) { + } + + @Override + public void visit(PutElementInstruction insn) { + } + + @Override + public void visit(InvokeInstruction insn) { + } + + @Override + public void visit(InvokeDynamicInstruction insn) { + } + + @Override + public void visit(IsInstanceInstruction insn) { + } + + @Override + public void visit(InitClassInstruction insn) { + } + + @Override + public void visit(NullCheckInstruction insn) { + } + + @Override + public void visit(MonitorEnterInstruction insn) { + } + + @Override + public void visit(MonitorExitInstruction insn) { + } +} diff --git a/core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java b/core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java index f0a2b1c84..6b8d27343 100644 --- a/core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java +++ b/core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java @@ -17,10 +17,6 @@ package org.teavm.model.instructions; import org.teavm.model.InvokeDynamicInstruction; -/** - * - * @author Alexey Andreev - */ public interface InstructionVisitor { void visit(EmptyInstruction insn);