committed by
SvyatoslavScherbina
parent
ce27695290
commit
5c3e8d63d4
+3
-1
@@ -34,6 +34,7 @@ private class StructDefImpl(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
override val fields = mutableListOf<Field>()
|
override val fields = mutableListOf<Field>()
|
||||||
|
override val bitFields = mutableListOf<BitField>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EnumDefImpl(spelling: String, type: Type) : EnumDef(spelling, type) {
|
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))
|
val typeAlign = clang_Type_getAlignOf(clang_getCursorType(fieldCursor))
|
||||||
structDef.fields.add(Field(name, fieldType, offset, typeAlign))
|
structDef.fields.add(Field(name, fieldType, offset, typeAlign))
|
||||||
} else {
|
} else {
|
||||||
// Ignore bit fields for now.
|
val size = clang_getFieldDeclBitWidth(fieldCursor)
|
||||||
|
structDef.bitFields.add(BitField(name, fieldType, offset, size))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Unnamed field.
|
// 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
|
val Field.isAligned: Boolean
|
||||||
get() = offset % (typeAlign * 8) == 0L
|
get() = offset % (typeAlign * 8) == 0L
|
||||||
|
|
||||||
|
class BitField(val name: String, val type: Type, val offset: Long, val size: Int)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* C struct declaration.
|
* C struct declaration.
|
||||||
*/
|
*/
|
||||||
@@ -74,6 +76,8 @@ abstract class StructDef(val size: Long, val align: Int,
|
|||||||
val hasNaturalLayout: Boolean) {
|
val hasNaturalLayout: Boolean) {
|
||||||
|
|
||||||
abstract val fields: List<Field>
|
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
|
private external fun malloc(size: Long, align: Int): NativePtr
|
||||||
|
|
||||||
@SymbolName("Kotlin_interop_free")
|
@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}")
|
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.
|
* 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 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 objCObject = packageScope.getContributedClassifier("ObjCObject") as ClassDescriptor
|
||||||
val objCPointerHolder = packageScope.getContributedClassifier("ObjCPointerHolder") as ClassDescriptor
|
val objCPointerHolder = packageScope.getContributedClassifier("ObjCPointerHolder") as ClassDescriptor
|
||||||
|
|
||||||
|
|||||||
+30
@@ -345,8 +345,38 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
return LLVMBlockAddress(function, bbLabel)!!
|
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 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 */
|
/* integers comparisons */
|
||||||
fun icmpEq(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, arg0, arg1, name)!!
|
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)
|
genGetObjCMessenger(args, isLU = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interop.readBits -> genReadBits(args)
|
||||||
|
interop.writeBits -> genWriteBits(args)
|
||||||
|
|
||||||
else -> TODO(callee.descriptor.original.toString())
|
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 {
|
private fun genGetObjCClass(classDescriptor: ClassDescriptor): LLVMValueRef {
|
||||||
assert(!classDescriptor.isInterface)
|
assert(!classDescriptor.isInterface)
|
||||||
|
|
||||||
|
|||||||
+3
@@ -126,6 +126,7 @@ internal val RuntimeAware.kTypeInfoPtr: LLVMTypeRef
|
|||||||
get() = pointerType(kTypeInfo)
|
get() = pointerType(kTypeInfo)
|
||||||
internal val kInt1 = LLVMInt1Type()!!
|
internal val kInt1 = LLVMInt1Type()!!
|
||||||
internal val kBoolean = kInt1
|
internal val kBoolean = kInt1
|
||||||
|
internal val kInt64 = LLVMInt64Type()!!
|
||||||
internal val kInt8Ptr = pointerType(int8Type)
|
internal val kInt8Ptr = pointerType(int8Type)
|
||||||
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
|
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
|
||||||
internal val kNullInt8Ptr = LLVMConstNull(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 String.mdString() = LLVMMDString(this, this.length)!!
|
||||||
internal fun node(vararg it:LLVMValueRef) = LLVMMDNode(it.toList().toCValues(), it.size)
|
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'
|
flavor 'native'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitfields {
|
||||||
|
defFile 'interop/basics/bitfields.def'
|
||||||
|
flavor 'native'
|
||||||
|
}
|
||||||
|
|
||||||
if (isMac()) {
|
if (isMac()) {
|
||||||
opengl {
|
opengl {
|
||||||
defFile '../../samples/opengl/src/main/c_interop/opengl.def'
|
defFile '../../samples/opengl/src/main/c_interop/opengl.def'
|
||||||
@@ -2046,6 +2051,12 @@ task interop4(type: RunInteropKonanTest) {
|
|||||||
interop = 'cstdio'
|
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) {
|
task interop_echo_server(type: RunInteropKonanTest) {
|
||||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
if (!isMac()) {
|
if (!isMac()) {
|
||||||
@@ -2066,6 +2077,7 @@ if (isMac()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task interop_objc_smoke(type: RunInteropKonanTest) {
|
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"
|
goldValue = "Hello, World!\nKotlin says: Hello, everybody!\nHello from Kotlin\n2, 1\nDeallocated\nDeallocated\n"
|
||||||
|
|
||||||
source = "interop/objc/smoke.kt"
|
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