diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 62fba9b81..f9053e076 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -767,12 +767,12 @@ public class ClassGenerator { int lower = ranges.get(0).lower; int upper = ranges.get(ranges.size() - 1).upper; - isSupertypeWriter.println("if (tag < " + lower + " || tag > " + upper + ") return INT32_C(0);"); + isSupertypeWriter.println("if (tag < " + lower + " || tag >= " + upper + ") return INT32_C(0);"); for (int i = 1; i < ranges.size(); ++i) { lower = ranges.get(i - 1).upper; upper = ranges.get(i).lower; - isSupertypeWriter.println("if (tag < " + lower + " || tag > " + upper + ") return INT32_C(0);"); + isSupertypeWriter.println("if (tag >= " + lower + " || tag < " + upper + ") return INT32_C(0);"); } isSupertypeWriter.println("return INT32_C(1);"); diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java index bd4fff17d..454d12b06 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java @@ -15,6 +15,8 @@ */ package org.teavm.model.lowlevel; +import com.carrotsearch.hppc.IntHashSet; +import com.carrotsearch.hppc.IntSet; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -134,6 +136,9 @@ public class ExceptionHandlingShadowStackContributor { int[] currentJointSources = new int[program.variableCount()]; int[] jointReceiverMap = new int[program.variableCount()]; Arrays.fill(currentJointSources, -1); + Arrays.fill(jointReceiverMap, -1); + IntSet outgoingVariablesToRemove = new IntHashSet(); + IntSet variablesDefinedHere = new IntHashSet(); for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { for (Phi phi : tryCatch.getHandler().getPhis()) { @@ -162,6 +167,7 @@ public class ExceptionHandlingShadowStackContributor { DefinitionExtractor defExtractor = new DefinitionExtractor(); List blocksToClearHandlers = new ArrayList<>(); blocksToClearHandlers.add(block); + BasicBlock initialBlock = block; for (Instruction insn : block) { if (isCallInstruction(insn)) { @@ -210,7 +216,8 @@ public class ExceptionHandlingShadowStackContributor { CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), location); callSites.add(callSite); List pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation()); - List post = getInstructionsAfterCallSite(block, next, callSite, currentJointSources); + List post = getInstructionsAfterCallSite(initialBlock, block, next, callSite, + currentJointSources, outgoingVariablesToRemove, variablesDefinedHere); post = setLocation(post, insn.getLocation()); block.getLastInstruction().insertPreviousAll(pre); block.addAll(post); @@ -220,15 +227,27 @@ public class ExceptionHandlingShadowStackContributor { break; } block = next; + outgoingVariablesToRemove.clear(); + variablesDefinedHere.clear(); } insn.acceptVisitor(defExtractor); for (Variable definedVar : defExtractor.getDefinedVariables()) { int jointReceiver = jointReceiverMap[definedVar.getIndex()]; - currentJointSources[jointReceiver] = definedVar.getIndex(); + if (jointReceiver >= 0) { + int formerVar = currentJointSources[jointReceiver]; + if (formerVar >= 0) { + if (variableDefinitionPlaces[formerVar] == initialBlock) { + outgoingVariablesToRemove.add(formerVar); + } + } + currentJointSources[jointReceiver] = definedVar.getIndex(); + variablesDefinedHere.add(definedVar.getIndex()); + } } } + fixOutgoingPhis(initialBlock, block, currentJointSources, outgoingVariablesToRemove, variablesDefinedHere); for (BasicBlock blockToClear : blocksToClearHandlers) { blockToClear.getTryCatchBlocks().clear(); } @@ -287,8 +306,9 @@ public class ExceptionHandlingShadowStackContributor { return instructions; } - private List getInstructionsAfterCallSite(BasicBlock block, BasicBlock next, - CallSiteDescriptor callSite, int[] currentJointSources) { + private List getInstructionsAfterCallSite(BasicBlock initialBlock, BasicBlock block, BasicBlock next, + CallSiteDescriptor callSite, int[] currentJointSources, IntSet outgoingVariablesToRemove, + IntSet variablesDefinedHere) { Program program = block.getProgram(); List instructions = new ArrayList<>(); @@ -325,20 +345,8 @@ public class ExceptionHandlingShadowStackContributor { catchEntry.setCondition(handler.getId()); switchInsn.getEntries().add(catchEntry); } - - for (Phi phi : tryCatch.getHandler().getPhis()) { - int value = currentJointSources[phi.getReceiver().getIndex()]; - if (value < 0) { - continue; - } - for (Incoming incoming : phi.getIncomings()) { - if (incoming.getValue().getIndex() == value) { - incoming.setSource(block); - break; - } - } - } } + fixOutgoingPhis(initialBlock, block, currentJointSources, outgoingVariablesToRemove, variablesDefinedHere); if (!defaultExists) { switchInsn.setDefaultTarget(getDefaultExceptionHandler()); @@ -370,6 +378,41 @@ public class ExceptionHandlingShadowStackContributor { return instructions; } + private void fixOutgoingPhis(BasicBlock block, BasicBlock newBlock, int[] currentJointSources, + IntSet outgoingVariablesToRemove, IntSet variablesDefinedHere) { + for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { + for (Phi phi : tryCatch.getHandler().getPhis()) { + int value = currentJointSources[phi.getReceiver().getIndex()]; + if (value < 0) { + continue; + } + List additionalIncomings = new ArrayList<>(); + for (int i = 0; i < phi.getIncomings().size(); i++) { + Incoming incoming = phi.getIncomings().get(i); + if (incoming.getSource() != block) { + continue; + } + if (outgoingVariablesToRemove.contains(incoming.getValue().getIndex())) { + phi.getIncomings().remove(i--); + break; + } else if (incoming.getValue().getIndex() == value) { + if (variablesDefinedHere.contains(value)) { + incoming.setSource(newBlock); + } else { + Incoming incomingCopy = new Incoming(); + incomingCopy.setSource(newBlock); + incomingCopy.setValue(incoming.getValue()); + additionalIncomings.add(incomingCopy); + } + break; + } + } + + phi.getIncomings().addAll(additionalIncomings); + } + } + } + private BasicBlock getDefaultExceptionHandler() { if (defaultExceptionHandler == null) { defaultExceptionHandler = program.createBasicBlock(); diff --git a/core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java b/core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java index e564f0011..19083cca0 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java +++ b/core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java @@ -16,10 +16,8 @@ package org.teavm.model.lowlevel; import org.teavm.model.BasicBlock; -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; @@ -36,46 +34,29 @@ import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.LongConstantInstruction; import org.teavm.model.instructions.NullCheckInstruction; import org.teavm.model.instructions.NullConstantInstruction; -import org.teavm.model.optimization.RedundantJumpElimination; -import org.teavm.model.util.ProgramUtils; +import org.teavm.model.util.BasicBlockSplitter; import org.teavm.runtime.ExceptionHandling; public class NullCheckTransformation { public void apply(Program program, ValueType returnType) { - int[] mappings = new int[program.basicBlockCount()]; - for (int i = 0; i < mappings.length; ++i) { - mappings[i] = i; - } + BasicBlockSplitter splitter = new BasicBlockSplitter(program); BasicBlock returnBlock = null; - int count = program.basicBlockCount(); for (int i = 0; i < count; ++i) { BasicBlock next = program.basicBlockAt(i); - BasicBlock block = null; - int newIndex = i; + BasicBlock block; while (next != null) { block = next; - newIndex = block.getIndex(); next = null; for (Instruction instruction : block) { if (!(instruction instanceof NullCheckInstruction)) { continue; } NullCheckInstruction nullCheck = (NullCheckInstruction) instruction; - - BasicBlock continueBlock = program.createBasicBlock(); - - continueBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); - while (nullCheck.getNext() != null) { - Instruction nextInstruction = nullCheck.getNext(); - nextInstruction.delete(); - continueBlock.add(nextInstruction); - } - + BasicBlock continueBlock = splitter.split(block, nullCheck); BasicBlock throwBlock = program.createBasicBlock(); - throwBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); InvokeInstruction throwNPE = new InvokeInstruction(); throwNPE.setType(InvocationType.SPECIAL); throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException", @@ -109,8 +90,6 @@ public class NullCheckTransformation { break; } } - - mappings[i] = newIndex; } if (returnBlock != null) { @@ -123,18 +102,7 @@ public class NullCheckTransformation { returnBlock.add(fakeExit); } - for (BasicBlock block : program.getBasicBlocks()) { - for (Phi phi : block.getPhis()) { - for (Incoming incoming : phi.getIncomings()) { - int source = incoming.getSource().getIndex(); - if (source < mappings.length && mappings[source] != source) { - incoming.setSource(program.basicBlockAt(mappings[source])); - } - } - } - } - - RedundantJumpElimination.optimize(program); + splitter.fixProgram(); } private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) { diff --git a/core/src/main/java/org/teavm/model/text/ListingBuilder.java b/core/src/main/java/org/teavm/model/text/ListingBuilder.java index 59bc02a03..79521afd4 100644 --- a/core/src/main/java/org/teavm/model/text/ListingBuilder.java +++ b/core/src/main/java/org/teavm/model/text/ListingBuilder.java @@ -38,6 +38,7 @@ public class ListingBuilder { } sb.append(prefix).append("var @").append(stringifier.getVariableLabel(i)); sb.append(" as ").append(var.getDebugName()); + sb.append(" // " + var.getIndex()); sb.append('\n'); } for (int i = 0; i < program.basicBlockCount(); ++i) { diff --git a/core/src/main/java/org/teavm/model/util/BasicBlockSplitter.java b/core/src/main/java/org/teavm/model/util/BasicBlockSplitter.java new file mode 100644 index 000000000..d03cde370 --- /dev/null +++ b/core/src/main/java/org/teavm/model/util/BasicBlockSplitter.java @@ -0,0 +1,188 @@ +/* + * Copyright 2018 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.util; + +import com.carrotsearch.hppc.ByteArrayList; +import com.carrotsearch.hppc.ByteIndexedContainer; +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.IntIndexedContainer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.teavm.model.BasicBlock; +import org.teavm.model.Incoming; +import org.teavm.model.Instruction; +import org.teavm.model.Phi; +import org.teavm.model.Program; +import org.teavm.model.Variable; +import org.teavm.model.optimization.RedundantJumpElimination; + +public class BasicBlockSplitter { + private Program program; + private int[] mappings; + private IntIndexedContainer previousPtr; + private IntIndexedContainer firstPtr; + private ByteIndexedContainer isLastInSequence; + private BasicBlock[] variableDefinedAt; + + public BasicBlockSplitter(Program program) { + this.program = program; + } + + private void initIfNecessary() { + if (mappings != null) { + return; + } + + mappings = new int[program.basicBlockCount()]; + previousPtr = new IntArrayList(program.basicBlockCount() * 2); + firstPtr = new IntArrayList(program.basicBlockCount() * 2); + isLastInSequence = new ByteArrayList(program.basicBlockCount() * 2); + for (int i = 0; i < mappings.length; ++i) { + mappings[i] = i; + previousPtr.add(i); + firstPtr.add(i); + isLastInSequence.add((byte) 1); + } + + variableDefinedAt = ProgramUtils.getVariableDefinitionPlaces(program); + } + + public BasicBlock split(BasicBlock block, Instruction afterInstruction) { + initIfNecessary(); + + if (afterInstruction.getBasicBlock() != block) { + throw new IllegalArgumentException(); + } + + if (isLastInSequence.get(block.getIndex()) == 0) { + throw new IllegalArgumentException(); + } + + BasicBlock splitBlock = program.createBasicBlock(); + while (previousPtr.size() < splitBlock.getIndex()) { + previousPtr.add(previousPtr.size()); + firstPtr.add(firstPtr.size()); + isLastInSequence.add((byte) 1); + } + isLastInSequence.set(block.getIndex(), (byte) 0); + previousPtr.add(block.getIndex()); + firstPtr.add(firstPtr.get(block.getIndex())); + mappings[firstPtr.get(block.getIndex())] = splitBlock.getIndex(); + isLastInSequence.add((byte) 1); + + splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); + while (afterInstruction.getNext() != null) { + Instruction nextInstruction = afterInstruction.getNext(); + nextInstruction.delete(); + splitBlock.add(nextInstruction); + } + + return splitBlock; + } + + public void fixProgram() { + if (mappings == null) { + return; + } + + for (BasicBlock block : program.getBasicBlocks()) { + Map> incomingsBySource = new LinkedHashMap<>(); + for (Phi phi : block.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + if (mappings[incoming.getSource().getIndex()] == incoming.getSource().getIndex()) { + continue; + } + incomingsBySource.computeIfAbsent(incoming.getSource(), b -> new ArrayList<>()).add(incoming); + } + } + + for (BasicBlock source : incomingsBySource.keySet()) { + boolean isExceptionHandler = source.getTryCatchBlocks().stream() + .anyMatch(tryCatch -> tryCatch.getHandler() == block); + if (isExceptionHandler) { + fixIncomingsInExceptionHandler(source, incomingsBySource.get(source)); + } else { + BasicBlock newSource = program.basicBlockAt(mappings[source.getIndex()]); + for (Incoming incoming : incomingsBySource.get(source)) { + incoming.setSource(newSource); + } + } + } + } + + RedundantJumpElimination.optimize(program); + } + + private void fixIncomingsInExceptionHandler(BasicBlock source, List incomings) { + List sourceParts = buildBasicBlocksSequence(source); + assert sourceParts.get(0) == source; + Map> incomingsByValue = groupIncomingsByValue(incomings); + Map lastDefinedValues = new HashMap<>(); + + for (Incoming incoming : incomings) { + if (variableDefinedAt[incoming.getValue().getIndex()] != source) { + lastDefinedValues.put(incoming.getPhi(), incoming.getValue()); + } + } + + DefinitionExtractor defExtractor = new DefinitionExtractor(); + for (BasicBlock block : sourceParts) { + if (block != null) { + for (Map.Entry lastDefinedEntry : lastDefinedValues.entrySet()) { + Incoming incomingCopy = new Incoming(); + incomingCopy.setSource(block); + incomingCopy.setValue(lastDefinedEntry.getValue()); + lastDefinedEntry.getKey().getIncomings().add(incomingCopy); + } + } + + List definedVars = ProgramUtils.getVariablesDefinedInBlock(block, defExtractor); + for (Variable definedVar : definedVars) { + List incomingsOfDefinedVar = incomingsByValue.get(definedVar); + if (incomingsOfDefinedVar != null) { + for (Incoming incoming : incomingsOfDefinedVar) { + incoming.setSource(block); + lastDefinedValues.put(incoming.getPhi(), definedVar); + } + } + } + } + } + + private List buildBasicBlocksSequence(BasicBlock first) { + List result = new ArrayList<>(2); + BasicBlock block = program.basicBlockAt(mappings[first.getIndex()]); + while (previousPtr.get(block.getIndex()) != block.getIndex()) { + result.add(block); + block = program.basicBlockAt(previousPtr.get(block.getIndex())); + } + result.add(block); + Collections.reverse(result); + return result; + } + + private Map> groupIncomingsByValue(List incomings) { + Map> incoingsByValue = new HashMap<>(); + for (Incoming incoming : incomings) { + incoingsByValue.computeIfAbsent(incoming.getValue(), i -> new ArrayList<>()).add(incoming); + } + return incoingsByValue; + } +} diff --git a/core/src/main/java/org/teavm/model/util/ProgramUtils.java b/core/src/main/java/org/teavm/model/util/ProgramUtils.java index ffac84ec2..e4f87bcaf 100644 --- a/core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -228,4 +228,22 @@ public final class ProgramUtils { return Arrays.asList(newNPE, initNPE, raise); } + + public static List getVariablesDefinedInBlock(BasicBlock block, DefinitionExtractor defExtractor) { + List varsDefinedInBlock = new ArrayList<>(); + for (Phi phi : block.getPhis()) { + varsDefinedInBlock.add(phi.getReceiver()); + } + if (block.getExceptionVariable() != null) { + varsDefinedInBlock.add(block.getExceptionVariable()); + } + for (Instruction instruction : block) { + instruction.acceptVisitor(defExtractor); + Variable[] varsDefinedByInstruction = defExtractor.getDefinedVariables(); + if (varsDefinedByInstruction != null) { + varsDefinedInBlock.addAll(Arrays.asList(varsDefinedByInstruction)); + } + } + return varsDefinedInBlock; + } } diff --git a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java index 4126615d7..a449efe24 100644 --- a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java +++ b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java @@ -150,6 +150,8 @@ public class RegisterAllocator { Program program = phi.getBasicBlock().getProgram(); AssignInstruction copyInstruction = new AssignInstruction(); Variable firstCopy = program.createVariable(); + firstCopy.setLabel(phi.getReceiver().getLabel()); + firstCopy.setDebugName(phi.getReceiver().getDebugName()); copyInstruction.setReceiver(firstCopy); copyInstruction.setAssignee(incoming.getValue()); BasicBlock source = blockMap.get(incoming.getSource()); diff --git a/tests/src/test/java/org/teavm/classlib/java/io/OutputStreamWriterTest.java b/tests/src/test/java/org/teavm/classlib/java/io/OutputStreamWriterTest.java index 70b5e2c73..09e8c99f0 100644 --- a/tests/src/test/java/org/teavm/classlib/java/io/OutputStreamWriterTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/io/OutputStreamWriterTest.java @@ -476,7 +476,7 @@ public class OutputStreamWriterTest { assertEquals("invalid conversion 4", "\u001b$B$($(\u001b(B", new String(bout.toByteArray(), "ISO8859_1")); } catch (UnsupportedEncodingException e) { // Can't test missing converter - System.out.println(e); + e.printStackTrace(); } }