Make more precise type inference. The old algorithm sometimes reported wrong results on multidimensional primitive arrays

This commit is contained in:
Alexey Andreev 2018-05-19 20:40:47 +03:00
parent 15b77ee752
commit 27a4848947

View File

@ -23,16 +23,16 @@ import org.teavm.model.*;
import org.teavm.model.instructions.*;
public class TypeInferer {
private static VariableType[] typesByOrdinal = VariableType.values();
VariableType[] types;
private static InferenceKind[] typesByOrdinal = InferenceKind.values();
InferenceType[] types;
GraphBuilder builder;
GraphBuilder arrayElemBuilder;
public void inferTypes(ProgramReader program, MethodReference method) {
int sz = program.variableCount();
types = new VariableType[sz];
types = new InferenceType[sz];
types[0] = VariableType.OBJECT;
types[0] = new InferenceType(InferenceKind.OBJECT, 0);
for (int i = 0; i < method.parameterCount(); ++i) {
ValueType param = method.parameterType(i);
types[i + 1] = convert(param);
@ -44,7 +44,7 @@ public class TypeInferer {
BasicBlockReader block = program.basicBlockAt(i);
if (block.getExceptionVariable() != null) {
types[block.getExceptionVariable().getIndex()] = VariableType.OBJECT;
types[block.getExceptionVariable().getIndex()] = new InferenceType(InferenceKind.OBJECT, 0);
}
block.readAllInstructions(reader);
@ -60,7 +60,8 @@ public class TypeInferer {
Graph arrayElemGraph = arrayElemBuilder.build();
for (int i = 0; i < graph.size(); ++i) {
if (types[i] != null && graph.outgoingEdgesCount(i) > 0) {
stack.push(types[i].ordinal());
stack.push(types[i].kind.ordinal());
stack.push(types[i].degree);
stack.push(i);
types[i] = null;
}
@ -68,21 +69,24 @@ public class TypeInferer {
while (!stack.isEmpty()) {
int node = stack.pop();
VariableType type = typesByOrdinal[stack.pop()];
int degree = stack.pop();
InferenceKind kind = typesByOrdinal[stack.pop()];
if (types[node] != null) {
continue;
}
types[node] = type;
types[node] = new InferenceType(kind, degree);
for (int successor : graph.outgoingEdges(node)) {
if (types[successor] == null) {
stack.push(type.ordinal());
stack.push(kind.ordinal());
stack.push(degree);
stack.push(successor);
}
}
for (int successor : arrayElemGraph.outgoingEdges(node)) {
if (types[successor] == null) {
stack.push(convertFromArray(type).ordinal());
stack.push(kind.ordinal());
stack.push(degree - 1);
stack.push(successor);
}
}
@ -90,53 +94,15 @@ public class TypeInferer {
}
public VariableType typeOf(int variableIndex) {
VariableType result = types[variableIndex];
return result != null ? result : VariableType.OBJECT;
InferenceType result = types[variableIndex];
if (result == null) {
return VariableType.OBJECT;
}
VariableType convert(ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
if (result.degree == 0) {
switch (result.kind) {
case BYTE:
case SHORT:
case CHARACTER:
case INTEGER:
return VariableType.INT;
case FLOAT:
return VariableType.FLOAT;
case DOUBLE:
return VariableType.DOUBLE;
case LONG:
return VariableType.LONG;
}
} else if (type instanceof ValueType.Array) {
ValueType item = ((ValueType.Array) type).getItemType();
return convertArray(item);
}
return VariableType.OBJECT;
}
VariableType convertFromArray(VariableType type) {
switch (type) {
case BYTE_ARRAY:
case SHORT_ARRAY:
case CHAR_ARRAY:
case INT_ARRAY:
return VariableType.INT;
case LONG_ARRAY:
return VariableType.LONG;
case FLOAT_ARRAY:
return VariableType.FLOAT;
case DOUBLE_ARRAY:
return VariableType.DOUBLE;
default:
return VariableType.OBJECT;
}
}
VariableType convert(NumericOperandType type) {
switch (type) {
case CHAR:
case INT:
return VariableType.INT;
case LONG:
@ -145,34 +111,77 @@ public class TypeInferer {
return VariableType.FLOAT;
case DOUBLE:
return VariableType.DOUBLE;
default:
throw new AssertionError();
case OBJECT:
break;
}
}
VariableType convertArray(ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
return VariableType.OBJECT;
} else if (result.degree == 1) {
switch (result.kind) {
case BYTE:
return VariableType.BYTE_ARRAY;
case SHORT:
return VariableType.SHORT_ARRAY;
case CHARACTER:
case CHAR:
return VariableType.CHAR_ARRAY;
case INTEGER:
case INT:
return VariableType.INT_ARRAY;
case LONG:
return VariableType.LONG_ARRAY;
case FLOAT:
return VariableType.FLOAT_ARRAY;
case DOUBLE:
return VariableType.DOUBLE_ARRAY;
case LONG:
return VariableType.LONG_ARRAY;
case OBJECT:
break;
}
}
return VariableType.OBJECT_ARRAY;
}
InferenceType convert(ValueType type) {
int degree = 0;
while (type instanceof ValueType.Array) {
++degree;
type = ((ValueType.Array) type).getItemType();
}
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
case BYTE:
return new InferenceType(InferenceKind.BYTE, degree);
case SHORT:
return new InferenceType(InferenceKind.SHORT, degree);
case CHARACTER:
return new InferenceType(InferenceKind.CHAR, degree);
case INTEGER:
return new InferenceType(InferenceKind.INT, degree);
case FLOAT:
return new InferenceType(InferenceKind.FLOAT, degree);
case DOUBLE:
return new InferenceType(InferenceKind.DOUBLE, degree);
case LONG:
return new InferenceType(InferenceKind.LONG, degree);
}
}
return new InferenceType(InferenceKind.OBJECT, degree);
}
InferenceKind convert(NumericOperandType type) {
switch (type) {
case INT:
return InferenceKind.INT;
case LONG:
return InferenceKind.LONG;
case FLOAT:
return InferenceKind.FLOAT;
case DOUBLE:
return InferenceKind.DOUBLE;
default:
throw new AssertionError();
}
}
InstructionReader reader = new AbstractInstructionReader() {
@Override
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
@ -181,7 +190,7 @@ public class TypeInferer {
@Override
public void stringConstant(VariableReader receiver, String cst) {
types[receiver.getIndex()] = VariableType.OBJECT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.OBJECT, 0);
}
@Override
@ -191,17 +200,17 @@ public class TypeInferer {
@Override
public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
types[receiver.getIndex()] = convert(type);
types[receiver.getIndex()] = new InferenceType(convert(type), 0);
}
@Override
public void longConstant(VariableReader receiver, long cst) {
types[receiver.getIndex()] = VariableType.LONG;
types[receiver.getIndex()] = new InferenceType(InferenceKind.LONG, 0);
}
@Override
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
types[receiver.getIndex()] = VariableType.INT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.BYTE, 0);
}
@Override
@ -223,7 +232,7 @@ public class TypeInferer {
@Override
public void integerConstant(VariableReader receiver, int cst) {
types[receiver.getIndex()] = VariableType.INT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.INT, 0);
}
@Override
@ -240,12 +249,12 @@ public class TypeInferer {
@Override
public void floatConstant(VariableReader receiver, float cst) {
types[receiver.getIndex()] = VariableType.FLOAT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.FLOAT, 0);
}
@Override
public void doubleConstant(VariableReader receiver, double cst) {
types[receiver.getIndex()] = VariableType.DOUBLE;
types[receiver.getIndex()] = new InferenceType(InferenceKind.DOUBLE, 0);
}
@Override
@ -261,7 +270,7 @@ public class TypeInferer {
@Override
public void create(VariableReader receiver, String type) {
types[receiver.getIndex()] = VariableType.OBJECT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.OBJECT, 0);
}
@Override
@ -271,19 +280,19 @@ public class TypeInferer {
@Override
public void classConstant(VariableReader receiver, ValueType cst) {
types[receiver.getIndex()] = VariableType.OBJECT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.OBJECT, 0);
}
@Override
public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
CastIntegerDirection targetType) {
types[receiver.getIndex()] = VariableType.INT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.BYTE, 0);
}
@Override
public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
NumericOperandType targetType) {
types[receiver.getIndex()] = convert(targetType);
types[receiver.getIndex()] = new InferenceType(convert(targetType), 0);
}
@Override
@ -296,10 +305,10 @@ public class TypeInferer {
NumericOperandType type) {
switch (op) {
case COMPARE:
types[receiver.getIndex()] = VariableType.INT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.INT, 0);
break;
default:
types[receiver.getIndex()] = convert(type);
types[receiver.getIndex()] = new InferenceType(convert(type), 0);
break;
}
}
@ -311,7 +320,28 @@ public class TypeInferer {
@Override
public void arrayLength(VariableReader receiver, VariableReader array) {
types[receiver.getIndex()] = VariableType.INT;
types[receiver.getIndex()] = new InferenceType(InferenceKind.INT, 0);
}
};
enum InferenceKind {
BYTE,
SHORT,
CHAR,
INT,
LONG,
FLOAT,
DOUBLE,
OBJECT,
}
static class InferenceType {
final InferenceKind kind;
final int degree;
InferenceType(InferenceKind kind, int degree) {
this.kind = kind;
this.degree = degree;
}
}
}