[K/N] Enforce the native thread state for skiko's _nFlushAndSubmit

`org_jetbrains_skia_DirectContext__1nFlushAndSubmit` is a function from
Skiko written in C++ and called from Kotlin through a
`@SymbolName external fun`.

As discovered in KT-75895, this function might block and wait for
another Kotlin thread. Therefore, it violates the contract of
`SymbolName`. Switching the thread state to "native" is required when
calling such a function, which `SymbolName` doesn't do.

This commit adds a hack to the compiler: when calling a function with
such a symbol name, enforce thread state switching.

The hack is configurable: it is added in the form of a binary option,
`-Xbinary=forceNativeThreadStateForFunctions=fun1;fun2`, with
`org_jetbrains_skia_DirectContext__1nFlushAndSubmit` as the default
value.
It can also be disabled with
`-Xbinary=forceNativeThreadStateForFunctions=`.

Specifying a value for `forceNativeThreadStateForFunctions` disabled
compiler caches (since callsites to listed functions might be in the
caches).

^KT-77489 Fixed
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt
index ad0b59a..2eb4055 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt
@@ -84,6 +84,8 @@
 
     val enableSafepointSignposts by booleanOption()
 
+    val forceNativeThreadStateForFunctions by listOption(StringValueParser)
+
     val packFields by booleanOption()
 
     val cInterfaceMode by option<CInterfaceGenerationMode>()
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt
index 376c942..5f55861 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt
@@ -274,6 +274,12 @@
         }
     } ?: false // Disabled by default because of KT-68928
 
+    val forceNativeThreadStateForFunctions: Set<String> =
+            configuration.get(BinaryOptions.forceNativeThreadStateForFunctions)?.toSet()
+                    ?: setOf(
+                            "org_jetbrains_skia_DirectContext__1nFlushAndSubmit", // KT-75895
+                    )
+
     val globalDataLazyInit: Boolean by lazy {
         configuration.get(BinaryOptions.globalDataLazyInit) ?: true
     }
@@ -598,6 +604,7 @@
     internal val ignoreCacheReason = when {
         optimizationsEnabled -> "for optimized compilation"
         runtimeLogsEnabled -> "with runtime logs"
+        configuration.get(BinaryOptions.forceNativeThreadStateForFunctions) != null -> "with non-default forceNativeThreadStateForFunctions"
         else -> null
     }
 
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
index 85e8f77..95a2194 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
@@ -2598,15 +2598,41 @@
                      resultLifetime: Lifetime, resultSlot: LLVMValueRef?): LLVMValueRef {
         check(!function.isTypedIntrinsic)
 
-        val needsNativeThreadState = function.needsNativeThreadState
-        val exceptionHandler = function.annotations.findAnnotation(RuntimeNames.filterExceptions)?.let {
-            val foreignExceptionMode = ForeignExceptionMode.byValue(it.getAnnotationValueOrNull<String>("mode"))
+        val foreignExceptionModeFromAnnotation = function.annotations.findAnnotation(RuntimeNames.filterExceptions)?.let {
+            ForeignExceptionMode.byValue(it.getAnnotationValueOrNull<String>("mode"))
+        }
+
+        val needsNativeThreadState: Boolean
+        val filterExceptionWith: ForeignExceptionMode.Mode?
+
+        if (llvmCallable.name in context.config.forceNativeThreadStateForFunctions) {
+            // This is a quick hack for functions that break the contract of `SymbolName` by being blocking,
+            // and therefore need the native thread state.
+            // See e.g., KT-75895.
+            needsNativeThreadState = true
+
+            // Switching to the native thread state requires a filteringExceptionHandler,
+            // so we enforce one here.
+            // Otherwise, nothing will switch the state back to runnable in case of exception.
+            // A more flexible approach can be implemented but is not necessary for this quick hack.
+            filterExceptionWith = foreignExceptionModeFromAnnotation ?: ForeignExceptionMode.Mode.TERMINATE
+        } else {
+            needsNativeThreadState = function.needsNativeThreadState
+            filterExceptionWith = foreignExceptionModeFromAnnotation
+        }
+
+        val exceptionHandler = if (filterExceptionWith != null) {
             functionGenerationContext.filteringExceptionHandler(
                     currentCodeContext.exceptionHandler,
-                    foreignExceptionMode,
+                    filterExceptionWith,
                     needsNativeThreadState
             )
-        } ?: currentCodeContext.exceptionHandler
+        } else {
+            check(!needsNativeThreadState) {
+                "${llvmCallable.name} needs native thread state, but doesn't have a filtering exception handler"
+            }
+            currentCodeContext.exceptionHandler
+        }
 
         if (needsNativeThreadState) {
             functionGenerationContext.switchThreadState(ThreadState.Native)