[DFG] Added type to some operations

* Array read/write
* Fields read/write
* Calls
This commit is contained in:
Igor Chevdar
2019-05-14 16:34:31 +03:00
parent 960f8048ca
commit 61c18ff586
5 changed files with 115 additions and 73 deletions
@@ -98,12 +98,13 @@ internal class CallGraphBuilder(val context: Context,
is DataFlowIR.Node.Call -> block(node)
is DataFlowIR.Node.Singleton ->
node.constructor?.let { block(DataFlowIR.Node.Call(it, emptyList(), null)) }
node.constructor?.let { block(DataFlowIR.Node.Call(it, emptyList(), node.type, null)) }
is DataFlowIR.Node.ArrayRead ->
block(DataFlowIR.Node.Call(
callee = moduleDFG.symbolTable.mapFunction(arrayGet),
arguments = listOf(node.array, node.index),
returnType = node.type,
irCallSite = null)
)
@@ -111,6 +112,7 @@ internal class CallGraphBuilder(val context: Context,
block(DataFlowIR.Node.Call(
callee = moduleDFG.symbolTable.mapFunction(arraySet),
arguments = listOf(node.array, node.index, node.value),
returnType = moduleDFG.symbolTable.mapType(context.irBuiltIns.unitType),
irCallSite = null)
)
@@ -118,6 +120,7 @@ internal class CallGraphBuilder(val context: Context,
block(DataFlowIR.Node.Call(
callee = node.symbol,
arguments = emptyList(),
returnType = node.symbol.returnParameter.type,
irCallSite = null
))
}
@@ -514,6 +514,17 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
)
else DataFlowIR.Edge(getNode(expression), null)
private fun mapReturnType(actualType: IrType, returnType: IrType): DataFlowIR.Type {
val returnedInlinedClass = returnType.getInlinedClassNative()
val actualInlinedClass = actualType.getInlinedClassNative()
return if (returnedInlinedClass == null) {
if (actualInlinedClass == null) symbolTable.mapType(actualType) else symbolTable.mapClassReferenceType(actualInlinedClass)
} else {
symbolTable.mapType(returnType)
}
}
private fun getNode(expression: IrExpression): DataFlowIR.Node {
if (expression is IrGetValue) {
val valueDeclaration = expression.symbol.owner
@@ -554,7 +565,10 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
is IrFunctionReference -> {
val callee = value.symbol.owner
DataFlowIR.Node.FunctionReference(symbolTable.mapFunction(callee), symbolTable.mapType(value.type))
DataFlowIR.Node.FunctionReference(
symbolTable.mapFunction(callee),
symbolTable.mapType(value.type),
/*TODO: substitute*/symbolTable.mapType(callee.returnType))
}
is IrConst<*> ->
@@ -584,11 +598,17 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
is IrCall -> when (value.symbol) {
getContinuationSymbol -> getContinuation()
arrayGetSymbol -> DataFlowIR.Node.ArrayRead(expressionToEdge(value.dispatchReceiver!!),
expressionToEdge(value.getValueArgument(0)!!), value)
arrayGetSymbol -> DataFlowIR.Node.ArrayRead(
array = expressionToEdge(value.dispatchReceiver!!),
index = expressionToEdge(value.getValueArgument(0)!!),
type = mapReturnType(value.type, context.irBuiltIns.anyType),
irCallSite = value)
arraySetSymbol -> DataFlowIR.Node.ArrayWrite(expressionToEdge(value.dispatchReceiver!!),
expressionToEdge(value.getValueArgument(0)!!), expressionToEdge(value.getValueArgument(1)!!))
arraySetSymbol -> DataFlowIR.Node.ArrayWrite(
array = expressionToEdge(value.dispatchReceiver!!),
index = expressionToEdge(value.getValueArgument(0)!!),
value = expressionToEdge(value.getValueArgument(1)!!),
type = mapReturnType(value.getValueArgument(1)!!.type, context.irBuiltIns.anyType))
createUninitializedInstanceSymbol ->
DataFlowIR.Node.AllocInstance(symbolTable.mapClassReferenceType(
@@ -606,6 +626,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapFunction(callee),
arguments,
symbolTable.mapClassReferenceType(callee.constructedClass),
symbolTable.mapClassReferenceType(symbols.unit.owner),
null
)
}
@@ -652,6 +673,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
receiverType,
calleeHash,
arguments,
mapReturnType(value.type, callee.target.returnType),
value
)
} else {
@@ -661,6 +683,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
receiverType,
vtableIndex,
arguments,
mapReturnType(value.type, callee.target.returnType),
value
)
}
@@ -670,6 +693,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapFunction(actualCallee),
arguments,
actualCallee.dispatchReceiverParameter?.let { symbolTable.mapType(it.type) },
mapReturnType(value.type, actualCallee.returnType),
value
)
}
@@ -686,6 +710,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapFunction(value.symbol.owner),
arguments.map { expressionToEdge(it) },
symbolTable.mapType(thiz.type),
symbolTable.mapClassReferenceType(symbols.unit.owner),
value
)
}
@@ -702,6 +727,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
name.localHash.value,
takeName { name }
),
mapReturnType(value.type, value.symbol.owner.type),
value
)
}
@@ -718,7 +744,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
name.localHash.value,
takeName { name }
),
expressionToEdge(value.value)
expressionToEdge(value.value),
mapReturnType(value.value.type, value.symbol.owner.type)
)
}
@@ -451,13 +451,14 @@ internal object DFGSerializer {
}
}
class Call(val callee: Int, val arguments: Array<Edge>) {
class Call(val callee: Int, val arguments: Array<Edge>, val returnType: Int) {
constructor(data: ArraySlice) : this(data.readInt(), data.readArray { Edge(this) })
constructor(data: ArraySlice) : this(data.readInt(), data.readArray { Edge(this) }, data.readInt())
fun write(result: ArraySlice) {
result.writeInt(callee)
result.writeArray(arguments) { it.write(this) }
result.writeInt(returnType)
}
}
@@ -530,55 +531,60 @@ internal object DFGSerializer {
}
}
class FunctionReference(val symbol: Int, val type: Int) {
class FunctionReference(val symbol: Int, val type: Int, val returnType: Int) {
constructor(data: ArraySlice) : this(data.readInt(), data.readInt())
constructor(data: ArraySlice) : this(data.readInt(), data.readInt(), data.readInt())
fun write(result: ArraySlice) {
result.writeInt(symbol)
result.writeInt(type)
result.writeInt(returnType)
}
}
class FieldRead(val receiver: Edge?, val field: Field) {
class FieldRead(val receiver: Edge?, val field: Field, val type: Int) {
constructor(data: ArraySlice) : this(data.readNullable { Edge(this) }, Field(data))
constructor(data: ArraySlice) : this(data.readNullable { Edge(this) }, Field(data), data.readInt())
fun write(result: ArraySlice) {
result.writeNullable(receiver) { it.write(this) }
field.write(result)
result.writeInt(type)
}
}
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) {
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge, val type: Int) {
constructor(data: ArraySlice) : this(data.readNullable { Edge(this) }, Field(data), Edge(data))
constructor(data: ArraySlice) : this(data.readNullable { Edge(this) }, Field(data), Edge(data), data.readInt())
fun write(result: ArraySlice) {
result.writeNullable(receiver) { it.write(this) }
field.write(result)
value.write(result)
result.writeInt(type)
}
}
class ArrayRead(val array: Edge, val index: Edge) {
class ArrayRead(val array: Edge, val index: Edge, val type: Int) {
constructor(data: ArraySlice) : this(Edge(data), Edge(data))
constructor(data: ArraySlice) : this(Edge(data), Edge(data), data.readInt())
fun write(result: ArraySlice) {
array.write(result)
index.write(result)
result.writeInt(type)
}
}
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge) {
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge, val type: Int) {
constructor(data: ArraySlice) : this(Edge(data), Edge(data), Edge(data))
constructor(data: ArraySlice) : this(Edge(data), Edge(data), Edge(data), data.readInt())
fun write(result: ArraySlice) {
array.write(result)
index.write(result)
value.write(result)
result.writeInt(type)
}
}
@@ -693,20 +699,20 @@ internal object DFGSerializer {
fun allocInst(type: Int) =
Node().also { it.allocInstance = AllocInstance(type) }
fun functionReference(symbol: Int, type: Int) =
Node().also { it.functionReference = FunctionReference(symbol, type) }
fun functionReference(symbol: Int, type: Int, returnType: Int) =
Node().also { it.functionReference = FunctionReference(symbol, type, returnType) }
fun fieldRead(receiver: Edge?, field: Field) =
Node().also { it.fieldRead = FieldRead(receiver, field) }
fun fieldRead(receiver: Edge?, field: Field, type: Int) =
Node().also { it.fieldRead = FieldRead(receiver, field, type) }
fun fieldWrite(receiver: Edge?, field: Field, value: Edge) =
Node().also { it.fieldWrite = FieldWrite(receiver, field, value) }
fun fieldWrite(receiver: Edge?, field: Field, value: Edge, type: Int) =
Node().also { it.fieldWrite = FieldWrite(receiver, field, value, type) }
fun arrayRead(array: Edge, index: Edge) =
Node().also { it.arrayRead = ArrayRead(array, index) }
fun arrayRead(array: Edge, index: Edge, type: Int) =
Node().also { it.arrayRead = ArrayRead(array, index, type) }
fun arrayWrite(array: Edge, index: Edge, value: Edge) =
Node().also { it.arrayWrite = ArrayWrite(array, index, value) }
fun arrayWrite(array: Edge, index: Edge, value: Edge, type: Int) =
Node().also { it.arrayWrite = ArrayWrite(array, index, value, type) }
fun variable(values: Array<Edge>, type: Int, kind: DataFlowIR.VariableKind) =
Node().also { it.variable = Variable(values, type, kind.ordinal.toByte()) }
@@ -897,7 +903,8 @@ internal object DFGSerializer {
fun buildCall(call: DataFlowIR.Node.Call) =
Call(
functionSymbolMap[call.callee]!!,
call.arguments.map { buildEdge(it) }.toTypedArray()
call.arguments.map { buildEdge(it) }.toTypedArray(),
typeMap[call.returnType]!!
)
fun buildVirtualCall(virtualCall: DataFlowIR.Node.VirtualCall) =
@@ -932,19 +939,19 @@ internal object DFGSerializer {
Node.allocInst(typeMap[node.type]!!)
is DataFlowIR.Node.FunctionReference ->
Node.functionReference(functionSymbolMap[node.symbol]!!, typeMap[node.type]!!)
Node.functionReference(functionSymbolMap[node.symbol]!!, typeMap[node.type]!!, typeMap[node.returnType]!!)
is DataFlowIR.Node.FieldRead ->
Node.fieldRead(node.receiver?.let { buildEdge(it) }, buildField(node.field))
Node.fieldRead(node.receiver?.let { buildEdge(it) }, buildField(node.field), typeMap[node.type]!!)
is DataFlowIR.Node.FieldWrite ->
Node.fieldWrite(node.receiver?.let { buildEdge(it) }, buildField(node.field), buildEdge(node.value))
Node.fieldWrite(node.receiver?.let { buildEdge(it) }, buildField(node.field), buildEdge(node.value), typeMap[node.type]!!)
is DataFlowIR.Node.ArrayRead ->
Node.arrayRead(buildEdge(node.array), buildEdge(node.index))
Node.arrayRead(buildEdge(node.array), buildEdge(node.index), typeMap[node.type]!!)
is DataFlowIR.Node.ArrayWrite ->
Node.arrayWrite(buildEdge(node.array), buildEdge(node.index), buildEdge(node.value))
Node.arrayWrite(buildEdge(node.array), buildEdge(node.index), buildEdge(node.value), typeMap[node.type]!!)
is DataFlowIR.Node.Variable ->
Node.variable(node.values.map { buildEdge(it) }.toTypedArray(), typeMap[node.type]!!, node.kind)
@@ -1110,6 +1117,7 @@ internal object DFGSerializer {
DataFlowIR.Node.Call(
functionSymbols[call.callee],
call.arguments.map { deserializeEdge(it) },
types[call.returnType],
irCallSite = null
)
@@ -1119,6 +1127,7 @@ internal object DFGSerializer {
call.callee,
call.arguments,
types[virtualCall.receiverType],
types[virtualCall.call.returnType],
irCallSite = null
)
}
@@ -1141,7 +1150,7 @@ internal object DFGSerializer {
val staticCall = it.staticCall!!
val call = deserializeCall(staticCall.call)
val receiverType = staticCall.receiverType?.let { types[it] }
DataFlowIR.Node.StaticCall(call.callee, call.arguments, receiverType, irCallSite = null)
DataFlowIR.Node.StaticCall(call.callee, call.arguments, receiverType, call.returnType, irCallSite = null)
}
NodeType.NEW_OBJECT -> {
@@ -1158,6 +1167,7 @@ internal object DFGSerializer {
virtualCall.receiverType,
vtableCall.calleeVtableIndex,
virtualCall.arguments,
virtualCall.returnType,
virtualCall.irCallSite
)
}
@@ -1170,6 +1180,7 @@ internal object DFGSerializer {
virtualCall.receiverType,
itableCall.calleeHash,
virtualCall.arguments,
virtualCall.returnType,
virtualCall.irCallSite
)
}
@@ -1185,29 +1196,29 @@ internal object DFGSerializer {
NodeType.FUNCTION_REFERENCE -> {
val functionReference = it.functionReference!!
DataFlowIR.Node.FunctionReference(functionSymbols[functionReference.symbol], types[functionReference.type])
DataFlowIR.Node.FunctionReference(functionSymbols[functionReference.symbol], types[functionReference.type], types[functionReference.returnType])
}
NodeType.FIELD_READ -> {
val fieldRead = it.fieldRead!!
val receiver = fieldRead.receiver?.let { deserializeEdge(it) }
DataFlowIR.Node.FieldRead(receiver, deserializeField(fieldRead.field), null)
DataFlowIR.Node.FieldRead(receiver, deserializeField(fieldRead.field), types[fieldRead.type], null)
}
NodeType.FIELD_WRITE -> {
val fieldWrite = it.fieldWrite!!
val receiver = fieldWrite.receiver?.let { deserializeEdge(it) }
DataFlowIR.Node.FieldWrite(receiver, deserializeField(fieldWrite.field), deserializeEdge(fieldWrite.value))
DataFlowIR.Node.FieldWrite(receiver, deserializeField(fieldWrite.field), deserializeEdge(fieldWrite.value), types[fieldWrite.type])
}
NodeType.ARRAY_READ -> {
val arrayRead = it.arrayRead!!
DataFlowIR.Node.ArrayRead(deserializeEdge(arrayRead.array), deserializeEdge(arrayRead.index), null)
DataFlowIR.Node.ArrayRead(deserializeEdge(arrayRead.array), deserializeEdge(arrayRead.index), types[arrayRead.type], null)
}
NodeType.ARRAY_WRITE -> {
val arrayWrite = it.arrayWrite!!
DataFlowIR.Node.ArrayWrite(deserializeEdge(arrayWrite.array), deserializeEdge(arrayWrite.index), deserializeEdge(arrayWrite.value))
DataFlowIR.Node.ArrayWrite(deserializeEdge(arrayWrite.array), deserializeEdge(arrayWrite.index), deserializeEdge(arrayWrite.value), types[arrayWrite.type])
}
NodeType.VARIABLE -> {
@@ -223,43 +223,43 @@ internal object DataFlowIR {
object Null : Node()
open class Call(val callee: FunctionSymbol, val arguments: List<Edge>,
open class Call(val callee: FunctionSymbol, val arguments: List<Edge>, val returnType: Type,
open val irCallSite: IrFunctionAccessExpression?) : Node()
class StaticCall(callee: FunctionSymbol, arguments: List<Edge>,
val receiverType: Type?, irCallSite: IrFunctionAccessExpression?)
: Call(callee, arguments, irCallSite)
val receiverType: Type?, returnType: Type, irCallSite: IrFunctionAccessExpression?)
: Call(callee, arguments, returnType, irCallSite)
// TODO: It can be replaced with a pair(AllocInstance, constructor Call), remove.
class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, val constructedType: Type,
override val irCallSite: IrConstructorCall?)
: Call(constructor, arguments, irCallSite)
class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, val constructedType: Type, override val irCallSite: IrConstructorCall?)
: Call(constructor, arguments, constructedType, irCallSite)
open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>,
val receiverType: Type, override val irCallSite: IrCall?)
: Call(callee, arguments, irCallSite)
val receiverType: Type, returnType: Type, override val irCallSite: IrCall?)
: Call(callee, arguments, returnType, irCallSite)
class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int,
arguments: List<Edge>, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, irCallSite)
arguments: List<Edge>, returnType: Type, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, returnType, irCallSite)
class ItableCall(callee: FunctionSymbol, receiverType: Type, val calleeHash: Long,
arguments: List<Edge>, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, irCallSite)
arguments: List<Edge>, returnType: Type, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, returnType, irCallSite)
class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node()
class AllocInstance(val type: Type) : Node()
class FunctionReference(val symbol: FunctionSymbol, val type: Type) : Node()
class FunctionReference(val symbol: FunctionSymbol, val type: Type, val returnType: Type) : Node()
class FieldRead(val receiver: Edge?, val field: Field, val ir: IrGetField?) : Node()
// TODO: Add type (similar to arrays)?
class FieldRead(val receiver: Edge?, val field: Field, val type: Type, val ir: IrGetField?) : Node()
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) : Node()
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge, val type: Type) : Node()
class ArrayRead(val array: Edge, val index: Edge, val irCallSite: IrCall?) : Node()
class ArrayRead(val array: Edge, val index: Edge, val type: Type, val irCallSite: IrCall?) : Node()
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge) : Node()
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge, val type: Type) : Node()
class Variable(values: List<Edge>, val type: Type, val kind: VariableKind) : Node() {
val values = mutableListOf<Edge>().also { it += values }
@@ -551,7 +551,8 @@ internal object DataFlowIR {
primitiveBinaryType,
module,
-1,
null
null,
takeName { primitiveBinaryType.name }
)
}
@@ -253,7 +253,7 @@ internal object Devirtualization {
addInstantiatingClass(symbolTable.mapType(context.irBuiltIns.stringType))
}
// Traverse call graph from the roots.
rootSet.forEach { dfs(it) }
rootSet.forEach { dfs(it, it.returnParameter.type) }
return instantiatingClasses
}
@@ -282,7 +282,7 @@ internal object Devirtualization {
else -> error("Unreachable")
}
dfs(callee)
dfs(callee, virtualCall.returnType)
}
private fun checkSupertypes(type: DataFlowIR.Type.Declared,
@@ -317,13 +317,13 @@ internal object Devirtualization {
.forEach { checkSupertypes(it, inheritor, seenTypes) }
}
private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
private fun dfs(symbol: DataFlowIR.FunctionSymbol, returnType: DataFlowIR.Type) {
val resolvedFunctionSymbol = symbol.resolved()
if (resolvedFunctionSymbol is DataFlowIR.FunctionSymbol.External) {
DEBUG_OUTPUT(1) { println("Function $resolvedFunctionSymbol is external") }
val resolvedReturnType = symbol.returnParameter.type.resolved()
val resolvedReturnType = returnType.resolved()
if (resolvedReturnType.isFinal) {
DEBUG_OUTPUT(1) { println("Adding return type as it is final") }
@@ -346,12 +346,12 @@ internal object Devirtualization {
when (node) {
is DataFlowIR.Node.NewObject -> {
addInstantiatingClass(node.constructedType)
dfs(node.callee)
dfs(node.callee, node.constructedType)
}
is DataFlowIR.Node.Singleton -> {
addInstantiatingClass(node.type)
node.constructor?.let { dfs(it) }
node.constructor?.let { dfs(it, node.type) }
}
is DataFlowIR.Node.AllocInstance -> {
@@ -361,10 +361,10 @@ internal object Devirtualization {
is DataFlowIR.Node.Const -> addInstantiatingClass(node.type)
is DataFlowIR.Node.StaticCall ->
dfs(node.callee)
dfs(node.callee, node.returnType)
is DataFlowIR.Node.FunctionReference ->
dfs(node.symbol)
dfs(node.symbol, node.returnType)
is DataFlowIR.Node.FieldRead ->
if (entryPoint == null && node.field.type.isFinal)
@@ -889,7 +889,7 @@ internal object Devirtualization {
function.parameters[node.index]
is DataFlowIR.Node.StaticCall ->
doCall(node.callee, node.arguments, node.callee.returnParameter.type.resolved(),
doCall(node.callee, node.arguments, node.returnType.resolved(),
node.receiverType?.resolved())
is DataFlowIR.Node.NewObject -> {
@@ -933,7 +933,7 @@ internal object Devirtualization {
println()
}
val returnType = node.callee.returnParameter.type.resolved()
val returnType = node.returnType.resolved()
val receiverNode = edgeToConstraintNode(node.arguments[0])
if (receiverType == DataFlowIR.Type.Virtual)
constraintGraph.virtualNode.addEdge(receiverNode)
@@ -983,20 +983,20 @@ internal object Devirtualization {
}
is DataFlowIR.Node.FieldRead ->
fieldNode(node.field)
doCast(function, fieldNode(node.field), node.type.resolved())
is DataFlowIR.Node.FieldWrite -> {
val fieldNode = fieldNode(node.field)
edgeToConstraintNode(node.value).addEdge(fieldNode)
doCast(function, edgeToConstraintNode(node.value), node.type.resolved()).addEdge(fieldNode)
constraintGraph.voidNode
}
is DataFlowIR.Node.ArrayRead ->
fieldNode(constraintGraph.arrayItemField)
doCast(function, fieldNode(constraintGraph.arrayItemField), node.type.resolved())
is DataFlowIR.Node.ArrayWrite -> {
val fieldNode = fieldNode(constraintGraph.arrayItemField)
edgeToConstraintNode(node.value).addEdge(fieldNode)
doCast(function, edgeToConstraintNode(node.value), node.type.resolved()).addEdge(fieldNode)
constraintGraph.voidNode
}