IR: copy each file before lowering.

Avoid inter-file dependencies while lowering.
This commit is contained in:
Georgy Bronnikov
2020-12-14 20:15:41 +03:00
parent 103f82c95c
commit d154c8d8e6
7 changed files with 201 additions and 4 deletions
@@ -13,7 +13,8 @@ import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irString
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
interface LoggingContext {
var inVerbosePhase: Boolean
@@ -36,4 +37,10 @@ interface CommonBackendContext : BackendContext, LoggingContext {
}
val mapping: Mapping
// Adjust internal structures after a deep copy of some declarations.
fun handleDeepCopy(
classSymbolMap: MutableMap<IrClassSymbol, IrClassSymbol>,
functionSymbolMap: MutableMap<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>
) {}
}
@@ -29,6 +29,9 @@ interface Mapping {
operator fun setValue(thisRef: K, desc: KProperty<*>, value: V?) {
set(thisRef, value)
}
abstract val keys: Set<K>
abstract val values: Collection<V>
}
}
@@ -57,6 +60,12 @@ open class DefaultMapping : Mapping {
map[key] = value
}
}
override val keys: Set<K>
get() = map.keys
override val values: Collection<V>
get() = map.values
}
}
@@ -13,7 +13,9 @@ class PhaserState<Data>(
var depth: Int = 0,
var phaseCount: Int = 0,
val stickyPostconditions: MutableSet<Checker<Data>> = mutableSetOf()
)
) {
fun copyOf() = PhaserState(alreadyDone.toMutableSet(), depth, phaseCount, stickyPostconditions)
}
// Copy state, forgetting the sticky postconditions (which will not be applicable to the new type)
fun <Input, Output> PhaserState<Input>.changeType() = PhaserState<Output>(alreadyDone, depth, phaseCount, mutableSetOf())
@@ -13,6 +13,17 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
import org.jetbrains.kotlin.ir.util.copyTypeAndValueArgumentsFrom
import org.jetbrains.kotlin.ir.util.deepCopySavingMetadata
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
@@ -142,7 +153,7 @@ private class PerformByIrFilePhase<Context : CommonBackendContext>(
CodegenUtil.reportBackendException(e, "IR lowering", irFile.fileEntry.name)
}
}
// TODO: no guarantee that module identity is preserved by `lower`
return input
}
@@ -155,8 +166,13 @@ private class PerformByIrFilePhase<Context : CommonBackendContext>(
// We can only report one exception through ISE
val thrownFromThread = AtomicReference<Pair<Throwable, IrFile>?>(null)
val remappedFunctions = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>()
val remappedClasses = mutableMapOf<IrClassSymbol, IrClassSymbol>()
// Each thread needs its own copy of phaserState.alreadyDone
val filesAndStates = input.files.map { it to phaserState.clone() }
val filesAndStates = input.files.map {
it.copySavingMappings(remappedFunctions, remappedClasses) to phaserState.copyOf()
}
val executor = Executors.newFixedThreadPool(nThreads)
for ((irFile, state) in filesAndStates) {
@@ -181,6 +197,13 @@ private class PerformByIrFilePhase<Context : CommonBackendContext>(
// Presumably each thread has run through the same list of phases.
phaserState.alreadyDone.addAll(filesAndStates[0].second.alreadyDone)
input.files.clear()
input.files.addAll(filesAndStates.map { (irFile, _) -> irFile }.toMutableList())
adjustDefaultArgumentStubs(context, remappedFunctions)
input.transformChildrenVoid(CrossFileCallAdjuster(remappedFunctions))
context.handleDeepCopy(remappedClasses, remappedFunctions)
// TODO: no guarantee that module identity is preserved by `lower`
return input
}
@@ -256,3 +279,71 @@ fun <Context : CommonBackendContext, OldData, NewData> transform(op: (OldData) -
object : CompilerPhase<Context, OldData, NewData> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<OldData>, context: Context, input: OldData) = op(input)
}
// We need to remap inline function calls after lowering files
fun IrFile.copySavingMappings(
remappedFunctions: MutableMap<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>,
remappedClasses: MutableMap<IrClassSymbol, IrClassSymbol>,
): IrFile {
val symbolRemapper = DeepCopySymbolRemapperSavingFunctions()
val newIrFile = deepCopySavingMetadata(symbolRemapper = symbolRemapper)
for (function in symbolRemapper.declaredFunctions) {
remappedFunctions[function] = symbolRemapper.getReferencedSimpleFunction(function)
}
for (klass in symbolRemapper.declaredClasses) {
remappedClasses[klass] = symbolRemapper.getReferencedClass(klass)
}
return newIrFile
}
private class DeepCopySymbolRemapperSavingFunctions : DeepCopySymbolRemapper() {
val declaredFunctions = mutableSetOf<IrSimpleFunctionSymbol>()
val declaredClasses = mutableSetOf<IrClassSymbol>()
override fun getDeclaredFunction(symbol: IrSimpleFunctionSymbol): IrSimpleFunctionSymbol {
declaredFunctions.add(symbol)
return super.getDeclaredFunction(symbol)
}
override fun getDeclaredClass(symbol: IrClassSymbol): IrClassSymbol {
declaredClasses.add(symbol)
return super.getDeclaredClass(symbol)
}
}
fun adjustDefaultArgumentStubs(
context: CommonBackendContext,
remappedFunctions: MutableMap<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>,
) {
for (defaultStub in context.mapping.defaultArgumentsOriginalFunction.keys) {
if (defaultStub !is IrSimpleFunction) continue
val original = context.mapping.defaultArgumentsOriginalFunction[defaultStub] as? IrSimpleFunction ?: continue
val originalNew = remappedFunctions[original.symbol]?.owner ?: continue
val defaultStubNew = context.mapping.defaultArgumentsDispatchFunction[originalNew] ?: continue
remappedFunctions[defaultStub.symbol] = defaultStubNew.symbol as IrSimpleFunctionSymbol
}
}
private class CrossFileCallAdjuster(
val remappedFunctions: Map<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>
) : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
return remappedFunctions[expression.symbol]?.let { newSymbol ->
with(expression) {
IrCallImpl(
startOffset, endOffset, type,
newSymbol,
typeArgumentsCount, valueArgumentsCount, origin,
superQualifierSymbol // TODO
).apply {
copyTypeAndValueArgumentsFrom(expression)
}
}
} ?: expression
}
}