[Runtime] Use mimalloc aligned allocation for interop types (#3973)
Rationale: This is a workaround for some 32-bit platforms where `calloc` does not align for vector type properly. More general, the patch implements conservative approach taking into account that vector type is not a C standard type and may require explicit alignment when allocated in the heap. * [Runtime] Use mimalloc aligned allocation for interop types Fixed [KT-37272] when mimalloc used (opt-in now); not supported by std alloc yet * Move vector types into a separate test and run it both with mimalloc and with standard allocator * fixup! Move vector types into a separate test and run it both with mimalloc and with standard allocator Co-authored-by: Vladimir Ivanov <vladimir.d.ivanov@jetbrains.com> Co-authored-by: Pavel Punegov <pavel.punegov@jetbrains.com>
This commit is contained in:
@@ -3379,6 +3379,10 @@ createInterop("cvalues") {
|
|||||||
it.defFile 'interop/basics/cvalues.def'
|
it.defFile 'interop/basics/cvalues.def'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createInterop("cvectors") {
|
||||||
|
it.defFile 'interop/basics/cvectors.def'
|
||||||
|
}
|
||||||
|
|
||||||
createInterop("cstructs") {
|
createInterop("cstructs") {
|
||||||
it.defFile 'interop/basics/cstructs.def'
|
it.defFile 'interop/basics/cstructs.def'
|
||||||
}
|
}
|
||||||
@@ -3560,6 +3564,20 @@ interopTest("interop_types") {
|
|||||||
interop = 'ctypes'
|
interop = 'ctypes'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interopTest("interop_vectors") {
|
||||||
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
|
source = "interop/basics/vectors.kt"
|
||||||
|
interop = 'cvectors'
|
||||||
|
}
|
||||||
|
|
||||||
|
interopTest("interop_vectors_mimalloc") {
|
||||||
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
|
source = "interop/basics/vectors.kt"
|
||||||
|
interop = 'cvectors'
|
||||||
|
flags = [ "-Xallocator=mimalloc" ]
|
||||||
|
arguments = [ "mimalloc" ]
|
||||||
|
}
|
||||||
|
|
||||||
interopTest("interop_mangling") {
|
interopTest("interop_mangling") {
|
||||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
source = "interop/basics/mangling.kt"
|
source = "interop/basics/mangling.kt"
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
---
|
---
|
||||||
typedef float __attribute__ ((__vector_size__ (16))) KVector4f;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int i;
|
int i;
|
||||||
} Trivial;
|
} Trivial;
|
||||||
@@ -11,7 +9,6 @@ enum E {
|
|||||||
|
|
||||||
struct Complex {
|
struct Complex {
|
||||||
unsigned int ui;
|
unsigned int ui;
|
||||||
KVector4f vec4f;
|
|
||||||
Trivial t;
|
Trivial t;
|
||||||
struct Complex* next;
|
struct Complex* next;
|
||||||
enum E e;
|
enum E e;
|
||||||
@@ -19,15 +16,12 @@ struct Complex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Complex produceComplex() {
|
struct Complex produceComplex() {
|
||||||
struct Complex complex;
|
struct Complex complex = {
|
||||||
complex.ui = 128;
|
.ui = 128,
|
||||||
KVector4f vec = { 1.0, 1.0, 1.0, 1.0 };
|
.t = {1},
|
||||||
complex.vec4f = vec;
|
.next = 0,
|
||||||
Trivial t = { .i = 1 };
|
.e = R,
|
||||||
complex.t = t;
|
.arr = {-51, -19}
|
||||||
complex.next = 0;
|
};
|
||||||
complex.e = R;
|
|
||||||
complex.arr[0] = -51;
|
|
||||||
complex.arr[1] = -19;
|
|
||||||
return complex;
|
return complex;
|
||||||
};
|
};
|
||||||
@@ -87,15 +87,3 @@ enum EnumExplicitChar : char {
|
|||||||
EnumExplicitCharB = 'b',
|
EnumExplicitCharB = 'b',
|
||||||
EnumExplicitCharDup = 'a'
|
EnumExplicitCharDup = 'a'
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef float __attribute__ ((__vector_size__ (16))) KVector4f;
|
|
||||||
typedef int __attribute__ ((__vector_size__ (16))) KVector4i32;
|
|
||||||
|
|
||||||
static float sendV4F(KVector4f v) {
|
|
||||||
return v[0] + 2 * v[1] + 4 * v[2] + 8 * v[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sendV4I(KVector4i32 v) {
|
|
||||||
return v[0] + 2 * v[1] + 4 * v[2] + 8 * v[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
typedef float __attribute__ ((__vector_size__ (16))) KVector4f;
|
||||||
|
typedef int __attribute__ ((__vector_size__ (16))) KVector4i32;
|
||||||
|
|
||||||
|
struct Complex {
|
||||||
|
unsigned int ui;
|
||||||
|
KVector4f vec4f;
|
||||||
|
struct Complex* next;
|
||||||
|
int arr[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Complex produceComplex() {
|
||||||
|
struct Complex complex = {
|
||||||
|
.ui = 128,
|
||||||
|
.vec4f = {1.0, 1.0, 1.0, 1.0},
|
||||||
|
.next = 0,
|
||||||
|
.arr = {-51, -19}
|
||||||
|
};
|
||||||
|
return complex;
|
||||||
|
};
|
||||||
|
|
||||||
|
static float sendV4F(KVector4f v) {
|
||||||
|
return v[0] + 2 * v[1] + 4 * v[2] + 8 * v[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sendV4I(KVector4i32 v) {
|
||||||
|
return v[0] + 2 * v[1] + 4 * v[2] + 8 * v[3];
|
||||||
|
}
|
||||||
@@ -19,10 +19,6 @@ fun main() {
|
|||||||
next = null
|
next = null
|
||||||
assertEquals(next, null)
|
assertEquals(next, null)
|
||||||
|
|
||||||
assertEquals(vec4f, vectorOf(1.0f, 1.0f, 1.0f, 1.0f))
|
|
||||||
vec4f = vectorOf(0.0f, 0.0f, 0.0f, 0.0f)
|
|
||||||
assertEquals(vec4f, vectorOf(0.0f, 0.0f, 0.0f, 0.0f))
|
|
||||||
|
|
||||||
assertEquals(e, E.R)
|
assertEquals(e, E.R)
|
||||||
e = E.G
|
e = E.G
|
||||||
assertEquals(e, E.G)
|
assertEquals(e, E.G)
|
||||||
|
|||||||
@@ -27,18 +27,5 @@ fun main() {
|
|||||||
assertEquals('a'.toByte(), EnumExplicitCharA)
|
assertEquals('a'.toByte(), EnumExplicitCharA)
|
||||||
assertEquals('b'.toByte(), EnumExplicitCharB)
|
assertEquals('b'.toByte(), EnumExplicitCharB)
|
||||||
assertEquals(EnumExplicitCharA, EnumExplicitCharDup)
|
assertEquals(EnumExplicitCharA, EnumExplicitCharDup)
|
||||||
|
|
||||||
// FIXME: KT-36285
|
|
||||||
if (Platform.osFamily != OsFamily.LINUX && Platform.cpuArchitecture != CpuArchitecture.ARM32) {
|
|
||||||
assertEquals(49, sendV4I(vectorOf(1, 2, 3, 4)))
|
|
||||||
}
|
|
||||||
assertEquals(49, (sendV4F(vectorOf(1f, 2f, 3f, 4f)) + 0.00001).toInt())
|
|
||||||
|
|
||||||
memScoped {
|
|
||||||
val vector = alloc<KVector4i32Var>().also {
|
|
||||||
it.value = vectorOf(1, 2, 3, 4)
|
|
||||||
}
|
|
||||||
assertEquals(vector.value, vectorOf(1, 2, 3, 4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.native.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import cvectors.*
|
||||||
|
|
||||||
|
fun isWin32() = Platform.osFamily == OsFamily.WINDOWS && Platform.cpuArchitecture == CpuArchitecture.X86
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val mimalloc = args.takeIf { it.size == 1 }?.get(0) == "mimalloc"
|
||||||
|
|
||||||
|
// See KT-37272. Fixed in mimalloc
|
||||||
|
if (mimalloc || !isWin32()) {
|
||||||
|
produceComplex().useContents {
|
||||||
|
assertEquals(vec4f, vectorOf(1.0f, 1.0f, 1.0f, 1.0f))
|
||||||
|
vec4f = vectorOf(0.0f, 0.0f, 0.0f, 0.0f)
|
||||||
|
assertEquals(vec4f, vectorOf(0.0f, 0.0f, 0.0f, 0.0f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: KT-36285
|
||||||
|
if (Platform.osFamily != OsFamily.LINUX || Platform.cpuArchitecture != CpuArchitecture.ARM32) {
|
||||||
|
assertEquals(49, sendV4I(vectorOf(1, 2, 3, 4)))
|
||||||
|
}
|
||||||
|
assertEquals(49, (sendV4F(vectorOf(1f, 2f, 3f, 4f)) + 0.00001).toInt())
|
||||||
|
|
||||||
|
if (mimalloc || !isWin32()) {
|
||||||
|
memScoped {
|
||||||
|
val vector = alloc<KVector4i32Var>().also {
|
||||||
|
it.value = vectorOf(1, 2, 3, 4)
|
||||||
|
}
|
||||||
|
assertEquals(vector.value, vectorOf(1, 2, 3, 4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ void* Kotlin_interop_malloc(KLong size, KInt align) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* result = konan::calloc(1, size);
|
void* result = konan::calloc_aligned(1, size, align);
|
||||||
if ((reinterpret_cast<uintptr_t>(result) & (align - 1)) != 0) {
|
if ((reinterpret_cast<uintptr_t>(result) & (align - 1)) != 0) {
|
||||||
// Unaligned!
|
// Unaligned!
|
||||||
RuntimeAssert(false, "unsupported alignment");
|
RuntimeAssert(false, "unsupported alignment");
|
||||||
|
|||||||
@@ -236,17 +236,25 @@ extern "C" void* dlcalloc(size_t, size_t);
|
|||||||
extern "C" void dlfree(void*);
|
extern "C" void dlfree(void*);
|
||||||
#define calloc_impl dlcalloc
|
#define calloc_impl dlcalloc
|
||||||
#define free_impl dlfree
|
#define free_impl dlfree
|
||||||
|
#define calloc_aligned_impl(count, size, alignment) dlcalloc(count, size)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
extern "C" void* konan_calloc_impl(size_t, size_t);
|
extern "C" void* konan_calloc_impl(size_t, size_t);
|
||||||
extern "C" void konan_free_impl(void*);
|
extern "C" void konan_free_impl(void*);
|
||||||
|
extern "C" void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment);
|
||||||
#define calloc_impl konan_calloc_impl
|
#define calloc_impl konan_calloc_impl
|
||||||
#define free_impl konan_free_impl
|
#define free_impl konan_free_impl
|
||||||
|
#define calloc_aligned_impl konan_calloc_aligned_impl
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void* calloc(size_t count, size_t size) {
|
void* calloc(size_t count, size_t size) {
|
||||||
return calloc_impl(count, size);
|
return calloc_impl(count, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* calloc_aligned(size_t count, size_t size, size_t alignment) {
|
||||||
|
return calloc_aligned_impl(count, size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
void free(void* pointer) {
|
void free(void* pointer) {
|
||||||
free_impl(pointer);
|
free_impl(pointer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ void *memset(void *b, int c, size_t len);
|
|||||||
|
|
||||||
// Memory operations.
|
// Memory operations.
|
||||||
void* calloc(size_t count, size_t size);
|
void* calloc(size_t count, size_t size);
|
||||||
|
void* calloc_aligned(size_t count, size_t size, size_t alignment);
|
||||||
void free(void* ptr);
|
void free(void* ptr);
|
||||||
|
|
||||||
// Time operations.
|
// Time operations.
|
||||||
|
|||||||
@@ -8,9 +8,16 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
void* mi_calloc(size_t, size_t);
|
void* mi_calloc(size_t, size_t);
|
||||||
void mi_free(void*);
|
void mi_free(void*);
|
||||||
|
void* mi_calloc_aligned(size_t count, size_t size, size_t alignment);
|
||||||
|
|
||||||
void* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
void* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
||||||
return mi_calloc(n_elements, elem_size);
|
return mi_calloc(n_elements, elem_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment) {
|
||||||
|
return mi_calloc_aligned(count, size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
void konan_free_impl (void* mem) {
|
void konan_free_impl (void* mem) {
|
||||||
mi_free(mem);
|
mi_free(mem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,14 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
// Memory operations.
|
// Memory operations.
|
||||||
void* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
void* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
||||||
return calloc(n_elements, elem_size);
|
return calloc(n_elements, elem_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment) {
|
||||||
|
// alignment is not supported by std alloc - use mimalloc
|
||||||
|
return calloc(count, size);
|
||||||
|
}
|
||||||
|
|
||||||
void konan_free_impl (void* mem) {
|
void konan_free_impl (void* mem) {
|
||||||
free(mem);
|
free(mem);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user