Apply minor refactoring to exception handling

This commit is contained in:
Svyatoslav Scherbina
2018-01-09 12:36:39 +03:00
committed by SvyatoslavScherbina
parent b700f96e0c
commit d4f39f8142
4 changed files with 39 additions and 33 deletions
@@ -180,7 +180,7 @@ private class ExportedElement(val kind: ElementKind,
val numParams = LLVMCountParams(llvmFunction) val numParams = LLVMCountParams(llvmFunction)
val args = (0 .. numParams - 1).map { index -> param(index) } val args = (0 .. numParams - 1).map { index -> param(index) }
val callee = lookupVirtualImpl(receiver, function) val callee = lookupVirtualImpl(receiver, function)
val result = callAtFunctionScopeVerbatim(callee, args) val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true)
ret(result) ret(result)
} }
@@ -51,6 +51,14 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
fun functionHash(descriptor: FunctionDescriptor): LLVMValueRef = descriptor.functionName.localHash.llvm fun functionHash(descriptor: FunctionDescriptor): LLVMValueRef = descriptor.functionName.localHash.llvm
} }
internal sealed class ExceptionHandler {
object None : ExceptionHandler()
object Caller : ExceptionHandler()
abstract class Local : ExceptionHandler() {
abstract val unwind: LLVMBasicBlockRef
}
}
val LLVMValueRef.name:String? val LLVMValueRef.name:String?
get() = LLVMGetValueName(this)?.toKString() get() = LLVMGetValueName(this)?.toKString()
@@ -132,6 +140,9 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
private val epilogueBb = basicBlockInFunction("epilogue", endLocation) private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation) private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
/**
* TODO: consider merging this with [ExceptionHandler].
*/
var forwardingForeignExceptionsTerminatedWith: LLVMValueRef? = null var forwardingForeignExceptionsTerminatedWith: LLVMValueRef? = null
init { init {
@@ -241,17 +252,9 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
fun callAtFunctionScope(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
lifetime: Lifetime) =
call(llvmFunction, args, lifetime, { cleanupLandingpad })
fun callAtFunctionScopeVerbatim(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>) =
call(llvmFunction, args, Lifetime.IRRELEVANT, { cleanupLandingpad }, true)
fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT, resultLifetime: Lifetime = Lifetime.IRRELEVANT,
lazyLandingpad: () -> LLVMBasicBlockRef? = { null }, exceptionHandler: ExceptionHandler = ExceptionHandler.None,
verbatim: Boolean = false): LLVMValueRef { verbatim: Boolean = false): LLVMValueRef {
val callArgs = if (verbatim || !isObjectReturn(llvmFunction.type)) { val callArgs = if (verbatim || !isObjectReturn(llvmFunction.type)) {
args args
@@ -291,30 +294,34 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
} }
args + resultSlot args + resultSlot
} }
return callRaw(llvmFunction, callArgs, lazyLandingpad) return callRaw(llvmFunction, callArgs, exceptionHandler)
} }
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
lazyLandingpad: () -> LLVMBasicBlockRef?): LLVMValueRef { exceptionHandler: ExceptionHandler): LLVMValueRef {
val rargs = args.toCValues() val rargs = args.toCValues()
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ && if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
isFunctionNoUnwind(llvmFunction)) { isFunctionNoUnwind(llvmFunction)) {
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!! return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
} else { } else {
val landingpad = lazyLandingpad() val unwind = when (exceptionHandler) {
ExceptionHandler.Caller -> cleanupLandingpad
is ExceptionHandler.Local -> exceptionHandler.unwind
if (landingpad == null) { ExceptionHandler.None -> {
// When calling a function that is not marked as nounwind (can throw an exception), // When calling a function that is not marked as nounwind (can throw an exception),
// it is required to specify a landingpad to handle exceptions properly. // it is required to specify an unwind label to handle exceptions properly.
// Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`. // Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`.
val functionName = llvmFunction.name val functionName = llvmFunction.name
val message = "no landingpad specified when calling function $functionName without nounwind attr" val message =
throw IllegalArgumentException(message) "no exception handler specified when calling function $functionName without nounwind attr"
throw IllegalArgumentException(message)
}
} }
val success = basicBlock("call_success", position()) val success = basicBlock("call_success", position())
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, "")!! val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, unwind, "")!!
positionAtEnd(success) positionAtEnd(success)
return result return result
} }
@@ -165,7 +165,7 @@ internal interface CodeContext {
fun genContinue(destination: IrContinue) fun genContinue(destination: IrContinue)
fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef val exceptionHandler: ExceptionHandler
fun genThrow(exception: LLVMValueRef) fun genThrow(exception: LLVMValueRef)
@@ -237,7 +237,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
override fun genContinue(destination: IrContinue) = unsupported() override fun genContinue(destination: IrContinue) = unsupported()
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime) = unsupported(function) override val exceptionHandler get() = unsupported()
override fun genThrow(exception: LLVMValueRef) = unsupported() override fun genThrow(exception: LLVMValueRef) = unsupported()
@@ -558,14 +558,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
} }
} }
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime) = override val exceptionHandler get() = ExceptionHandler.Caller
functionGenerationContext.callAtFunctionScope(function, args, resultLifetime)
override fun genThrow(exception: LLVMValueRef) { override fun genThrow(exception: LLVMValueRef) {
val objHeaderPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, exception) val objHeaderPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, exception)
val args = listOf(objHeaderPtr) val args = listOf(objHeaderPtr)
this.genCall(context.llvm.throwExceptionFunction, args, Lifetime.IRRELEVANT) functionGenerationContext.call(
context.llvm.throwExceptionFunction, args, Lifetime.IRRELEVANT, this.exceptionHandler
)
functionGenerationContext.unreachable() functionGenerationContext.unreachable()
} }
@@ -938,10 +939,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
} }
} }
// The call inside [CatchingScope] must be configured to dispatch exception to the scope's handler. override val exceptionHandler: ExceptionHandler get() = object : ExceptionHandler.Local() {
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef { override val unwind get() = landingpad
val res = functionGenerationContext.call(function, args, resultLifetime, this::landingpad)
return res
} }
override fun genThrow(exception: LLVMValueRef) { override fun genThrow(exception: LLVMValueRef) {
@@ -2363,7 +2362,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>, private fun call(function: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT): LLVMValueRef { resultLifetime: Lifetime = Lifetime.IRRELEVANT): LLVMValueRef {
return currentCodeContext.genCall(function, args, resultLifetime) return functionGenerationContext.call(function, args, resultLifetime, currentCodeContext.exceptionHandler)
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -2420,7 +2419,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
// launcher, so we could optimize out creating slot for 'parameter' in // launcher, so we could optimize out creating slot for 'parameter' in
// this function. // this function.
val parameter = LLVMGetParam(selector, 0)!! val parameter = LLVMGetParam(selector, 0)!!
callAtFunctionScope(entryPoint, listOf(parameter), Lifetime.IRRELEVANT) call(entryPoint, listOf(parameter), Lifetime.IRRELEVANT, ExceptionHandler.Caller)
ret(null) ret(null)
} }
return selector return selector
@@ -65,7 +65,7 @@ internal class ObjCExportCodeGenerator(
// TODO: it is required only for Kotlin-to-Objective-C bridges. // TODO: it is required only for Kotlin-to-Objective-C bridges.
this.forwardingForeignExceptionsTerminatedWith = objcTerminate this.forwardingForeignExceptionsTerminatedWith = objcTerminate
return callAtFunctionScope(function, args, resultLifetime) return call(function, args, resultLifetime, ExceptionHandler.Caller)
} }
fun FunctionGenerationContext.genSendMessage( fun FunctionGenerationContext.genSendMessage(