[K/N] Tests for function attributes in bitcode generator
Besides, well, tests themselves, this commit extends FileCheck infra to make it possible to test bitcode for direct and reverse interop.
This commit is contained in:
@@ -5830,15 +5830,39 @@ Task fileCheckTest(String name, Closure<FileCheckTest> configureClosure) {
|
||||
task.llvmIr = project.file("$testOutputFileCheck/$name/out.ll")
|
||||
if (task.enabled) {
|
||||
konanArtifacts {
|
||||
// We could use `bitcode` here to make things faster. But:
|
||||
// 1. `bitcode` has no other usages.
|
||||
// 2. Unification should help porting to the new test infra.
|
||||
// 3. Compiler might behave differently when outputting bitcode instead of object files.
|
||||
program(name, targets: [ target ]) {
|
||||
srcFiles task.annotatedSource
|
||||
baseDir "$testOutputFileCheck/$name"
|
||||
extraOpts project.globalTestArgs
|
||||
extraOpts "-Xtemporary-files-dir=$testOutputFileCheck/$name", "-Xsave-llvm-ir"
|
||||
def lib = task.interop
|
||||
if (lib != null) {
|
||||
UtilsKt.dependsOnKonanBuildingTask(task, lib, target)
|
||||
}
|
||||
if (!task.generateFramework) {
|
||||
// We could use `bitcode` here to make things faster. But:
|
||||
// 1. `bitcode` has no other usages.
|
||||
// 2. Unification should help porting to the new test infra.
|
||||
// 3. Compiler might behave differently when outputting bitcode instead of object files.
|
||||
program(name, targets: [target]) {
|
||||
srcFiles task.annotatedSource
|
||||
baseDir "$testOutputFileCheck/$name"
|
||||
extraOpts project.globalTestArgs
|
||||
extraOpts "-Xtemporary-files-dir=$testOutputFileCheck/$name", "-Xsave-llvm-ir"
|
||||
if (lib != null) {
|
||||
libraries {
|
||||
artifact lib
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Framework support is much weakier than `frameworkTest`, but we don't need much.
|
||||
framework(name, targets: [target]) {
|
||||
srcFiles task.annotatedSource
|
||||
baseDir "$testOutputFileCheck/$name"
|
||||
extraOpts project.globalTestArgs
|
||||
extraOpts "-Xtemporary-files-dir=$testOutputFileCheck/$name", "-Xsave-llvm-ir", "-Xmeaningful-bridge-names"
|
||||
if (lib != null) {
|
||||
libraries {
|
||||
artifact lib
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UtilsKt.dependsOnKonanBuildingTask(task, name, target)
|
||||
}
|
||||
@@ -5874,6 +5898,30 @@ fileCheckTest("filecheck_bce") {
|
||||
annotatedSource = project.file('filecheck/bce.kt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_signext_zeroext0") {
|
||||
annotatedSource = project.file('filecheck/signext_zeroext0.kt')
|
||||
}
|
||||
|
||||
createInterop("filecheck_signext_zeroext_interop_input") {
|
||||
it.defFile 'filecheck/signext_zeroext_interop_input.def'
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_signext_zeroext_interop") {
|
||||
annotatedSource = project.file('filecheck/signext_zeroext_interop.kt')
|
||||
interop = "filecheck_signext_zeroext_interop_input"
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_signext_zeroext_objc_export") {
|
||||
annotatedSource = project.file('filecheck/signext_zeroext_objc_export.kt')
|
||||
generateFramework = true
|
||||
enabled = target.family.appleFamily
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_function_attributes_at_callsite") {
|
||||
annotatedSource = project.file('filecheck/function_attributes_at_callsite.kt')
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
nopPluginApi project(kotlinCompilerModule)
|
||||
nopPluginApi project(":native:kotlin-native-utils")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
// CHECK: declare void @ThrowException(%struct.ObjHeader*) #[[THROW_EXCEPTION_DECLARATION_ATTRIBUTES:[0-9]+]]
|
||||
|
||||
// CHECK: void @"kfun:#flameThrower(){}kotlin.Nothing"() #[[FLAME_THROWER_DECLARATION_ATTRIBUTES:[0-9]+]]
|
||||
fun flameThrower(): Nothing {
|
||||
// CHECK: invoke void @ThrowException(%struct.ObjHeader* {{.*}}) #[[THROW_EXCEPTION_CALLSITE_ATTRIBUTES:[0-9]+]]
|
||||
throw Throwable("🔥")
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @"kfun:#main(){}"()
|
||||
fun main() {
|
||||
// CHECK: call void @"kfun:#flameThrower(){}kotlin.Nothing"() #[[FLAME_THROWER_CALLSITE_ATTRIBUTES:[0-9]+]]
|
||||
flameThrower()
|
||||
}
|
||||
|
||||
// CHECK-DAG: attributes #[[THROW_EXCEPTION_DECLARATION_ATTRIBUTES]] = { noreturn uwtable {{.+}} }
|
||||
// CHECK-DAG: attributes #[[THROW_EXCEPTION_CALLSITE_ATTRIBUTES]] = { noreturn uwtable }
|
||||
// CHECK-DAG: attributes #[[FLAME_THROWER_DECLARATION_ATTRIBUTES]] = { noreturn {{.+}} }
|
||||
// CHECK-DAG: attributes #[[FLAME_THROWER_CALLSITE_ATTRIBUTES]] = { noreturn }
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
// CHECK-LABEL: zeroext i1 @"kfun:#id(kotlin.Boolean){}kotlin.Boolean"(i1 zeroext %0)
|
||||
fun id(arg: Boolean): Boolean {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: signext i8 @"kfun:#id(kotlin.Byte){}kotlin.Byte"(i8 signext %0)
|
||||
fun id(arg: Byte): Byte {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: zeroext i16 @"kfun:#id(kotlin.Char){}kotlin.Char"(i16 zeroext %0)
|
||||
fun id(arg: Char): Char {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: signext i16 @"kfun:#id(kotlin.Short){}kotlin.Short"(i16 signext %0)
|
||||
fun id(arg: Short): Short {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: i32 @"kfun:#id(kotlin.Int){}kotlin.Int"(i32 %0)
|
||||
fun id(arg: Int): Int {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: i64 @"kfun:#id(kotlin.Long){}kotlin.Long"(i64 %0)
|
||||
fun id(arg: Long): Long {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: float @"kfun:#id(kotlin.Float){}kotlin.Float"(float %0)
|
||||
fun id(arg: Float): Float {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: double @"kfun:#id(kotlin.Double){}kotlin.Double"(double %0)
|
||||
fun id(arg: Double): Double {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define zeroext i8 @"kfun:#id(kotlin.UByte){}kotlin.UByte"(i8 zeroext %0)
|
||||
fun id(arg: UByte): UByte {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define zeroext i16 @"kfun:#id(kotlin.UShort){}kotlin.UShort"(i16 zeroext %0)
|
||||
fun id(arg: UShort): UShort {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define i32 @"kfun:#id(kotlin.UInt){}kotlin.UInt"(i32 %0)
|
||||
fun id(arg: UInt): UInt {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define i64 @"kfun:#id(kotlin.ULong){}kotlin.ULong"(i64 %0)
|
||||
fun id(arg: ULong): ULong {
|
||||
return arg
|
||||
}
|
||||
|
||||
fun checkPrimitives() {
|
||||
// CHECK: call zeroext i1 @"kfun:#id(kotlin.Boolean){}kotlin.Boolean"(i1 zeroext {{.*}})
|
||||
id(true)
|
||||
// CHECK: call signext i8 @"kfun:#id(kotlin.Byte){}kotlin.Byte"(i8 signext {{.*}})
|
||||
id(0.toByte())
|
||||
// CHECK: call zeroext i16 @"kfun:#id(kotlin.Char){}kotlin.Char"(i16 zeroext {{.*}})
|
||||
id('a')
|
||||
// CHECK: call signext i16 @"kfun:#id(kotlin.Short){}kotlin.Short"(i16 signext {{.*}})
|
||||
id(0.toShort())
|
||||
// CHECK: call i32 @"kfun:#id(kotlin.Int){}kotlin.Int"(i32 {{.*}})
|
||||
id(0)
|
||||
// CHECK: call i64 @"kfun:#id(kotlin.Long){}kotlin.Long"(i64 {{.*}})
|
||||
id(0L)
|
||||
// CHECK: call float @"kfun:#id(kotlin.Float){}kotlin.Float"(float {{.*}})
|
||||
id(0.5f)
|
||||
// CHECK: call double @"kfun:#id(kotlin.Double){}kotlin.Double"(double {{.*}})
|
||||
id(0.5)
|
||||
// CHECK: call zeroext i8 @"kfun:#id(kotlin.UByte){}kotlin.UByte"(i8 zeroext {{.*}})
|
||||
id(0.toUByte())
|
||||
// CHECK: call zeroext i16 @"kfun:#id(kotlin.UShort){}kotlin.UShort"(i16 zeroext {{.*}})
|
||||
id(0.toUShort())
|
||||
// CHECK: call i32 @"kfun:#id(kotlin.UInt){}kotlin.UInt"(i32 {{.*}})
|
||||
id(15u)
|
||||
// CHECK: call i64 @"kfun:#id(kotlin.ULong){}kotlin.ULong"(i64 {{.*}})
|
||||
id(15uL)
|
||||
}
|
||||
|
||||
value class CharWrapper(val ch: Char)
|
||||
|
||||
// CHECK-LABEL: zeroext i16 @"kfun:#id(CharWrapper){}CharWrapper"(i16 zeroext %0)
|
||||
fun id(arg: CharWrapper): CharWrapper {
|
||||
return arg
|
||||
}
|
||||
|
||||
// Check that value classes doesn't affect parameter attributes
|
||||
|
||||
fun checkInlineClasses() {
|
||||
// CHECK: call zeroext i16 @"kfun:#id(CharWrapper){}CharWrapper"(i16 zeroext {{.*}})
|
||||
id(CharWrapper('c'))
|
||||
}
|
||||
|
||||
// CHECK-LABEL: %struct.ObjHeader* @"kfun:#nullableId(kotlin.Byte?){}kotlin.Byte?"(%struct.ObjHeader* %0, %struct.ObjHeader** %1)
|
||||
fun nullableId(arg: Byte?): Byte? {
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: %struct.ObjHeader* @"kfun:#nullableId(CharWrapper?){}CharWrapper?"(%struct.ObjHeader* %0, %struct.ObjHeader** %1)
|
||||
fun nullableId(arg: CharWrapper?): CharWrapper? {
|
||||
return arg
|
||||
}
|
||||
|
||||
// Check that we don't pass primitive-specific attributes to their boxes
|
||||
fun checkBoxes() {
|
||||
// CHECK: invoke %struct.ObjHeader* @"kfun:#nullableId(kotlin.Byte?){}kotlin.Byte?"(%struct.ObjHeader* {{.*}}, %struct.ObjHeader** {{.*}})
|
||||
nullableId(1.toByte())
|
||||
// CHECK: invoke %struct.ObjHeader* @"kfun:#nullableId(CharWrapper?){}CharWrapper?"(%struct.ObjHeader* {{.*}}, %struct.ObjHeader** {{.*}})
|
||||
nullableId(CharWrapper('a'))
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @"kfun:#main(){}"()
|
||||
fun main() {
|
||||
checkPrimitives()
|
||||
checkInlineClasses()
|
||||
checkBoxes()
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
import signext_zeroext_interop_input.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
// CHECK: declare zeroext i1 @Kotlin_Char_isHighSurrogate(i16 zeroext)
|
||||
|
||||
// Check that we pass attributes to functions imported from runtime.
|
||||
// CHECK-LABEL: void @"kfun:#checkRuntimeFunctionImport(){}"()
|
||||
fun checkRuntimeFunctionImport() {
|
||||
// CHECK: call zeroext i1 @Kotlin_Char_isHighSurrogate(i16 zeroext {{.*}})
|
||||
'c'.isHighSurrogate()
|
||||
// CHECK: call zeroext i1 @Kotlin_Float_isNaN(float {{.*}}
|
||||
0.0f.isNaN()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @"kfun:#checkDirectInterop(){}"()
|
||||
fun checkDirectInterop() {
|
||||
// compiler generates quite lovely names for bridges
|
||||
// (e.g. `_66696c65636865636b5f7369676e6578745f7a65726f6578745f696e7465726f70_knbridge0`),
|
||||
// so we don't check exact function names here.
|
||||
// CHECK: invoke signext i8 [[CHAR_ID_BRIDGE:@_.*_knbridge[0-9]+]](i8 signext {{.*}})
|
||||
char_id(0.toByte())
|
||||
// CHECK: invoke zeroext i8 [[UNSIGNED_CHAR_ID_BRIDGE:@_.*_knbridge[0-9]+]](i8 zeroext {{.*}})
|
||||
unsigned_char_id(0.toUByte())
|
||||
// CHECK: invoke signext i16 [[SHORT_ID_BRIDGE:@_.*_knbridge[0-9]+]](i16 signext {{.*}})
|
||||
short_id(0.toShort())
|
||||
// CHECK: invoke zeroext i16 [[UNSIGNED_SHORT_ID_BRIDGE:@_.*_knbridge[0-9]+]](i16 zeroext {{.*}})
|
||||
unsigned_short_id(0.toUShort())
|
||||
// CHECK: invoke i32 [[CALLBACK_USER_BRIDGE:@_.*_knbridge[0-9]+]](i8* bitcast (i32 (i32, i16)* [[STATIC_C_FUNCTION_BRIDGE:@_.*_kncfun[0-9]+]] to i8*))
|
||||
callbackUser(staticCFunction { int: Int, short: Short -> int + short })
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @"kfun:#main(){}"()
|
||||
fun main() {
|
||||
checkRuntimeFunctionImport()
|
||||
checkDirectInterop()
|
||||
}
|
||||
|
||||
// CHECK: define signext i8 [[CHAR_ID_BRIDGE]](i8 signext %0)
|
||||
// CHECK: [[CHAR_ID_PTR:%[0-9]+]] = load i8 (i8)*, i8 (i8)** @{{.*char_id}}
|
||||
// CHECK: call signext i8 [[CHAR_ID_PTR]](i8 signext %0)
|
||||
|
||||
// CHECK: define zeroext i8 [[UNSIGNED_CHAR_ID_BRIDGE]](i8 zeroext %0)
|
||||
// CHECK: [[UNSIGNED_CHAR_ID_PTR:%[0-9]+]] = load i8 (i8)*, i8 (i8)** @{{.*unsigned_char_id}}
|
||||
// CHECK: call zeroext i8 [[UNSIGNED_CHAR_ID_PTR]](i8 zeroext %0)
|
||||
|
||||
// CHECK: define signext i16 [[SHORT_ID_BRIDGE]](i16 signext %0)
|
||||
// CHECK: [[SHORT_ID_PTR:%[0-9]+]] = load i16 (i16)*, i16 (i16)** @{{.*short_id}}
|
||||
// CHECK: call signext i16 [[SHORT_ID_PTR]](i16 signext %0)
|
||||
|
||||
// CHECK: define zeroext i16 [[UNSIGNED_SHORT_ID_BRIDGE]](i16 zeroext %0)
|
||||
// CHECK: [[UNSIGNED_SHORT_ID_PTR:%[0-9]+]] = load i16 (i16)*, i16 (i16)** @{{.*unsigned_short_id}}
|
||||
// CHECK: call zeroext i16 [[UNSIGNED_SHORT_ID_PTR]](i16 zeroext %0)
|
||||
|
||||
// CHECK: define i32 [[STATIC_C_FUNCTION_BRIDGE]](i32 %0, i16 signext %1)
|
||||
// CHECK: call i32 {{@_.*_knbridge[0-9]+}}(i32 %0, i16 signext %1)
|
||||
|
||||
// CHECK: define i32 [[CALLBACK_USER_BRIDGE]](i8* %0)
|
||||
// CHECK: [[CALLBACK_USER_PTR:%[0-9]+]] = load i32 (i8*)*, i32 (i8*)** @{{.*callbackUser}}
|
||||
// CHECK: call i32 [[CALLBACK_USER_PTR]](i8* %0)
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
char char_id(char c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
unsigned char unsigned_char_id(unsigned char c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
short short_id(short s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned short unsigned_short_id(unsigned short s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
int callbackUser(int (*fn)(int, short)) {
|
||||
return fn(5,5);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
//
|
||||
// Here we check generated ObjC bridges, not original declarations.
|
||||
//
|
||||
|
||||
// CHECK-LABEL: zeroext i16 @objc2kotlin_heyIJustMetYou(i8* %0, i8* %1, i16 zeroext %2)
|
||||
fun heyIJustMetYou(arg: Char): Char {
|
||||
// CHECK: invoke zeroext i16 @"kfun:#heyIJustMetYou(kotlin.Char){}kotlin.Char"(i16 zeroext %2)
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: signext i8 @objc2kotlin_andThisIsCrazy(i8* %0, i8* %1, i8 signext %2)
|
||||
fun andThisIsCrazy(arg: Byte): Byte {
|
||||
// CHECK: invoke signext i8 @"kfun:#andThisIsCrazy(kotlin.Byte){}kotlin.Byte"(i8 signext %2)
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: signext i16 @objc2kotlin_butHereIsMyNumber(i8* %0, i8* %1, i16 signext %2)
|
||||
fun butHereIsMyNumber(arg: Short): Short {
|
||||
// CHECK: invoke signext i16 @"kfun:#butHereIsMyNumber(kotlin.Short){}kotlin.Short"(i16 signext %2)
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: signext i8 @objc2kotlin_soCallMeMaybe(i8* %0, i8* %1, i8 signext %2)
|
||||
fun soCallMeMaybe(arg: Boolean): Boolean {
|
||||
//CHECK: invoke zeroext i1 @"kfun:#soCallMeMaybe(kotlin.Boolean){}kotlin.Boolean"(i1 zeroext {{.*}})
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: zeroext i8 @objc2kotlin_itsHardToLook(i8* %0, i8* %1, i8 zeroext %2)
|
||||
fun itsHardToLook(arg: UByte): UByte {
|
||||
// CHECK: invoke zeroext i8 @"kfun:#itsHardToLook(kotlin.UByte){}kotlin.UByte"(i8 zeroext %2)
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: zeroext i16 @objc2kotlin_rightAtYouBaby(i8* %0, i8* %1, i16 zeroext %2)
|
||||
fun rightAtYouBaby(arg: UShort): UShort {
|
||||
// CHECK: invoke zeroext i16 @"kfun:#rightAtYouBaby(kotlin.UShort){}kotlin.UShort"(i16 zeroext %2)
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: float @objc2kotlin_butHereIsMyNumber1(i8* %0, i8* %1, float %2)
|
||||
fun butHereIsMyNumber1(arg: Float): Float {
|
||||
// CHECK: invoke float @"kfun:#butHereIsMyNumber1(kotlin.Float){}kotlin.Float"(float %2)
|
||||
return arg
|
||||
}
|
||||
|
||||
// CHECK-LABEL: i8* @objc2kotlin_soCallMeMaybe1(i8* %0, i8* %1, i8* %2)
|
||||
fun soCallMeMaybe1(arg: Any?): Any? {
|
||||
// CHECK: invoke %struct.ObjHeader* @"kfun:#soCallMeMaybe1(kotlin.Any?){}kotlin.Any?"(%struct.ObjHeader* {{.*}}, %struct.ObjHeader** {{.*}})
|
||||
return arg
|
||||
}
|
||||
|
||||
@@ -42,6 +42,13 @@ open class FileCheckTest : DefaultTask() {
|
||||
@get:Internal
|
||||
lateinit var llvmIr: File
|
||||
|
||||
/**
|
||||
* Optional cinterop task dependency.
|
||||
*/
|
||||
@Optional
|
||||
@Input
|
||||
var interop: String? = null
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
runFileCheck(annotatedSource.toPath(), llvmIr.toPath())
|
||||
@@ -54,6 +61,13 @@ open class FileCheckTest : DefaultTask() {
|
||||
@Optional
|
||||
var checkPrefix: String? = null
|
||||
|
||||
/**
|
||||
* Should we generate framework instead of an executable?
|
||||
* This option is useful for, well, checking framework-specific code.
|
||||
*/
|
||||
@Input
|
||||
var generateFramework: Boolean = false
|
||||
|
||||
/**
|
||||
* Check that [inputFile] matches [annotatedFile] with FileCheck.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user