[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
@@ -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()) {