[K/N] Support of @Volatile annotation
^KT-54944
This commit is contained in:
committed by
Space Team
parent
a29c418b63
commit
9dea349752
+6
@@ -50,6 +50,10 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION
|
||||
|
||||
internal class NativeMapping : DefaultMapping() {
|
||||
data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections)
|
||||
enum class AtomicFunctionType {
|
||||
COMPARE_AND_SWAP, COMPARE_AND_SET, GET_AND_SET, GET_AND_ADD;
|
||||
}
|
||||
data class AtomicFunctionKey(val field: IrField, val type: AtomicFunctionType)
|
||||
|
||||
val outerThisFields = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
|
||||
val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
||||
@@ -62,6 +66,8 @@ internal class NativeMapping : DefaultMapping() {
|
||||
val boxFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
val unboxFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
val loweredInlineClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrSimpleFunction>()
|
||||
val volatileFieldToAtomicFunction = mutableMapOf<AtomicFunctionKey, IrSimpleFunction>()
|
||||
val functionToVolatileField = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrSimpleFunction, IrField>()
|
||||
}
|
||||
|
||||
internal class Context(
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ object KonanFqNames {
|
||||
val cancellationException = FqName("kotlin.coroutines.cancellation.CancellationException")
|
||||
val threadLocal = FqName("kotlin.native.concurrent.ThreadLocal")
|
||||
val sharedImmutable = FqName("kotlin.native.concurrent.SharedImmutable")
|
||||
val volatile = FqName("kotlin.concurrent.Volatile")
|
||||
val frozen = FqName("kotlin.native.internal.Frozen")
|
||||
val frozenLegacyMM = FqName("kotlin.native.internal.FrozenLegacyMM")
|
||||
val leakDetectorCandidate = FqName("kotlin.native.internal.LeakDetectorCandidate")
|
||||
|
||||
+9
-1
@@ -274,6 +274,13 @@ internal val tailrecPhase = makeKonanFileLoweringPhase(
|
||||
prerequisite = setOf(localFunctionsPhase)
|
||||
)
|
||||
|
||||
internal val volatilePhase = makeKonanFileLoweringPhase(
|
||||
::VolatileFieldsLowering,
|
||||
name = "VolatileFields",
|
||||
description = "Volatile fields processing",
|
||||
prerequisite = setOf(localFunctionsPhase)
|
||||
)
|
||||
|
||||
internal val defaultParameterExtentPhase = makeKonanFileOpPhase(
|
||||
{ context, irFile ->
|
||||
KonanDefaultArgumentStubGenerator(context).lower(irFile)
|
||||
@@ -335,7 +342,8 @@ internal val testProcessorPhase = makeKonanFileOpPhase(
|
||||
internal val delegationPhase = makeKonanFileLoweringPhase(
|
||||
{ PropertyDelegationLowering(it.generationState) },
|
||||
name = "Delegation",
|
||||
description = "Delegation lowering"
|
||||
description = "Delegation lowering",
|
||||
prerequisite = setOf(volatilePhase)
|
||||
)
|
||||
|
||||
internal val functionReferencePhase = makeKonanFileLoweringPhase(
|
||||
|
||||
+1
@@ -165,6 +165,7 @@ internal val allLoweringsPhase = SameTypeNamedCompilerPhase(
|
||||
enumConstructorsPhase,
|
||||
initializersPhase,
|
||||
localFunctionsPhase,
|
||||
volatilePhase,
|
||||
tailrecPhase,
|
||||
defaultParameterExtentPhase,
|
||||
innerClassPhase,
|
||||
|
||||
+3
@@ -99,6 +99,7 @@ internal abstract class KonanSymbols(
|
||||
val symbolName = topLevelClass(RuntimeNames.symbolNameAnnotation)
|
||||
val filterExceptions = topLevelClass(RuntimeNames.filterExceptions)
|
||||
val exportForCppRuntime = topLevelClass(RuntimeNames.exportForCppRuntime)
|
||||
val typedIntrinsic = topLevelClass(RuntimeNames.typedIntrinsicAnnotation)
|
||||
|
||||
val objCMethodImp = symbolTable.referenceClass(descriptorsLookup.interopBuiltIns.objCMethodImp)
|
||||
|
||||
@@ -415,6 +416,8 @@ internal abstract class KonanSymbols(
|
||||
|
||||
val sharedImmutable = topLevelClass(KonanFqNames.sharedImmutable)
|
||||
|
||||
val volatile = topLevelClass(KonanFqNames.volatile)
|
||||
|
||||
val eagerInitialization = topLevelClass(KonanFqNames.eagerInitialization)
|
||||
|
||||
private fun topLevelClass(fqName: FqName): IrClassSymbol = irBuiltIns.findClass(fqName.shortName(), fqName.parent())!!
|
||||
|
||||
+28
-18
@@ -560,21 +560,22 @@ internal abstract class FunctionGenerationContext(
|
||||
|
||||
fun param(index: Int): LLVMValueRef = LLVMGetParam(this.function, index)!!
|
||||
|
||||
fun load(address: LLVMValueRef, name: String = "", memoryOrder: LLVMAtomicOrdering? = null): LLVMValueRef {
|
||||
fun load(address: LLVMValueRef, name: String = "",
|
||||
memoryOrder: LLVMAtomicOrdering? = null, alignment: Int? = null
|
||||
): LLVMValueRef {
|
||||
val value = LLVMBuildLoad(builder, address, name)!!
|
||||
if (memoryOrder != null) {
|
||||
LLVMSetOrdering(value, memoryOrder)
|
||||
}
|
||||
memoryOrder?.let { LLVMSetOrdering(value, it) }
|
||||
alignment?.let { LLVMSetAlignment(value, it) }
|
||||
// Use loadSlot() API for that.
|
||||
assert(!isObjectRef(value))
|
||||
return value
|
||||
}
|
||||
|
||||
fun loadSlot(address: LLVMValueRef, isVar: Boolean, resultSlot: LLVMValueRef? = null, name: String = "", memoryOrder: LLVMAtomicOrdering? = null): LLVMValueRef {
|
||||
fun loadSlot(address: LLVMValueRef, isVar: Boolean, resultSlot: LLVMValueRef? = null, name: String = "",
|
||||
memoryOrder: LLVMAtomicOrdering? = null, alignment: Int? = null): LLVMValueRef {
|
||||
val value = LLVMBuildLoad(builder, address, name)!!
|
||||
if (memoryOrder != null) {
|
||||
LLVMSetOrdering(value, memoryOrder)
|
||||
}
|
||||
memoryOrder?.let { LLVMSetOrdering(value, it) }
|
||||
alignment?.let { LLVMSetAlignment(value, it) }
|
||||
if (isObjectRef(value) && isVar) {
|
||||
val slot = resultSlot ?: alloca(LLVMTypeOf(value), variableLocation = null)
|
||||
storeStackRef(value, slot)
|
||||
@@ -582,8 +583,10 @@ internal abstract class FunctionGenerationContext(
|
||||
return value
|
||||
}
|
||||
|
||||
fun store(value: LLVMValueRef, ptr: LLVMValueRef) {
|
||||
LLVMBuildStore(builder, value, ptr)
|
||||
fun store(value: LLVMValueRef, ptr: LLVMValueRef, memoryOrder: LLVMAtomicOrdering? = null, alignment: Int? = null) {
|
||||
val store = LLVMBuildStore(builder, value, ptr)
|
||||
memoryOrder?.let { LLVMSetOrdering(store, it) }
|
||||
alignment?.let { LLVMSetAlignment(store, it) }
|
||||
}
|
||||
|
||||
fun storeHeapRef(value: LLVMValueRef, ptr: LLVMValueRef) {
|
||||
@@ -594,12 +597,12 @@ internal abstract class FunctionGenerationContext(
|
||||
updateRef(value, ptr, onStack = true)
|
||||
}
|
||||
|
||||
fun storeAny(value: LLVMValueRef, ptr: LLVMValueRef, onStack: Boolean) = if (isObjectRef(value)) {
|
||||
if (onStack) storeStackRef(value, ptr) else storeHeapRef(value, ptr)
|
||||
null
|
||||
} else {
|
||||
LLVMBuildStore(builder, value, ptr)
|
||||
fun storeAny(value: LLVMValueRef, ptr: LLVMValueRef, onStack: Boolean, isVolatile: Boolean = false, alignment: Int? = null) {
|
||||
when {
|
||||
isObjectRef(value) -> updateRef(value, ptr, onStack, isVolatile, alignment)
|
||||
else -> store(value, ptr, if (isVolatile) LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent else null, alignment)
|
||||
}
|
||||
}
|
||||
|
||||
fun freeze(value: LLVMValueRef, exceptionHandler: ExceptionHandler) {
|
||||
if (isObjectRef(value))
|
||||
@@ -618,14 +621,21 @@ internal abstract class FunctionGenerationContext(
|
||||
call(llvm.updateReturnRefFunction, listOf(address, value))
|
||||
}
|
||||
|
||||
private fun updateRef(value: LLVMValueRef, address: LLVMValueRef, onStack: Boolean) {
|
||||
private fun updateRef(value: LLVMValueRef, address: LLVMValueRef, onStack: Boolean,
|
||||
isVolatile: Boolean = false, alignment: Int? = null) {
|
||||
require(alignment == null || alignment == runtime.pointerAlignment)
|
||||
if (onStack) {
|
||||
require(!isVolatile) { "Stack ref update can't be volatile"}
|
||||
if (context.memoryModel == MemoryModel.STRICT)
|
||||
store(value, address)
|
||||
else
|
||||
call(llvm.updateStackRefFunction, listOf(address, value))
|
||||
} else {
|
||||
call(llvm.updateHeapRefFunction, listOf(address, value))
|
||||
if (isVolatile && context.memoryModel == MemoryModel.EXPERIMENTAL) {
|
||||
call(llvm.UpdateVolatileHeapRef, listOf(address, value))
|
||||
} else {
|
||||
call(llvm.updateHeapRefFunction, listOf(address, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1349,7 +1359,7 @@ internal abstract class FunctionGenerationContext(
|
||||
}
|
||||
addPhiIncoming(slotsPhi!!, prologueBb to slots)
|
||||
memScoped {
|
||||
slotToVariableLocation.forEach { slot, variable ->
|
||||
slotToVariableLocation.forEach { (slot, variable) ->
|
||||
val expr = longArrayOf(DwarfOp.DW_OP_plus_uconst.value,
|
||||
runtime.pointerSize * slot.toLong()).toCValues()
|
||||
DIInsertDeclaration(
|
||||
|
||||
+6
@@ -457,6 +457,12 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu
|
||||
val Kotlin_processFieldInMark by lazyRtFunction
|
||||
val Kotlin_processEmptyObjectInMark by lazyRtFunction
|
||||
|
||||
val UpdateVolatileHeapRef by lazyRtFunction
|
||||
val CompareAndSetVolatileHeapRef by lazyRtFunction
|
||||
val CompareAndSwapVolatileHeapRef by lazyRtFunction
|
||||
val GetAndSetVolatileHeapRef by lazyRtFunction
|
||||
|
||||
|
||||
val tlsMode by lazy {
|
||||
when (target) {
|
||||
KonanTarget.WASM32,
|
||||
|
||||
+81
-2
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.objc.genObjCSelector
|
||||
import org.jetbrains.kotlin.backend.konan.reportCompilationError
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
@@ -90,7 +91,16 @@ internal enum class IntrinsicType {
|
||||
INTEROP_FUNPTR_INVOKE,
|
||||
INTEROP_MEMORY_COPY,
|
||||
// Worker
|
||||
WORKER_EXECUTE
|
||||
WORKER_EXECUTE,
|
||||
// Atomics
|
||||
COMPARE_AND_SET_FIELD,
|
||||
COMPARE_AND_SWAP_FIELD,
|
||||
GET_AND_SET_FIELD,
|
||||
GET_AND_ADD_FIELD,
|
||||
COMPARE_AND_SET,
|
||||
COMPARE_AND_SWAP,
|
||||
GET_AND_SET,
|
||||
GET_AND_ADD,
|
||||
}
|
||||
|
||||
internal enum class ConstantConstructorIntrinsicType {
|
||||
@@ -117,6 +127,8 @@ internal interface IntrinsicGeneratorEnvironment {
|
||||
fun evaluateExplicitArgs(expression: IrFunctionAccessExpression): List<LLVMValueRef>
|
||||
|
||||
fun evaluateExpression(value: IrExpression, resultSlot: LLVMValueRef?): LLVMValueRef
|
||||
|
||||
fun getObjectFieldPointer(thisRef: LLVMValueRef, field: IrField): LLVMValueRef
|
||||
}
|
||||
|
||||
internal fun tryGetIntrinsicType(callSite: IrFunctionAccessExpression): IntrinsicType? =
|
||||
@@ -242,6 +254,10 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
|
||||
IntrinsicType.INTEROP_MEMORY_COPY -> emitMemoryCopy(callSite, args)
|
||||
IntrinsicType.IS_EXPERIMENTAL_MM -> emitIsExperimentalMM()
|
||||
IntrinsicType.THE_UNIT_INSTANCE -> theUnitInstanceRef.llvm
|
||||
IntrinsicType.COMPARE_AND_SET -> emitCompareAndSet(callSite, args)
|
||||
IntrinsicType.COMPARE_AND_SWAP -> emitCompareAndSwap(callSite, args, resultSlot)
|
||||
IntrinsicType.GET_AND_SET -> emitGetAndSet(callSite, args, resultSlot)
|
||||
IntrinsicType.GET_AND_ADD -> emitGetAndAdd(callSite, args)
|
||||
IntrinsicType.GET_CONTINUATION,
|
||||
IntrinsicType.RETURN_IF_SUSPENDED,
|
||||
IntrinsicType.INTEROP_BITS_TO_FLOAT,
|
||||
@@ -253,7 +269,11 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
|
||||
IntrinsicType.INTEROP_CONVERT,
|
||||
IntrinsicType.ENUM_VALUES,
|
||||
IntrinsicType.ENUM_VALUE_OF,
|
||||
IntrinsicType.WORKER_EXECUTE ->
|
||||
IntrinsicType.WORKER_EXECUTE,
|
||||
IntrinsicType.COMPARE_AND_SET_FIELD,
|
||||
IntrinsicType.COMPARE_AND_SWAP_FIELD,
|
||||
IntrinsicType.GET_AND_SET_FIELD,
|
||||
IntrinsicType.GET_AND_ADD_FIELD ->
|
||||
reportNonLoweredIntrinsic(intrinsicType)
|
||||
IntrinsicType.INIT_INSTANCE,
|
||||
IntrinsicType.OBJC_INIT_BY,
|
||||
@@ -292,6 +312,65 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
|
||||
private fun FunctionGenerationContext.emitIsExperimentalMM(): LLVMValueRef =
|
||||
llvm.int1(context.memoryModel == MemoryModel.EXPERIMENTAL)
|
||||
|
||||
// cmpxcgh llvm instruction return pair. idnex is index of required element of this pair
|
||||
enum class CmpExchangeMode(val index:Int) {
|
||||
SWAP(0),
|
||||
SET(1)
|
||||
}
|
||||
|
||||
private fun FunctionGenerationContext.emitCmpExchange(callSite: IrCall, args: List<LLVMValueRef>, mode: CmpExchangeMode, resultSlot: LLVMValueRef?): LLVMValueRef {
|
||||
val field = context.mapping.functionToVolatileField[callSite.symbol.owner]!!
|
||||
require(args.size == 3)
|
||||
val address = environment.getObjectFieldPointer(args[0], field)
|
||||
return if (isObjectRef(args[1])) {
|
||||
require(context.memoryModel == MemoryModel.EXPERIMENTAL)
|
||||
when (mode) {
|
||||
CmpExchangeMode.SET -> call(llvm.CompareAndSetVolatileHeapRef, listOf(address, args[1], args[2]))
|
||||
CmpExchangeMode.SWAP -> call(llvm.CompareAndSwapVolatileHeapRef, listOf(address, args[1], args[2]),
|
||||
environment.calculateLifetime(callSite), resultSlot = resultSlot)
|
||||
}
|
||||
} else {
|
||||
val cmp = LLVMBuildAtomicCmpXchg(builder, address, args[1], args[2],
|
||||
LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent,
|
||||
LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent,
|
||||
SingleThread = 0
|
||||
)!!
|
||||
|
||||
LLVMBuildExtractValue(builder, cmp, mode.index, "")!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionGenerationContext.emitAtomicRMW(callSite: IrCall, args: List<LLVMValueRef>, op: LLVMAtomicRMWBinOp, resultSlot: LLVMValueRef?): LLVMValueRef {
|
||||
val field = context.mapping.functionToVolatileField[callSite.symbol.owner]!!
|
||||
require(args.size == 2)
|
||||
val address = environment.getObjectFieldPointer(args[0], field)
|
||||
return if (isObjectRef(args[1])) {
|
||||
require(op == LLVMAtomicRMWBinOp.LLVMAtomicRMWBinOpXchg)
|
||||
require(context.memoryModel == MemoryModel.EXPERIMENTAL)
|
||||
call(llvm.GetAndSetVolatileHeapRef, listOf(address, args[1]),
|
||||
environment.calculateLifetime(callSite), resultSlot = resultSlot)
|
||||
} else {
|
||||
LLVMBuildAtomicRMW(builder, op, address, args[1],
|
||||
LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent,
|
||||
singleThread = 0
|
||||
)!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionGenerationContext.emitCompareAndSet(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
return emitCmpExchange(callSite, args, CmpExchangeMode.SET, null)
|
||||
}
|
||||
private fun FunctionGenerationContext.emitCompareAndSwap(callSite: IrCall, args: List<LLVMValueRef>, resultSlot: LLVMValueRef?): LLVMValueRef {
|
||||
return emitCmpExchange(callSite, args, CmpExchangeMode.SWAP, resultSlot)
|
||||
}
|
||||
private fun FunctionGenerationContext.emitGetAndSet(callSite: IrCall, args: List<LLVMValueRef>, resultSlot: LLVMValueRef?): LLVMValueRef {
|
||||
return emitAtomicRMW(callSite, args, LLVMAtomicRMWBinOp.LLVMAtomicRMWBinOpXchg, resultSlot)
|
||||
}
|
||||
private fun FunctionGenerationContext.emitGetAndAdd(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
return emitAtomicRMW(callSite, args, LLVMAtomicRMWBinOp.LLVMAtomicRMWBinOpAdd, null)
|
||||
}
|
||||
|
||||
|
||||
private fun FunctionGenerationContext.emitGetNativeNullPtr(): LLVMValueRef =
|
||||
llvm.kNullInt8Ptr
|
||||
|
||||
|
||||
+49
-30
@@ -228,6 +228,9 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
|
||||
|
||||
override fun evaluateExpression(value: IrExpression, resultSlot: LLVMValueRef?): LLVMValueRef =
|
||||
this@CodeGeneratorVisitor.evaluateExpression(value, resultSlot)
|
||||
|
||||
override fun getObjectFieldPointer(thisRef: LLVMValueRef, field: IrField): LLVMValueRef =
|
||||
this@CodeGeneratorVisitor.fieldPtrOfClass(thisRef, field)
|
||||
}
|
||||
|
||||
private val intrinsicGenerator = IntrinsicGenerator(intrinsicGeneratorEnvironment)
|
||||
@@ -1660,35 +1663,46 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
|
||||
|
||||
private fun evaluateGetField(value: IrGetField, resultSlot: LLVMValueRef?): LLVMValueRef {
|
||||
context.log { "evaluateGetField : ${ir2string(value)}" }
|
||||
return if (!value.symbol.owner.isStatic) {
|
||||
val thisPtr = evaluateExpression(value.receiver!!)
|
||||
functionGenerationContext.loadSlot(
|
||||
fieldPtrOfClass(thisPtr, value.symbol.owner), !value.symbol.owner.isFinal, resultSlot)
|
||||
} else {
|
||||
assert(value.receiver == null)
|
||||
if (value.symbol.owner.correspondingPropertySymbol?.owner?.isConst == true) {
|
||||
evaluateConst(value.symbol.owner.initializer?.expression as IrConst<*>).llvm
|
||||
} else {
|
||||
val alignment = when {
|
||||
value.type.classifierOrNull?.isClassWithFqName(vectorType) == true -> 8
|
||||
else -> null
|
||||
}
|
||||
val order = when {
|
||||
value.symbol.owner.hasAnnotation(KonanFqNames.volatile) ->
|
||||
LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent
|
||||
else -> null
|
||||
}
|
||||
|
||||
val fieldAddress = when {
|
||||
!value.symbol.owner.isStatic -> {
|
||||
fieldPtrOfClass(evaluateExpression(value.receiver!!), value.symbol.owner)
|
||||
}
|
||||
value.symbol.owner.correspondingPropertySymbol?.owner?.isConst == true -> {
|
||||
// TODO: probably can be removed, as they are inlined.
|
||||
return evaluateConst(value.symbol.owner.initializer?.expression as IrConst<*>).llvm
|
||||
}
|
||||
else -> {
|
||||
if (context.config.threadsAreAllowed && value.symbol.owner.isGlobalNonPrimitive(context)) {
|
||||
functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler)
|
||||
}
|
||||
val ptr = generationState.llvmDeclarations.forStaticField(value.symbol.owner).storageAddressAccess.getAddress(
|
||||
functionGenerationContext
|
||||
)
|
||||
functionGenerationContext.loadSlot(ptr, !value.symbol.owner.isFinal, resultSlot)
|
||||
generationState.llvmDeclarations
|
||||
.forStaticField(value.symbol.owner)
|
||||
.storageAddressAccess
|
||||
.getAddress(functionGenerationContext)
|
||||
}
|
||||
}.also {
|
||||
if (value.type.classifierOrNull?.isClassWithFqName(vectorType) == true)
|
||||
LLVMSetAlignment(it, 8)
|
||||
|
||||
}
|
||||
return functionGenerationContext.loadSlot(
|
||||
fieldAddress, !value.symbol.owner.isFinal, resultSlot,
|
||||
memoryOrder = order,
|
||||
alignment = alignment
|
||||
)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
private fun needMutationCheck(irClass: IrClass): Boolean {
|
||||
private fun needMutationCheck(irField: IrField): Boolean {
|
||||
// For now we omit mutation checks on immutable types, as this allows initialization in constructor
|
||||
// and it is assumed that API doesn't allow to change them.
|
||||
return context.config.freezing.enableFreezeChecks && !irClass.isFrozen(context)
|
||||
return context.config.freezing.enableFreezeChecks && !irField.parentAsClass.isFrozen(context) && !irField.hasAnnotation(KonanFqNames.volatile)
|
||||
}
|
||||
|
||||
private fun needLifetimeConstraintsCheck(valueToAssign: LLVMValueRef, irClass: IrClass): Boolean {
|
||||
@@ -1724,13 +1738,14 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
|
||||
}
|
||||
|
||||
val valueToAssign = evaluateExpression(value.value)
|
||||
val store = if (!value.symbol.owner.isStatic) {
|
||||
val address: LLVMValueRef
|
||||
if (!value.symbol.owner.isStatic) {
|
||||
val thisPtr = evaluateExpression(value.receiver!!)
|
||||
assert(thisPtr.type == codegen.kObjHeaderPtr) {
|
||||
LLVMPrintTypeToString(thisPtr.type)?.toKString().toString()
|
||||
}
|
||||
val parentAsClass = value.symbol.owner.parentAsClass
|
||||
if (needMutationCheck(parentAsClass)) {
|
||||
if (needMutationCheck(value.symbol.owner)) {
|
||||
functionGenerationContext.call(llvm.mutationCheck,
|
||||
listOf(functionGenerationContext.bitcast(codegen.kObjHeaderPtr, thisPtr)),
|
||||
Lifetime.IRRELEVANT, currentCodeContext.exceptionHandler)
|
||||
@@ -1738,21 +1753,26 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
|
||||
if (needLifetimeConstraintsCheck(valueToAssign, parentAsClass)) {
|
||||
functionGenerationContext.call(llvm.checkLifetimesConstraint, listOf(thisPtr, valueToAssign))
|
||||
}
|
||||
functionGenerationContext.storeAny(valueToAssign, fieldPtrOfClass(thisPtr, value.symbol.owner), false)
|
||||
address = fieldPtrOfClass(thisPtr, value.symbol.owner)
|
||||
} else {
|
||||
assert(value.receiver == null)
|
||||
val globalAddress = generationState.llvmDeclarations.forStaticField(value.symbol.owner).storageAddressAccess.getAddress(
|
||||
functionGenerationContext
|
||||
)
|
||||
if (context.config.threadsAreAllowed && value.symbol.owner.storageKind(context) == FieldStorageKind.GLOBAL)
|
||||
functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler)
|
||||
if (value.symbol.owner.shouldBeFrozen(context) && value.origin != ObjectClassLowering.IrStatementOriginFieldPreInit)
|
||||
functionGenerationContext.freeze(valueToAssign, currentCodeContext.exceptionHandler)
|
||||
functionGenerationContext.storeAny(valueToAssign, globalAddress, false)
|
||||
address = generationState.llvmDeclarations.forStaticField(value.symbol.owner).storageAddressAccess.getAddress(
|
||||
functionGenerationContext
|
||||
)
|
||||
}
|
||||
if (store != null && value.value.type.classifierOrNull?.isClassWithFqName(vectorType) == true) {
|
||||
LLVMSetAlignment(store, 8)
|
||||
val alignment = when {
|
||||
value.value.type.classifierOrNull?.isClassWithFqName(vectorType) == true -> 8
|
||||
else -> null
|
||||
}
|
||||
functionGenerationContext.storeAny(
|
||||
valueToAssign, address, false,
|
||||
isVolatile = value.symbol.owner.hasAnnotation(KonanFqNames.volatile),
|
||||
alignment = alignment,
|
||||
)
|
||||
|
||||
assert (value.type.isUnit())
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
@@ -2379,8 +2399,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
|
||||
val bbExit = basicBlock("label_continue", null)
|
||||
moveBlockAfterEntry(bbExit)
|
||||
moveBlockAfterEntry(bbInit)
|
||||
val state = load(statePtr)
|
||||
LLVMSetOrdering(state, LLVMAtomicOrdering.LLVMAtomicOrderingAcquire)
|
||||
val state = load(statePtr, memoryOrder = LLVMAtomicOrdering.LLVMAtomicOrderingAcquire)
|
||||
condBr(icmpEq(state, llvm.int32(FILE_INITIALIZED)), bbExit, bbInit)
|
||||
positionAtEnd(bbInit)
|
||||
call(llvm.callInitGlobalPossiblyLock, listOf(statePtr, initializerPtr),
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.ir.addDispatchReceiver
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.NativeMapping
|
||||
import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.irString
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.*
|
||||
|
||||
object IR_DECLARATION_ORIGIN_VOLATILE : IrDeclarationOriginImpl("VOLATILE")
|
||||
|
||||
internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass {
|
||||
private fun buildIntrinsicFunction(irField: IrField, intrinsicType: IntrinsicType, builder: IrSimpleFunction.() -> Unit) = context.irFactory.buildFun {
|
||||
isExternal = true
|
||||
origin = IR_DECLARATION_ORIGIN_VOLATILE
|
||||
name = Name.special("<${intrinsicType.name.decapitalizeSmart()}-${irField.name}>")
|
||||
startOffset = irField.startOffset
|
||||
endOffset = irField.endOffset
|
||||
}.apply {
|
||||
val parentClass = irField.parents.filterIsInstance<IrClass>().first()
|
||||
parent = parentClass
|
||||
addDispatchReceiver {
|
||||
startOffset = irField.startOffset
|
||||
endOffset = irField.endOffset
|
||||
type = parentClass.defaultType
|
||||
}
|
||||
builder()
|
||||
annotations += buildSimpleAnnotation(context.irBuiltIns,
|
||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
|
||||
context.ir.symbols.typedIntrinsic.owner, intrinsicType.name)
|
||||
}
|
||||
|
||||
private fun buildCasFunction(irField: IrField, intrinsicType: IntrinsicType, functionReturnType: IrType) =
|
||||
buildIntrinsicFunction(irField, intrinsicType ) {
|
||||
returnType = functionReturnType
|
||||
addValueParameter {
|
||||
startOffset = irField.startOffset
|
||||
endOffset = irField.endOffset
|
||||
name = Name.identifier("expectedValue")
|
||||
type = irField.type
|
||||
}
|
||||
addValueParameter {
|
||||
startOffset = irField.startOffset
|
||||
endOffset = irField.endOffset
|
||||
name = Name.identifier("newValue")
|
||||
type = irField.type
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildAtomicRWMFunction(irField: IrField, intrinsicType: IntrinsicType) =
|
||||
buildIntrinsicFunction(irField, intrinsicType) {
|
||||
returnType = irField.type
|
||||
addValueParameter {
|
||||
startOffset = irField.startOffset
|
||||
endOffset = irField.endOffset
|
||||
name = Name.identifier("value")
|
||||
type = irField.type
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private inline fun atomicFunction(irField: IrField, type: NativeMapping.AtomicFunctionType, builder: () -> IrSimpleFunction): IrSimpleFunction {
|
||||
val key = NativeMapping.AtomicFunctionKey(irField, type)
|
||||
return context.mapping.volatileFieldToAtomicFunction.getOrPut(key) {
|
||||
builder().also {
|
||||
context.mapping.functionToVolatileField[it] = irField
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareAndSetFunction(irField: IrField) = atomicFunction(irField, NativeMapping.AtomicFunctionType.COMPARE_AND_SET) {
|
||||
this.buildCasFunction(irField, IntrinsicType.COMPARE_AND_SET, this.context.irBuiltIns.booleanType)
|
||||
}
|
||||
private fun compareAndSwapFunction(irField: IrField) = atomicFunction(irField, NativeMapping.AtomicFunctionType.COMPARE_AND_SWAP) {
|
||||
this.buildCasFunction(irField, IntrinsicType.COMPARE_AND_SWAP, irField.type)
|
||||
}
|
||||
private fun getAndSetFunction(irField: IrField) = atomicFunction(irField, NativeMapping.AtomicFunctionType.GET_AND_SET) {
|
||||
this.buildAtomicRWMFunction(irField, IntrinsicType.GET_AND_SET)
|
||||
}
|
||||
private fun getAndAddFunction(irField: IrField) = atomicFunction(irField, NativeMapping.AtomicFunctionType.GET_AND_ADD) {
|
||||
this.buildAtomicRWMFunction(irField, IntrinsicType.GET_AND_ADD)
|
||||
}
|
||||
|
||||
private fun IrField.isInteger() = type == context.irBuiltIns.intType ||
|
||||
type == context.irBuiltIns.longType ||
|
||||
type == context.irBuiltIns.shortType ||
|
||||
type == context.irBuiltIns.byteType
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrBuildingTransformer(context) {
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
declaration.declarations.transformFlat {
|
||||
when {
|
||||
it !is IrProperty -> null
|
||||
it.backingField?.hasAnnotation(KonanFqNames.volatile) != true -> null
|
||||
else -> {
|
||||
val field = it.backingField!!
|
||||
if (field.type.binaryTypeIsReference() && context.memoryModel != MemoryModel.EXPERIMENTAL) {
|
||||
it.annotations = it.annotations.filterNot { it.symbol.owner.parentAsClass.hasEqualFqName(KonanFqNames.volatile) }
|
||||
null
|
||||
} else {
|
||||
listOfNotNull(it,
|
||||
compareAndSetFunction(field),
|
||||
compareAndSwapFunction(field),
|
||||
getAndSetFunction(field),
|
||||
if (field.isInteger()) getAndAddFunction(field) else null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
declaration.transformChildrenVoid()
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun unsupported(message: String) = builder.irCall(context.ir.symbols.throwIllegalArgumentExceptionWithMessage).apply {
|
||||
putValueArgument(0, builder.irString(message))
|
||||
}
|
||||
|
||||
private val intrinsicMap = mapOf(
|
||||
IntrinsicType.COMPARE_AND_SET_FIELD to ::compareAndSetFunction,
|
||||
IntrinsicType.COMPARE_AND_SWAP_FIELD to ::compareAndSwapFunction,
|
||||
IntrinsicType.GET_AND_SET_FIELD to ::getAndSetFunction,
|
||||
IntrinsicType.GET_AND_ADD_FIELD to ::getAndAddFunction,
|
||||
)
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
val intrinsicType = tryGetIntrinsicType(expression).takeIf { it in intrinsicMap } ?: return expression
|
||||
builder.at(expression)
|
||||
val reference = expression.getValueArgument(0) as? IrPropertyReference
|
||||
?: return unsupported("Only compile-time known IrProperties supported for $intrinsicType")
|
||||
val property = reference.symbol.owner
|
||||
val backingField = property.backingField
|
||||
if (backingField?.type?.binaryTypeIsReference() == true && context.memoryModel != MemoryModel.EXPERIMENTAL) {
|
||||
return unsupported("Only primitives are supported for $intrinsicType with legacy memory model")
|
||||
}
|
||||
if (backingField?.hasAnnotation(KonanFqNames.volatile) != true) {
|
||||
return unsupported("Only volatile properties are supported for $intrinsicType")
|
||||
}
|
||||
val function = intrinsicMap[intrinsicType]!!(backingField)
|
||||
return builder.irCall(function).apply {
|
||||
dispatchReceiver = expression.extensionReceiver
|
||||
putValueArgument(0, expression.getValueArgument(1))
|
||||
if (intrinsicType == IntrinsicType.COMPARE_AND_SET_FIELD || intrinsicType == IntrinsicType.COMPARE_AND_SWAP_FIELD) {
|
||||
putValueArgument(1, expression.getValueArgument(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+54
-31
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
|
||||
internal class ExternalModulesDFG(val allTypes: List<DataFlowIR.Type.Declared>,
|
||||
val publicTypes: Map<Long, DataFlowIR.Type.Public>,
|
||||
@@ -258,7 +259,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
|
||||
context.logMultiple {
|
||||
+"SYMBOL TABLE:"
|
||||
symbolTable.classMap.forEach { irClass, type ->
|
||||
symbolTable.classMap.forEach { (irClass, type) ->
|
||||
+" DESCRIPTOR: ${irClass.descriptor}"
|
||||
+" TYPE: $type"
|
||||
if (type !is DataFlowIR.Type.Declared)
|
||||
@@ -309,39 +310,61 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
expressions += expression to currentLoop
|
||||
}
|
||||
|
||||
if (expression is IrCall && expression.symbol == initInstanceSymbol) {
|
||||
// Skip the constructor call as initInstance is handled specially later.
|
||||
val thiz = expression.getValueArgument(0)!!
|
||||
val constructorCall = expression.getValueArgument(1)!!
|
||||
thiz.acceptVoid(this)
|
||||
constructorCall.acceptChildrenVoid(this)
|
||||
return
|
||||
if (expression is IrCall) {
|
||||
if (expression.symbol == initInstanceSymbol) {
|
||||
// Skip the constructor call as initInstance is handled specially later.
|
||||
val thiz = expression.getValueArgument(0)!!
|
||||
val constructorCall = expression.getValueArgument(1)!!
|
||||
thiz.acceptVoid(this)
|
||||
constructorCall.acceptChildrenVoid(this)
|
||||
return
|
||||
}
|
||||
if (expression.symbol == executeImplSymbol) {
|
||||
// Producer and job of executeImpl are called externally, we need to reflect this somehow.
|
||||
val producerInvocation = IrCallImpl.fromSymbolDescriptor(expression.startOffset, expression.endOffset,
|
||||
executeImplProducerInvoke.returnType,
|
||||
executeImplProducerInvoke.symbol,
|
||||
executeImplProducerInvoke.symbol.owner.typeParameters.size,
|
||||
executeImplProducerInvoke.symbol.owner.valueParameters.size,
|
||||
STATEMENT_ORIGIN_PRODUCER_INVOCATION)
|
||||
producerInvocation.dispatchReceiver = expression.getValueArgument(2)
|
||||
|
||||
expressions += producerInvocation to currentLoop
|
||||
|
||||
val jobFunctionReference = expression.getValueArgument(3) as? IrFunctionReference
|
||||
?: error("A function reference expected")
|
||||
val jobInvocation = IrCallImpl.fromSymbolDescriptor(expression.startOffset, expression.endOffset,
|
||||
jobFunctionReference.symbol.owner.returnType,
|
||||
jobFunctionReference.symbol as IrSimpleFunctionSymbol,
|
||||
jobFunctionReference.symbol.owner.typeParameters.size,
|
||||
jobFunctionReference.symbol.owner.valueParameters.size,
|
||||
STATEMENT_ORIGIN_JOB_INVOCATION)
|
||||
jobInvocation.putValueArgument(0, producerInvocation)
|
||||
|
||||
expressions += jobInvocation to currentLoop
|
||||
}
|
||||
val intrinsicType = tryGetIntrinsicType(expression)
|
||||
if (intrinsicType == IntrinsicType.COMPARE_AND_SET || intrinsicType == IntrinsicType.COMPARE_AND_SWAP) {
|
||||
expressions += IrSetFieldImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
context.mapping.functionToVolatileField[expression.symbol.owner]!!.symbol,
|
||||
expression.dispatchReceiver,
|
||||
expression.getValueArgument(1)!!,
|
||||
context.irBuiltIns.unitType
|
||||
) to currentLoop
|
||||
}
|
||||
if (intrinsicType == IntrinsicType.GET_AND_SET) {
|
||||
expressions += IrSetFieldImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
context.mapping.functionToVolatileField[expression.symbol.owner]!!.symbol,
|
||||
expression.dispatchReceiver,
|
||||
expression.getValueArgument(0)!!,
|
||||
context.irBuiltIns.unitType
|
||||
) to currentLoop
|
||||
}
|
||||
}
|
||||
|
||||
if (expression is IrCall && expression.symbol == executeImplSymbol) {
|
||||
// Producer and job of executeImpl are called externally, we need to reflect this somehow.
|
||||
val producerInvocation = IrCallImpl.fromSymbolDescriptor(expression.startOffset, expression.endOffset,
|
||||
executeImplProducerInvoke.returnType,
|
||||
executeImplProducerInvoke.symbol,
|
||||
executeImplProducerInvoke.symbol.owner.typeParameters.size,
|
||||
executeImplProducerInvoke.symbol.owner.valueParameters.size,
|
||||
STATEMENT_ORIGIN_PRODUCER_INVOCATION)
|
||||
producerInvocation.dispatchReceiver = expression.getValueArgument(2)
|
||||
|
||||
expressions += producerInvocation to currentLoop
|
||||
|
||||
val jobFunctionReference = expression.getValueArgument(3) as? IrFunctionReference
|
||||
?: error("A function reference expected")
|
||||
val jobInvocation = IrCallImpl.fromSymbolDescriptor(expression.startOffset, expression.endOffset,
|
||||
jobFunctionReference.symbol.owner.returnType,
|
||||
jobFunctionReference.symbol as IrSimpleFunctionSymbol,
|
||||
jobFunctionReference.symbol.owner.typeParameters.size,
|
||||
jobFunctionReference.symbol.owner.valueParameters.size,
|
||||
STATEMENT_ORIGIN_JOB_INVOCATION)
|
||||
jobInvocation.putValueArgument(0, producerInvocation)
|
||||
|
||||
expressions += jobInvocation to currentLoop
|
||||
}
|
||||
|
||||
// TODO: A little bit hacky but it is the simplest solution.
|
||||
// See ObjC instanceOf code generation for details.
|
||||
|
||||
Reference in New Issue
Block a user