[K/N] Support volatile intrinsics on globals

Also, make intrinsics signature more consistent with other intrinsics,
e.g. with isInitialized on lateinit field.

^KT-54944
This commit is contained in:
Pavel Kunyavskiy
2022-12-15 10:40:58 +01:00
committed by Space Team
parent 49d286e4e8
commit 6ab00a65dd
8 changed files with 224 additions and 111 deletions
@@ -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
}
+22 -22
View File
@@ -23,44 +23,44 @@ interface RefWrapper<T> : Wrapper<T> {
class IntWrapper(@Volatile var x : Int) : IncWrapper<Int> {
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<Long> {
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<Short> {
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<Byte> {
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<String> {
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<T>(@Volatile var x : T) : RefWrapper<T> {
override fun compareAndSwap(expected: T, new: T) = compareAndSwapField(GenericWrapper<T>::x, expected, new)
override fun compareAndSet(expected: T, new: T) = compareAndSetField(GenericWrapper<T>::x, expected, new)
override fun getAndSet(new: T) = getAndSetField(GenericWrapper<T>::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)
}
@@ -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"
}
@@ -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<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)
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<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])) {
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
)!!
@@ -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<String>) =
llvm.staticData.kotlinStringLiteral(value.value)
@@ -51,12 +51,17 @@ internal class VolatileFieldsLowering(val context: Context) : FileLoweringPass {
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
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<IrDeclaration>) {
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()) {
@@ -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<T>(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, S> T.compareAndSetField(filedRef: KMutableProperty1<T, S>, expectedValue: S, newValue: S): Boolean
internal external fun <T> KMutableProperty0<T>.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, S> T.compareAndSwapField(filedRef: KMutableProperty1<T, S>, expectedValue: S, newValue: S): S
internal external fun <T> KMutableProperty0<T>.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, S> T.getAndSetField(filedRef: KMutableProperty1<T, S>, newValue: S): S
internal external fun <T> KMutableProperty0<T>.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> T.getAndAddField(filedRef: KMutableProperty1<T, Short>, delta: Short): Short
internal external fun KMutableProperty0<Short>.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> T.getAndAddField(filedRef: KMutableProperty1<T, Int>, newValue: Int): Int
internal external fun KMutableProperty0<Int>.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> T.getAndAddField(filedRef: KMutableProperty1<T, Long>, newValue: Long): Long
internal external fun KMutableProperty0<Long>.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> T.getAndAddField(filedRef: KMutableProperty1<T, Byte>, newValue: Byte): Byte
internal external fun KMutableProperty0<Byte>.getAndAddField(newValue: Byte): Byte
@@ -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 {