Skip to content

[HLSL] Make memory representation of boolean vectors in HLSL, vectors of i32. Add support for boolean swizzling. #123977

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2568,6 +2568,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isVectorType() const; // GCC vector type.
bool isExtVectorType() const; // Extended vector type.
bool isExtVectorBoolType() const; // Extended vector type with bool element.
// Extended vector type with bool element that is packed. HLSL doesn't pack
// its bool vectors.
bool isPackedVectorBoolType(const ASTContext &ctx) const;
bool isSubscriptableVectorType() const;
bool isMatrixType() const; // Matrix type.
bool isConstantMatrixType() const; // Constant matrix type.
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2019,8 +2019,9 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
case Type::Vector: {
const auto *VT = cast<VectorType>(T);
TypeInfo EltInfo = getTypeInfo(VT->getElementType());
Width = VT->isExtVectorBoolType() ? VT->getNumElements()
: EltInfo.Width * VT->getNumElements();
Width = VT->isPackedVectorBoolType(*this)
? VT->getNumElements()
: EltInfo.Width * VT->getNumElements();
// Enforce at least byte size and alignment.
Width = std::max<unsigned>(8, Width);
Align = std::max<unsigned>(8, Width);
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));
PrimType ElemT = *Ctx.classify(ElemType);
// Special case, since the bools here are packed.
bool PackedBools = FieldDesc->getType()->isExtVectorBoolType();
bool PackedBools =
FieldDesc->getType()->isPackedVectorBoolType(Ctx.getASTContext());
unsigned NumElems = FieldDesc->getNumElems();
bool Ok = true;
for (unsigned I = P.getIndex(); I != NumElems; ++I) {
Expand Down Expand Up @@ -227,7 +228,7 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
QualType EltTy = VT->getElementType();
unsigned NElts = VT->getNumElements();
unsigned EltSize =
VT->isExtVectorBoolType() ? 1 : ASTCtx.getTypeSize(EltTy);
VT->isPackedVectorBoolType(ASTCtx) ? 1 : ASTCtx.getTypeSize(EltTy);

if ((NElts * EltSize) % ASTCtx.getCharWidth() != 0) {
// The vector's size in bits is not a multiple of the target's byte size,
Expand Down
9 changes: 5 additions & 4 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7438,7 +7438,7 @@ class APValueToBufferConverter {
QualType EltTy = VTy->getElementType();
unsigned NElts = VTy->getNumElements();

if (VTy->isExtVectorBoolType()) {
if (VTy->isPackedVectorBoolType(Info.Ctx)) {
// Special handling for OpenCL bool vectors:
// Since these vectors are stored as packed bits, but we can't write
// individual bits to the BitCastBuffer, we'll buffer all of the elements
Expand Down Expand Up @@ -7701,11 +7701,11 @@ class BufferToAPValueConverter {
QualType EltTy = VTy->getElementType();
unsigned NElts = VTy->getNumElements();
unsigned EltSize =
VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy);
VTy->isPackedVectorBoolType(Info.Ctx) ? 1 : Info.Ctx.getTypeSize(EltTy);

SmallVector<APValue, 4> Elts;
Elts.reserve(NElts);
if (VTy->isExtVectorBoolType()) {
if (VTy->isPackedVectorBoolType(Info.Ctx)) {
// Special handling for OpenCL bool vectors:
// Since these vectors are stored as packed bits, but we can't read
// individual bits from the BitCastBuffer, we'll buffer all of the
Expand Down Expand Up @@ -7834,7 +7834,8 @@ static bool checkBitCastConstexprEligibilityType(SourceLocation Loc,
if (const auto *VTy = Ty->getAs<VectorType>()) {
QualType EltTy = VTy->getElementType();
unsigned NElts = VTy->getNumElements();
unsigned EltSize = VTy->isExtVectorBoolType() ? 1 : Ctx.getTypeSize(EltTy);
unsigned EltSize =
VTy->isPackedVectorBoolType(Ctx) ? 1 : Ctx.getTypeSize(EltTy);

if ((NElts * EltSize) % Ctx.getCharWidth() != 0) {
// The vector's size in bits is not a multiple of the target's byte size,
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ VectorType::VectorType(TypeClass tc, QualType vecType, unsigned nElements,
VectorTypeBits.NumElements = nElements;
}

bool Type::isPackedVectorBoolType(const ASTContext &ctx) const {
if (ctx.getLangOpts().HLSL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a dumb suggestion, but is the a way to just check if the mem reprsentation is i32 or i1? HLSL is probably the only language mode that needs this distinction but it feel like this shouldn't have a lang opt toggle based on the function name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code to state that a bool vector should be a vector of i32s isn't accessible here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make for slightly better abstraction to add a hasPackedBoolVectors() accessor to LangOptions that returns !HLSL, similar to how allowArrayReturnTypes() works.

return false;
return isExtVectorBoolType();
}

BitIntType::BitIntType(bool IsUnsigned, unsigned NumBits)
: Type(BitInt, QualType{}, TypeDependence::None), IsUnsigned(IsUnsigned),
NumBits(NumBits) {}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3292,7 +3292,7 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty,

llvm::DIType *CGDebugInfo::CreateType(const VectorType *Ty,
llvm::DIFile *Unit) {
if (Ty->isExtVectorBoolType()) {
if (Ty->isPackedVectorBoolType(CGM.getContext())) {
// Boolean ext_vector_type(N) are special because their real element type
// (bits of bit size) is not their Clang element type (_Bool of size byte).
// For now, we pretend the boolean vector were actually a vector of bytes
Expand Down
70 changes: 55 additions & 15 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1985,7 +1985,7 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile,

if (const auto *ClangVecTy = Ty->getAs<VectorType>()) {
// Boolean vectors use `iN` as storage type.
if (ClangVecTy->isExtVectorBoolType()) {
if (ClangVecTy->isPackedVectorBoolType(getContext())) {
llvm::Type *ValTy = ConvertType(Ty);
unsigned ValNumElems =
cast<llvm::FixedVectorType>(ValTy)->getNumElements();
Expand Down Expand Up @@ -2064,6 +2064,10 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {

if (Ty->isExtVectorBoolType()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you replaced a bunch of isExtVectorBoolType() with isPackedVectorBoolType. We are only doing HLSL modifications on the isExtVectorBoolType(). But isExtVectorBoolType doesn't mean the vector is not packed. Is the Zero extend and truncation to get them into a form that they will unpack?

Copy link
Contributor Author

@spall spall Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention was to replace calls to 'isExtVectorBoolType' with 'isPackedVectorBoolType' anywhere we want an hlsl boolean vector to follow the normal handling path for vectors; Hopefully reviews will verify I got this right.
Here we can't follow the normal vector path because it returns the value unchanged, and we need to convert a vec of i1s to a vec of i32s, which is why we zero extend here. The normally "boolean vector packing" does something different.

llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
if (StoreTy->isVectorTy() && StoreTy->getScalarSizeInBits() >
Value->getType()->getScalarSizeInBits())
return Builder.CreateZExt(Value, StoreTy);

// Expand to the memory bit width.
unsigned MemNumElems = StoreTy->getPrimitiveSizeInBits();
// <N x i1> --> <P x i1>.
Expand All @@ -2079,8 +2083,9 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
/// by convertTypeForLoadStore) to its primary IR type (as returned
/// by ConvertType).
llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
if (Ty->isExtVectorBoolType()) {
if (Ty->isPackedVectorBoolType(getContext())) {
const auto *RawIntTy = Value->getType();

// Bitcast iP --> <P x i1>.
auto *PaddedVecTy = llvm::FixedVectorType::get(
Builder.getInt1Ty(), RawIntTy->getPrimitiveSizeInBits());
Expand All @@ -2091,10 +2096,10 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
return emitBoolVecConversion(V, ValNumElems, "extractvec");
}

if (hasBooleanRepresentation(Ty) || Ty->isBitIntType()) {
llvm::Type *ResTy = ConvertType(Ty);
llvm::Type *ResTy = ConvertType(Ty);
if (hasBooleanRepresentation(Ty) || Ty->isBitIntType() ||
Ty->isExtVectorBoolType())
return Builder.CreateTrunc(Value, ResTy, "loadedv");
}

return Value;
}
Expand Down Expand Up @@ -2152,7 +2157,8 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, Address Addr,
if (auto *VecTy = dyn_cast<llvm::FixedVectorType>(SrcTy)) {
auto *NewVecTy =
CGM.getABIInfo().getOptimalVectorMemoryType(VecTy, getLangOpts());
if (!ClangVecTy->isExtVectorBoolType() && VecTy != NewVecTy) {
if (!ClangVecTy->isPackedVectorBoolType(getContext()) &&
VecTy != NewVecTy) {
SmallVector<int, 16> Mask(NewVecTy->getNumElements(), -1);
std::iota(Mask.begin(), Mask.begin() + VecTy->getNumElements(), 0);
Value = Builder.CreateShuffleVector(Value, Mask, "extractVec");
Expand Down Expand Up @@ -2343,7 +2349,15 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) {
if (!ExprVT) {
unsigned InIdx = getAccessedFieldNo(0, Elts);
llvm::Value *Elt = llvm::ConstantInt::get(SizeTy, InIdx);
return RValue::get(Builder.CreateExtractElement(Vec, Elt));

llvm::Value *Element = Builder.CreateExtractElement(Vec, Elt);

llvm::Type *LVTy = ConvertType(LV.getType());
if (Element->getType()->getPrimitiveSizeInBits() >
LVTy->getPrimitiveSizeInBits())
Element = Builder.CreateTrunc(Element, LVTy);

return RValue::get(Element);
}

// Always use shuffle vector to try to retain the original program structure
Expand All @@ -2354,6 +2368,10 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) {
Mask.push_back(getAccessedFieldNo(i, Elts));

Vec = Builder.CreateShuffleVector(Vec, Mask);

if (LV.getType()->isExtVectorBoolType())
Vec = Builder.CreateTrunc(Vec, ConvertType(LV.getType()), "truncv");
Comment on lines +2372 to +2373
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this have a check for bitsize to avoid a no-op trunc, like we do elsewhere in this patch?


return RValue::get(Vec);
}

Expand Down Expand Up @@ -2407,26 +2425,35 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
// Read/modify/write the vector, inserting the new element.
llvm::Value *Vec = Builder.CreateLoad(Dst.getVectorAddress(),
Dst.isVolatileQualified());
llvm::Type *VecTy = Vec->getType();
llvm::Value *SrcVal = Src.getScalarVal();

if (SrcVal->getType()->getPrimitiveSizeInBits() <
VecTy->getScalarSizeInBits())
SrcVal = Builder.CreateZExt(SrcVal, VecTy->getScalarType());

auto *IRStoreTy = dyn_cast<llvm::IntegerType>(Vec->getType());
if (IRStoreTy) {
auto *IRVecTy = llvm::FixedVectorType::get(
Builder.getInt1Ty(), IRStoreTy->getPrimitiveSizeInBits());
Vec = Builder.CreateBitCast(Vec, IRVecTy);
// iN --> <N x i1>.
}
llvm::Value *SrcVal = Src.getScalarVal();

// Allow inserting `<1 x T>` into an `<N x T>`. It can happen with scalar
// types which are mapped to vector LLVM IR types (e.g. for implementing
// an ABI).
if (auto *EltTy = dyn_cast<llvm::FixedVectorType>(SrcVal->getType());
EltTy && EltTy->getNumElements() == 1)
SrcVal = Builder.CreateBitCast(SrcVal, EltTy->getElementType());

Vec = Builder.CreateInsertElement(Vec, SrcVal, Dst.getVectorIdx(),
"vecins");
if (IRStoreTy) {
// <N x i1> --> <iN>.
Vec = Builder.CreateBitCast(Vec, IRStoreTy);
}

Builder.CreateStore(Vec, Dst.getVectorAddress(),
Dst.isVolatileQualified());
return;
Expand Down Expand Up @@ -2623,14 +2650,12 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src,
// This access turns into a read/modify/write of the vector. Load the input
// value now.
llvm::Value *Vec = Builder.CreateLoad(DstAddr, Dst.isVolatileQualified());
llvm::Type *VecTy = Vec->getType();
const llvm::Constant *Elts = Dst.getExtVectorElts();

llvm::Value *SrcVal = Src.getScalarVal();

if (const VectorType *VTy = Dst.getType()->getAs<VectorType>()) {
unsigned NumSrcElts = VTy->getNumElements();
unsigned NumDstElts =
cast<llvm::FixedVectorType>(Vec->getType())->getNumElements();
unsigned NumDstElts = cast<llvm::FixedVectorType>(VecTy)->getNumElements();
if (NumDstElts == NumSrcElts) {
// Use shuffle vector is the src and destination are the same number of
// elements and restore the vector mask since it is on the side it will be
Expand All @@ -2639,6 +2664,11 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src,
for (unsigned i = 0; i != NumSrcElts; ++i)
Mask[getAccessedFieldNo(i, Elts)] = i;

llvm::Value *SrcVal = Src.getScalarVal();
if (VecTy->getScalarSizeInBits() >
SrcVal->getType()->getScalarSizeInBits())
SrcVal = Builder.CreateZExt(SrcVal, VecTy);

Vec = Builder.CreateShuffleVector(SrcVal, Mask);
} else if (NumDstElts > NumSrcElts) {
// Extended the source vector to the same length and then shuffle it
Expand All @@ -2649,7 +2679,8 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src,
for (unsigned i = 0; i != NumSrcElts; ++i)
ExtMask.push_back(i);
ExtMask.resize(NumDstElts, -1);
llvm::Value *ExtSrcVal = Builder.CreateShuffleVector(SrcVal, ExtMask);
llvm::Value *ExtSrcVal =
Builder.CreateShuffleVector(Src.getScalarVal(), ExtMask);
// build identity
SmallVector<int, 4> Mask;
for (unsigned i = 0; i != NumDstElts; ++i)
Expand All @@ -2674,6 +2705,11 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src,
// be updating one element.
unsigned InIdx = getAccessedFieldNo(0, Elts);
llvm::Value *Elt = llvm::ConstantInt::get(SizeTy, InIdx);

llvm::Value *SrcVal = Src.getScalarVal();
if (VecTy->getScalarSizeInBits() > SrcVal->getType()->getScalarSizeInBits())
SrcVal = Builder.CreateZExt(SrcVal, VecTy->getScalarType());

Vec = Builder.CreateInsertElement(Vec, SrcVal, Elt);
}

Expand Down Expand Up @@ -4701,9 +4737,13 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) {

// Store the vector to memory (because LValue wants an address).
Address VecMem = CreateMemTemp(E->getBase()->getType());
// need to zero extend an hlsl boolean vector to store it back to memory
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to phrase this comment in terms of packed/non-packed boolean vectors given the abstraction in LangOpts

QualType Ty = E->getBase()->getType();
llvm::Type *LTy = convertTypeForLoadStore(Ty, Vec->getType());
if (LTy->getScalarSizeInBits() > Vec->getType()->getScalarSizeInBits())
Vec = Builder.CreateZExt(Vec, LTy);
Builder.CreateStore(Vec, VecMem);
Base = MakeAddrLValue(VecMem, E->getBase()->getType(),
AlignmentSource::Decl);
Base = MakeAddrLValue(VecMem, Ty, AlignmentSource::Decl);
}

QualType type =
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1978,7 +1978,10 @@ llvm::Constant *ConstantEmitter::emitForMemory(CodeGenModule &CGM,
}

// Zero-extend bool.
if (C->getType()->isIntegerTy(1) && !destType->isBitIntType()) {
// In HLSL bool vectors are stored in memory as a vector of i32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

if ((C->getType()->isIntegerTy(1) && !destType->isBitIntType()) ||
(destType->isExtVectorBoolType() &&
!destType->isPackedVectorBoolType(CGM.getContext()))) {
llvm::Type *boolTy = CGM.getTypes().ConvertTypeForMem(destType);
llvm::Constant *Res = llvm::ConstantFoldCastOperand(
llvm::Instruction::ZExt, C, boolTy, CGM.getDataLayout());
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/CodeGen/CodeGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T) {
// Check for the boolean vector case.
if (T->isExtVectorBoolType()) {
auto *FixedVT = cast<llvm::FixedVectorType>(R);

if (Context.getLangOpts().HLSL) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use the hasPackedBoolVectors accessor I suggested elsewhere.

llvm::Type *IRElemTy = ConvertTypeForMem(Context.BoolTy);
return llvm::FixedVectorType::get(IRElemTy, FixedVT->getNumElements());
}

// Pad to at least one byte.
uint64_t BytePadded = std::max<uint64_t>(FixedVT->getNumElements(), 8);
return llvm::IntegerType::get(FixedVT->getContext(), BytePadded);
Expand Down Expand Up @@ -654,7 +660,7 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
case Type::Vector: {
const auto *VT = cast<VectorType>(Ty);
// An ext_vector_type of Bool is really a vector of bits.
llvm::Type *IRElemTy = VT->isExtVectorBoolType()
llvm::Type *IRElemTy = VT->isPackedVectorBoolType(Context)
? llvm::Type::getInt1Ty(getLLVMContext())
: VT->getElementType()->isMFloat8Type()
? llvm::Type::getInt8Ty(getLLVMContext())
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1697,7 +1697,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
QualType(), false);
}

if (BaseType->isExtVectorBoolType()) {
if (BaseType->isPackedVectorBoolType(S.Context)) {
// We disallow element access for ext_vector_type bool. There is no way to
// materialize a reference to a vector element as a pointer (each element is
// one bit in the vector).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,28 +91,26 @@ void l4_to_i2() {

// CHECK-LABEL: i2_to_b2
// CHECK: [[l2:%.*]] = alloca <2 x i32>
// CHECK: [[b2:%.*]] = alloca i8
// CHECK: [[b2:%.*]] = alloca <2 x i32>
// CHECK: store <2 x i32> splat (i32 8), ptr [[i2]]
// CHECK: [[veci2:%.*]] = load <2 x i32>, ptr [[i2]]
// CHECK: [[vecb2:%.*]] = icmp ne <2 x i32> [[veci2]], zeroinitializer
// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecb2]], <2 x i1> poison, <8 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
// CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8
// CHECK: store i8 [[i8]], ptr [[b2]]
// CHECK: [[vecb8:%.*]] = zext <2 x i1> [[vecb2]] to <2 x i32>
// CHECK: store <2 x i32> [[vecb8]], ptr [[b2]]
void i2_to_b2() {
vector<int, 2> i2 = 8;
vector<bool, 2> b2 = i2;
}

// CHECK-LABEL: d4_to_b2
// CHECK: [[d4:%.*]] = alloca <4 x double>
// CHECK: [[b2:%.*]] = alloca i8
// CHECK: [[b2:%.*]] = alloca <2 x i32>
// CHECK: store <4 x double> splat (double 9.000000e+00), ptr [[d4]]
// CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
// CHECK: [[vecb4:%.*]] = fcmp reassoc nnan ninf nsz arcp afn une <4 x double> [[vecd4]], zeroinitializer
// CHECK: [[vecd2:%.*]] = shufflevector <4 x i1> [[vecb4]], <4 x i1> poison, <2 x i32> <i32 0, i32 1>
// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecd2]], <2 x i1> poison, <8 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
// CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8
// CHECK: store i8 [[i8]], ptr [[b2]]
// CHECK: [[vecb8:%.*]] = zext <2 x i1> [[vecd2]] to <2 x i32>
// CHECK: store <2 x i32> [[vecb8]], ptr [[b2]]
void d4_to_b2() {
vector<double,4> d4 = 9.0;
vector<bool, 2> b2 = d4;
Expand Down
Loading
Loading