diff --git a/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp index b690ef5dbc4..b24f0eafd5d 100644 --- a/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -12,17 +12,14 @@ //===----------------------------------------------------------------------===// #include "InstCombine.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/Loads.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/PatternMatch.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" using namespace llvm; -using namespace llvm::PatternMatch; #define DEBUG_TYPE "instcombine" @@ -348,142 +345,6 @@ static LoadInst *combineLoadToNewType(InstCombiner &IC, LoadInst &LI, Type *NewT return NewLoad; } -/// \brief Combine integer loads to vector stores when the integers bits are -/// just a concatenation of non-integer (and non-vector) types. -/// -/// This specifically matches the pattern of loading an integer, right-shifting, -/// trucating, and casting it to a non-integer type. When the shift is an exact -/// multiple of the result non-integer type's size, this is more naturally -/// expressed as a load of a vector and an extractelement. This shows up largely -/// because large integers are sometimes used to represent a "generic" load or -/// store, and only later optimization may uncover that there is a more natural -/// type to represent the load with. -static Instruction *combineIntegerLoadToVectorLoad(InstCombiner &IC, - LoadInst &LI) { - // FIXME: This is probably a reasonable transform to make for atomic stores. - assert(LI.isSimple() && "Do not call for non-simple stores!"); - - const DataLayout &DL = *IC.getDataLayout(); - unsigned BaseBits = LI.getType()->getIntegerBitWidth(); - Type *ElementTy = nullptr; - int ElementSize; - - // We match any number of element extractions from the loaded integer. Each of - // these should be RAUW'ed with an actual extract element instruction at the - // given index of a loaded vector. - struct ExtractedElement { - Instruction *Element; - int Index; - }; - SmallVector Elements; - - // Lambda to match the bit cast in the extracted element (which is the root - // pattern matched). Accepts the instruction and shifted bits, returns false - // if at any point we failed to match a suitable bitcast for element - // extraction. - auto MatchCast = [&](Instruction *I, unsigned ShiftBits) { - // The truncate must be casted to some element type. This cast can only be - // a bitcast or an inttoptr cast which is the same size. - if (!isa(I)) { - if (auto *PC = dyn_cast(I)) { - // Ensure that the pointer and integer have the exact same size. - if (PC->getOperand(0)->getType()->getIntegerBitWidth() != - DL.getTypeSizeInBits(PC->getType())) - return false; - } else { - // We only support bitcast and inttoptr. - return false; - } - } - - // All of the elements inserted need to be the same type. Either capture the - // first element type or check this element type against the previous - // element types. - if (!ElementTy) { - ElementTy = I->getType(); - // We don't handle integers, sub-vectors, or any aggregate types. We - // handle pointers and floating ponit types. - if (!ElementTy->isSingleValueType() || ElementTy->isIntegerTy() || - ElementTy->isVectorTy()) - return false; - - ElementSize = DL.getTypeSizeInBits(ElementTy); - // The base integer size and the shift need to be multiples of the element - // size in bits. - if (BaseBits % ElementSize || ShiftBits % ElementSize) - return false; - } else if (ElementTy != I->getType()) { - return false; - } - - // Compute the vector index and store the element with it. - int Index = - (DL.isLittleEndian() ? ShiftBits : BaseBits - ElementSize - ShiftBits) / - ElementSize; - ExtractedElement E = {I, Index}; - Elements.push_back(std::move(E)); - return true; - }; - - // Lambda to match the truncate in the extracted element. Accepts the - // instruction and shifted bits. Returns false if at any point we failed to - // match a suitable truncate for element extraction. - auto MatchTruncate = [&](Instruction *I, unsigned ShiftBits) { - // Handle the truncate to the bit size of the element. - auto *T = dyn_cast(I); - if (!T) - return false; - - // Walk all the users of the truncate, whuch must all be bitcasts. - for (User *TU : T->users()) - if (!MatchCast(cast(TU), ShiftBits)) - return false; - return true; - }; - - for (User *U : LI.users()) { - Instruction *I = cast(U); - - // Strip off a logical shift right and retain the shifted amount. - ConstantInt *ShiftC; - if (!match(I, m_LShr(m_Value(), m_ConstantInt(ShiftC)))) { - // This must be a direct truncate. - if (!MatchTruncate(I, 0)) - return nullptr; - continue; - } - - unsigned ShiftBits = ShiftC->getLimitedValue(BaseBits); - // We can't handle shifts of more than the number of bits in the integer. - if (ShiftBits == BaseBits) - return nullptr; - - // Match all the element extraction users of the shift. - for (User *IU : I->users()) - if (!MatchTruncate(cast(IU), ShiftBits)) - return nullptr; - } - - // If didn't find any extracted elements, there is nothing to do here. - if (Elements.empty()) - return nullptr; - - // Create a vector load and rewrite all of the elements extracted as - // extractelement instructions. - VectorType *VTy = VectorType::get(ElementTy, BaseBits / ElementSize); - LoadInst *NewLI = combineLoadToNewType(IC, LI, VTy); - - for (const auto &E : Elements) { - IC.Builder->SetInsertPoint(E.Element); - E.Element->replaceAllUsesWith( - IC.Builder->CreateExtractElement(NewLI, IC.Builder->getInt32(E.Index))); - IC.EraseInstFromFunction(*E.Element); - } - - // Return the original load to indicate it has been combined away. - return &LI; -} - /// \brief Combine loads to match the type of value their uses after looking /// through intervening bitcasts. /// @@ -512,8 +373,6 @@ static Instruction *combineLoadToOperationType(InstCombiner &IC, LoadInst &LI) { // Fold away bit casts of the loaded value by loading the desired type. - // FIXME: We should also canonicalize loads of vectors when their elements are - // cast to other types. if (LI.hasOneUse()) if (auto *BC = dyn_cast(LI.user_back())) { LoadInst *NewLoad = combineLoadToNewType(IC, LI, BC->getDestTy()); @@ -522,12 +381,8 @@ static Instruction *combineLoadToOperationType(InstCombiner &IC, LoadInst &LI) { return &LI; } - // Try to combine integer loads into vector loads when the integer is just - // loading a bag of bits that are casted into vector element chunks. - if (LI.getType()->isIntegerTy()) - if (Instruction *R = combineIntegerLoadToVectorLoad(IC, LI)) - return R; - + // FIXME: We should also canonicalize loads of vectors when their elements are + // cast to other types. return nullptr; } @@ -636,201 +491,6 @@ Instruction *InstCombiner::visitLoadInst(LoadInst &LI) { return nullptr; } -/// \brief Helper to combine a store to use a new value. -/// -/// This just does the work of combining a store to use a new value, potentially -/// of a different type. It handles metadata, etc., and returns the new -/// instruction. The new value is stored to a bitcast of the pointer argument to -/// the original store. -/// -/// Note that this will create the instructions with whatever insert point the -/// \c InstCombiner currently is using. -static StoreInst *combineStoreToNewValue(InstCombiner &IC, StoreInst &OldSI, - Value *V) { - Value *Ptr = OldSI.getPointerOperand(); - unsigned AS = OldSI.getPointerAddressSpace(); - SmallVector, 8> MD; - OldSI.getAllMetadata(MD); - - StoreInst *NewSI = IC.Builder->CreateAlignedStore( - V, IC.Builder->CreateBitCast(Ptr, V->getType()->getPointerTo(AS)), - OldSI.getAlignment()); - for (const auto &MDPair : MD) { - unsigned ID = MDPair.first; - MDNode *N = MDPair.second; - // Note, essentially every kind of metadata should be preserved here! This - // routine is supposed to clone a store instruction changing *only its - // type*. The only metadata it makes sense to drop is metadata which is - // invalidated when the pointer type changes. This should essentially - // never be the case in LLVM, but we explicitly switch over only known - // metadata to be conservatively correct. If you are adding metadata to - // LLVM which pertains to stores, you almost certainly want to add it - // here. - switch (ID) { - case LLVMContext::MD_dbg: - case LLVMContext::MD_tbaa: - case LLVMContext::MD_prof: - case LLVMContext::MD_fpmath: - case LLVMContext::MD_tbaa_struct: - case LLVMContext::MD_alias_scope: - case LLVMContext::MD_noalias: - case LLVMContext::MD_nontemporal: - case LLVMContext::MD_mem_parallel_loop_access: - case LLVMContext::MD_nonnull: - // All of these directly apply. - NewSI->setMetadata(ID, N); - break; - - case LLVMContext::MD_invariant_load: - case LLVMContext::MD_range: - break; - } - } - return NewSI; -} - -/// \brief Combine integer stores to vector stores when the integers bits are -/// just a concatenation of non-integer (and non-vector) types. -/// -/// This specifically matches the pattern of taking a sequence of non-integer -/// types, casting them to integers, extending, shifting, and or-ing them -/// together to make a concatenation, and then storing the result. This shows up -/// because large integers are sometimes used to represent a "generic" load or -/// store, and only later optimization may uncover that there is a more natural -/// type to represent the store with. -/// -/// \returns true if the store was successfully combined away. This indicates -/// the caller must erase the store instruction. We have to let the caller erase -/// the store instruction sas otherwise there is no way to signal whether it was -/// combined or not: IC.EraseInstFromFunction returns a null pointer. -static bool combineIntegerStoreToVectorStore(InstCombiner &IC, StoreInst &SI) { - // FIXME: This is probably a reasonable transform to make for atomic stores. - assert(SI.isSimple() && "Do not call for non-simple stores!"); - - Instruction *OrigV = dyn_cast(SI.getValueOperand()); - if (!OrigV) - return false; - - // We only handle values which are used entirely to store to memory. If the - // value is used directly as an SSA value, then even if there are matching - // element insertion and element extraction, we rely on basic integer - // combining to forward the bits and delete the intermediate math. Here we - // just need to clean up the places where it actually reaches memory. - SmallVector Stores; - for (User *U : OrigV->users()) - if (auto *SIU = dyn_cast(U)) - Stores.push_back(SIU); - else - return false; - - const DataLayout &DL = *IC.getDataLayout(); - unsigned BaseBits = OrigV->getType()->getIntegerBitWidth(); - Type *ElementTy = nullptr; - int ElementSize; - - // We need to match some number of element insertions into an integer. Each - // insertion takes the form of an element value (and type), index (multiple of - // the bitwidth of the type) of insertion, and the base it was inserted into. - struct InsertedElement { - Value *Base; - Value *Element; - int Index; - }; - auto MatchInsertedElement = [&](Value *V) -> Optional { - // Handle a null input to make it easy to loop over bases. - if (!V) - return Optional(); - - assert(!V->getType()->isVectorTy() && "Must not be a vector."); - assert(V->getType()->isIntegerTy() && "Must be an integer value."); - - Value *Base = nullptr, *Cast; - ConstantInt *ShiftC = nullptr; - auto InsertPattern = m_CombineOr( - m_Shl(m_OneUse(m_ZExt(m_OneUse(m_Value(Cast)))), m_ConstantInt(ShiftC)), - m_ZExt(m_OneUse(m_Value(Cast)))); - if (!match(V, m_CombineOr(m_CombineOr(m_Or(m_OneUse(m_Value(Base)), - m_OneUse(InsertPattern)), - m_Or(m_OneUse(InsertPattern), - m_OneUse(m_Value(Base)))), - InsertPattern))) - return Optional(); - - Value *Element; - if (auto *BC = dyn_cast(Cast)) { - // Bit casts are trivially correct here. - Element = BC->getOperand(0); - } else if (auto *PC = dyn_cast(Cast)) { - Element = PC->getOperand(0); - // If this changes the bit width at all, reject it. - if (PC->getType()->getIntegerBitWidth() != - DL.getTypeSizeInBits(Element->getType())) - return Optional(); - } else { - // All other casts are rejected. - return Optional(); - } - - // We can't handle shifts wider than the number of bits in the integer. - unsigned ShiftBits = ShiftC ? ShiftC->getLimitedValue(BaseBits) : 0; - if (ShiftBits == BaseBits) - return Optional(); - - // All of the elements inserted need to be the same type. Either capture the - // first element type or check this element type against the previous - // element types. - if (!ElementTy) { - ElementTy = Element->getType(); - // The base integer size and the shift need to be multiples of the element - // size in bits. - ElementSize = DL.getTypeSizeInBits(ElementTy); - if (BaseBits % ElementSize || ShiftBits % ElementSize) - return Optional(); - } else if (ElementTy != Element->getType()) { - return Optional(); - } - - // We don't handle integers, sub-vectors, or any aggregate types. We - // handle pointers and floating ponit types. - if (!ElementTy->isSingleValueType() || ElementTy->isIntegerTy() || - ElementTy->isVectorTy()) - return Optional(); - - int Index = - (DL.isLittleEndian() ? ShiftBits : BaseBits - ElementSize - ShiftBits) / - ElementSize; - InsertedElement Result = {Base, Element, Index}; - return Result; - }; - - SmallVector Elements; - Value *V = OrigV; - while (Optional E = MatchInsertedElement(V)) { - V = E->Base; - Elements.push_back(std::move(*E)); - } - // If searching for elements found none, or didn't terminate in either an - // undef or a direct zext, we can't form a vector. - if (Elements.empty() || (V && !isa(V))) - return false; - - // Build a storable vector by looping over the inserted elements. - VectorType *VTy = VectorType::get(ElementTy, BaseBits / ElementSize); - V = UndefValue::get(VTy); - IC.Builder->SetInsertPoint(OrigV); - for (const auto &E : Elements) - V = IC.Builder->CreateInsertElement(V, E.Element, - IC.Builder->getInt32(E.Index)); - - for (StoreInst *OldSI : Stores) { - IC.Builder->SetInsertPoint(OldSI); - combineStoreToNewValue(IC, *OldSI, V); - if (OldSI != &SI) - IC.EraseInstFromFunction(*OldSI); - } - return true; -} - /// \brief Combine stores to match the type of value being stored. /// /// The core idea here is that the memory does not have any intrinsic type and @@ -857,20 +517,52 @@ static bool combineStoreToValueType(InstCombiner &IC, StoreInst &SI) { if (!SI.isSimple()) return false; + Value *Ptr = SI.getPointerOperand(); Value *V = SI.getValueOperand(); + unsigned AS = SI.getPointerAddressSpace(); + SmallVector, 8> MD; + SI.getAllMetadata(MD); // Fold away bit casts of the stored value by storing the original type. if (auto *BC = dyn_cast(V)) { - combineStoreToNewValue(IC, SI, BC->getOperand(0)); + V = BC->getOperand(0); + StoreInst *NewStore = IC.Builder->CreateAlignedStore( + V, IC.Builder->CreateBitCast(Ptr, V->getType()->getPointerTo(AS)), + SI.getAlignment()); + for (const auto &MDPair : MD) { + unsigned ID = MDPair.first; + MDNode *N = MDPair.second; + // Note, essentially every kind of metadata should be preserved here! This + // routine is supposed to clone a store instruction changing *only its + // type*. The only metadata it makes sense to drop is metadata which is + // invalidated when the pointer type changes. This should essentially + // never be the case in LLVM, but we explicitly switch over only known + // metadata to be conservatively correct. If you are adding metadata to + // LLVM which pertains to stores, you almost certainly want to add it + // here. + switch (ID) { + case LLVMContext::MD_dbg: + case LLVMContext::MD_tbaa: + case LLVMContext::MD_prof: + case LLVMContext::MD_fpmath: + case LLVMContext::MD_tbaa_struct: + case LLVMContext::MD_alias_scope: + case LLVMContext::MD_noalias: + case LLVMContext::MD_nontemporal: + case LLVMContext::MD_mem_parallel_loop_access: + case LLVMContext::MD_nonnull: + // All of these directly apply. + NewStore->setMetadata(ID, N); + break; + + case LLVMContext::MD_invariant_load: + case LLVMContext::MD_range: + break; + } + } return true; } - // If this is an integer store and we have data layout, look for a pattern of - // storing a vector as an integer (modeled as a bag of bits). - if (V->getType()->isIntegerTy() && IC.getDataLayout() && - combineIntegerStoreToVectorStore(IC, SI)) - return true; - // FIXME: We should also canonicalize loads of vectors when their elements are // cast to other types. return false; diff --git a/test/Transforms/InstCombine/loadstore-vector.ll b/test/Transforms/InstCombine/loadstore-vector.ll deleted file mode 100644 index 6c1d0123d51..00000000000 --- a/test/Transforms/InstCombine/loadstore-vector.ll +++ /dev/null @@ -1,210 +0,0 @@ -; RUN: opt -instcombine -S < %s | FileCheck %s -target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" - -; Basic test for turning element extraction from integer loads and element -; insertion into integer stores into extraction and insertion with vectors. -define void @test1({ float, float }* %x, float %a, float %b, { float, float }* %out) { -; CHECK-LABEL: @test1( -entry: - %x.cast = bitcast { float, float }* %x to i64* - %x.load = load i64* %x.cast, align 4 -; CHECK-NOT: load i64* -; CHECK: %[[LOAD:.*]] = load <2 x float>* - - %lo.trunc = trunc i64 %x.load to i32 - %hi.shift = lshr i64 %x.load, 32 - %hi.trunc = trunc i64 %hi.shift to i32 - %hi.cast = bitcast i32 %hi.trunc to float - %lo.cast = bitcast i32 %lo.trunc to float -; CHECK-NOT: trunc -; CHECK-NOT: lshr -; CHECK: %[[HI:.*]] = extractelement <2 x float> %[[LOAD]], i32 1 -; CHECK: %[[LO:.*]] = extractelement <2 x float> %[[LOAD]], i32 0 - - %add.i.i = fadd float %lo.cast, %a - %add5.i.i = fadd float %hi.cast, %b -; CHECK: %[[LO_SUM:.*]] = fadd float %[[LO]], %a -; CHECK: %[[HI_SUM:.*]] = fadd float %[[HI]], %b - - %add.lo.cast = bitcast float %add.i.i to i32 - %add.hi.cast = bitcast float %add5.i.i to i32 - %add.hi.ext = zext i32 %add.hi.cast to i64 - %add.hi.shift = shl nuw i64 %add.hi.ext, 32 - %add.lo.ext = zext i32 %add.lo.cast to i64 - %add.lo.or = or i64 %add.hi.shift, %add.lo.ext -; CHECK-NOT: zext i32 -; CHECK-NOT: shl {{.*}} i64 -; CHECK-NOT: or i64 -; CHECK: %[[INSERT1:.*]] = insertelement <2 x float> undef, float %[[LO_SUM]], i32 0 -; CHECK: %[[INSERT2:.*]] = insertelement <2 x float> %[[INSERT1]], float %[[HI_SUM]], i32 1 - - %out.cast = bitcast { float, float }* %out to i64* - store i64 %add.lo.or, i64* %out.cast, align 4 -; CHECK-NOT: store i64 -; CHECK: store <2 x float> %[[INSERT2]] - - ret void -} - -define void @test2({ float, float }* %x, float %a, float %b, { float, float }* %out1, { float, float }* %out2) { -; CHECK-LABEL: @test2( -entry: - %x.cast = bitcast { float, float }* %x to i64* - %x.load = load i64* %x.cast, align 4 -; CHECK-NOT: load i64* -; CHECK: %[[LOAD:.*]] = load <2 x float>* - - %lo.trunc = trunc i64 %x.load to i32 - %hi.shift = lshr i64 %x.load, 32 - %hi.trunc = trunc i64 %hi.shift to i32 - %hi.cast = bitcast i32 %hi.trunc to float - %lo.cast = bitcast i32 %lo.trunc to float -; CHECK-NOT: trunc -; CHECK-NOT: lshr -; CHECK: %[[HI:.*]] = extractelement <2 x float> %[[LOAD]], i32 1 -; CHECK: %[[LO:.*]] = extractelement <2 x float> %[[LOAD]], i32 0 - - %add.i.i = fadd float %lo.cast, %a - %add5.i.i = fadd float %hi.cast, %b -; CHECK: %[[LO_SUM:.*]] = fadd float %[[LO]], %a -; CHECK: %[[HI_SUM:.*]] = fadd float %[[HI]], %b - - %add.lo.cast = bitcast float %add.i.i to i32 - %add.hi.cast = bitcast float %add5.i.i to i32 - %add.hi.ext = zext i32 %add.hi.cast to i64 - %add.hi.shift = shl nuw i64 %add.hi.ext, 32 - %add.lo.ext = zext i32 %add.lo.cast to i64 - %add.lo.or = or i64 %add.hi.shift, %add.lo.ext -; CHECK-NOT: zext i32 -; CHECK-NOT: shl {{.*}} i64 -; CHECK-NOT: or i64 -; CHECK: %[[INSERT1:.*]] = insertelement <2 x float> undef, float %[[LO_SUM]], i32 0 -; CHECK: %[[INSERT2:.*]] = insertelement <2 x float> %[[INSERT1]], float %[[HI_SUM]], i32 1 - - %out1.cast = bitcast { float, float }* %out1 to i64* - store i64 %add.lo.or, i64* %out1.cast, align 4 - %out2.cast = bitcast { float, float }* %out2 to i64* - store i64 %add.lo.or, i64* %out2.cast, align 4 -; CHECK-NOT: store i64 -; CHECK: store <2 x float> %[[INSERT2]] -; CHECK-NOT: store i64 -; CHECK: store <2 x float> %[[INSERT2]] - - ret void -} - -; We handle some cases where there is partial CSE but not complete CSE of -; repeated insertion and extraction. Currently, we don't catch the store side -; yet because it would require extreme heroics to match this reliably. -define void @test3({ float, float, float }* %x, float %a, float %b, { float, float, float }* %out1, { float, float, float }* %out2) { -; CHECK-LABEL: @test3( -entry: - %x.cast = bitcast { float, float, float }* %x to i96* - %x.load = load i96* %x.cast, align 4 -; CHECK-NOT: load i96* -; CHECK: %[[LOAD:.*]] = load <3 x float>* - - %lo.trunc = trunc i96 %x.load to i32 - %lo.cast = bitcast i32 %lo.trunc to float - %mid.shift = lshr i96 %x.load, 32 - %mid.trunc = trunc i96 %mid.shift to i32 - %mid.cast = bitcast i32 %mid.trunc to float - %mid.trunc2 = trunc i96 %mid.shift to i32 - %mid.cast2 = bitcast i32 %mid.trunc2 to float - %hi.shift = lshr i96 %mid.shift, 32 - %hi.trunc = trunc i96 %hi.shift to i32 - %hi.cast = bitcast i32 %hi.trunc to float -; CHECK-NOT: trunc -; CHECK-NOT: lshr -; CHECK: %[[LO:.*]] = extractelement <3 x float> %[[LOAD]], i32 0 -; CHECK: %[[MID1:.*]] = extractelement <3 x float> %[[LOAD]], i32 1 -; CHECK: %[[MID2:.*]] = extractelement <3 x float> %[[LOAD]], i32 1 -; CHECK: %[[HI:.*]] = extractelement <3 x float> %[[LOAD]], i32 2 - - %add.lo = fadd float %lo.cast, %a - %add.mid = fadd float %mid.cast, %b - %add.hi = fadd float %hi.cast, %mid.cast2 -; CHECK: %[[LO_SUM:.*]] = fadd float %[[LO]], %a -; CHECK: %[[MID_SUM:.*]] = fadd float %[[MID1]], %b -; CHECK: %[[HI_SUM:.*]] = fadd float %[[HI]], %[[MID2]] - - %add.lo.cast = bitcast float %add.lo to i32 - %add.mid.cast = bitcast float %add.mid to i32 - %add.hi.cast = bitcast float %add.hi to i32 - %result.hi.ext = zext i32 %add.hi.cast to i96 - %result.hi.shift = shl nuw i96 %result.hi.ext, 32 - %result.mid.ext = zext i32 %add.mid.cast to i96 - %result.mid.or = or i96 %result.hi.shift, %result.mid.ext - %result.mid.shift = shl nuw i96 %result.mid.or, 32 - %result.lo.ext = zext i32 %add.lo.cast to i96 - %result.lo.or = or i96 %result.mid.shift, %result.lo.ext -; FIXME-NOT: zext i32 -; FIXME-NOT: shl {{.*}} i64 -; FIXME-NOT: or i64 -; FIXME: %[[INSERT1:.*]] = insertelement <3 x float> undef, float %[[HI_SUM]], i32 2 -; FIXME: %[[INSERT2:.*]] = insertelement <3 x float> %[[INSERT1]], float %[[MID_SUM]], i32 1 -; FIXME: %[[INSERT3:.*]] = insertelement <3 x float> %[[INSERT2]], float %[[LO_SUM]], i32 0 - - %out1.cast = bitcast { float, float, float }* %out1 to i96* - store i96 %result.lo.or, i96* %out1.cast, align 4 -; FIXME-NOT: store i96 -; FIXME: store <3 x float> %[[INSERT3]] - - %result2.lo.ext = zext i32 %add.lo.cast to i96 - %result2.lo.or = or i96 %result.mid.shift, %result2.lo.ext -; FIXME-NOT: zext i32 -; FIXME-NOT: shl {{.*}} i64 -; FIXME-NOT: or i64 -; FIXME: %[[INSERT4:.*]] = insertelement <3 x float> %[[INSERT2]], float %[[LO_SUM]], i32 0 - - %out2.cast = bitcast { float, float, float }* %out2 to i96* - store i96 %result2.lo.or, i96* %out2.cast, align 4 -; FIXME-NOT: store i96 -; FIXME: store <3 x float> - - ret void -} - -; Basic test that pointers work correctly as the element type. -define void @test4({ i8*, i8* }* %x, i64 %a, i64 %b, { i8*, i8* }* %out) { -; CHECK-LABEL: @test4( -entry: - %x.cast = bitcast { i8*, i8* }* %x to i128* - %x.load = load i128* %x.cast, align 4 -; CHECK-NOT: load i128* -; CHECK: %[[LOAD:.*]] = load <2 x i8*>* {{.*}}, align 4 - - %lo.trunc = trunc i128 %x.load to i64 - %hi.shift = lshr i128 %x.load, 64 - %hi.trunc = trunc i128 %hi.shift to i64 - %hi.cast = inttoptr i64 %hi.trunc to i8* - %lo.cast = inttoptr i64 %lo.trunc to i8* -; CHECK-NOT: trunc -; CHECK-NOT: lshr -; CHECK: %[[HI:.*]] = extractelement <2 x i8*> %[[LOAD]], i32 1 -; CHECK: %[[LO:.*]] = extractelement <2 x i8*> %[[LOAD]], i32 0 - - %gep.lo = getelementptr i8* %lo.cast, i64 %a - %gep.hi = getelementptr i8* %hi.cast, i64 %b -; CHECK: %[[LO_GEP:.*]] = getelementptr i8* %[[LO]], i64 %a -; CHECK: %[[HI_GEP:.*]] = getelementptr i8* %[[HI]], i64 %b - - %gep.lo.cast = ptrtoint i8* %gep.lo to i64 - %gep.hi.cast = ptrtoint i8* %gep.hi to i64 - %gep.hi.ext = zext i64 %gep.hi.cast to i128 - %gep.hi.shift = shl nuw i128 %gep.hi.ext, 64 - %gep.lo.ext = zext i64 %gep.lo.cast to i128 - %gep.lo.or = or i128 %gep.hi.shift, %gep.lo.ext -; CHECK-NOT: zext i32 -; CHECK-NOT: shl {{.*}} i64 -; CHECK-NOT: or i64 -; CHECK: %[[INSERT1:.*]] = insertelement <2 x i8*> undef, i8* %[[LO_GEP]], i32 0 -; CHECK: %[[INSERT2:.*]] = insertelement <2 x i8*> %[[INSERT1]], i8* %[[HI_GEP]], i32 1 - - %out.cast = bitcast { i8*, i8* }* %out to i128* - store i128 %gep.lo.or, i128* %out.cast, align 4 -; CHECK-NOT: store i128 -; CHECK: store <2 x i8*> %[[INSERT2]], <2 x i8*>* {{.*}}, align 4 - - ret void -}