[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:
committed by
Space Team
parent
49d286e4e8
commit
6ab00a65dd
@@ -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
@@ -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"
|
||||
}
|
||||
+37
-10
@@ -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
|
||||
)!!
|
||||
|
||||
+16
-16
@@ -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)
|
||||
|
||||
+30
-12
@@ -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
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user