C backend: fix more bugs, pass more tests

This commit is contained in:
Alexey Andreev 2018-05-05 15:17:35 +03:00
parent 899725b6bd
commit d2aa37d6a4
8 changed files with 277 additions and 57 deletions

View File

@ -767,12 +767,12 @@ public class ClassGenerator {
int lower = ranges.get(0).lower; int lower = ranges.get(0).lower;
int upper = ranges.get(ranges.size() - 1).upper; 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) { for (int i = 1; i < ranges.size(); ++i) {
lower = ranges.get(i - 1).upper; lower = ranges.get(i - 1).upper;
upper = ranges.get(i).lower; 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);"); isSupertypeWriter.println("return INT32_C(1);");

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.model.lowlevel; package org.teavm.model.lowlevel;
import com.carrotsearch.hppc.IntHashSet;
import com.carrotsearch.hppc.IntSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
@ -134,6 +136,9 @@ public class ExceptionHandlingShadowStackContributor {
int[] currentJointSources = new int[program.variableCount()]; int[] currentJointSources = new int[program.variableCount()];
int[] jointReceiverMap = new int[program.variableCount()]; int[] jointReceiverMap = new int[program.variableCount()];
Arrays.fill(currentJointSources, -1); Arrays.fill(currentJointSources, -1);
Arrays.fill(jointReceiverMap, -1);
IntSet outgoingVariablesToRemove = new IntHashSet();
IntSet variablesDefinedHere = new IntHashSet();
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
for (Phi phi : tryCatch.getHandler().getPhis()) { for (Phi phi : tryCatch.getHandler().getPhis()) {
@ -162,6 +167,7 @@ public class ExceptionHandlingShadowStackContributor {
DefinitionExtractor defExtractor = new DefinitionExtractor(); DefinitionExtractor defExtractor = new DefinitionExtractor();
List<BasicBlock> blocksToClearHandlers = new ArrayList<>(); List<BasicBlock> blocksToClearHandlers = new ArrayList<>();
blocksToClearHandlers.add(block); blocksToClearHandlers.add(block);
BasicBlock initialBlock = block;
for (Instruction insn : block) { for (Instruction insn : block) {
if (isCallInstruction(insn)) { if (isCallInstruction(insn)) {
@ -210,7 +216,8 @@ public class ExceptionHandlingShadowStackContributor {
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), location); CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), location);
callSites.add(callSite); callSites.add(callSite);
List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation()); List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
List<Instruction> post = getInstructionsAfterCallSite(block, next, callSite, currentJointSources); List<Instruction> post = getInstructionsAfterCallSite(initialBlock, block, next, callSite,
currentJointSources, outgoingVariablesToRemove, variablesDefinedHere);
post = setLocation(post, insn.getLocation()); post = setLocation(post, insn.getLocation());
block.getLastInstruction().insertPreviousAll(pre); block.getLastInstruction().insertPreviousAll(pre);
block.addAll(post); block.addAll(post);
@ -220,15 +227,27 @@ public class ExceptionHandlingShadowStackContributor {
break; break;
} }
block = next; block = next;
outgoingVariablesToRemove.clear();
variablesDefinedHere.clear();
} }
insn.acceptVisitor(defExtractor); insn.acceptVisitor(defExtractor);
for (Variable definedVar : defExtractor.getDefinedVariables()) { for (Variable definedVar : defExtractor.getDefinedVariables()) {
int jointReceiver = jointReceiverMap[definedVar.getIndex()]; 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) { for (BasicBlock blockToClear : blocksToClearHandlers) {
blockToClear.getTryCatchBlocks().clear(); blockToClear.getTryCatchBlocks().clear();
} }
@ -287,8 +306,9 @@ public class ExceptionHandlingShadowStackContributor {
return instructions; return instructions;
} }
private List<Instruction> getInstructionsAfterCallSite(BasicBlock block, BasicBlock next, private List<Instruction> getInstructionsAfterCallSite(BasicBlock initialBlock, BasicBlock block, BasicBlock next,
CallSiteDescriptor callSite, int[] currentJointSources) { CallSiteDescriptor callSite, int[] currentJointSources, IntSet outgoingVariablesToRemove,
IntSet variablesDefinedHere) {
Program program = block.getProgram(); Program program = block.getProgram();
List<Instruction> instructions = new ArrayList<>(); List<Instruction> instructions = new ArrayList<>();
@ -325,20 +345,8 @@ public class ExceptionHandlingShadowStackContributor {
catchEntry.setCondition(handler.getId()); catchEntry.setCondition(handler.getId());
switchInsn.getEntries().add(catchEntry); 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) { if (!defaultExists) {
switchInsn.setDefaultTarget(getDefaultExceptionHandler()); switchInsn.setDefaultTarget(getDefaultExceptionHandler());
@ -370,6 +378,41 @@ public class ExceptionHandlingShadowStackContributor {
return instructions; 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<Incoming> 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() { private BasicBlock getDefaultExceptionHandler() {
if (defaultExceptionHandler == null) { if (defaultExceptionHandler == null) {
defaultExceptionHandler = program.createBasicBlock(); defaultExceptionHandler = program.createBasicBlock();

View File

@ -16,10 +16,8 @@
package org.teavm.model.lowlevel; package org.teavm.model.lowlevel;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.Incoming;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.Variable; 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.LongConstantInstruction;
import org.teavm.model.instructions.NullCheckInstruction; import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.NullConstantInstruction; import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.optimization.RedundantJumpElimination; import org.teavm.model.util.BasicBlockSplitter;
import org.teavm.model.util.ProgramUtils;
import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.ExceptionHandling;
public class NullCheckTransformation { public class NullCheckTransformation {
public void apply(Program program, ValueType returnType) { public void apply(Program program, ValueType returnType) {
int[] mappings = new int[program.basicBlockCount()]; BasicBlockSplitter splitter = new BasicBlockSplitter(program);
for (int i = 0; i < mappings.length; ++i) {
mappings[i] = i;
}
BasicBlock returnBlock = null; BasicBlock returnBlock = null;
int count = program.basicBlockCount(); int count = program.basicBlockCount();
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
BasicBlock next = program.basicBlockAt(i); BasicBlock next = program.basicBlockAt(i);
BasicBlock block = null; BasicBlock block;
int newIndex = i;
while (next != null) { while (next != null) {
block = next; block = next;
newIndex = block.getIndex();
next = null; next = null;
for (Instruction instruction : block) { for (Instruction instruction : block) {
if (!(instruction instanceof NullCheckInstruction)) { if (!(instruction instanceof NullCheckInstruction)) {
continue; continue;
} }
NullCheckInstruction nullCheck = (NullCheckInstruction) instruction; NullCheckInstruction nullCheck = (NullCheckInstruction) instruction;
BasicBlock continueBlock = splitter.split(block, nullCheck);
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 throwBlock = program.createBasicBlock(); BasicBlock throwBlock = program.createBasicBlock();
throwBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
InvokeInstruction throwNPE = new InvokeInstruction(); InvokeInstruction throwNPE = new InvokeInstruction();
throwNPE.setType(InvocationType.SPECIAL); throwNPE.setType(InvocationType.SPECIAL);
throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException", throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
@ -109,8 +90,6 @@ public class NullCheckTransformation {
break; break;
} }
} }
mappings[i] = newIndex;
} }
if (returnBlock != null) { if (returnBlock != null) {
@ -123,18 +102,7 @@ public class NullCheckTransformation {
returnBlock.add(fakeExit); returnBlock.add(fakeExit);
} }
for (BasicBlock block : program.getBasicBlocks()) { splitter.fixProgram();
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);
} }
private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) { private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) {

View File

@ -38,6 +38,7 @@ public class ListingBuilder {
} }
sb.append(prefix).append("var @").append(stringifier.getVariableLabel(i)); sb.append(prefix).append("var @").append(stringifier.getVariableLabel(i));
sb.append(" as ").append(var.getDebugName()); sb.append(" as ").append(var.getDebugName());
sb.append(" // " + var.getIndex());
sb.append('\n'); sb.append('\n');
} }
for (int i = 0; i < program.basicBlockCount(); ++i) { for (int i = 0; i < program.basicBlockCount(); ++i) {

View File

@ -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<BasicBlock, List<Incoming>> 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<Incoming> incomings) {
List<BasicBlock> sourceParts = buildBasicBlocksSequence(source);
assert sourceParts.get(0) == source;
Map<Variable, List<Incoming>> incomingsByValue = groupIncomingsByValue(incomings);
Map<Phi, Variable> 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<Phi, Variable> lastDefinedEntry : lastDefinedValues.entrySet()) {
Incoming incomingCopy = new Incoming();
incomingCopy.setSource(block);
incomingCopy.setValue(lastDefinedEntry.getValue());
lastDefinedEntry.getKey().getIncomings().add(incomingCopy);
}
}
List<Variable> definedVars = ProgramUtils.getVariablesDefinedInBlock(block, defExtractor);
for (Variable definedVar : definedVars) {
List<Incoming> incomingsOfDefinedVar = incomingsByValue.get(definedVar);
if (incomingsOfDefinedVar != null) {
for (Incoming incoming : incomingsOfDefinedVar) {
incoming.setSource(block);
lastDefinedValues.put(incoming.getPhi(), definedVar);
}
}
}
}
}
private List<BasicBlock> buildBasicBlocksSequence(BasicBlock first) {
List<BasicBlock> 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<Variable, List<Incoming>> groupIncomingsByValue(List<Incoming> incomings) {
Map<Variable, List<Incoming>> incoingsByValue = new HashMap<>();
for (Incoming incoming : incomings) {
incoingsByValue.computeIfAbsent(incoming.getValue(), i -> new ArrayList<>()).add(incoming);
}
return incoingsByValue;
}
}

View File

@ -228,4 +228,22 @@ public final class ProgramUtils {
return Arrays.asList(newNPE, initNPE, raise); return Arrays.asList(newNPE, initNPE, raise);
} }
public static List<Variable> getVariablesDefinedInBlock(BasicBlock block, DefinitionExtractor defExtractor) {
List<Variable> 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;
}
} }

View File

@ -150,6 +150,8 @@ public class RegisterAllocator {
Program program = phi.getBasicBlock().getProgram(); Program program = phi.getBasicBlock().getProgram();
AssignInstruction copyInstruction = new AssignInstruction(); AssignInstruction copyInstruction = new AssignInstruction();
Variable firstCopy = program.createVariable(); Variable firstCopy = program.createVariable();
firstCopy.setLabel(phi.getReceiver().getLabel());
firstCopy.setDebugName(phi.getReceiver().getDebugName());
copyInstruction.setReceiver(firstCopy); copyInstruction.setReceiver(firstCopy);
copyInstruction.setAssignee(incoming.getValue()); copyInstruction.setAssignee(incoming.getValue());
BasicBlock source = blockMap.get(incoming.getSource()); BasicBlock source = blockMap.get(incoming.getSource());

View File

@ -476,7 +476,7 @@ public class OutputStreamWriterTest {
assertEquals("invalid conversion 4", "\u001b$B$($(\u001b(B", new String(bout.toByteArray(), "ISO8859_1")); assertEquals("invalid conversion 4", "\u001b$B$($(\u001b(B", new String(bout.toByteArray(), "ISO8859_1"));
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
// Can't test missing converter // Can't test missing converter
System.out.println(e); e.printStackTrace();
} }
} }