Skip to content

Commit

Permalink
[EarlyCSE] Compare GEP instructions based on offset
Browse files Browse the repository at this point in the history
This will provide more opportunities for
constant propagation for subsequent optimizations.
  • Loading branch information
DianQK committed Sep 16, 2023
1 parent ac1daad commit 31e2ec9
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 118 deletions.
153 changes: 133 additions & 20 deletions llvm/lib/Transforms/Scalar/EarlyCSE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ struct SimpleValue {
!CI->getFunction()->isPresplitCoroutine();
}
return isa<CastInst>(Inst) || isa<UnaryOperator>(Inst) ||
isa<BinaryOperator>(Inst) || isa<GetElementPtrInst>(Inst) ||
isa<CmpInst>(Inst) || isa<SelectInst>(Inst) ||
isa<ExtractElementInst>(Inst) || isa<InsertElementInst>(Inst) ||
isa<ShuffleVectorInst>(Inst) || isa<ExtractValueInst>(Inst) ||
isa<InsertValueInst>(Inst) || isa<FreezeInst>(Inst);
isa<BinaryOperator>(Inst) || isa<CmpInst>(Inst) ||
isa<SelectInst>(Inst) || isa<ExtractElementInst>(Inst) ||
isa<InsertElementInst>(Inst) || isa<ShuffleVectorInst>(Inst) ||
isa<ExtractValueInst>(Inst) || isa<InsertValueInst>(Inst) ||
isa<FreezeInst>(Inst);
}
};

Expand Down Expand Up @@ -307,10 +307,9 @@ static unsigned getHashValueImpl(SimpleValue Val) {
IVI->getOperand(1),
hash_combine_range(IVI->idx_begin(), IVI->idx_end()));

