diff --git a/compiler/testData/codegen/box/volatile/crossModuleIntrinsic.kt b/compiler/testData/codegen/box/volatile/crossModuleIntrinsic.kt index b7bb13b3af6..3ea7b1f0055 100644 --- a/compiler/testData/codegen/box/volatile/crossModuleIntrinsic.kt +++ b/compiler/testData/codegen/box/volatile/crossModuleIntrinsic.kt @@ -22,5 +22,5 @@ import kotlin.concurrent.* fun box() : String { val o = "O" val x = Box(o) - return x.compareAndSwapField(Box::value, o, "K") + x.value + return x::value.compareAndSwapField(o, "K") + x.value } \ No newline at end of file diff --git a/compiler/testData/codegen/box/volatile/intrinsics.kt b/compiler/testData/codegen/box/volatile/intrinsics.kt index 702e545cfee..04e9a7a1fc1 100644 --- a/compiler/testData/codegen/box/volatile/intrinsics.kt +++ b/compiler/testData/codegen/box/volatile/intrinsics.kt @@ -23,44 +23,44 @@ interface RefWrapper : Wrapper { class IntWrapper(@Volatile var x : Int) : IncWrapper { - override fun compareAndSwap(expected: Int, new: Int) = compareAndSwapField(IntWrapper::x, expected, new) - override fun compareAndSet(expected: Int, new: Int) = compareAndSetField(IntWrapper::x, expected, new) - override fun getAndSet(new: Int) = getAndSetField(IntWrapper::x, new) - override fun getAndAdd(delta: Int) = getAndAddField(IntWrapper::x, delta) + override fun compareAndSwap(expected: Int, new: Int) = this::x.compareAndSwapField(expected, new) + override fun compareAndSet(expected: Int, new: Int) = this::x.compareAndSetField(expected, new) + override fun getAndSet(new: Int) = this::x.getAndSetField(new) + override fun getAndAdd(delta: Int) = this::x.getAndAddField(delta) } class LongWrapper(@Volatile var x : Long) : IncWrapper { - override fun compareAndSwap(expected: Long, new: Long) = compareAndSwapField(LongWrapper::x, expected, new) - override fun compareAndSet(expected: Long, new: Long) = compareAndSetField(LongWrapper::x, expected, new) - override fun getAndSet(new: Long) = getAndSetField(LongWrapper::x, new) - override fun getAndAdd(delta: Long) = getAndAddField(LongWrapper::x, delta) + override fun compareAndSwap(expected: Long, new: Long) = this::x.compareAndSwapField(expected, new) + override fun compareAndSet(expected: Long, new: Long) = this::x.compareAndSetField(expected, new) + override fun getAndSet(new: Long) = this::x.getAndSetField(new) + override fun getAndAdd(delta: Long) = this::x.getAndAddField(delta) } class ShortWrapper(@Volatile var x : Short) : IncWrapper { - override fun compareAndSwap(expected: Short, new: Short) = compareAndSwapField(ShortWrapper::x, expected, new) - override fun compareAndSet(expected: Short, new: Short) = compareAndSetField(ShortWrapper::x, expected, new) - override fun getAndSet(new: Short) = getAndSetField(ShortWrapper::x, new) - override fun getAndAdd(delta: Short) = getAndAddField(ShortWrapper::x, delta) + override fun compareAndSwap(expected: Short, new: Short) = this::x.compareAndSwapField(expected, new) + override fun compareAndSet(expected: Short, new: Short) = this::x.compareAndSetField(expected, new) + override fun getAndSet(new: Short) = this::x.getAndSetField(new) + override fun getAndAdd(delta: Short) = this::x.getAndAddField(delta) } class ByteWrapper(@Volatile var x : Byte) : IncWrapper { - override fun compareAndSwap(expected: Byte, new: Byte) = compareAndSwapField(ByteWrapper::x, expected, new) - override fun compareAndSet(expected: Byte, new: Byte) = compareAndSetField(ByteWrapper::x, expected, new) - override fun getAndSet(new: Byte) = getAndSetField(ByteWrapper::x, new) - override fun getAndAdd(delta: Byte) = getAndAddField(ByteWrapper::x, delta) + override fun compareAndSwap(expected: Byte, new: Byte) = this::x.compareAndSwapField(expected, new) + override fun compareAndSet(expected: Byte, new: Byte) = this::x.compareAndSetField(expected, new) + override fun getAndSet(new: Byte) = this::x.getAndSetField(new) + override fun getAndAdd(delta: Byte) = this::x.getAndAddField(delta) } class StringWrapper(@Volatile var x : String) : RefWrapper { - override fun compareAndSwap(expected: String, new: String) = compareAndSwapField(StringWrapper::x, expected, new) - override fun compareAndSet(expected: String, new: String) = compareAndSetField(StringWrapper::x, expected, new) - override fun getAndSet(new: String) = getAndSetField(StringWrapper::x, new) + override fun compareAndSwap(expected: String, new: String) = this::x.compareAndSwapField(expected, new) + override fun compareAndSet(expected: String, new: String) = this::x.compareAndSetField(expected, new) + override fun getAndSet(new: String) = this::x.getAndSetField(new) } class GenericWrapper(@Volatile var x : T) : RefWrapper { - override fun compareAndSwap(expected: T, new: T) = compareAndSwapField(GenericWrapper::x, expected, new) - override fun compareAndSet(expected: T, new: T) = compareAndSetField(GenericWrapper::x, expected, new) - override fun getAndSet(new: T) = getAndSetField(GenericWrapper::x, new) + override fun compareAndSwap(expected: T, new: T) = this::x.compareAndSwapField(expected, new) + override fun compareAndSet(expected: T, new: T) = this::x.compareAndSetField(expected, new) + override fun getAndSet(new: T) = this::x.getAndSetField(new) } diff --git a/compiler/testData/codegen/box/volatile/intrinsicsOnGlobal.kt b/compiler/testData/codegen/box/volatile/intrinsicsOnGlobal.kt new file mode 100644 index 00000000000..a724f64d291 --- /dev/null +++ b/compiler/testData/codegen/box/volatile/intrinsicsOnGlobal.kt @@ -0,0 +1,62 @@ +// TARGET_BACKEND: NATIVE +// KT-55904 +// IGNORE_BACKEND_K2: NATIVE + + +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@file:OptIn(kotlin.ExperimentalStdlibApi::class) + +import kotlin.native.concurrent.* +import kotlin.concurrent.* + +val a = "1" +val b = "2" +val c = "3" + +@Volatile var x: Int = 1 +@Volatile var y: Long = 1L +@Volatile var z: String = a +@Volatile var t: Boolean = true + +fun box() : String { + if (::x.compareAndSetField(1, 2) != true) return "FAIL Int: 1" + if (::x.compareAndSetField(1, 2) != false) return "FAIL Int: 2" + if (::x.compareAndSwapField(2, 1) != 2) return "FAIL Int: 3" + if (::x.compareAndSwapField(2, 1) != 1) return "FAIL Int: 4" + if (::x.getAndSetField(3) != 1) return "FAIL Int: 5" + if (::x.getAndSetField(1) != 3) return "FAIL Int: 6" + if (::x.getAndAddField(1) != 1) return "FAIL Int: 7" + if (::x.getAndAddField(1) != 2) return "FAIL Int: 8" + if (x != 3) return "FAIL Int: 9" + + if (::y.compareAndSetField(1L, 2L) != true) return "FAIL Long: 1" + if (::y.compareAndSetField(1L, 2L) != false) return "FAIL Long: 2" + if (::y.compareAndSwapField(2L, 1L) != 2L) return "FAIL Long: 3" + if (::y.compareAndSwapField(2L, 1L) != 1L) return "FAIL Long: 4" + if (::y.getAndSetField(3L) != 1L) return "FAIL Long: 5" + if (::y.getAndSetField(1L) != 3L) return "FAIL Long: 6" + if (::y.getAndAddField(1L) != 1L) return "FAIL Long: 7" + if (::y.getAndAddField(1L) != 2L) return "FAIL Long: 8" + if (y != 3L) return "FAIL Long: 9" + + + if (isExperimentalMM()) { + if (::z.compareAndSetField(a, b) != true) return "FAIL String: 1" + if (::z.compareAndSetField(a, b) != false) return "FAIL String: 2" + if (::z.compareAndSwapField(b, a) != b) return "FAIL String: 3" + if (::z.compareAndSwapField(b, a) != a) return "FAIL String: 4" + if (::z.getAndSetField(c) != a) return "FAIL String: 5" + if (::z.getAndSetField(a) != c) return "FAIL String: 6" + if (z != a) return "FAIL String: 7" + } + + if (::t.compareAndSetField(true, false) != true) return "FAIL Bool: 1" + if (::t.compareAndSetField(true, false) != false) return "FAIL Bool: 2" + if (::t.compareAndSwapField(false, true) != false) return "FAIL Bool: 3" + if (::t.compareAndSwapField(false, true) != true) return "FAIL Bool: 4" + if (::t.getAndSetField(false) != true) return "FAIL Bool: 5" + if (::t.getAndSetField(true) != false) return "FAIL Bool: 6" + if (t != true) return "FAIL Bool: 7" + + return "OK" +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt index b8864dc2981..ddc325ec11e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt @@ -129,6 +129,8 @@ internal interface IntrinsicGeneratorEnvironment { fun evaluateExpression(value: IrExpression, resultSlot: LLVMValueRef?): LLVMValueRef fun getObjectFieldPointer(thisRef: LLVMValueRef, field: IrField): LLVMValueRef + + fun getStaticFieldPointer(field: IrField): LLVMValueRef } internal fun tryGetIntrinsicType(callSite: IrFunctionAccessExpression): IntrinsicType? = @@ -320,17 +322,31 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv private fun FunctionGenerationContext.emitCmpExchange(callSite: IrCall, args: List, mode: CmpExchangeMode, resultSlot: LLVMValueRef?): LLVMValueRef { val field = context.mapping.functionToVolatileField[callSite.symbol.owner]!! - require(args.size == 3) - val address = environment.getObjectFieldPointer(args[0], field) + val address: LLVMValueRef + val expected: LLVMValueRef + val new: LLVMValueRef + if (callSite.dispatchReceiver != null) { + require(!field.isStatic) + require(args.size == 3) + address = environment.getObjectFieldPointer(args[0], field) + expected = args[1] + new = args[2] + } else { + require(field.isStatic) + require(args.size == 2) + address = environment.getStaticFieldPointer(field) + expected = args[0] + new = args[1] + } 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]), + CmpExchangeMode.SET -> call(llvm.CompareAndSetVolatileHeapRef, listOf(address, expected, new)) + CmpExchangeMode.SWAP -> call(llvm.CompareAndSwapVolatileHeapRef, listOf(address, expected, new), environment.calculateLifetime(callSite), resultSlot = resultSlot) } } else { - val cmp = LLVMBuildAtomicCmpXchg(builder, address, args[1], args[2], + val cmp = LLVMBuildAtomicCmpXchg(builder, address, expected, new, LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent, LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent, SingleThread = 0 @@ -342,15 +358,26 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv private fun FunctionGenerationContext.emitAtomicRMW(callSite: IrCall, args: List, 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])) { + val address: LLVMValueRef + val value: LLVMValueRef + if (callSite.dispatchReceiver != null) { + require(!field.isStatic) + require(args.size == 2) + address = environment.getObjectFieldPointer(args[0], field) + value = args[1] + } else { + require(field.isStatic) + require(args.size == 1) + address = environment.getStaticFieldPointer(field) + value = args[0] + } + return if (isObjectRef(value)) { require(op == LLVMAtomicRMWBinOp.LLVMAtomicRMWBinOpXchg) require(context.memoryModel == MemoryModel.EXPERIMENTAL) - call(llvm.GetAndSetVolatileHeapRef, listOf(address, args[1]), + call(llvm.GetAndSetVolatileHeapRef, listOf(address, value), environment.calculateLifetime(callSite), resultSlot = resultSlot) } else { - LLVMBuildAtomicRMW(builder, op, address, args[1], + LLVMBuildAtomicRMW(builder, op, address, value, LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent, singleThread = 0 )!! 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 d47e67a0031..b35bca4f1b5 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 @@ -231,6 +231,9 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, override fun getObjectFieldPointer(thisRef: LLVMValueRef, field: IrField): LLVMValueRef = this@CodeGeneratorVisitor.fieldPtrOfClass(thisRef, field) + + override fun getStaticFieldPointer(field: IrField) = + this@CodeGeneratorVisitor.staticFieldPtr(field, functionGenerationContext) } private val intrinsicGenerator = IntrinsicGenerator(intrinsicGeneratorEnvironment) @@ -339,12 +342,12 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, private fun FunctionGenerationContext.initThreadLocalField(irField: IrField) { val initializer = irField.initializer ?: return - val address = generationState.llvmDeclarations.forStaticField(irField).storageAddressAccess.getAddress(this) + val address = staticFieldPtr(irField, this) storeAny(evaluateExpression(initializer.expression), address, false) } private fun FunctionGenerationContext.initGlobalField(irField: IrField) { - val address = generationState.llvmDeclarations.forStaticField(irField).storageAddressAccess.getAddress(this) + val address = staticFieldPtr(irField, this) val initialValue = if (irField.hasNonConstInitializer) { val initialization = evaluateExpression(irField.initializer!!.expression) if (irField.shouldBeFrozen(context)) @@ -514,9 +517,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, // Only if a subject for memory management. .forEach { irField -> if (irField.type.binaryTypeIsReference() && irField.storageKind(context) != FieldStorageKind.THREAD_LOCAL) { - val address = generationState.llvmDeclarations.forStaticField(irField).storageAddressAccess.getAddress( - functionGenerationContext - ) + val address = staticFieldPtr(irField, functionGenerationContext) storeHeapRef(codegen.kNullObjHeaderPtr, address) } } @@ -1685,12 +1686,8 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, if (context.config.threadsAreAllowed && value.symbol.owner.isGlobalNonPrimitive(context)) { functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler) } - val info = generationState.llvmDeclarations - .forStaticField(value.symbol.owner) - fieldAddress = info - .storageAddressAccess - .getAddress(functionGenerationContext) - alignment = info.alignment + fieldAddress = staticFieldPtr(value.symbol.owner, functionGenerationContext) + alignment = generationState.llvmDeclarations.forStaticField(value.symbol.owner).alignment } } return functionGenerationContext.loadSlot( @@ -1764,9 +1761,8 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler) if (value.symbol.owner.shouldBeFrozen(context) && value.origin != ObjectClassLowering.IrStatementOriginFieldPreInit) functionGenerationContext.freeze(valueToAssign, currentCodeContext.exceptionHandler) - val info = generationState.llvmDeclarations.forStaticField(value.symbol.owner) - address = info.storageAddressAccess.getAddress(functionGenerationContext) - alignment = info.alignment + address = staticFieldPtr(value.symbol.owner, functionGenerationContext) + alignment = generationState.llvmDeclarations.forStaticField(value.symbol.owner).alignment } functionGenerationContext.storeAny( valueToAssign, address, false, @@ -1778,8 +1774,6 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, return codegen.theUnitInstanceRef.llvm } - private val vectorType = FqName("kotlin.native.Vector128").toUnsafe() - //-------------------------------------------------------------------------// private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: IrField): LLVMValueRef { val fieldInfo = generationState.llvmDeclarations.forField(value) @@ -1791,6 +1785,12 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, return fieldPtr!! } + private fun staticFieldPtr(value: IrField, context: FunctionGenerationContext) = + generationState.llvmDeclarations + .forStaticField(value.symbol.owner) + .storageAddressAccess + .getAddress(context) + //-------------------------------------------------------------------------// private fun evaluateStringConst(value: IrConst) = llvm.staticData.kotlinStringLiteral(value.value) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VolatileFieldsLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VolatileFieldsLowering.kt index 27fdee99409..34e5cc8cc1d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VolatileFieldsLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VolatileFieldsLowering.kt @@ -51,12 +51,17 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass { startOffset = irField.startOffset endOffset = irField.endOffset }.apply { - val parentClass = irField.parents.filterIsInstance().first() - parent = parentClass - addDispatchReceiver { - startOffset = irField.startOffset - endOffset = irField.endOffset - type = parentClass.defaultType + val property = irField.correspondingPropertySymbol?.owner + val scope = property?.parent + require(scope != null) + require(scope is IrClass || scope is IrFile) + parent = scope + if (scope is IrClass) { + addDispatchReceiver { + startOffset = irField.startOffset + endOffset = irField.endOffset + type = scope.defaultType + } } builder() annotations += buildSimpleAnnotation(context.irBuiltIns, @@ -124,7 +129,18 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass { irFile.transformChildrenVoid(object : IrBuildingTransformer(context) { override fun visitClass(declaration: IrClass): IrStatement { declaration.transformChildrenVoid() - declaration.declarations.transformFlat { + processDeclarationList(declaration.declarations) + return declaration + } + + override fun visitFile(declaration: IrFile): IrFile { + declaration.transformChildrenVoid() + processDeclarationList(declaration.declarations) + return declaration + } + + private fun processDeclarationList(declarations: MutableList) { + declarations.transformFlat { when { it !is IrProperty -> null it.backingField?.hasAnnotation(KonanFqNames.volatile) != true -> null @@ -144,13 +160,15 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass { } } } - return declaration } override fun visitField(declaration: IrField): IrStatement { if (declaration.type == irBuiltins.booleanType && declaration.hasAnnotation(KonanFqNames.volatile)) { convertedBooleanFields.add(declaration.symbol) declaration.type = irBuiltins.byteType + declaration.initializer?.let { + it.expression = context.createIrBuilder(declaration.symbol).at(it.expression).irBoolToByte(it.expression) + } } return super.visitField(declaration) } @@ -189,7 +207,7 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass { expression.transformChildrenVoid(this) val intrinsicType = tryGetIntrinsicType(expression).takeIf { it in intrinsicMap } ?: return expression builder.at(expression) - val reference = expression.getValueArgument(0) as? IrPropertyReference + val reference = expression.extensionReceiver as? IrPropertyReference ?: return unsupported("Only compile-time known IrProperties supported for $intrinsicType") val property = reference.symbol.owner val backingField = property.backingField @@ -201,10 +219,10 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass { } val function = intrinsicMap[intrinsicType]!!(backingField) return builder.irCall(function).apply { - dispatchReceiver = expression.extensionReceiver - putValueArgument(0, expression.getValueArgument(1)) + dispatchReceiver = reference.dispatchReceiver + putValueArgument(0, expression.getValueArgument(0)) if (intrinsicType == IntrinsicType.COMPARE_AND_SET_FIELD || intrinsicType == IntrinsicType.COMPARE_AND_SWAP_FIELD) { - putValueArgument(1, expression.getValueArgument(2)) + putValueArgument(1, expression.getValueArgument(1)) } }.let { if (backingField.requiresBooleanConversion()) { diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt index 270875b264b..3f598f30fee 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt @@ -26,7 +26,7 @@ public class AtomicInt(public @Volatile var value: Int) { * @param delta the value to add * @return the new value */ - public fun addAndGet(delta: Int): Int = getAndAddField(AtomicInt::value, delta) + delta + public fun addAndGet(delta: Int): Int = this::value.getAndAddField(delta) + delta /** * Compares value with [expected] and replaces it with [new] value if values matches. @@ -35,7 +35,7 @@ public class AtomicInt(public @Volatile var value: Int) { * @param new the new value * @return the old value */ - public fun compareAndSwap(expected: Int, new: Int): Int = compareAndSwapField(AtomicInt::value, expected, new) + public fun compareAndSwap(expected: Int, new: Int): Int = this::value.compareAndSwapField(expected, new) /** * Compares value with [expected] and replaces it with [new] value if values matches. @@ -44,7 +44,7 @@ public class AtomicInt(public @Volatile var value: Int) { * @param new the new value * @return true if successful */ - public fun compareAndSet(expected: Int, new: Int): Boolean = compareAndSetField(AtomicInt::value, expected, new) + public fun compareAndSet(expected: Int, new: Int): Boolean = this::value.compareAndSetField(expected, new) /** * Increments value by one. @@ -85,7 +85,7 @@ public class AtomicLong(public @Volatile var value: Long = 0) { * @param delta the value to add * @return the new value */ - public fun addAndGet(delta: Long): Long = getAndAddField(AtomicLong::value, delta) + delta + public fun addAndGet(delta: Long): Long = this::value.getAndAddField(delta) + delta /** * Increments the value by [delta] and returns the new value. @@ -102,7 +102,7 @@ public class AtomicLong(public @Volatile var value: Long = 0) { * @param new the new value * @return the old value */ - public fun compareAndSwap(expected: Long, new: Long): Long = compareAndSwapField(AtomicLong::value, expected, new) + public fun compareAndSwap(expected: Long, new: Long): Long = this::value.compareAndSwapField(expected, new) /** * Compares value with [expected] and replaces it with [new] value if values matches. @@ -111,7 +111,7 @@ public class AtomicLong(public @Volatile var value: Long = 0) { * @param new the new value * @return true if successful, false if state is unchanged */ - public fun compareAndSet(expected: Long, new: Long): Boolean = compareAndSetField(AtomicLong::value, expected, new) + public fun compareAndSet(expected: Long, new: Long): Boolean = this::value.compareAndSetField(expected, new) /** * Increments value by one. @@ -154,7 +154,7 @@ public class AtomicNativePtr(public @Volatile var value: NativePtr) { * @return the old value */ public fun compareAndSwap(expected: NativePtr, new: NativePtr): NativePtr = - compareAndSwapField(AtomicNativePtr::value, expected, new) + this::value.compareAndSwapField(expected, new) /** * Compares value with [expected] and replaces it with [new] value if values matches. @@ -164,7 +164,7 @@ public class AtomicNativePtr(public @Volatile var value: NativePtr) { * @return true if successful */ public fun compareAndSet(expected: NativePtr, new: NativePtr): Boolean = - compareAndSetField(AtomicNativePtr::value, expected, new) + this::value.compareAndSetField(expected, new) /** * Returns the string representation of this object. @@ -390,131 +390,131 @@ public class FreezableAtomicReference(private var value_: T) { /** - * Compares the value of the field referenced by [fieldRef] from [this] object to [expectedValue], and if they are equal, + * Compares the value of the field referenced by [this] to [expectedValue], and if they are equal, * atomically replaces it with [newValue]. * - * For now, it can be used only within the same file, where class [T] is defined. + * For now, it can be used only within the same file, where property is defined. * Check https://youtrack.jetbrains.com/issue/KT-55426 for details. * * Comparison is done by reference or value depending on field representation. * - * If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * - * If property referenced by [fieldRef] has nontrivial setter it will not be called. + * If property referenced by [this] has nontrivial setter it will not be called. * * Returns true if the actual field value matched [expectedValue] * - * Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. + * Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. */ @PublishedApi @TypedIntrinsic(IntrinsicType.COMPARE_AND_SET_FIELD) -internal external fun T.compareAndSetField(filedRef: KMutableProperty1, expectedValue: S, newValue: S): Boolean +internal external fun KMutableProperty0.compareAndSetField(expectedValue: T, newValue: T): Boolean /** - * Compares the value of the field referenced by [fieldRef] from [this] object to [expectedValue], and if they are equal, + * Compares the value of the field referenced by [this] to [expectedValue], and if they are equal, * atomically replaces it with [newValue]. * - * For now, it can be used only within the same file, where class [T] is defined. + * For now, it can be used only within the same file, where property is defined. * Check https://youtrack.jetbrains.com/issue/KT-55426 for details. * * Comparison is done by reference or value depending on field representation. * - * If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * - * If property referenced by [fieldRef] has nontrivial setter it will not be called. + * If property referenced by [this] has nontrivial setter it will not be called. * * Returns true if the actual field value before operation. * - * Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. + * Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. */ @PublishedApi @TypedIntrinsic(IntrinsicType.COMPARE_AND_SWAP_FIELD) -internal external fun T.compareAndSwapField(filedRef: KMutableProperty1, expectedValue: S, newValue: S): S +internal external fun KMutableProperty0.compareAndSwapField(expectedValue: T, newValue: T): T /** - * Atomically sets value of the field referenced by [fieldRef] from [this] object to [newValue] and returns old field value. + * Atomically sets value of the field referenced by [this] to [newValue] and returns old field value. * - * For now, it can be used only within the same file, where class [T] is defined. + * For now, it can be used only within the same file, where property is defined. * Check https://youtrack.jetbrains.com/issue/KT-55426 for details. * - * If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * - * If property referenced by [fieldRef] has nontrivial setter it will not be called. + * If property referenced by [this] has nontrivial setter it will not be called. * - * Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. + * Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. */ @PublishedApi @TypedIntrinsic(IntrinsicType.GET_AND_SET_FIELD) -internal external fun T.getAndSetField(filedRef: KMutableProperty1, newValue: S): S +internal external fun KMutableProperty0.getAndSetField(newValue: T): T /** - * Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value. + * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. * - * For now, it can be used only within the same file, where class [T] is defined. + * For now, it can be used only within the same file, where property is defined. * Check https://youtrack.jetbrains.com/issue/KT-55426 for details. * - * If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * - * If property referenced by [fieldRef] has nontrivial setter it will not be called. + * If property referenced by [this] has nontrivial setter it will not be called. * - * Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. + * Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. */ @PublishedApi @TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD) -internal external fun T.getAndAddField(filedRef: KMutableProperty1, delta: Short): Short +internal external fun KMutableProperty0.getAndAddField(delta: Short): Short /** - * Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value. + * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. * - * For now, it can be used only within the same file, where class [T] is defined. + * For now, it can be used only within the same file, where property is defined. * Check https://youtrack.jetbrains.com/issue/KT-55426 for details. * - * If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * - * If property referenced by [fieldRef] has nontrivial setter it will not be called. + * If property referenced by [this] has nontrivial setter it will not be called. * - * Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. + * Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. */ @PublishedApi @TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD) -internal external fun T.getAndAddField(filedRef: KMutableProperty1, newValue: Int): Int +internal external fun KMutableProperty0.getAndAddField(newValue: Int): Int /** - * Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value. + * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. * - * For now, it can be used only within the same file, where class [T] is defined. + * For now, it can be used only within the same file, where property is defined. * Check https://youtrack.jetbrains.com/issue/KT-55426 for details. * - * If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * - * If property referenced by [fieldRef] has nontrivial setter it will not be called. + * If property referenced by [this] has nontrivial setter it will not be called. * - * Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. + * Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. */ @PublishedApi @TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD) -internal external fun T.getAndAddField(filedRef: KMutableProperty1, newValue: Long): Long +internal external fun KMutableProperty0.getAndAddField(newValue: Long): Long /** - * Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value. + * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. * - * For now, it can be used only within the same file, where class [T] is defined. + * For now, it can be used only within the same file, where property is defined. * Check https://youtrack.jetbrains.com/issue/KT-55426 for details. * - * If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * - * If property referenced by [fieldRef] has nontrivial setter it will not be called. + * If property referenced by [this] has nontrivial setter it will not be called. * - * Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. + * Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown. */ @PublishedApi @TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD) -internal external fun T.getAndAddField(filedRef: KMutableProperty1, newValue: Byte): Byte +internal external fun KMutableProperty0.getAndAddField(newValue: Byte): Byte diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index 7b37dde40f7..a0070aa86ac 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java @@ -39686,6 +39686,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest runTest("compiler/testData/codegen/box/volatile/intrinsics.kt"); } + @Test + @TestMetadata("intrinsicsOnGlobal.kt") + public void testIntrinsicsOnGlobal() throws Exception { + runTest("compiler/testData/codegen/box/volatile/intrinsicsOnGlobal.kt"); + } + @Test @TestMetadata("messagePassing.kt") public void testMessagePassing() throws Exception {