Skip to content

Commit b008838

Browse files
author
Arch D. Robison
committed
Use LLVM alignment for tuples that become LLVM vectors.
But take care when that alignment exceeds the GC maximum alignment.
1 parent c765cfd commit b008838

File tree

3 files changed

+94
-52
lines changed

3 files changed

+94
-52
lines changed

src/alloc.c

+7
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,13 @@ void jl_compute_field_offsets(jl_datatype_t *st)
643643
jl_throw(jl_overflow_exception);
644644
sz += fsz;
645645
}
646+
if (jl_is_tuple_type(st)) {
647+
// Some tuples become LLVM vectors with stronger alignment than what was calculated above.
648+
unsigned al = jl_llvm_special_tuple_alignment(st);
649+
assert(al % alignm == 0);
650+
if (al)
651+
alignm = al;
652+
}
646653
st->alignment = alignm;
647654
st->size = LLT_ALIGN(sz, alignm);
648655
if (st->size > sz)

src/cgutils.cpp

+85-52
Original file line numberDiff line numberDiff line change
@@ -348,70 +348,103 @@ JL_DLLEXPORT Type *julia_type_to_llvm(jl_value_t *jt, bool *isboxed)
348348
}
349349
}
350350

351+
// Set jst->struct_decl to the LLVM type corresponding to jst.
352+
// jst must be a tuple or structtype.
353+
static void set_struct_decl(jl_datatype_t *jst) {
354+
assert(jst->struct_decl == nullptr);
355+
bool isTuple = jl_is_tuple_type(jst);
356+
bool isVecElement = is_vecelement_type((jl_value_t*)jst);
357+
StructType *structdecl;
358+
if (!isTuple) {
359+
if (!isVecElement) {
360+
structdecl = StructType::create(jl_LLVMContext, jl_symbol_name(jst->name->name));
361+
jst->struct_decl = structdecl;
362+
}
363+
}
364+
size_t ntypes = jl_datatype_nfields(jst);
365+
assert(ntypes > 0);
366+
std::vector<Type*> latypes(0);
367+
bool isvector = true;
368+
Type *lasttype = NULL;
369+
for(size_t i = 0; i < ntypes; ++i) {
370+
Type *lty;
371+
if (jl_field_isptr(jst, i))
372+
lty = T_pjlvalue;
373+
else {
374+
jl_value_t *ty = jl_svecref(jst->types, i);
375+
lty = ty==(jl_value_t*)jl_bool_type ? T_int8 : julia_type_to_llvm(ty);
376+
}
377+
if (lasttype != NULL && lasttype != lty)
378+
isvector = false;
379+
lasttype = lty;
380+
if (type_is_ghost(lty))
381+
lty = NoopType;
382+
latypes.push_back(lty);
383+
}
384+
if (!isTuple) {
385+
if (!isVecElement)
386+
structdecl->setBody(latypes);
387+
else
388+
// VecElement type is unwrapped in LLVM
389+
jst->struct_decl = latypes[0];
390+
}
391+
else {
392+
if (isvector && lasttype != T_int1 && !type_is_ghost(lasttype)) {
393+
// Homogeneous tuple
394+
// TODO: currently we get LLVM assertion failures for other vector sizes
395+
bool validVectorSize = (ntypes == 2 || ntypes == 4 || ntypes == 8 || ntypes == 16);
396+
if (lasttype->isSingleValueType() && !lasttype->isVectorTy() && validVectorSize && is_vecelement_type(jl_svecref(jst->types,0)))
397+
jst->struct_decl = VectorType::get(lasttype, ntypes);
398+
else
399+
jst->struct_decl = ArrayType::get(lasttype, ntypes);
400+
}
401+
else {
402+
// Heterogeneous tuple
403+
jst->struct_decl = StructType::get(jl_LLVMContext,ArrayRef<Type*>(&latypes[0],ntypes));
404+
}
405+
}
406+
}
407+
351408
static Type *julia_struct_to_llvm(jl_value_t *jt, bool *isboxed)
352409
{
353410
// this function converts a Julia Type into the equivalent LLVM struct
354411
// use this where C-compatible (unboxed) structs are desired
355412
// use julia_type_to_llvm directly when you want to preserve Julia's type semantics
356-
bool isTuple = jl_is_tuple_type(jt);
357413
if (isboxed) *isboxed = false;
358-
if ((isTuple || jl_is_structtype(jt)) && !jl_is_array_type(jt)) {
414+
if ((jl_is_tuple_type(jt) || jl_is_structtype(jt)) && !jl_is_array_type(jt)) {
359415
if (!jl_is_leaf_type(jt))
360-
return NULL;
416+
return nullptr;
361417
jl_datatype_t *jst = (jl_datatype_t*)jt;
362-
if (jst->struct_decl == NULL) {
363-
size_t ntypes = jl_datatype_nfields(jst);
364-
if (ntypes == 0 || jst->size == 0)
418+
if (jst->struct_decl == nullptr) {
419+
assert(jl_datatype_nfields(jst) != 0 || jst->size == 0);
420+
if (jst->size == 0)
365421
return T_void;
366-
StructType *structdecl;
367-
if (!isTuple) {
368-
structdecl = StructType::create(jl_LLVMContext, jl_symbol_name(jst->name->name));
369-
jst->struct_decl = structdecl;
370-
}
371-
std::vector<Type*> latypes(0);
372-
size_t i;
373-
bool isvector = true;
374-
Type *lasttype = NULL;
375-
for(i = 0; i < ntypes; i++) {
376-
jl_value_t *ty = jl_svecref(jst->types, i);
377-
Type *lty;
378-
if (jl_field_isptr(jst, i))
379-
lty = T_pjlvalue;
380-
else
381-
lty = ty==(jl_value_t*)jl_bool_type ? T_int8 : julia_type_to_llvm(ty);
382-
if (lasttype != NULL && lasttype != lty)
383-
isvector = false;
384-
lasttype = lty;
385-
if (type_is_ghost(lty))
386-
lty = NoopType;
387-
latypes.push_back(lty);
388-
}
389-
if (!isTuple) {
390-
if (is_vecelement_type(jt))
391-
// VecElement type is unwrapped in LLVM
392-
jst->struct_decl = latypes[0];
393-
else
394-
structdecl->setBody(latypes);
395-
}
396-
else {
397-
if (isvector && lasttype != T_int1 && !type_is_ghost(lasttype)) {
398-
// TODO: currently we get LLVM assertion failures for other vector sizes
399-
bool validVectorSize = (ntypes == 2 || ntypes == 4 || ntypes == 8 || ntypes == 16);
400-
if (lasttype->isSingleValueType() && !lasttype->isVectorTy() && validVectorSize && is_vecelement_type(jl_svecref(jst->types,0)))
401-
jst->struct_decl = VectorType::get(lasttype, ntypes);
402-
else
403-
jst->struct_decl = ArrayType::get(lasttype, ntypes);
404-
}
405-
else {
406-
jst->struct_decl = StructType::get(jl_LLVMContext,ArrayRef<Type*>(&latypes[0],ntypes));
407-
}
408-
}
422+
set_struct_decl(jst);
409423
}
410424
return (Type*)jst->struct_decl;
411425
}
412426
return julia_type_to_llvm(jt, isboxed);
413427
}
414428

429+
// Returns 0 if no special rule applies
430+
unsigned jl_llvm_special_tuple_alignment(jl_tupletype_t *st)
431+
{
432+
size_t ntypes = jl_datatype_nfields(st);
433+
// Run fast rejection tests
434+
if (ntypes==0)
435+
return 0;
436+
for(size_t i = 0; i < ntypes; i++) {
437+
jl_value_t *ty = jl_svecref(st->types, i);
438+
if (!is_vecelement_type(ty))
439+
return 0;
440+
}
441+
// Not rejected. Get definitive answer from corresponding LLVM type.
442+
jl_datatype_t *jst = (jl_datatype_t*)st;
443+
if (!jst->struct_decl)
444+
set_struct_decl(jst);
445+
return jl_ExecutionEngine->getDataLayout().getABITypeAlignment((Type*)jst->struct_decl);
446+
}
447+
415448
static bool is_datatype_all_pointers(jl_datatype_t *dt)
416449
{
417450
size_t i, l = jl_datatype_nfields(dt);
@@ -766,9 +799,9 @@ static Value *emit_bounds_check(const jl_cgval_t &ainfo, jl_value_t *ty, Value *
766799
// Parameter ptr should be the pointer argument for the LoadInst or StoreInst.
767800
// It is currently unused, but might be used in the future for a more precise answer.
768801
static unsigned julia_alignment(Value* /*ptr*/, jl_value_t *jltype, unsigned alignment) {
769-
if (!alignment && ((jl_datatype_t*)jltype)->size >= 32) {
770-
// Type might contain a 32-byte vector. Use Julia alignment to prevent llvm from assuming default alignment.
771-
return ((jl_datatype_t*)jltype)->alignment;
802+
if (!alignment && ((jl_datatype_t*)jltype)->alignment > MAX_ALIGN) {
803+
// Type's natural alignment exceeds strictess alignment promised in heap, so return the heap alignment.
804+
return MAX_ALIGN;
772805
}
773806
return alignment;
774807
}

src/julia_internal.h

+2
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m);
213213

214214
jl_value_t *jl_nth_slot_type(jl_tupletype_t *sig, size_t i);
215215
void jl_compute_field_offsets(jl_datatype_t *st);
216+
unsigned jl_llvm_special_tuple_alignment(jl_datatype_t *st);
217+
216218
jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, size_t *dims,
217219
int isunboxed, int elsz);
218220
jl_lambda_info_t *jl_copy_lambda_info(jl_lambda_info_t *linfo);

0 commit comments

Comments
 (0)