[K/N] Intrinsics for atomic get/set of volatile properties.
These intrinsics are equivalent to KMutableProperty0.get/set invocation and used internally to optimize allocation of a property reference. Merge-request: KT-MR-11233 Merged-by: Maria Sokolova <maria.sokolova@jetbrains.com>
This commit is contained in:
+23
-3
@@ -24,6 +24,8 @@ internal external fun KMutableProperty0<Byte>.getAndAddFieldLocal(newValue: Byte
|
||||
|
||||
|
||||
interface Wrapper<T> {
|
||||
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<T> : Wrapper<T> {
|
||||
|
||||
|
||||
class IntWrapper(@Volatile var x : Int) : IncWrapper<Int> {
|
||||
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<Int> {
|
||||
}
|
||||
|
||||
class LongWrapper(@Volatile var x : Long) : IncWrapper<Long> {
|
||||
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<Long> {
|
||||
}
|
||||
|
||||
class ShortWrapper(@Volatile var x : Short) : IncWrapper<Short> {
|
||||
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<Short> {
|
||||
}
|
||||
|
||||
class ByteWrapper(@Volatile var x : Byte) : IncWrapper<Byte> {
|
||||
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<Byte> {
|
||||
|
||||
|
||||
class StringWrapper(@Volatile var x : String) : RefWrapper<String> {
|
||||
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<T>(@Volatile var x : T) : RefWrapper<T> {
|
||||
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 <T> test(one: T, two: T, three: T, wrap: (T) -> Wrapper<T>) : 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<T>) {
|
||||
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
|
||||
}
|
||||
|
||||
+4
@@ -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)
|
||||
|
||||
+12
-5
@@ -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()) {
|
||||
|
||||
@@ -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 <T> KMutableProperty0<T>.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 <T> KMutableProperty0<T>.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 <T> KMutableProperty0<T>.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 <T> KMutableProperty0<T>.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 <T> KMutableProperty0<T>.compareAndExchangeField(expectedV
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_SET_FIELD)
|
||||
internal external fun <T> KMutableProperty0<T>.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 <T> KMutableProperty0<T>.getAndSetField(newValue: T): T
|
||||
internal external fun KMutableProperty0<Short>.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<Short>.getAndAddField(delta: Short): Sho
|
||||
internal external fun KMutableProperty0<Int>.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<Int>.getAndAddField(newValue: Int): Int
|
||||
internal external fun KMutableProperty0<Long>.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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user