assert((isa<CallInst>(Inst) || isa<GetElementPtrInst>(Inst) ||
isa<ExtractElementInst>(Inst) || isa<InsertElementInst>(Inst) ||
isa<ShuffleVectorInst>(Inst) || isa<UnaryOperator>(Inst) ||
isa<FreezeInst>(Inst)) &&
assert((isa<CallInst>(Inst) || isa<ExtractElementInst>(Inst) ||
isa<InsertElementInst>(Inst) || isa<ShuffleVectorInst>(Inst) ||
isa<UnaryOperator>(Inst) || isa<FreezeInst>(Inst)) &&
"Invalid/unknown instruction");

// Handle intrinsics with commutative operands.
Expand Down Expand Up @@ -553,6 +552,77 @@ bool DenseMapInfo<CallValue>::isEqual(CallValue LHS, CallValue RHS) {
return LHSI->isIdenticalTo(RHSI);
}

//===----------------------------------------------------------------------===//
// GEPValue
//===----------------------------------------------------------------------===//

namespace {

struct GEPValue {
Instruction *Inst;
APInt ConstantOffset;
bool HasConstantOffset;

GEPValue(Instruction *I) : Inst(I), HasConstantOffset(false) {
assert((isSentinel() || canHandle(I)) && "Inst can't be handled!");
}
GEPValue(Instruction *I, APInt ConstantOffset, bool HasConstantOffset)
: Inst(I), ConstantOffset(ConstantOffset),
HasConstantOffset(HasConstantOffset) {
assert((isSentinel() || canHandle(I)) && "Inst can't be handled!");
}

bool isSentinel() const {
return Inst == DenseMapInfo<Instruction *>::getEmptyKey() ||
Inst == DenseMapInfo<Instruction *>::getTombstoneKey();
}

static bool canHandle(Instruction *Inst) {
return isa<GetElementPtrInst>(Inst);
}
};

} // namespace

namespace llvm {

template <> struct DenseMapInfo<GEPValue> {
static inline GEPValue getEmptyKey() {
return DenseMapInfo<Instruction *>::getEmptyKey();
}

static inline GEPValue getTombstoneKey() {
return DenseMapInfo<Instruction *>::getTombstoneKey();
}

static unsigned getHashValue(GEPValue Val);
static bool isEqual(GEPValue LHS, GEPValue RHS);
};

} // end namespace llvm

unsigned DenseMapInfo<GEPValue>::getHashValue(GEPValue Val) {
GetElementPtrInst *GEP = cast<GetElementPtrInst>(Val.Inst);
if (Val.HasConstantOffset)
return hash_combine(GEP->getOpcode(), GEP->getPointerOperand(),
Val.ConstantOffset);
return hash_combine(
GEP->getOpcode(),
hash_combine_range(GEP->value_op_begin(), GEP->value_op_end()));
}

bool DenseMapInfo<GEPValue>::isEqual(GEPValue LHS, GEPValue RHS) {
if (LHS.isSentinel() || RHS.isSentinel())
return LHS.Inst == RHS.Inst;
GetElementPtrInst *LGEP = cast<GetElementPtrInst>(LHS.Inst);
GetElementPtrInst *RGEP = cast<GetElementPtrInst>(RHS.Inst);
if (LGEP->getPointerOperand() != RGEP->getPointerOperand())
return false;
if (LHS.HasConstantOffset && RHS.HasConstantOffset)
return LHS.ConstantOffset == RHS.ConstantOffset;
return LGEP->isIdenticalToWhenDefined(RGEP);
}

//===----------------------------------------------------------------------===//
// EarlyCSE implementation
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -647,6 +717,13 @@ class EarlyCSE {
ScopedHashTable<CallValue, std::pair<Instruction *, unsigned>>;
CallHTType AvailableCalls;

using GEPMapAllocatorTy =
RecyclingAllocator<BumpPtrAllocator,
ScopedHashTableVal<GEPValue, Value *>>;
using GEPHTType = ScopedHashTable<GEPValue, Value *, DenseMapInfo<GEPValue>,
GEPMapAllocatorTy>;
GEPHTType AvailableGEPs;

/// This is the current generation of the memory value.
unsigned CurrentGeneration = 0;

Expand All @@ -667,9 +744,11 @@ class EarlyCSE {
class NodeScope {
public:
NodeScope(ScopedHTType &AvailableValues, LoadHTType &AvailableLoads,
InvariantHTType &AvailableInvariants, CallHTType &AvailableCalls)
: Scope(AvailableValues), LoadScope(AvailableLoads),
InvariantScope(AvailableInvariants), CallScope(AvailableCalls) {}
InvariantHTType &AvailableInvariants, CallHTType &AvailableCalls,
GEPHTType &AvailableGEPs)
: Scope(AvailableValues), LoadScope(AvailableLoads),
InvariantScope(AvailableInvariants), CallScope(AvailableCalls),
GEPScope(AvailableGEPs) {}
NodeScope(const NodeScope &) = delete;
NodeScope &operator=(const NodeScope &) = delete;

Expand All @@ -678,6 +757,7 @@ class EarlyCSE {
LoadHTType::ScopeTy LoadScope;
InvariantHTType::ScopeTy InvariantScope;
CallHTType::ScopeTy CallScope;
GEPHTType::ScopeTy GEPScope;
};

// Contains all the needed information to create a stack for doing a depth
Expand All @@ -688,13 +768,13 @@ class EarlyCSE {
public:
StackNode(ScopedHTType &AvailableValues, LoadHTType &AvailableLoads,
InvariantHTType &AvailableInvariants, CallHTType &AvailableCalls,
unsigned cg, DomTreeNode *n, DomTreeNode::const_iterator child,
GEPHTType &AvailableGEPs, unsigned cg, DomTreeNode *n,
DomTreeNode::const_iterator child,
DomTreeNode::const_iterator end)
: CurrentGeneration(cg), ChildGeneration(cg), Node(n), ChildIter(child),
EndIter(end),
Scopes(AvailableValues, AvailableLoads, AvailableInvariants,
AvailableCalls)
{}
AvailableCalls, AvailableGEPs) {}
StackNode(const StackNode &) = delete;
StackNode &operator=(const StackNode &) = delete;

Expand Down Expand Up @@ -1561,6 +1641,39 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
continue;
}

if (GEPValue::canHandle(&Inst)) {
GetElementPtrInst *GEP = cast<GetElementPtrInst>(&Inst);
APInt Offset(SQ.DL.getIndexTypeSizeInBits(GEP->getType()), 0);
bool HasConstantOffset = GEP->accumulateConstantOffset(SQ.DL, Offset);
GEPValue GEPVal(GEP, Offset, HasConstantOffset);
if (Value *V = AvailableGEPs.lookup(GEPVal)) {
LLVM_DEBUG(dbgs() << "EarlyCSE CSE: " << Inst << " to: " << *V
<< '\n');
if (auto *I = dyn_cast<Instruction>(V)) {
// If I being poison triggers UB, there is no need to drop those
// flags. Otherwise, only retain flags present on both I and Inst.
// TODO: Currently some fast-math flags are not treated as
// poison-generating even though they should. Until this is fixed,
// always retain flags present on both I and Inst for floating point
// instructions.
if (isa<FPMathOperator>(I) ||
(I->hasPoisonGeneratingFlags() && !programUndefinedIfPoison(I)))
I->andIRFlags(&Inst);
}
Inst.replaceAllUsesWith(V);
salvageKnowledge(&Inst, &AC);
removeMSSA(Inst);
Inst.eraseFromParent();
Changed = true;
++NumCSE;
continue;
}

// Otherwise, just remember that this value is available.
AvailableGEPs.insert(GEPVal, &Inst);
continue;
}

// A release fence requires that all stores complete before it, but does
// not prevent the reordering of following loads 'before' the fence. As a
// result, we don't need to consider it as writing to memory and don't need
Expand Down Expand Up @@ -1675,7 +1788,7 @@ bool EarlyCSE::run() {
// Process the root node.
nodesToProcess.push_back(new StackNode(
AvailableValues, AvailableLoads, AvailableInvariants, AvailableCalls,
CurrentGeneration, DT.getRootNode(),
AvailableGEPs, CurrentGeneration, DT.getRootNode(),
DT.getRootNode()->begin(), DT.getRootNode()->end()));

assert(!CurrentGeneration && "Create a new EarlyCSE instance to rerun it.");
Expand All @@ -1698,10 +1811,10 @@ bool EarlyCSE::run() {
} else if (NodeToProcess->childIter() != NodeToProcess->end()) {
// Push the next child onto the stack.
DomTreeNode *child = NodeToProcess->nextChild();
nodesToProcess.push_back(
new StackNode(AvailableValues, AvailableLoads, AvailableInvariants,
AvailableCalls, NodeToProcess->childGeneration(),
child, child->begin(), child->end()));
nodesToProcess.push_back(new StackNode(
AvailableValues, AvailableLoads, AvailableInvariants, AvailableCalls,
AvailableGEPs, NodeToProcess->childGeneration(), child,
child->begin(), child->end()));
} else {
// It has been processed, and there are no more children to process,
// so delete it and pop it off the stack.
Expand Down
3 changes: 0 additions & 3 deletions llvm/test/Transforms/EarlyCSE/gep.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ define void @foo(ptr %a, <4 x i64> %b, i64 %i) {
; CHECK-LABEL: define void @foo(
; CHECK-SAME: ptr [[A:%.*]], <4 x i64> [[B:%.*]], i64 [[I:%.*]]) {
; CHECK-NEXT: [[S1A:%.*]] = getelementptr i8, ptr [[A]], i64 8
; CHECK-NEXT: [[S1C:%.*]] = getelementptr [[T1:%.*]], ptr [[A]], i64 0, i32 1
; CHECK-NEXT: [[N1D:%.*]] = getelementptr i8, ptr [[A]], i64 7
; CHECK-NEXT: [[S1E:%.*]] = getelementptr i64, ptr [[A]], i64 1
; CHECK-NEXT: [[S1F:%.*]] = getelementptr i32, ptr [[A]], i64 2
; CHECK-NEXT: [[N1G:%.*]] = getelementptr i32, ptr [[A]], i64 1
; CHECK-NEXT: [[N1H:%.*]] = getelementptr i8, ptr [[A]], i64 [[I]]
; CHECK-NEXT: [[V:%.*]] = getelementptr i64, ptr [[A]], <4 x i64> <i64 1, i64 1, i64 1, i64 1>
Expand Down
96 changes: 1 addition & 95 deletions llvm/test/Transforms/PhaseOrdering/X86/unroll-vectorizer.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,101 +10,7 @@ define void @foo(ptr %a, <32 x i8> %_0) #0 {
; CHECK-LABEL: define void @foo(
; CHECK-SAME: ptr nocapture writeonly [[A:%.*]], <32 x i8> [[_0:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: start:
; CHECK-NEXT: [[_34I:%.*]] = getelementptr i8, ptr [[A]], i64 1
; CHECK-NEXT: [[Z_SROA_0_16_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 0
; CHECK-NEXT: store i8 [[Z_SROA_0_16_VEC_EXTRACT]], ptr [[A]], align 1
; CHECK-NEXT: [[_34I_1:%.*]] = getelementptr i8, ptr [[A]], i64 2
; CHECK-NEXT: [[Z_SROA_0_17_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 1
; CHECK-NEXT: store i8 [[Z_SROA_0_17_VEC_EXTRACT]], ptr [[_34I]], align 1
; CHECK-NEXT: [[_34I_2:%.*]] = getelementptr i8, ptr [[A]], i64 3
; CHECK-NEXT: [[Z_SROA_0_18_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 2
; CHECK-NEXT: store i8 [[Z_SROA_0_18_VEC_EXTRACT]], ptr [[_34I_1]], align 1
; CHECK-NEXT: [[_34I_3:%.*]] = getelementptr i8, ptr [[A]], i64 4
; CHECK-NEXT: [[Z_SROA_0_19_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 3
; CHECK-NEXT: store i8 [[Z_SROA_0_19_VEC_EXTRACT]], ptr [[_34I_2]], align 1
; CHECK-NEXT: [[_34I_4:%.*]] = getelementptr i8, ptr [[A]], i64 5
; CHECK-NEXT: [[Z_SROA_0_20_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 4
; CHECK-NEXT: store i8 [[Z_SROA_0_20_VEC_EXTRACT]], ptr [[_34I_3]], align 1
; CHECK-NEXT: [[_34I_5:%.*]] = getelementptr i8, ptr [[A]], i64 6
; CHECK-NEXT: [[Z_SROA_0_21_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 5
; CHECK-NEXT: store i8 [[Z_SROA_0_21_VEC_EXTRACT]], ptr [[_34I_4]], align 1
; CHECK-NEXT: [[_34I_6:%.*]] = getelementptr i8, ptr [[A]], i64 7
; CHECK-NEXT: [[Z_SROA_0_22_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 6
; CHECK-NEXT: store i8 [[Z_SROA_0_22_VEC_EXTRACT]], ptr [[_34I_5]], align 1
; CHECK-NEXT: [[_34I_7:%.*]] = getelementptr i8, ptr [[A]], i64 8
; CHECK-NEXT: [[Z_SROA_0_23_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 7
; CHECK-NEXT: store i8 [[Z_SROA_0_23_VEC_EXTRACT]], ptr [[_34I_6]], align 1
; CHECK-NEXT: [[_34I_8:%.*]] = getelementptr i8, ptr [[A]], i64 9
; CHECK-NEXT: [[Z_SROA_0_24_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 8
; CHECK-NEXT: store i8 [[Z_SROA_0_24_VEC_EXTRACT]], ptr [[_34I_7]], align 1
; CHECK-NEXT: [[_34I_9:%.*]] = getelementptr i8, ptr [[A]], i64 10
; CHECK-NEXT: [[Z_SROA_0_25_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 9
; CHECK-NEXT: store i8 [[Z_SROA_0_25_VEC_EXTRACT]], ptr [[_34I_8]], align 1
; CHECK-NEXT: [[_34I_10:%.*]] = getelementptr i8, ptr [[A]], i64 11
; CHECK-NEXT: [[Z_SROA_0_26_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 10
; CHECK-NEXT: store i8 [[Z_SROA_0_26_VEC_EXTRACT]], ptr [[_34I_9]], align 1
; CHECK-NEXT: [[_34I_11:%.*]] = getelementptr i8, ptr [[A]], i64 12
; CHECK-NEXT: [[Z_SROA_0_27_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 11
; CHECK-NEXT: store i8 [[Z_SROA_0_27_VEC_EXTRACT]], ptr [[_34I_10]], align 1
; CHECK-NEXT: [[_34I_12:%.*]] = getelementptr i8, ptr [[A]], i64 13
; CHECK-NEXT: [[Z_SROA_0_28_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 12
; CHECK-NEXT: store i8 [[Z_SROA_0_28_VEC_EXTRACT]], ptr [[_34I_11]], align 1
; CHECK-NEXT: [[_34I_13:%.*]] = getelementptr i8, ptr [[A]], i64 14
; CHECK-NEXT: [[Z_SROA_0_29_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 13
; CHECK-NEXT: store i8 [[Z_SROA_0_29_VEC_EXTRACT]], ptr [[_34I_12]], align 1
; CHECK-NEXT: [[_34I_14:%.*]] = getelementptr i8, ptr [[A]], i64 15
; CHECK-NEXT: [[Z_SROA_0_30_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 14
; CHECK-NEXT: store i8 [[Z_SROA_0_30_VEC_EXTRACT]], ptr [[_34I_13]], align 1
; CHECK-NEXT: [[_34I_15:%.*]] = getelementptr i8, ptr [[A]], i64 16
; CHECK-NEXT: [[Z_SROA_0_31_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 15
; CHECK-NEXT: store i8 [[Z_SROA_0_31_VEC_EXTRACT]], ptr [[_34I_14]], align 1
; CHECK-NEXT: [[_34I_16:%.*]] = getelementptr i8, ptr [[A]], i64 17
; CHECK-NEXT: [[Z_SROA_0_32_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 16
; CHECK-NEXT: store i8 [[Z_SROA_0_32_VEC_EXTRACT]], ptr [[_34I_15]], align 1
; CHECK-NEXT: [[_34I_17:%.*]] = getelementptr i8, ptr [[A]], i64 18
; CHECK-NEXT: [[Z_SROA_0_33_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 17
; CHECK-NEXT: store i8 [[Z_SROA_0_33_VEC_EXTRACT]], ptr [[_34I_16]], align 1
; CHECK-NEXT: [[_34I_18:%.*]] = getelementptr i8, ptr [[A]], i64 19
; CHECK-NEXT: [[Z_SROA_0_34_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 18
; CHECK-NEXT: store i8 [[Z_SROA_0_34_VEC_EXTRACT]], ptr [[_34I_17]], align 1
; CHECK-NEXT: [[_34I_19:%.*]] = getelementptr i8, ptr [[A]], i64 20
; CHECK-NEXT: [[Z_SROA_0_35_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 19
; CHECK-NEXT: store i8 [[Z_SROA_0_35_VEC_EXTRACT]], ptr [[_34I_18]], align 1
; CHECK-NEXT: [[_34I_20:%.*]] = getelementptr i8, ptr [[A]], i64 21
; CHECK-NEXT: [[Z_SROA_0_36_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 20
; CHECK-NEXT: store i8 [[Z_SROA_0_36_VEC_EXTRACT]], ptr [[_34I_19]], align 1
; CHECK-NEXT: [[_34I_21:%.*]] = getelementptr i8, ptr [[A]], i64 22
; CHECK-NEXT: [[Z_SROA_0_37_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 21
; CHECK-NEXT: store i8 [[Z_SROA_0_37_VEC_EXTRACT]], ptr [[_34I_20]], align 1
; CHECK-NEXT: [[_34I_22:%.*]] = getelementptr i8, ptr [[A]], i64 23
; CHECK-NEXT: [[Z_SROA_0_38_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 22
; CHECK-NEXT: store i8 [[Z_SROA_0_38_VEC_EXTRACT]], ptr [[_34I_21]], align 1
; CHECK-NEXT: [[_34I_23:%.*]] = getelementptr i8, ptr [[A]], i64 24
; CHECK-NEXT: [[Z_SROA_0_39_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 23
; CHECK-NEXT: store i8 [[Z_SROA_0_39_VEC_EXTRACT]], ptr [[_34I_22]], align 1
; CHECK-NEXT: [[_34I_24:%.*]] = getelementptr i8, ptr [[A]], i64 25
; CHECK-NEXT: [[Z_SROA_0_40_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 24
; CHECK-NEXT: store i8 [[Z_SROA_0_40_VEC_EXTRACT]], ptr [[_34I_23]], align 1
; CHECK-NEXT: [[_34I_25:%.*]] = getelementptr i8, ptr [[A]], i64 26
; CHECK-NEXT: [[Z_SROA_0_41_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 25
; CHECK-NEXT: store i8 [[Z_SROA_0_41_VEC_EXTRACT]], ptr [[_34I_24]], align 1
; CHECK-NEXT: [[_34I_26:%.*]] = getelementptr i8, ptr [[A]], i64 27
; CHECK-NEXT: [[Z_SROA_0_42_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 26
; CHECK-NEXT: store i8 [[Z_SROA_0_42_VEC_EXTRACT]], ptr [[_34I_25]], align 1
; CHECK-NEXT: [[_34I_27:%.*]] = getelementptr i8, ptr [[A]], i64 28
; CHECK-NEXT: [[Z_SROA_0_43_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 27
; CHECK-NEXT: store i8 [[Z_SROA_0_43_VEC_EXTRACT]], ptr [[_34I_26]], align 1
; CHECK-NEXT: [[_34I_28:%.*]] = getelementptr i8, ptr [[A]], i64 29
; CHECK-NEXT: [[Z_SROA_0_44_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 28
; CHECK-NEXT: store i8 [[Z_SROA_0_44_VEC_EXTRACT]], ptr [[_34I_27]], align 1
; CHECK-NEXT: [[_34I_29:%.*]] = getelementptr i8, ptr [[A]], i64 30
; CHECK-NEXT: [[Z_SROA_0_45_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 29
; CHECK-NEXT: store i8 [[Z_SROA_0_45_VEC_EXTRACT]], ptr [[_34I_28]], align 1
; CHECK-NEXT: [[_34I_30:%.*]] = getelementptr i8, ptr [[A]], i64 31
; CHECK-NEXT: [[Z_SROA_0_46_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 30
; CHECK-NEXT: store i8 [[Z_SROA_0_46_VEC_EXTRACT]], ptr [[_34I_29]], align 1
; CHECK-NEXT: [[Z_SROA_0_47_VEC_EXTRACT:%.*]] = extractelement <32 x i8> [[_0]], i64 31
; CHECK-NEXT: store i8 [[Z_SROA_0_47_VEC_EXTRACT]], ptr [[_34I_30]], align 1
; CHECK-NEXT: store <32 x i8> [[_0]], ptr [[A]], align 1
; CHECK-NEXT: ret void
;
start:
Expand Down

0 comments on commit 31e2ec9

Please sign in to comment.