Native: allow subclassing FunctionGenerationContext

This allows one to have domain-specific generator without polluting
the global namespace.

Also introduce builders and do minor refactoring.
This commit is contained in:
Svyatoslav Scherbina
2021-08-17 16:57:43 +03:00
committed by Space
parent be1acc8103
commit e00fd6b9b3
4 changed files with 131 additions and 53 deletions
@@ -121,11 +121,13 @@ val LLVMValueRef.isConst:Boolean
get() = (LLVMIsConstant(this) == 1) get() = (LLVMIsConstant(this) == 1)
internal inline fun<R> generateFunction(codegen: CodeGenerator, internal inline fun generateFunction(
function: IrFunction, codegen: CodeGenerator,
startLocation: LocationInfo?, function: IrFunction,
endLocation: LocationInfo?, startLocation: LocationInfo?,
code: FunctionGenerationContext.(FunctionGenerationContext) -> R) { endLocation: LocationInfo?,
code: FunctionGenerationContext.() -> Unit
) {
val llvmFunction = codegen.llvmFunction(function) val llvmFunction = codegen.llvmFunction(function)
val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE
@@ -155,11 +157,24 @@ internal inline fun<R> generateFunction(codegen: CodeGenerator,
LLVMVerifyFunction(llvmFunction, LLVMVerifierFailureAction.LLVMAbortProcessAction) LLVMVerifyFunction(llvmFunction, LLVMVerifierFailureAction.LLVMAbortProcessAction)
} }
internal inline fun <T : FunctionGenerationContext> FunctionGenerationContextBuilder<T>.generate(code: T.() -> Unit): LLVMValueRef {
val functionGenerationContext = this.build()
return try {
generateFunctionBody(functionGenerationContext, code)
functionGenerationContext.function
} finally {
functionGenerationContext.dispose()
}
}
internal inline fun<R> generateFunction(codegen: CodeGenerator, function: LLVMValueRef, internal inline fun generateFunction(
startLocation: LocationInfo? = null, endLocation: LocationInfo? = null, codegen: CodeGenerator,
switchToRunnable: Boolean = false, function: LLVMValueRef,
code:FunctionGenerationContext.(FunctionGenerationContext) -> R) { startLocation: LocationInfo? = null,
endLocation: LocationInfo? = null,
switchToRunnable: Boolean = false,
code: FunctionGenerationContext.() -> Unit
) {
val functionGenerationContext = FunctionGenerationContext( val functionGenerationContext = FunctionGenerationContext(
function, function,
codegen, codegen,
@@ -179,7 +194,7 @@ internal inline fun generateFunction(
functionType: LLVMTypeRef, functionType: LLVMTypeRef,
name: String, name: String,
switchToRunnable: Boolean = false, switchToRunnable: Boolean = false,
block: FunctionGenerationContext.(FunctionGenerationContext) -> Unit block: FunctionGenerationContext.() -> Unit
): LLVMValueRef { ): LLVMValueRef {
val function = addLlvmFunctionWithDefaultAttributes( val function = addLlvmFunctionWithDefaultAttributes(
codegen.context, codegen.context,
@@ -192,10 +207,10 @@ internal inline fun generateFunction(
} }
// TODO: Consider using different abstraction than `FunctionGenerationContext` for `generateFunctionNoRuntime`. // TODO: Consider using different abstraction than `FunctionGenerationContext` for `generateFunctionNoRuntime`.
internal inline fun <R> generateFunctionNoRuntime( internal inline fun generateFunctionNoRuntime(
codegen: CodeGenerator, codegen: CodeGenerator,
function: LLVMValueRef, function: LLVMValueRef,
code: FunctionGenerationContext.(FunctionGenerationContext) -> R, code: FunctionGenerationContext.() -> Unit,
) { ) {
val functionGenerationContext = FunctionGenerationContext(function, codegen, null, null, switchToRunnable = false) val functionGenerationContext = FunctionGenerationContext(function, codegen, null, null, switchToRunnable = false)
try { try {
@@ -214,7 +229,7 @@ internal inline fun generateFunctionNoRuntime(
codegen: CodeGenerator, codegen: CodeGenerator,
functionType: LLVMTypeRef, functionType: LLVMTypeRef,
name: String, name: String,
code: FunctionGenerationContext.(FunctionGenerationContext) -> Unit, code: FunctionGenerationContext.() -> Unit,
): LLVMValueRef { ): LLVMValueRef {
val function = addLlvmFunctionWithDefaultAttributes( val function = addLlvmFunctionWithDefaultAttributes(
codegen.context, codegen.context,
@@ -226,11 +241,11 @@ internal inline fun generateFunctionNoRuntime(
return function return function
} }
private inline fun <R> generateFunctionBody( private inline fun <T : FunctionGenerationContext> generateFunctionBody(
functionGenerationContext: FunctionGenerationContext, functionGenerationContext: T,
code: FunctionGenerationContext.(FunctionGenerationContext) -> R) { code: T.() -> Unit) {
functionGenerationContext.prologue() functionGenerationContext.prologue()
functionGenerationContext.code(functionGenerationContext) functionGenerationContext.code()
if (!functionGenerationContext.isAfterTerminator()) if (!functionGenerationContext.isAfterTerminator())
functionGenerationContext.unreachable() functionGenerationContext.unreachable()
functionGenerationContext.epilogue() functionGenerationContext.epilogue()
@@ -387,12 +402,46 @@ internal class StackLocalsManagerImpl(
} }
} }
internal class FunctionGenerationContext(val function: LLVMValueRef, internal abstract class FunctionGenerationContextBuilder<T : FunctionGenerationContext>(
val codegen: CodeGenerator, val function: LLVMValueRef,
private val startLocation: LocationInfo?, val codegen: CodeGenerator
private val endLocation: LocationInfo?, ) {
private val switchToRunnable: Boolean, constructor(functionType: LLVMTypeRef, functionName: String, codegen: CodeGenerator) :
internal val irFunction: IrFunction? = null): ContextUtils { this(
addLlvmFunctionWithDefaultAttributes(
codegen.context,
codegen.context.llvmModule!!,
functionName,
functionType
),
codegen
)
var startLocation: LocationInfo? = null
var endLocation: LocationInfo? = null
var switchToRunnable = false
var irFunction: IrFunction? = null
abstract fun build(): T
}
internal open class FunctionGenerationContext(
val function: LLVMValueRef,
val codegen: CodeGenerator,
private val startLocation: LocationInfo?,
private val endLocation: LocationInfo?,
private val switchToRunnable: Boolean,
internal val irFunction: IrFunction? = null
) : ContextUtils {
constructor(builder: FunctionGenerationContextBuilder<*>) : this(
function = builder.function,
codegen = builder.codegen,
startLocation = builder.startLocation,
endLocation = builder.endLocation,
switchToRunnable = builder.switchToRunnable,
irFunction = builder.irFunction
)
override val context = codegen.context override val context = codegen.context
val vars = VariableManager(this) val vars = VariableManager(this)
@@ -371,7 +371,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
context.llvm.initializersGenerationState.globalInitFunction?.let { fileInitFunction -> context.llvm.initializersGenerationState.globalInitFunction?.let { fileInitFunction ->
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) { generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
using(FunctionScope(fileInitFunction, it)) { using(FunctionScope(fileInitFunction, this)) {
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext) val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
using(parameterScope) usingParameterScope@{ using(parameterScope) usingParameterScope@{
using(VariableScope()) usingVariableScope@{ using(VariableScope()) usingVariableScope@{
@@ -388,7 +388,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
context.llvm.initializersGenerationState.threadLocalInitFunction?.let { fileInitFunction -> context.llvm.initializersGenerationState.threadLocalInitFunction?.let { fileInitFunction ->
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) { generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
using(FunctionScope(fileInitFunction, it)) { using(FunctionScope(fileInitFunction, this)) {
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext) val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
using(parameterScope) usingParameterScope@{ using(parameterScope) usingParameterScope@{
using(VariableScope()) usingVariableScope@{ using(VariableScope()) usingVariableScope@{
@@ -474,7 +474,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
) )
LLVMSetLinkage(initFunction, LLVMLinkage.LLVMPrivateLinkage) LLVMSetLinkage(initFunction, LLVMLinkage.LLVMPrivateLinkage)
generateFunction(codegen, initFunction) { generateFunction(codegen, initFunction) {
using(FunctionScope(initFunction, it)) { using(FunctionScope(initFunction, this)) {
val bbInit = basicBlock("init", null) val bbInit = basicBlock("init", null)
val bbLocalInit = basicBlock("local_init", null) val bbLocalInit = basicBlock("local_init", null)
val bbLocalAlloc = basicBlock("local_alloc", null) val bbLocalAlloc = basicBlock("local_alloc", null)
@@ -831,7 +831,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
generateFunction(codegen, declaration, generateFunction(codegen, declaration,
declaration.location(start = true), declaration.location(start = true),
declaration.location(start = false)) { declaration.location(start = false)) {
using(FunctionScope(declaration, it)) { using(FunctionScope(declaration, this)) {
val parameterScope = ParameterScope(declaration, functionGenerationContext) val parameterScope = ParameterScope(declaration, functionGenerationContext)
using(parameterScope) usingParameterScope@{ using(parameterScope) usingParameterScope@{
using(VariableScope()) usingVariableScope@{ using(VariableScope()) usingVariableScope@{
@@ -30,11 +30,10 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
structType(codegen.kObjHeader) structType(codegen.kObjHeader)
} }
val invokeImpl = generateFunction( val invokeImpl = functionGenerator(
codegen,
codegen.getLlvmFunctionType(invokeMethod), codegen.getLlvmFunctionType(invokeMethod),
"invokeFunction${bridge.nameSuffix}" "invokeFunction${bridge.nameSuffix}"
) { ).generate {
val args = (0 until bridge.numberOfParameters).map { index -> val args = (0 until bridge.numberOfParameters).map { index ->
kotlinReferenceToObjC(param(index + 1)) kotlinReferenceToObjC(param(index + 1))
} }
@@ -71,11 +70,10 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
immutable = true immutable = true
) )
return generateFunction( return functionGenerator(
codegen,
functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr), functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr),
"convertBlock${bridge.nameSuffix}" "convertBlock${bridge.nameSuffix}"
) { ).generate {
val blockPtr = param(0) val blockPtr = param(0)
ifThen(icmpEq(blockPtr, kNullInt8Ptr)) { ifThen(icmpEq(blockPtr, kNullInt8Ptr)) {
ret(kNullObjHeaderPtr) ret(kNullObjHeaderPtr)
@@ -235,7 +233,9 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
invokeName: String, invokeName: String,
genBody: FunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit genBody: FunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit
): ConstPointer { ): ConstPointer {
val result = generateFunction(codegen, blockType.blockInvokeLlvmType, invokeName, switchToRunnable = true) { val result = functionGenerator(blockType.blockInvokeLlvmType, invokeName) {
switchToRunnable = true
}.generate {
val blockPtr = bitcast(pointerType(blockLiteralType), param(0)) val blockPtr = bitcast(pointerType(blockLiteralType), param(0))
val kotlinObject = call( val kotlinObject = call(
context.llvm.kRefSharedHolderRef, context.llvm.kRefSharedHolderRef,
@@ -292,11 +292,10 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
generateDescriptorForBlock(blockType) generateDescriptorForBlock(blockType)
) )
return generateFunction( return functionGenerator(
codegen,
functionType(int8TypePtr, false, codegen.kObjHeaderPtr), functionType(int8TypePtr, false, codegen.kObjHeaderPtr),
convertName convertName
) { ).generate {
val kotlinRef = param(0) val kotlinRef = param(0)
ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) { ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) {
ret(kNullInt8Ptr) ret(kNullInt8Ptr)
@@ -47,6 +47,35 @@ internal fun TypeBridge.makeNothing() = when (this) {
is ValueTypeBridge -> LLVMConstNull(this.objCValueType.llvmType)!! is ValueTypeBridge -> LLVMConstNull(this.objCValueType.llvmType)!!
} }
internal class ObjCExportFunctionGenerationContext(
builder: ObjCExportFunctionGenerationContextBuilder
) : FunctionGenerationContext(builder) {
private val objCExportCodegen = builder.objCExportCodegen
}
internal class ObjCExportFunctionGenerationContextBuilder(
functionType: LLVMTypeRef,
functionName: String,
val objCExportCodegen: ObjCExportCodeGeneratorBase
) : FunctionGenerationContextBuilder<ObjCExportFunctionGenerationContext>(
functionType,
functionName,
objCExportCodegen.codegen
) {
override fun build() = ObjCExportFunctionGenerationContext(this)
}
internal inline fun ObjCExportCodeGeneratorBase.functionGenerator(
functionType: LLVMTypeRef,
functionName: String,
configure: ObjCExportFunctionGenerationContextBuilder.() -> Unit = {}
): ObjCExportFunctionGenerationContextBuilder = ObjCExportFunctionGenerationContextBuilder(
functionType,
functionName,
this
).apply(configure)
internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCodeGenerator(codegen) { internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCodeGenerator(codegen) {
val symbols get() = context.ir.symbols val symbols get() = context.ir.symbols
val runtime get() = codegen.runtime val runtime get() = codegen.runtime
@@ -637,7 +666,7 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
val boxClass = boxClassSymbol.owner val boxClass = boxClassSymbol.owner
val name = "${boxClass.name}ToNSNumber" val name = "${boxClass.name}ToNSNumber"
val converter = generateFunction(codegen, kotlinToObjCFunctionType, name) { val converter = functionGenerator(kotlinToObjCFunctionType, name).generate {
val unboxFunction = context.getUnboxFunction(boxClass).llvmFunction val unboxFunction = context.getUnboxFunction(boxClass).llvmFunction
val kotlinValue = callFromBridge( val kotlinValue = callFromBridge(
unboxFunction, unboxFunction,
@@ -760,25 +789,24 @@ private fun ObjCExportCodeGenerator.emitCollectionConverters() {
) )
} }
private fun ObjCExportFunctionGenerationContextBuilder.setupBridgeDebugInfo() {
val location = setupBridgeDebugInfo(this.objCExportCodegen.context, function)
startLocation = location
endLocation = location
}
private inline fun ObjCExportCodeGenerator.generateObjCImpBy( private inline fun ObjCExportCodeGenerator.generateObjCImpBy(
methodBridge: MethodBridge, methodBridge: MethodBridge,
debugInfo: Boolean = false, debugInfo: Boolean = false,
genBody: FunctionGenerationContext.() -> Unit genBody: FunctionGenerationContext.() -> Unit
): LLVMValueRef { ): LLVMValueRef {
val result = addLlvmFunctionWithDefaultAttributes( val result = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") {
context, if (debugInfo) {
context.llvmModule!!, this.setupBridgeDebugInfo()
"objc2kotlin", }
objCFunctionType(context, methodBridge)
)
val location = if (debugInfo) { switchToRunnable = true
setupBridgeDebugInfo(context, result) }.generate {
} else {
null
}
generateFunction(codegen, result, startLocation = location, endLocation = location, switchToRunnable = true) {
genBody() genBody()
} }
@@ -1019,7 +1047,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
val functionType = codegen.getLlvmFunctionType(irFunction) val functionType = codegen.getLlvmFunctionType(irFunction)
val result = generateFunction(codegen, functionType, "kotlin2objc") { val result = functionGenerator(functionType, "kotlin2objc").generate {
var errorOutPtr: LLVMValueRef? = null var errorOutPtr: LLVMValueRef? = null
val parameters = irFunction.allParameters.mapIndexed { index, parameterDescriptor -> val parameters = irFunction.allParameters.mapIndexed { index, parameterDescriptor ->
@@ -1526,7 +1554,9 @@ private inline fun ObjCExportCodeGenerator.generateObjCToKotlinSyntheticGetter(
MethodBridgeReceiver.Static, valueParameters = emptyList() MethodBridgeReceiver.Static, valueParameters = emptyList()
) )
val imp = generateFunction(codegen, objCFunctionType(context, methodBridge), "objc2kotlin", switchToRunnable = true) { val imp = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") {
switchToRunnable = true
}.generate {
block() block()
} }