From e4fab2be41872503c77a47801666c7cd9e55f005 Mon Sep 17 00:00:00 2001
From: Alexey Andreev <konsoletyper@gmail.com>
Date: Mon, 16 Jan 2017 23:53:30 +0300
Subject: [PATCH] Add escape analysis

---
 .../java/org/teavm/common/GraphBuilder.java   |   4 -
 .../teavm/model/analysis/EscapeAnalysis.java  | 367 ++++++++++++++++++
 .../instructions/NullConstantInstruction.java |   4 -
 .../model/optimization/ScalarReplacement.java | 245 ++++++++++++
 core/src/main/java/org/teavm/vm/TeaVM.java    |   2 +
 5 files changed, 614 insertions(+), 8 deletions(-)
 create mode 100644 core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java
 create mode 100644 core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java

diff --git a/core/src/main/java/org/teavm/common/GraphBuilder.java b/core/src/main/java/org/teavm/common/GraphBuilder.java
index 0d639aad5..f5d7055c6 100644
--- a/core/src/main/java/org/teavm/common/GraphBuilder.java
+++ b/core/src/main/java/org/teavm/common/GraphBuilder.java
@@ -22,10 +22,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-/**
- *
- * @author Alexey Andreev
- */
 public class GraphBuilder {
     private GraphImpl builtGraph;
     private List<IntSet> addedEdges = new ArrayList<>();
diff --git a/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java b/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java
new file mode 100644
index 000000000..940396f26
--- /dev/null
+++ b/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java
@@ -0,0 +1,367 @@
+/*
+ *  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.IntOpenHashSet;
+import com.carrotsearch.hppc.IntSet;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+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.ClassReader;
+import org.teavm.model.ClassReaderSource;
+import org.teavm.model.FieldReader;
+import org.teavm.model.FieldReference;
+import org.teavm.model.Incoming;
+import org.teavm.model.Instruction;
+import org.teavm.model.MethodReference;
+import org.teavm.model.Phi;
+import org.teavm.model.Program;
+import org.teavm.model.TryCatchBlock;
+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.ConstructInstruction;
+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.InstructionTransitionExtractor;
+import org.teavm.model.util.LivenessAnalyzer;
+import org.teavm.model.util.UsageExtractor;
+
+public class EscapeAnalysis {
+    private ClassReaderSource classSource;
+    private int[] definitionClasses;
+    private boolean[] escapingVars;
+    private FieldReference[][] fields;
+
+    public EscapeAnalysis(ClassReaderSource classSource) {
+        this.classSource = classSource;
+    }
+
+    public void analyze(Program program, MethodReference methodReference) {
+        InstructionEscapeVisitor visitor = new InstructionEscapeVisitor(program.variableCount(), classSource);
+        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()];
+        for (int i = 0; i < program.variableCount(); ++i) {
+            if (visitor.escapingVars[i]) {
+                escapingVars[definitionClasses[i]] = true;
+            }
+        }
+        analyzePhis(program);
+
+        fields = packFields(visitor.fields);
+    }
+
+    public boolean escapes(int var) {
+        return escapingVars[definitionClasses[var]];
+    }
+
+    public FieldReference[] getFields(int var) {
+        FieldReference[] varFields = fields[definitionClasses[var]];
+        return varFields != null ? varFields.clone() : null;
+    }
+
+    private void analyzePhis(Program program) {
+        LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer();
+        livenessAnalyzer.analyze(program);
+
+        GraphBuilder graphBuilder = new GraphBuilder(program.variableCount());
+        IntDeque queue = new IntArrayDeque();
+        for (BasicBlock block : program.getBasicBlocks()) {
+            IntSet sharedIncomingVars = new IntOpenHashSet();
+            BitSet usedVars = getUsedVarsInBlock(livenessAnalyzer, block);
+            for (Phi phi : block.getPhis()) {
+                for (Incoming incoming : phi.getIncomings()) {
+                    int var = incoming.getValue().getIndex();
+                    graphBuilder.addEdge(var, phi.getReceiver().getIndex());
+                    if (escapingVars[definitionClasses[var]] || !sharedIncomingVars.add(var) || usedVars.get(var)) {
+                        queue.addLast(var);
+                    }
+                }
+            }
+        }
+        Graph graph = graphBuilder.build();
+
+        IntSet visited = new IntOpenHashSet();
+        while (!queue.isEmpty()) {
+            int var = queue.removeFirst();
+            if (visited.add(var)) {
+                escapingVars[definitionClasses[var]] = true;
+                for (int successor : graph.outgoingEdges(var)) {
+                    queue.addLast(successor);
+                }
+            }
+        }
+    }
+
+    private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) {
+        BitSet usedVars = new BitSet();
+        InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
+        block.getLastInstruction().acceptVisitor(transitionExtractor);
+        for (BasicBlock successor : transitionExtractor.getTargets()) {
+            usedVars.or(liveness.liveIn(successor.getIndex()));
+        }
+        for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
+            usedVars.or(liveness.liveIn(tryCatch.getHandler().getIndex()));
+        }
+
+        UsageExtractor useExtractor = new UsageExtractor();
+        for (Instruction instruction : block) {
+            instruction.acceptVisitor(useExtractor);
+            for (Variable variable : useExtractor.getUsedVariables()) {
+                usedVars.set(variable.getIndex());
+            }
+        }
+        return usedVars;
+    }
+
+    private FieldReference[][] packFields(List<Set<FieldReference>> fields) {
+        List<Set<FieldReference>> 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<FieldReference> 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;
+    }
+
+    private static class InstructionEscapeVisitor extends AbstractInstructionVisitor {
+        ClassReaderSource classSource;
+        DisjointSet definitionClasses;
+        boolean[] escapingVars;
+        List<Set<FieldReference>> fields;
+
+        public InstructionEscapeVisitor(int variableCount, ClassReaderSource classSource) {
+            this.classSource = classSource;
+            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(ConstructInstruction insn) {
+            ClassReader cls = classSource.get(insn.getType());
+            if (cls == null) {
+                escapingVars[insn.getReceiver().getIndex()] = true;
+            }
+
+            while (cls != null) {
+                for (FieldReader field : cls.getFields()) {
+                    addField(insn.getReceiver(), field.getReference());
+                }
+                cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null;
+            }
+        }
+
+        @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());
+        }
+
+        @Override
+        public void visit(PutFieldInstruction insn) {
+            escapingVars[insn.getValue().getIndex()] = true;
+            addField(insn.getInstance(), insn.getField());
+        }
+
+        private void addField(Variable instance, FieldReference field) {
+            if (instance == null) {
+                return;
+            }
+            Set<FieldReference> fieldSet = fields.get(instance.getIndex());
+            if (fieldSet == null) {
+                fieldSet = new LinkedHashSet<>();
+                fields.set(instance.getIndex(), fieldSet);
+            }
+            fieldSet.add(field);
+        }
+
+        @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;
+            }
+        }
+    }
+}
diff --git a/core/src/main/java/org/teavm/model/instructions/NullConstantInstruction.java b/core/src/main/java/org/teavm/model/instructions/NullConstantInstruction.java
index 3cdcca69f..ac6cd361d 100644
--- a/core/src/main/java/org/teavm/model/instructions/NullConstantInstruction.java
+++ b/core/src/main/java/org/teavm/model/instructions/NullConstantInstruction.java
@@ -18,10 +18,6 @@ package org.teavm.model.instructions;
 import org.teavm.model.Instruction;
 import org.teavm.model.Variable;
 
-/**
- *
- * @author Alexey Andreev
- */
 public class NullConstantInstruction extends Instruction {
     private Variable receiver;
 
diff --git a/core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java b/core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java
new file mode 100644
index 000000000..13ca652e3
--- /dev/null
+++ b/core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java
@@ -0,0 +1,245 @@
+/*
+ *  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.optimization;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.teavm.model.BasicBlock;
+import org.teavm.model.ClassReaderSource;
+import org.teavm.model.FieldReader;
+import org.teavm.model.FieldReference;
+import org.teavm.model.Incoming;
+import org.teavm.model.Instruction;
+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.analysis.EscapeAnalysis;
+import org.teavm.model.instructions.AbstractInstructionVisitor;
+import org.teavm.model.instructions.AssignInstruction;
+import org.teavm.model.instructions.ConstructInstruction;
+import org.teavm.model.instructions.DoubleConstantInstruction;
+import org.teavm.model.instructions.FloatConstantInstruction;
+import org.teavm.model.instructions.GetFieldInstruction;
+import org.teavm.model.instructions.IntegerConstantInstruction;
+import org.teavm.model.instructions.LongConstantInstruction;
+import org.teavm.model.instructions.NullConstantInstruction;
+import org.teavm.model.instructions.PutFieldInstruction;
+import org.teavm.model.util.PhiUpdater;
+
+public class ScalarReplacement implements MethodOptimization {
+    @Override
+    public boolean optimize(MethodOptimizationContext context, Program program) {
+        boolean changed = false;
+        while (performOnce(context, program)) {
+            changed = true;
+        }
+        return changed;
+    }
+
+    private boolean performOnce(MethodOptimizationContext context, Program program) {
+        List<Map<FieldReference, Variable>> fieldMappings = new ArrayList<>(
+                Collections.nCopies(program.variableCount(), null));
+
+        MethodReference methodReference = context.getMethod().getReference();
+        EscapeAnalysis escapeAnalysis = new EscapeAnalysis(context.getClassSource());
+        escapeAnalysis.analyze(program, methodReference);
+        boolean canPerform = false;
+        for (int i = 0; i < fieldMappings.size(); ++i) {
+            FieldReference[] fields = escapeAnalysis.getFields(i);
+            if (!escapeAnalysis.escapes(i) && fields != null) {
+                Map<FieldReference, Variable> fieldMapping = new LinkedHashMap<>();
+                for (FieldReference field : fields) {
+                    fieldMapping.put(field, program.createVariable());
+                }
+                fieldMappings.set(i, fieldMapping);
+                canPerform = true;
+            }
+        }
+        if (!canPerform) {
+            return false;
+        }
+
+        ScalarReplacementVisitor visitor = new ScalarReplacementVisitor(escapeAnalysis, context.getClassSource(),
+                fieldMappings);
+        for (BasicBlock block : program.getBasicBlocks()) {
+            for (Instruction instruction : block) {
+                instruction.acceptVisitor(visitor);
+            }
+            List<Phi> additionalPhis = new ArrayList<>();
+            for (int i = 0; i < block.getPhis().size(); ++i) {
+                Phi phi = block.getPhis().get(i);
+                if (escapeAnalysis.escapes(phi.getReceiver().getIndex())) {
+                    continue;
+                }
+
+                FieldReference[] fields = escapeAnalysis.getFields(phi.getReceiver().getIndex());
+                if (fields == null) {
+                    continue;
+                }
+
+                for (FieldReference field : fields) {
+                    boolean allIncomingsInitialized = true;
+                    for (Incoming incoming : phi.getIncomings()) {
+                        if (fieldMappings.get(incoming.getValue().getIndex()).get(field) == null) {
+                            allIncomingsInitialized = false;
+                        }
+                    }
+
+                    if (!allIncomingsInitialized) {
+                        continue;
+                    }
+                    Phi phiReplacement = new Phi();
+                    phiReplacement.setReceiver(fieldMappings.get(phi.getReceiver().getIndex()).get(field));
+
+                    for (Incoming incoming : phi.getIncomings()) {
+                        Incoming incomingReplacement = new Incoming();
+                        incomingReplacement.setSource(incoming.getSource());
+                        incomingReplacement.setValue(fieldMappings.get(incoming.getValue().getIndex()).get(field));
+                        phiReplacement.getIncomings().add(incomingReplacement);
+                    }
+
+                    additionalPhis.add(phi);
+                }
+                block.getPhis().remove(i--);
+            }
+            block.getPhis().addAll(additionalPhis);
+        }
+
+        Variable[] arguments = new Variable[methodReference.parameterCount() + 1];
+        for (int i = 0; i < arguments.length; ++i) {
+            arguments[i] = program.variableAt(i);
+        }
+        new PhiUpdater().updatePhis(program, arguments);
+
+        return true;
+    }
+
+    static class ScalarReplacementVisitor extends AbstractInstructionVisitor {
+        private EscapeAnalysis escapeAnalysis;
+        private ClassReaderSource classSource;
+        private List<Map<FieldReference, Variable>> fieldMappings;
+
+        public ScalarReplacementVisitor(EscapeAnalysis escapeAnalysis, ClassReaderSource classSource,
+                List<Map<FieldReference, Variable>> fieldMappings) {
+            this.escapeAnalysis = escapeAnalysis;
+            this.classSource = classSource;
+            this.fieldMappings = fieldMappings;
+        }
+
+        @Override
+        public void visit(ConstructInstruction insn) {
+            int var = insn.getReceiver().getIndex();
+            if (!escapeAnalysis.escapes(var) && escapeAnalysis.getFields(var) != null) {
+                for (FieldReference fieldRef : escapeAnalysis.getFields(var)) {
+                    FieldReader field = classSource.resolve(fieldRef);
+                    Variable receiver = fieldMappings.get(insn.getReceiver().getIndex()).get(fieldRef);
+                    Instruction initializer = generateDefaultValue(field.getType(), receiver);
+                    initializer.setLocation(initializer.getLocation());
+                    insn.insertPrevious(initializer);
+                }
+                insn.delete();
+            }
+        }
+
+        @Override
+        public void visit(GetFieldInstruction insn) {
+            if (insn.getInstance() != null && !escapeAnalysis.escapes(insn.getInstance().getIndex())) {
+                Variable var = fieldMappings.get(insn.getInstance().getIndex()).get(insn.getField());
+                AssignInstruction assignment = new AssignInstruction();
+                assignment.setReceiver(insn.getReceiver());
+                assignment.setAssignee(var);
+                assignment.setLocation(insn.getLocation());
+                insn.replace(assignment);
+            }
+        }
+
+        @Override
+        public void visit(PutFieldInstruction insn) {
+            if (insn.getInstance() != null && !escapeAnalysis.escapes(insn.getInstance().getIndex())) {
+                Variable var = fieldMappings.get(insn.getInstance().getIndex()).get(insn.getField());
+                AssignInstruction assignment = new AssignInstruction();
+                assignment.setReceiver(var);
+                assignment.setAssignee(insn.getValue());
+                assignment.setLocation(insn.getLocation());
+                insn.replace(assignment);
+            }
+        }
+
+        @Override
+        public void visit(AssignInstruction insn) {
+            if (escapeAnalysis.escapes(insn.getAssignee().getIndex())) {
+                return;
+            }
+
+            FieldReference[] fields = escapeAnalysis.getFields(insn.getReceiver().getIndex());
+            if (fields == null) {
+                return;
+            }
+            for (FieldReference field : fields) {
+                Variable assignee = fieldMappings.get(insn.getAssignee().getIndex()).get(field);
+                if (assignee == null) {
+                    continue;
+                }
+                Variable receiver = fieldMappings.get(insn.getReceiver().getIndex()).get(field);
+                AssignInstruction assignment = new AssignInstruction();
+                assignment.setReceiver(receiver);
+                assignment.setAssignee(assignee);
+                assignment.setLocation(insn.getLocation());
+                insn.insertPrevious(assignment);
+            }
+            insn.delete();
+        }
+
+        private Instruction generateDefaultValue(ValueType type, Variable receiver) {
+            if (type instanceof ValueType.Primitive) {
+                switch (((ValueType.Primitive) type).getKind()) {
+                    case BOOLEAN:
+                    case BYTE:
+                    case SHORT:
+                    case CHARACTER:
+                    case INTEGER: {
+                        IntegerConstantInstruction insn = new IntegerConstantInstruction();
+                        insn.setReceiver(receiver);
+                        return insn;
+                    }
+                    case LONG: {
+                        LongConstantInstruction insn = new LongConstantInstruction();
+                        insn.setReceiver(receiver);
+                        return insn;
+                    }
+                    case FLOAT: {
+                        FloatConstantInstruction insn = new FloatConstantInstruction();
+                        insn.setReceiver(receiver);
+                        return insn;
+                    }
+                    case DOUBLE: {
+                        DoubleConstantInstruction insn = new DoubleConstantInstruction();
+                        insn.setReceiver(receiver);
+                        return insn;
+                    }
+                }
+            }
+            NullConstantInstruction insn = new NullConstantInstruction();
+            insn.setReceiver(receiver);
+            return insn;
+        }
+    }
+}
diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java
index 634bb2521..6e5e1d3e6 100644
--- a/core/src/main/java/org/teavm/vm/TeaVM.java
+++ b/core/src/main/java/org/teavm/vm/TeaVM.java
@@ -60,6 +60,7 @@ import org.teavm.model.optimization.LoopInvariantMotion;
 import org.teavm.model.optimization.MethodOptimization;
 import org.teavm.model.optimization.MethodOptimizationContext;
 import org.teavm.model.optimization.RedundantJumpElimination;
+import org.teavm.model.optimization.ScalarReplacement;
 import org.teavm.model.optimization.UnreachableBasicBlockElimination;
 import org.teavm.model.optimization.UnusedVariableElimination;
 import org.teavm.model.text.ListingBuilder;
@@ -551,6 +552,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
         optimizations.add(new RedundantJumpElimination());
         optimizations.add(new ArrayUnwrapMotion());
         if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.ADVANCED.ordinal()) {
+            optimizations.add(new ScalarReplacement());
             //optimizations.add(new LoopInversion());
             optimizations.add(new LoopInvariantMotion());
         }