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