[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:
Sergey Bogolepov
2021-10-06 15:16:26 +07:00
committed by Space
parent 851f4d86d9
commit fdd020eab6
7 changed files with 364 additions and 9 deletions
@@ -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.
*/