diff --git a/src/cgutils.cpp b/src/cgutils.cpp index ea59d4e298f1c..b560f84a4db8c 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -783,9 +783,6 @@ static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, lty = JuliaType::get_prjlvalue_ty(ctxt); isvector = false; } - else if (ty == (jl_value_t*)jl_bool_type) { - lty = getInt8Ty(ctxt); - } else if (jl_is_uniontype(ty)) { // pick an Integer type size such that alignment will generally be correct, // and always end with an Int8 (selector byte). diff --git a/src/codegen.cpp b/src/codegen.cpp index b9c796f2b3c1a..2415b268fa01e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2163,9 +2163,12 @@ static bool valid_as_globalinit(const Value *v) { return isa(v); } +static Value *zext_struct(jl_codectx_t &ctx, Value *V); + static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, Value *v, jl_value_t *typ, Value *tindex) { Value *loc; + v = zext_struct(ctx, v); if (valid_as_globalinit(v)) { // llvm can't handle all the things that could be inside a ConstantExpr assert(jl_is_concrete_type(typ)); // not legal to have an unboxed abstract type loc = get_pointer_to_constant(ctx.emission_context, cast(v), Align(julia_alignment(typ)), "_j_const", *jl_Module); @@ -2291,17 +2294,6 @@ static void alloc_def_flag(jl_codectx_t &ctx, jl_varinfo_t& vi) // --- utilities --- -static Constant *undef_value_for_type(Type *T) { - auto tracked = CountTrackedPointers(T); - Constant *undef; - if (tracked.count) - // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL - undef = Constant::getNullValue(T); - else - undef = UndefValue::get(T); - return undef; -} - static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block) { Function *f = irbuilder.GetInsertBlock()->getParent(); @@ -3574,7 +3566,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (f == jl_builtin_is && nargs == 2) { // emit comparison test Value *ans = emit_f_is(ctx, argv[1], argv[2]); - *ret = mark_julia_type(ctx, ctx.builder.CreateZExt(ans, getInt8Ty(ctx.builder.getContext())), false, jl_bool_type); + *ret = mark_julia_type(ctx, ans, false, jl_bool_type); return true; } @@ -3613,8 +3605,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_type_type(ty.typ) && !jl_has_free_typevars(ty.typ)) { jl_value_t *tp0 = jl_tparam0(ty.typ); Value *isa_result = emit_isa(ctx, arg, tp0, Twine()).first; - if (isa_result->getType() == getInt1Ty(ctx.builder.getContext())) - isa_result = ctx.builder.CreateZExt(isa_result, getInt8Ty(ctx.builder.getContext())); *ret = mark_julia_type(ctx, isa_result, false, jl_bool_type); return true; } @@ -5551,16 +5541,15 @@ static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const T emit_typecheck(ctx, condV, (jl_value_t*)jl_bool_type, msg); } if (isbool) { - Value *cond = emit_unbox(ctx, getInt8Ty(ctx.builder.getContext()), condV, (jl_value_t*)jl_bool_type); - assert(cond->getType() == getInt8Ty(ctx.builder.getContext())); - return ctx.builder.CreateXor(ctx.builder.CreateTrunc(cond, getInt1Ty(ctx.builder.getContext())), ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1)); + Value *cond = emit_unbox(ctx, getInt1Ty(ctx.builder.getContext()), condV, (jl_value_t*)jl_bool_type); + return ctx.builder.CreateNot(cond); } if (condV.isboxed) { return ctx.builder.CreateICmpEQ(boxed(ctx, condV), track_pjlvalue(ctx, literal_pointer_val(ctx, jl_false))); } - // not a boolean - return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); // TODO: replace with Undef + // not a boolean (unreachable dead code) + return UndefValue::get(getInt1Ty(ctx.builder.getContext())); } static Value *emit_condition(jl_codectx_t &ctx, jl_value_t *cond, const Twine &msg) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index b4d493ee80800..fde27b910faea 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -173,12 +173,7 @@ static Type *INTT(Type *t, const DataLayout &DL) static Value *uint_cnvt(jl_codectx_t &ctx, Type *to, Value *x) { - Type *t = x->getType(); - if (t == to) - return x; - if (to->getPrimitiveSizeInBits() < x->getType()->getPrimitiveSizeInBits()) - return ctx.builder.CreateTrunc(x, to); - return ctx.builder.CreateZExt(x, to); + return ctx.builder.CreateZExtOrTrunc(x, to); } static Constant *julia_const_to_llvm(jl_codectx_t &ctx, const void *ptr, jl_datatype_t *bt) @@ -317,25 +312,90 @@ static Constant *julia_const_to_llvm(jl_codectx_t &ctx, jl_value_t *e) return julia_const_to_llvm(ctx, e, (jl_datatype_t*)bt); } +static Constant *undef_value_for_type(Type *T) { + auto tracked = CountTrackedPointers(T); + Constant *undef; + if (tracked.count) + // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL + undef = Constant::getNullValue(T); + else + undef = UndefValue::get(T); + return undef; +} + +// rebuild a struct type with any i1 Bool (e.g. the llvmcall type) widened to i8 (the native size for memcpy) +static Type *zext_struct_type(Type *T) +{ + if (auto *AT = dyn_cast(T)) { + return ArrayType::get(AT->getElementType(), AT->getNumElements()); + } + else if (auto *ST = dyn_cast(T)) { + SmallVector Elements(ST->element_begin(), ST->element_end()); + for (size_t i = 0; i < Elements.size(); i++) { + Elements[i] = zext_struct_type(Elements[i]); + } + return StructType::get(ST->getContext(), Elements, ST->isPacked()); + } + else if (auto *VT = dyn_cast(T)) { + return VectorType::get(zext_struct_type(VT->getElementType()), VT); + } + else if (auto *IT = dyn_cast(T)) { + unsigned BitWidth = IT->getBitWidth(); + if (alignTo(BitWidth, 8) != BitWidth) + return IntegerType::get(IT->getContext(), alignTo(BitWidth, 8)); + } + return T; +} + +// rebuild a struct with any i1 Bool (e.g. the llvmcall type) widened to i8 (the native size for memcpy) +static Value *zext_struct_helper(jl_codectx_t &ctx, Value *V, Type *T2) +{ + Type *T = V->getType(); + if (T == T2) + return V; + if (auto *AT = dyn_cast(T2)) { + Value *V2 = undef_value_for_type(AT); + for (size_t i = 0; i < AT->getNumElements(); i++) { + Value *E = zext_struct_helper(ctx, ctx.builder.CreateExtractValue(V, i), AT->getElementType()); + V2 = ctx.builder.CreateInsertValue(V2, E, i); + } + return V2; + } + else if (auto *ST = dyn_cast(T2)) { + Value *V2 = undef_value_for_type(ST); + for (size_t i = 0; i < ST->getNumElements(); i++) { + Value *E = zext_struct_helper(ctx, ctx.builder.CreateExtractValue(V, i), ST->getElementType(i)); + V2 = ctx.builder.CreateInsertValue(V2, E, i); + } + return V2; + } + else if (T2->isIntegerTy() || T2->isVectorTy()) { + return ctx.builder.CreateZExt(V, T2); + } + return V; +} + +static Value *zext_struct(jl_codectx_t &ctx, Value *V) +{ + return zext_struct_helper(ctx, V, zext_struct_type(V->getType())); +} + static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) { + if (unboxed->getType() == to) + return unboxed; + if (CastInst::castIsValid(Instruction::Trunc, unboxed, to)) + return ctx.builder.CreateTrunc(unboxed, to); + unboxed = zext_struct(ctx, unboxed); Type *ty = unboxed->getType(); if (ty == to) return unboxed; bool frompointer = ty->isPointerTy(); bool topointer = to->isPointerTy(); const DataLayout &DL = jl_Module->getDataLayout(); - if (ty->isIntegerTy(1) && to->isIntegerTy(8)) { - // bools may be stored internally as int8 - unboxed = ctx.builder.CreateZExt(unboxed, to); - } - else if (ty->isIntegerTy(8) && to->isIntegerTy(1)) { - // bools may be stored internally as int8 - unboxed = ctx.builder.CreateTrunc(unboxed, to); - } - else if (ty->isVoidTy() || DL.getTypeSizeInBits(ty) != DL.getTypeSizeInBits(to)) { + if (ty->isVoidTy() || DL.getTypeSizeInBits(ty) != DL.getTypeSizeInBits(to)) { // this can happen in dead code - //emit_unreachable(ctx); + CreateTrap(ctx.builder); return UndefValue::get(to); } if (frompointer && topointer) { @@ -380,7 +440,7 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va if (type_is_ghost(to)) { return NULL; } - //emit_unreachable(ctx); + CreateTrap(ctx.builder); return UndefValue::get(to); // type mismatch error } @@ -446,17 +506,9 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest return; } - Value *unboxed = nullptr; - if (!x.ispointer()) { // already unboxed, but sometimes need conversion - unboxed = x.V; - assert(unboxed); - } - - // bools stored as int8, but can be narrowed to int1 often - if (x.typ == (jl_value_t*)jl_bool_type) - unboxed = emit_unbox(ctx, getInt8Ty(ctx.builder.getContext()), x, (jl_value_t*)jl_bool_type); - - if (unboxed) { + if (!x.ispointer()) { // already unboxed, but sometimes need conversion (e.g. f32 -> i32) + assert(x.V); + Value *unboxed = zext_struct(ctx, x.V); Type *dest_ty = unboxed->getType()->getPointerTo(); if (dest->getType() != dest_ty) dest = emit_bitcast(ctx, dest, dest_ty); @@ -1445,12 +1497,7 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg Intrinsic::smul_with_overflow : Intrinsic::umul_with_overflow))))); FunctionCallee intr = Intrinsic::getDeclaration(jl_Module, intr_id, ArrayRef(t)); - Value *res = ctx.builder.CreateCall(intr, {x, y}); - Value *val = ctx.builder.CreateExtractValue(res, ArrayRef(0)); - setName(ctx.emission_context, val, "checked"); - Value *obit = ctx.builder.CreateExtractValue(res, ArrayRef(1)); - setName(ctx.emission_context, obit, "overflow"); - Value *obyte = ctx.builder.CreateZExt(obit, getInt8Ty(ctx.builder.getContext())); + Value *tupval = ctx.builder.CreateCall(intr, {x, y}); jl_value_t *params[2]; params[0] = xtyp; @@ -1458,10 +1505,6 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); *newtyp = tuptyp; - Value *tupval; - tupval = UndefValue::get(julia_type_to_llvm(ctx, (jl_value_t*)tuptyp)); - tupval = ctx.builder.CreateInsertValue(tupval, val, ArrayRef(0)); - tupval = ctx.builder.CreateInsertValue(tupval, obyte, ArrayRef(1)); return tupval; } diff --git a/test/llvmcall2.jl b/test/llvmcall2.jl index 07b27fc407433..e3e89bb916f2d 100644 --- a/test/llvmcall2.jl +++ b/test/llvmcall2.jl @@ -73,3 +73,12 @@ end jl_str = unsafe_string(str) @test length(jl_str) > 4 end + + +# boolean structs +const NT4I = NTuple{4, VecElement{Int}} +const NT4B = NTuple{4, VecElement{Bool}} +f_nt4b(x, y) = ccall("llvm.sadd.with.overflow", llvmcall, Pair{NT4B, NT4B}, (NT4B, NT4B), x, y) +f_nt4i(x, y) = ccall("llvm.sadd.with.overflow", llvmcall, Pair{NT4I, NT4B}, (NT4I, NT4I), x, y) +@test f_nt4b((false, true, false, true), (false, false, true, true)) === (NT4B((false, true, true, false)) => NT4B((false, false, false, true))) +@test f_nt4i((typemin(Int), 0, typemax(Int), typemax(Int)), (-1, typemax(Int),-1, 1)) === (NT4I((typemax(Int), typemax(Int), typemax(Int)-1, typemin(Int))) => NT4B((true, false, false, true)))