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)
internal inline fun<R> generateFunction(codegen: CodeGenerator,
function: IrFunction,
startLocation: LocationInfo?,
endLocation: LocationInfo?,
code: FunctionGenerationContext.(FunctionGenerationContext) -> R) {
internal inline fun generateFunction(
codegen: CodeGenerator,
function: IrFunction,
startLocation: LocationInfo?,
endLocation: LocationInfo?,
code: FunctionGenerationContext.() -> Unit
) {
val llvmFunction = codegen.llvmFunction(function)
val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE
@@ -155,11 +157,24 @@ internal inline fun<R> generateFunction(codegen: CodeGenerator,
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,
startLocation: LocationInfo? = null, endLocation: LocationInfo? = null,
switchToRunnable: Boolean = false,
code:FunctionGenerationContext.(FunctionGenerationContext) -> R) {
internal inline fun generateFunction(
codegen: CodeGenerator,
function: LLVMValueRef,
startLocation: LocationInfo? = null,
endLocation: LocationInfo? = null,
switchToRunnable: Boolean = false,
code: FunctionGenerationContext.() -> Unit
) {
val functionGenerationContext = FunctionGenerationContext(
function,
codegen,
@@ -179,7 +194,7 @@ internal inline fun generateFunction(
functionType: LLVMTypeRef,
name: String,
switchToRunnable: Boolean = false,
block: FunctionGenerationContext.(FunctionGenerationContext) -> Unit
block: FunctionGenerationContext.() -> Unit
): LLVMValueRef {
val function = addLlvmFunctionWithDefaultAttributes(
codegen.context,
@@ -192,10 +207,10 @@ internal inline fun generateFunction(
}
// TODO: Consider using different abstraction than `FunctionGenerationContext` for `generateFunctionNoRuntime`.
internal inline fun <R> generateFunctionNoRuntime(
internal inline fun generateFunctionNoRuntime(
codegen: CodeGenerator,
function: LLVMValueRef,
code: FunctionGenerationContext.(FunctionGenerationContext) -> R,
code: FunctionGenerationContext.() -> Unit,
) {
val functionGenerationContext = FunctionGenerationContext(function, codegen, null, null, switchToRunnable = false)
try {
@@ -214,7 +229,7 @@ internal inline fun generateFunctionNoRuntime(
codegen: CodeGenerator,
functionType: LLVMTypeRef,
name: String,
code: FunctionGenerationContext.(FunctionGenerationContext) -> Unit,
code: FunctionGenerationContext.() -> Unit,
): LLVMValueRef {
val function = addLlvmFunctionWithDefaultAttributes(
codegen.context,
@@ -226,11 +241,11 @@ internal inline fun generateFunctionNoRuntime(
return function
}
private inline fun <R> generateFunctionBody(
functionGenerationContext: FunctionGenerationContext,
code: FunctionGenerationContext.(FunctionGenerationContext) -> R) {
private inline fun <T : FunctionGenerationContext> generateFunctionBody(
functionGenerationContext: T,
code: T.() -> Unit) {
functionGenerationContext.prologue()
functionGenerationContext.code(functionGenerationContext)
functionGenerationContext.code()
if (!functionGenerationContext.isAfterTerminator())
functionGenerationContext.unreachable()
functionGenerationContext.epilogue()
@@ -387,12 +402,46 @@ internal class StackLocalsManagerImpl(
}
}
internal 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 {
internal abstract class FunctionGenerationContextBuilder<T : FunctionGenerationContext>(
val function: LLVMValueRef,
val codegen: CodeGenerator
) {
constructor(functionType: LLVMTypeRef, functionName: String, codegen: CodeGenerator) :
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
val vars = VariableManager(this)
@@ -371,7 +371,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
context.llvm.initializersGenerationState.globalInitFunction?.let { fileInitFunction ->
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
using(FunctionScope(fileInitFunction, it)) {
using(FunctionScope(fileInitFunction, this)) {
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
using(parameterScope) usingParameterScope@{
using(VariableScope()) usingVariableScope@{
@@ -388,7 +388,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
context.llvm.initializersGenerationState.threadLocalInitFunction?.let { fileInitFunction ->
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
using(FunctionScope(fileInitFunction, it)) {
using(FunctionScope(fileInitFunction, this)) {
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
using(parameterScope) usingParameterScope@{
using(VariableScope()) usingVariableScope@{
@@ -474,7 +474,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
)
LLVMSetLinkage(initFunction, LLVMLinkage.LLVMPrivateLinkage)
generateFunction(codegen, initFunction) {
using(FunctionScope(initFunction, it)) {
using(FunctionScope(initFunction, this)) {
val bbInit = basicBlock("init", null)
val bbLocalInit = basicBlock("local_init", null)
val bbLocalAlloc = basicBlock("local_alloc", null)
@@ -831,7 +831,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
generateFunction(codegen, declaration,
declaration.location(start = true),
declaration.location(start = false)) {
using(FunctionScope(declaration, it)) {
using(FunctionScope(declaration, this)) {
val parameterScope = ParameterScope(declaration, functionGenerationContext)
using(parameterScope) usingParameterScope@{
using(VariableScope()) usingVariableScope@{
@@ -30,11 +30,10 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
structType(codegen.kObjHeader)
}
val invokeImpl = generateFunction(
codegen,
val invokeImpl = functionGenerator(
codegen.getLlvmFunctionType(invokeMethod),
"invokeFunction${bridge.nameSuffix}"
) {
).generate {
val args = (0 until bridge.numberOfParameters).map { index ->
kotlinReferenceToObjC(param(index + 1))
}
@@ -71,11 +70,10 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
immutable = true
)
return generateFunction(
codegen,
return functionGenerator(
functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr),
"convertBlock${bridge.nameSuffix}"
) {
).generate {
val blockPtr = param(0)
ifThen(icmpEq(blockPtr, kNullInt8Ptr)) {
ret(kNullObjHeaderPtr)
@@ -235,7 +233,9 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
invokeName: String,
genBody: FunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit
): 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 kotlinObject = call(
context.llvm.kRefSharedHolderRef,
@@ -292,11 +292,10 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
generateDescriptorForBlock(blockType)
)
return generateFunction(
codegen,
return functionGenerator(
functionType(int8TypePtr, false, codegen.kObjHeaderPtr),
convertName
) {
).generate {
val kotlinRef = param(0)
ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) {
ret(kNullInt8Ptr)
@@ -47,6 +47,35 @@ internal fun TypeBridge.makeNothing() = when (this) {
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) {
val symbols get() = context.ir.symbols
val runtime get() = codegen.runtime
@@ -637,7 +666,7 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
val boxClass = boxClassSymbol.owner
val name = "${boxClass.name}ToNSNumber"
val converter = generateFunction(codegen, kotlinToObjCFunctionType, name) {
val converter = functionGenerator(kotlinToObjCFunctionType, name).generate {
val unboxFunction = context.getUnboxFunction(boxClass).llvmFunction
val kotlinValue = callFromBridge(
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(
methodBridge: MethodBridge,
debugInfo: Boolean = false,
genBody: FunctionGenerationContext.() -> Unit
): LLVMValueRef {
val result = addLlvmFunctionWithDefaultAttributes(
context,
context.llvmModule!!,
"objc2kotlin",
objCFunctionType(context, methodBridge)
)
val result = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") {
if (debugInfo) {
this.setupBridgeDebugInfo()
}
val location = if (debugInfo) {
setupBridgeDebugInfo(context, result)
} else {
null
}
generateFunction(codegen, result, startLocation = location, endLocation = location, switchToRunnable = true) {
switchToRunnable = true
}.generate {
genBody()
}
@@ -1019,7 +1047,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
val functionType = codegen.getLlvmFunctionType(irFunction)
val result = generateFunction(codegen, functionType, "kotlin2objc") {
val result = functionGenerator(functionType, "kotlin2objc").generate {
var errorOutPtr: LLVMValueRef? = null
val parameters = irFunction.allParameters.mapIndexed { index, parameterDescriptor ->
@@ -1526,7 +1554,9 @@ private inline fun ObjCExportCodeGenerator.generateObjCToKotlinSyntheticGetter(
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()
}