diff --git a/compiler/testData/codegen/box/volatile/intrinsics.kt b/compiler/testData/codegen/box/volatile/intrinsics.kt index e3855073ede..dec3df6c6c6 100644 --- a/compiler/testData/codegen/box/volatile/intrinsics.kt +++ b/compiler/testData/codegen/box/volatile/intrinsics.kt @@ -24,6 +24,8 @@ internal external fun KMutableProperty0.getAndAddFieldLocal(newValue: Byte interface Wrapper { + fun get(): T + fun set(new: T): Unit fun compareAndSwap(expected: T, new: T): T fun compareAndSet(expected: T, new: T): Boolean fun getAndSet(expected: T): T @@ -38,6 +40,8 @@ interface RefWrapper : Wrapper { class IntWrapper(@Volatile var x : Int) : IncWrapper { + override fun get(): Int = this::x.atomicGetField() + override fun set(new: Int) = this::x.atomicSetField(new) override fun compareAndSwap(expected: Int, new: Int) = this::x.compareAndExchangeField(expected, new) override fun compareAndSet(expected: Int, new: Int) = this::x.compareAndSetField(expected, new) override fun getAndSet(new: Int) = this::x.getAndSetField(new) @@ -45,6 +49,8 @@ class IntWrapper(@Volatile var x : Int) : IncWrapper { } class LongWrapper(@Volatile var x : Long) : IncWrapper { + override fun get(): Long = this::x.atomicGetField() + override fun set(new: Long) = this::x.atomicSetField(new) override fun compareAndSwap(expected: Long, new: Long) = this::x.compareAndExchangeField(expected, new) override fun compareAndSet(expected: Long, new: Long) = this::x.compareAndSetField(expected, new) override fun getAndSet(new: Long) = this::x.getAndSetField(new) @@ -52,6 +58,8 @@ class LongWrapper(@Volatile var x : Long) : IncWrapper { } class ShortWrapper(@Volatile var x : Short) : IncWrapper { + override fun get(): Short = this::x.atomicGetField() + override fun set(new: Short) = this::x.atomicSetField(new) override fun compareAndSwap(expected: Short, new: Short) = this::x.compareAndExchangeField(expected, new) override fun compareAndSet(expected: Short, new: Short) = this::x.compareAndSetField(expected, new) override fun getAndSet(new: Short) = this::x.getAndSetField(new) @@ -59,6 +67,8 @@ class ShortWrapper(@Volatile var x : Short) : IncWrapper { } class ByteWrapper(@Volatile var x : Byte) : IncWrapper { + override fun get(): Byte = this::x.atomicGetField() + override fun set(new: Byte) = this::x.atomicSetField(new) override fun compareAndSwap(expected: Byte, new: Byte) = this::x.compareAndExchangeField(expected, new) override fun compareAndSet(expected: Byte, new: Byte) = this::x.compareAndSetField(expected, new) override fun getAndSet(new: Byte) = this::x.getAndSetField(new) @@ -67,12 +77,16 @@ class ByteWrapper(@Volatile var x : Byte) : IncWrapper { class StringWrapper(@Volatile var x : String) : RefWrapper { + override fun get(): String = this::x.atomicGetField() + override fun set(new: String) = this::x.atomicSetField(new) override fun compareAndSwap(expected: String, new: String) = this::x.compareAndExchangeField(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 get(): T = this::x.atomicGetField() + override fun set(new: T) = this::x.atomicSetField(new) override fun compareAndSwap(expected: T, new: T) = this::x.compareAndExchangeField(expected, new) override fun compareAndSet(expected: T, new: T) = this::x.compareAndSetField(expected, new) override fun getAndSet(new: T) = this::x.getAndSetField(new) @@ -103,10 +117,16 @@ fun test(one: T, two: T, three: T, wrap: (T) -> Wrapper) : String? { if (w.compareAndSwap(one, two) != two) return "FAIL 9" if (w.compareAndSwap(one, two) != two) return "FAIL 10" if (w.compareAndSwap(two, one) != two) return "FAIL 11" + if (w.get() != one) return "FAIL 12" + w.set(three) + if (w.get() != three) return "FAIL 13" + w.set(one) + if (w.get() != one) return "FAIL 14" if (w is IncWrapper) { - if (w.getAndAdd(one) != one) return "FAIL 12" - if (w.getAndAdd(one) != two) return "FAIL 13" - if (w.getAndAdd(one) != three) return "FAIL 14" + if (w.getAndAdd(one) != one) return "FAIL 15" + if (w.get() != two) return "FAIL 16" + if (w.getAndAdd(one) != two) return "FAIL 17" + if (w.getAndAdd(one) != three) return "FAIL 18" } return null } 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 a2ed4a9ba65..0c7849cce8a 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 @@ -94,6 +94,8 @@ internal enum class IntrinsicType { // Worker WORKER_EXECUTE, // Atomics + ATOMIC_GET_FIELD, + ATOMIC_SET_FIELD, COMPARE_AND_SET_FIELD, COMPARE_AND_EXCHANGE_FIELD, GET_AND_SET_FIELD, @@ -263,6 +265,8 @@ 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.ATOMIC_GET_FIELD -> reportNonLoweredIntrinsic(intrinsicType) + IntrinsicType.ATOMIC_SET_FIELD -> reportNonLoweredIntrinsic(intrinsicType) IntrinsicType.COMPARE_AND_SET -> emitCompareAndSet(callSite, args) IntrinsicType.COMPARE_AND_EXCHANGE -> emitCompareAndSwap(callSite, args, resultSlot) IntrinsicType.GET_AND_SET -> emitGetAndSet(callSite, args, resultSlot) 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 8afdab85d95..304301139d0 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 @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.* @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.capitalizeDecapitalize.* +import org.jetbrains.kotlin.ir.util.* object IR_DECLARATION_ORIGIN_VOLATILE : IrDeclarationOriginImpl("VOLATILE") @@ -219,7 +221,9 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass { override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid(this) - val intrinsicType = tryGetIntrinsicType(expression).takeIf { it in intrinsicMap } ?: return expression + val intrinsicType = tryGetIntrinsicType(expression).takeIf { + it in intrinsicMap || it == IntrinsicType.ATOMIC_GET_FIELD || it == IntrinsicType.ATOMIC_SET_FIELD + } ?: return expression builder.at(expression) val reference = getConstPropertyReference(expression.extensionReceiver, null) ?: return unsupported("Only compile-time known IrProperties supported for $intrinsicType") @@ -231,12 +235,15 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass { if (backingField?.hasAnnotation(KonanFqNames.volatile) != true) { return unsupported("Only volatile properties are supported for $intrinsicType") } - val function = intrinsicMap[intrinsicType]!!(backingField) + val function = when(intrinsicType) { + IntrinsicType.ATOMIC_GET_FIELD -> property.getter ?: error("Getter is not defined for the property: ${property.render()}") + IntrinsicType.ATOMIC_SET_FIELD -> property.setter ?: error("Setter is not defined for the property: ${property.render()}") + else -> intrinsicMap[intrinsicType]!!(backingField) + } return builder.irCall(function).apply { dispatchReceiver = reference.dispatchReceiver - putValueArgument(0, expression.getValueArgument(0)) - if (intrinsicType == IntrinsicType.COMPARE_AND_SET_FIELD || intrinsicType == IntrinsicType.COMPARE_AND_EXCHANGE_FIELD) { - putValueArgument(1, expression.getValueArgument(1)) + for (index in 0 until expression.valueArgumentsCount) { + putValueArgument(index, expression.getValueArgument(index)) } }.let { if (backingField.requiresBooleanConversion()) { diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/concurrent/Atomics.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/concurrent/Atomics.kt index 360e204785b..c29357cd735 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/concurrent/Atomics.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/concurrent/Atomics.kt @@ -282,49 +282,88 @@ private fun debugString(value: Any?): String { } /** - * Compares the value of the field referenced by [this] to [expectedValue], and if they are equal, - * atomically replaces it with [newValue]. + * Atomically gets the value of the field referenced by [this]. + * + * Provides sequential consistent ordering guarantees. + * + * This is equivalent to KMutableProperty0#get() invocation and used internally to optimize allocation of a property reference. * * 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 [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * * If property referenced by [this] has nontrivial setter it will not be called. + */ +@PublishedApi +@TypedIntrinsic(IntrinsicType.ATOMIC_GET_FIELD) +internal external fun KMutableProperty0.atomicGetField(): T + +/** + * Atomically sets the value of the field referenced by [this] to the [new value][newValue]. * - * Returns true if the actual field value matched [expectedValue] + * Provides sequential consistent ordering guarantees. * + * This is equivalent to KMutableProperty0#set(value: T) invocation and used internally to optimize allocation of a property reference. + * + * 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 [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * would be thrown. + * + * If property referenced by [this] has nontrivial setter it will not be called. + */ +@PublishedApi +@TypedIntrinsic(IntrinsicType.ATOMIC_SET_FIELD) +internal external fun KMutableProperty0.atomicSetField(newValue: T) + +/** + * Atomically sets the value of the field referenced by [this] to the [new value][newValue] + * if the current value equals the [expected value][expectedValue]. + * Returns true if the operation was successful and false only if the current value of the field was not equal to the expected value. + * + * Comparison is done by reference or value depending on field representation. + * + * Provides sequential consistent ordering guarantees and never fails spuriously. + * + * 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 [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] + * would be thrown. + * + * If property referenced by [this] has nontrivial setter it will not be called. */ @PublishedApi @TypedIntrinsic(IntrinsicType.COMPARE_AND_SET_FIELD) internal external fun KMutableProperty0.compareAndSetField(expectedValue: T, newValue: T): Boolean /** - * Compares the value of the field referenced by [this] to [expectedValue], and if they are equal, - * atomically replaces it with [newValue]. + * Atomically sets the value of the field referenced by [this] to the [new value][newValue] + * if the current value equals the [expected value][expectedValue] and returns the old value of the field in any case. + * + * Comparison is done by reference or value depending on field representation. + * + * Provides sequential consistent ordering guarantees and never fails spuriously. * * 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 [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException] * would be thrown. * * If property referenced by [this] has nontrivial setter it will not be called. - * - * Returns the field value before operation. - * */ @PublishedApi @TypedIntrinsic(IntrinsicType.COMPARE_AND_EXCHANGE_FIELD) internal external fun KMutableProperty0.compareAndExchangeField(expectedValue: T, newValue: T): T /** - * Atomically sets value of the field referenced by [this] to [newValue] and returns old field value. + * Atomically sets the value of the field referenced by [this] to the [new value][newValue] and returns the old value of the field. + * + * Provides sequential consistent ordering guarantees. * * 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. @@ -338,9 +377,10 @@ internal external fun KMutableProperty0.compareAndExchangeField(expectedV @TypedIntrinsic(IntrinsicType.GET_AND_SET_FIELD) internal external fun KMutableProperty0.getAndSetField(newValue: T): T - /** - * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. + * Atomically adds the given [delta] to the value of the field referenced by [this] and returns the old value of the field. + * + * Provides sequential consistent ordering guarantees. * * 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. @@ -355,7 +395,9 @@ internal external fun KMutableProperty0.getAndSetField(newValue: T): T internal external fun KMutableProperty0.getAndAddField(delta: Short): Short /** - * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. + * Atomically adds the given [delta] to the value of the field referenced by [this] and returns the old value of the field. + * + * Provides sequential consistent ordering guarantees. * * 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. @@ -370,7 +412,9 @@ internal external fun KMutableProperty0.getAndAddField(delta: Short): Sho internal external fun KMutableProperty0.getAndAddField(newValue: Int): Int /** - * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. + * Atomically adds the given [delta] to the value of the field referenced by [this] and returns the old value of the field. + * + * Provides sequential consistent ordering guarantees. * * 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. @@ -385,7 +429,9 @@ internal external fun KMutableProperty0.getAndAddField(newValue: Int): Int internal external fun KMutableProperty0.getAndAddField(newValue: Long): Long /** - * Atomically increments value of the field referenced by [this] by [delta] and returns old field value. + * Atomically adds the given [delta] to the value of the field referenced by [this] and returns the old value of the field. + * + * Provides sequential consistent ordering guarantees. * * 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. diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt index 3913cd9cf43..a76df5fe955 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt @@ -83,6 +83,8 @@ internal class IntrinsicType { const val WORKER_EXECUTE = "WORKER_EXECUTE" // Atomic + const val ATOMIC_GET_FIELD = "ATOMIC_GET_FIELD" + const val ATOMIC_SET_FIELD = "ATOMIC_SET_FIELD" const val COMPARE_AND_SET_FIELD = "COMPARE_AND_SET_FIELD" const val COMPARE_AND_EXCHANGE_FIELD = "COMPARE_AND_EXCHANGE_FIELD" const val GET_AND_SET_FIELD = "GET_AND_SET_FIELD"