committed by
SvyatoslavScherbina
parent
ce27695290
commit
5c3e8d63d4
+3
-1
@@ -34,6 +34,7 @@ private class StructDefImpl(
|
||||
) {
|
||||
|
||||
override val fields = mutableListOf<Field>()
|
||||
override val bitFields = mutableListOf<BitField>()
|
||||
}
|
||||
|
||||
private class EnumDefImpl(spelling: String, type: Type) : EnumDef(spelling, type) {
|
||||
@@ -158,7 +159,8 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
val typeAlign = clang_Type_getAlignOf(clang_getCursorType(fieldCursor))
|
||||
structDef.fields.add(Field(name, fieldType, offset, typeAlign))
|
||||
} else {
|
||||
// Ignore bit fields for now.
|
||||
val size = clang_getFieldDeclBitWidth(fieldCursor)
|
||||
structDef.bitFields.add(BitField(name, fieldType, offset, size))
|
||||
}
|
||||
} else {
|
||||
// Unnamed field.
|
||||
|
||||
+4
@@ -55,6 +55,8 @@ class Field(val name: String, val type: Type, val offset: Long, val typeAlign: L
|
||||
val Field.isAligned: Boolean
|
||||
get() = offset % (typeAlign * 8) == 0L
|
||||
|
||||
class BitField(val name: String, val type: Type, val offset: Long, val size: Int)
|
||||
|
||||
/**
|
||||
* C struct declaration.
|
||||
*/
|
||||
@@ -74,6 +76,8 @@ abstract class StructDef(val size: Long, val align: Int,
|
||||
val hasNaturalLayout: Boolean) {
|
||||
|
||||
abstract val fields: List<Field>
|
||||
// TODO: merge two lists to preserve declaration order.
|
||||
abstract val bitFields: List<BitField>
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -99,4 +99,7 @@ object nativeMemUtils {
|
||||
private external fun malloc(size: Long, align: Int): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_interop_free")
|
||||
private external fun cfree(ptr: NativePtr)
|
||||
private external fun cfree(ptr: NativePtr)
|
||||
|
||||
@Intrinsic external fun readBits(ptr: NativePtr, offset: Long, size: Int, signed: Boolean): Long
|
||||
@Intrinsic external fun writeBits(ptr: NativePtr, offset: Long, size: Int, value: Long)
|
||||
+30
@@ -303,9 +303,39 @@ class StubGenerator(
|
||||
log("Warning: cannot generate definition for field ${decl.kotlinName}.${field.name}")
|
||||
}
|
||||
}
|
||||
|
||||
if (platform == KotlinPlatform.NATIVE) {
|
||||
for (field in def.bitFields) {
|
||||
val typeMirror = mirror(field.type)
|
||||
val typeInfo = typeMirror.info
|
||||
val kotlinType = typeMirror.argType
|
||||
val rawType = typeInfo.bridgedType
|
||||
|
||||
out("var ${field.name.asSimpleName()}: $kotlinType")
|
||||
|
||||
val signed = field.type.getUnderlyingIntegerType().isSigned
|
||||
|
||||
val readBitsExpr =
|
||||
"readBits(this.rawPtr, ${field.offset}, ${field.size}, $signed).to${rawType.kotlinType}()"
|
||||
|
||||
out(" get() = ${typeInfo.argFromBridged(readBitsExpr)}")
|
||||
|
||||
val rawValue = typeInfo.argToBridged("value")
|
||||
val setExpr = "writeBits(this.rawPtr, ${field.offset}, ${field.size}, $rawValue.toLong())"
|
||||
out(" set(value) = $setExpr")
|
||||
out("")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private tailrec fun Type.getUnderlyingIntegerType(): IntegerType = when (this) {
|
||||
is IntegerType -> this
|
||||
is EnumType -> this.def.baseType.getUnderlyingIntegerType()
|
||||
is Typedef -> this.def.aliased.getUnderlyingIntegerType()
|
||||
else -> error(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces to [out] the definition of Kotlin class representing the reference to given forward (incomplete) struct.
|
||||
*/
|
||||
|
||||
+3
@@ -102,6 +102,9 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
|
||||
val narrow = packageScope.getContributedFunctions("narrow").single()
|
||||
|
||||
val readBits = packageScope.getContributedFunctions("readBits").single()
|
||||
val writeBits = packageScope.getContributedFunctions("writeBits").single()
|
||||
|
||||
val objCObject = packageScope.getContributedClassifier("ObjCObject") as ClassDescriptor
|
||||
val objCPointerHolder = packageScope.getContributedClassifier("ObjCPointerHolder") as ClassDescriptor
|
||||
|
||||
|
||||
+30
@@ -345,8 +345,38 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
return LLVMBlockAddress(function, bbLabel)!!
|
||||
}
|
||||
|
||||
fun and(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildAnd(builder, arg0, arg1, name)!!
|
||||
fun or(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildOr(builder, arg0, arg1, name)!!
|
||||
|
||||
fun zext(arg: LLVMValueRef, type: LLVMTypeRef): LLVMValueRef =
|
||||
LLVMBuildZExt(builder, arg, type, "")!!
|
||||
|
||||
fun sext(arg: LLVMValueRef, type: LLVMTypeRef): LLVMValueRef =
|
||||
LLVMBuildSExt(builder, arg, type, "")!!
|
||||
|
||||
fun ext(arg: LLVMValueRef, type: LLVMTypeRef, signed: Boolean): LLVMValueRef =
|
||||
if (signed) {
|
||||
sext(arg, type)
|
||||
} else {
|
||||
zext(arg, type)
|
||||
}
|
||||
|
||||
fun trunc(arg: LLVMValueRef, type: LLVMTypeRef): LLVMValueRef =
|
||||
LLVMBuildTrunc(builder, arg, type, "")!!
|
||||
|
||||
private fun shift(op: LLVMOpcode, arg: LLVMValueRef, amount: Int) =
|
||||
if (amount == 0) {
|
||||
arg
|
||||
} else {
|
||||
LLVMBuildBinOp(builder, op, arg, LLVMConstInt(arg.type, amount.toLong(), 0), "")!!
|
||||
}
|
||||
|
||||
fun shl(arg: LLVMValueRef, amount: Int) = shift(LLVMOpcode.LLVMShl, arg, amount)
|
||||
|
||||
fun shr(arg: LLVMValueRef, amount: Int, signed: Boolean) =
|
||||
shift(if (signed) LLVMOpcode.LLVMAShr else LLVMOpcode.LLVMLShr,
|
||||
arg, amount)
|
||||
|
||||
/* integers comparisons */
|
||||
fun icmpEq(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, arg0, arg1, name)!!
|
||||
|
||||
|
||||
+93
@@ -1991,10 +1991,103 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
genGetObjCMessenger(args, isLU = true)
|
||||
}
|
||||
|
||||
interop.readBits -> genReadBits(args)
|
||||
interop.writeBits -> genWriteBits(args)
|
||||
|
||||
else -> TODO(callee.descriptor.original.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun genReadBits(args: List<LLVMValueRef>): LLVMValueRef {
|
||||
val ptr = args[0]
|
||||
assert(ptr.type == int8TypePtr)
|
||||
|
||||
val offset = extractConstUnsignedInt(args[1])
|
||||
val size = extractConstUnsignedInt(args[2]).toInt()
|
||||
val signed = extractConstUnsignedInt(args[3]) != 0L
|
||||
|
||||
val prefixBitsNum = (offset % 8).toInt()
|
||||
val suffixBitsNum = (8 - ((size + offset) % 8).toInt()) % 8
|
||||
|
||||
// Note: LLVM allows to read without padding tail up to byte boundary, but the result seems to be incorrect.
|
||||
|
||||
val bitsWithPaddingNum = prefixBitsNum + size + suffixBitsNum
|
||||
val bitsWithPaddingType = LLVMIntType(bitsWithPaddingNum)!!
|
||||
|
||||
with (functionGenerationContext) {
|
||||
val bitsWithPaddingPtr = bitcast(pointerType(bitsWithPaddingType), gep(ptr, Int64(offset / 8).llvm))
|
||||
val bitsWithPadding = load(bitsWithPaddingPtr).setUnaligned()
|
||||
|
||||
val bits = shr(
|
||||
shl(bitsWithPadding, suffixBitsNum),
|
||||
prefixBitsNum + suffixBitsNum, signed
|
||||
)
|
||||
|
||||
return if (bitsWithPaddingNum == 64) {
|
||||
bits
|
||||
} else if (bitsWithPaddingNum > 64) {
|
||||
trunc(bits, kInt64)
|
||||
} else {
|
||||
ext(bits, kInt64, signed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun genWriteBits(args: List<LLVMValueRef>): LLVMValueRef {
|
||||
val ptr = args[0]
|
||||
assert(ptr.type == int8TypePtr)
|
||||
|
||||
val offset = extractConstUnsignedInt(args[1])
|
||||
val size = extractConstUnsignedInt(args[2]).toInt()
|
||||
|
||||
val value = args[3]
|
||||
assert(value.type == kInt64)
|
||||
|
||||
val bitsType = LLVMIntType(size)!!
|
||||
|
||||
val prefixBitsNum = (offset % 8).toInt()
|
||||
val suffixBitsNum = (8 - ((size + offset) % 8).toInt()) % 8
|
||||
|
||||
val bitsWithPaddingNum = prefixBitsNum + size + suffixBitsNum
|
||||
val bitsWithPaddingType = LLVMIntType(bitsWithPaddingNum)!!
|
||||
|
||||
// 0011111000:
|
||||
val discardBitsMask = LLVMConstShl(
|
||||
LLVMConstZExt(
|
||||
LLVMConstAllOnes(bitsType), // 11111
|
||||
bitsWithPaddingType
|
||||
), // 1111100000
|
||||
LLVMConstInt(bitsWithPaddingType, prefixBitsNum.toLong(), 0)
|
||||
)
|
||||
|
||||
val preservedBitsMask = LLVMConstNot(discardBitsMask)!!
|
||||
|
||||
with (functionGenerationContext) {
|
||||
val bitsWithPaddingPtr = bitcast(pointerType(bitsWithPaddingType), gep(ptr, Int64(offset / 8).llvm))
|
||||
|
||||
val bits = trunc(value, bitsType)
|
||||
|
||||
val bitsToStore = if (prefixBitsNum == 0 && suffixBitsNum == 0) {
|
||||
bits
|
||||
} else {
|
||||
val previousValue = load(bitsWithPaddingPtr).setUnaligned()
|
||||
val preservedBits = and(previousValue, preservedBitsMask)
|
||||
val bitsWithPadding = shl(zext(bits, bitsWithPaddingType), prefixBitsNum)
|
||||
|
||||
or(bitsWithPadding, preservedBits)
|
||||
}
|
||||
|
||||
LLVMBuildStore(builder, bitsToStore, bitsWithPaddingPtr)!!.setUnaligned()
|
||||
}
|
||||
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
}
|
||||
|
||||
private fun extractConstUnsignedInt(value: LLVMValueRef): Long {
|
||||
assert(LLVMIsConstant(value) != 0)
|
||||
return LLVMConstIntGetZExtValue(value)
|
||||
}
|
||||
|
||||
private fun genGetObjCClass(classDescriptor: ClassDescriptor): LLVMValueRef {
|
||||
assert(!classDescriptor.isInterface)
|
||||
|
||||
|
||||
+3
@@ -126,6 +126,7 @@ internal val RuntimeAware.kTypeInfoPtr: LLVMTypeRef
|
||||
get() = pointerType(kTypeInfo)
|
||||
internal val kInt1 = LLVMInt1Type()!!
|
||||
internal val kBoolean = kInt1
|
||||
internal val kInt64 = LLVMInt64Type()!!
|
||||
internal val kInt8Ptr = pointerType(int8Type)
|
||||
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
|
||||
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)!!
|
||||
@@ -258,3 +259,5 @@ fun parseBitcodeFile(path: String): LLVMModuleRef = memScoped {
|
||||
|
||||
internal fun String.mdString() = LLVMMDString(this, this.length)!!
|
||||
internal fun node(vararg it:LLVMValueRef) = LLVMMDNode(it.toList().toCValues(), it.size)
|
||||
|
||||
internal fun LLVMValueRef.setUnaligned() = apply { LLVMSetAlignment(this, 1) }
|
||||
|
||||
@@ -1989,6 +1989,11 @@ kotlinNativeInterop {
|
||||
flavor 'native'
|
||||
}
|
||||
|
||||
bitfields {
|
||||
defFile 'interop/basics/bitfields.def'
|
||||
flavor 'native'
|
||||
}
|
||||
|
||||
if (isMac()) {
|
||||
opengl {
|
||||
defFile '../../samples/opengl/src/main/c_interop/opengl.def'
|
||||
@@ -2046,6 +2051,12 @@ task interop4(type: RunInteropKonanTest) {
|
||||
interop = 'cstdio'
|
||||
}
|
||||
|
||||
task interop_bitfields(type: RunInteropKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
source = "interop/basics/bf.kt"
|
||||
interop = 'bitfields'
|
||||
}
|
||||
|
||||
task interop_echo_server(type: RunInteropKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
if (!isMac()) {
|
||||
@@ -2066,6 +2077,7 @@ if (isMac()) {
|
||||
}
|
||||
|
||||
task interop_objc_smoke(type: RunInteropKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
goldValue = "Hello, World!\nKotlin says: Hello, everybody!\nHello from Kotlin\n2, 1\nDeallocated\nDeallocated\n"
|
||||
|
||||
source = "interop/objc/smoke.kt"
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import bitfields.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
fun assertEquals(value1: Any?, value2: Any?) {
|
||||
if (value1 != value2)
|
||||
throw AssertionError("Expected $value1, got $value2")
|
||||
}
|
||||
|
||||
fun check(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) {
|
||||
assertEquals(x1, s.x1)
|
||||
assertEquals(x1, getX1(s.ptr))
|
||||
|
||||
assertEquals(x2, s.x2)
|
||||
assertEquals(x2, getX2(s.ptr))
|
||||
|
||||
assertEquals(x3, s.x3)
|
||||
assertEquals(x3, getX3(s.ptr))
|
||||
|
||||
assertEquals(x4, s.x4)
|
||||
assertEquals(x4, getX4(s.ptr))
|
||||
|
||||
assertEquals(x5, s.x5)
|
||||
assertEquals(x5, getX5(s.ptr))
|
||||
|
||||
assertEquals(x6, s.x6)
|
||||
assertEquals(x6, getX6(s.ptr))
|
||||
}
|
||||
|
||||
fun assign(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) {
|
||||
s.x1 = x1
|
||||
s.x2 = x2
|
||||
s.x3 = x3
|
||||
s.x4 = x4
|
||||
s.x5 = x5
|
||||
s.x6 = x6
|
||||
}
|
||||
|
||||
fun assignReversed(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) {
|
||||
s.x6 = x6
|
||||
s.x5 = x5
|
||||
s.x4 = x4
|
||||
s.x3 = x3
|
||||
s.x2 = x2
|
||||
s.x1 = x1
|
||||
}
|
||||
|
||||
fun test(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) {
|
||||
assign(s, x1, x2, x3, x4, x5, x6)
|
||||
check(s, x1, x2, x3, x4, x5, x6)
|
||||
|
||||
assignReversed(s, x1, x2, x3, x4, x5, x6)
|
||||
check(s, x1, x2, x3, x4, x5, x6)
|
||||
|
||||
// Also check with some insignificant bits modified:
|
||||
|
||||
assign(s, x1 + 2, x2, (x3 + 8).toShort(), x4 - 16, x5 + 32, x6 + Long.MIN_VALUE)
|
||||
check(s, x1, x2, x3, x4, x5, x6)
|
||||
|
||||
assignReversed(s, x1 + 2, x2, (x3 + 8).toShort(), x4 - 16, x5 + 32, x6 + Long.MIN_VALUE)
|
||||
check(s, x1, x2, x3, x4, x5, x6)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
memScoped {
|
||||
val s = alloc<S>()
|
||||
for (x1 in -1L..0L)
|
||||
for (x2 in B2.values())
|
||||
for (x3 in 0..7)
|
||||
for (x4 in intArrayOf(0, 6, 15))
|
||||
for (x5 in intArrayOf(-16, -2, -1, 0, 5, 15))
|
||||
for (x6 in longArrayOf(Long.MIN_VALUE/2, -1L shl 36, -325L, 0, 1L shl 48, Long.MAX_VALUE/2))
|
||||
test(s, x1, x2, x3.toShort(), x4, x5, x6)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
enum B2 {
|
||||
ZERO, ONE, TWO, THREE
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) S {
|
||||
long long x1 : 1;
|
||||
enum B2 x2 : 2;
|
||||
unsigned short x3 : 3;
|
||||
unsigned int x4 : 4;
|
||||
int x5 : 5;
|
||||
long long x6 : 63;
|
||||
};
|
||||
|
||||
static long long getX1(struct S* s) { return s->x1; }
|
||||
static enum B2 getX2(struct S* s) { return s->x2; }
|
||||
static unsigned short getX3(struct S* s) { return s->x3; }
|
||||
static unsigned int getX4(struct S* s) { return s->x4; }
|
||||
static int getX5(struct S* s) { return s->x5; }
|
||||
static long long getX6(struct S* s) { return s->x6; }
|
||||
Reference in New Issue
Block a user