[globalisel][legalizerinfo] Add support for legalization based on the MachineMemOperand

Summary:
Currently only the memory size is supported but others can be added as
needed.

narrowScalar for G_LOAD and G_STORE now correctly update the
MachineMemOperand and will refuse to legalize atomics since those need more
careful expansions to maintain atomicity.

Reviewers: ab, aditya_nandakumar, bogner, rtereshin, aemerson, javed.absar

Reviewed By: aemerson

Subscribers: aemerson, rovka, kristof.beyls, javed.absar, llvm-commits

Differential Revision: https://reviews.llvm.org/D45466

llvm-svn: 331071
This commit is contained in:
Daniel Sanders 2018-04-27 19:48:53 +00:00
parent 3a8a56b8b7
commit 27fe8a5011
10 changed files with 144 additions and 47 deletions

View File

@ -118,6 +118,20 @@ struct LegalityQuery {
unsigned Opcode;
ArrayRef<LLT> Types;
struct MemDesc {
uint64_t Size;
};
/// Operations which require memory can use this to place requirements on the
/// memory type for each MMO.
ArrayRef<MemDesc> MMODescrs;
constexpr LegalityQuery(unsigned Opcode, const ArrayRef<LLT> Types,
const ArrayRef<MemDesc> MMODescrs)
: Opcode(Opcode), Types(Types), MMODescrs(MMODescrs) {}
constexpr LegalityQuery(unsigned Opcode, const ArrayRef<LLT> Types)
: LegalityQuery(Opcode, Types, {}) {}
raw_ostream &print(raw_ostream &OS) const;
};
@ -157,6 +171,11 @@ LegalityPredicate typeInSet(unsigned TypeIdx,
LegalityPredicate
typePairInSet(unsigned TypeIdx0, unsigned TypeIdx1,
std::initializer_list<std::pair<LLT, LLT>> TypesInit);
/// True iff the given types for the given pair of type indexes is one of the
/// specified type pairs.
LegalityPredicate typePairAndMemSizeInSet(
unsigned TypeIdx0, unsigned TypeIdx1, unsigned MMOIdx,
std::initializer_list<std::tuple<LLT, LLT, unsigned>> TypesAndMemSizeInit);
/// True iff the specified type index is a scalar.
LegalityPredicate isScalar(unsigned TypeIdx);
/// True iff the specified type index is a scalar that's narrower than the given
@ -168,6 +187,8 @@ LegalityPredicate widerThan(unsigned TypeIdx, unsigned Size);
/// True iff the specified type index is a scalar whose size is not a power of
/// 2.
LegalityPredicate sizeNotPow2(unsigned TypeIdx);
/// True iff the specified MMO index has a size that is not a power of 2
LegalityPredicate memSizeInBytesNotPow2(unsigned MMOIdx);
/// True iff the specified type index is a vector whose element count is not a
/// power of 2.
LegalityPredicate numElementsNotPow2(unsigned TypeIdx);
@ -322,6 +343,13 @@ public:
LegalizeRuleSet &legalFor(std::initializer_list<std::pair<LLT, LLT>> Types) {
return actionFor(LegalizeAction::Legal, Types);
}
/// The instruction is legal when type indexes 0 and 1 along with the memory
/// size is any type and size tuple in the given list.
LegalizeRuleSet &legalForTypesWithMemSize(
std::initializer_list<std::tuple<LLT, LLT, unsigned>> TypesAndMemSize) {
return legalIf(LegalityPredicates::typePairAndMemSizeInSet(
0, 1, /*MMOIdx*/ 0, TypesAndMemSize));
}
/// The instruction is legal when type indexes 0 and 1 are both in the given
/// list. That is, the type pair is in the cartesian product of the list.
LegalizeRuleSet &legalForCartesianProduct(std::initializer_list<LLT> Types) {
@ -427,6 +455,10 @@ public:
LegalizeRuleSet &unsupportedIf(LegalityPredicate Predicate) {
return actionIf(LegalizeAction::Unsupported, Predicate);
}
LegalizeRuleSet &unsupportedIfMemSizeNotPow2() {
return actionIf(LegalizeAction::Unsupported,
LegalityPredicates::memSizeInBytesNotPow2(0));
}
LegalizeRuleSet &customIf(LegalityPredicate Predicate) {
return actionIf(LegalizeAction::Custom, Predicate);
@ -510,7 +542,7 @@ public:
return moreElementsIf(
[=](const LegalityQuery &Query) {
LLT VecTy = Query.Types[TypeIdx];
return VecTy.getElementType() == EltTy &&
return VecTy.isVector() && VecTy.getElementType() == EltTy &&
VecTy.getNumElements() < MinElements;
},
[=](const LegalityQuery &Query) {
@ -525,7 +557,7 @@ public:
return fewerElementsIf(
[=](const LegalityQuery &Query) {
LLT VecTy = Query.Types[TypeIdx];
return VecTy.getElementType() == EltTy &&
return VecTy.isVector() && VecTy.getElementType() == EltTy &&
VecTy.getNumElements() > MaxElements;
},
[=](const LegalityQuery &Query) {

View File

@ -41,6 +41,18 @@ LegalityPredicate LegalityPredicates::typePairInSet(
};
}
LegalityPredicate LegalityPredicates::typePairAndMemSizeInSet(
unsigned TypeIdx0, unsigned TypeIdx1, unsigned MMOIdx,
std::initializer_list<std::tuple<LLT, LLT, unsigned>> TypesAndMemSizeInit) {
SmallVector<std::tuple<LLT, LLT, unsigned>, 4> TypesAndMemSize = TypesAndMemSizeInit;
return [=](const LegalityQuery &Query) {
std::tuple<LLT, LLT, unsigned> Match = {
Query.Types[TypeIdx0], Query.Types[TypeIdx1], Query.MMODescrs[MMOIdx].Size};
return std::find(TypesAndMemSize.begin(), TypesAndMemSize.end(), Match) !=
TypesAndMemSize.end();
};
}
LegalityPredicate LegalityPredicates::isScalar(unsigned TypeIdx) {
return [=](const LegalityQuery &Query) {
return Query.Types[TypeIdx].isScalar();
@ -70,6 +82,12 @@ LegalityPredicate LegalityPredicates::sizeNotPow2(unsigned TypeIdx) {
};
}
LegalityPredicate LegalityPredicates::memSizeInBytesNotPow2(unsigned MMOIdx) {
return [=](const LegalityQuery &Query) {
return !isPowerOf2_32(Query.MMODescrs[MMOIdx].Size /* In Bytes */);
};
}
LegalityPredicate LegalityPredicates::numElementsNotPow2(unsigned TypeIdx) {
return [=](const LegalityQuery &Query) {
const LLT &QueryTy = Query.Types[TypeIdx];

View File

@ -272,8 +272,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
MIRBuilder.setInstr(MI);
int64_t SizeOp0 = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
int64_t NarrowSize = NarrowTy.getSizeInBits();
uint64_t SizeOp0 = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
uint64_t NarrowSize = NarrowTy.getSizeInBits();
switch (MI.getOpcode()) {
default:
@ -339,8 +339,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, SrcRegs);
unsigned OpReg = MI.getOperand(0).getReg();
int64_t OpStart = MI.getOperand(2).getImm();
int64_t OpSize = MRI.getType(OpReg).getSizeInBits();
uint64_t OpStart = MI.getOperand(2).getImm();
uint64_t OpSize = MRI.getType(OpReg).getSizeInBits();
for (int i = 0; i < NumParts; ++i) {
unsigned SrcStart = i * NarrowSize;
@ -355,7 +355,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
// OpSegStart is where this destination segment would start in OpReg if it
// extended infinitely in both directions.
int64_t ExtractOffset, SegSize;
int64_t ExtractOffset;
uint64_t SegSize;
if (OpStart < SrcStart) {
ExtractOffset = 0;
SegSize = std::min(NarrowSize, OpStart + OpSize - SrcStart);
@ -391,8 +392,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, SrcRegs);
unsigned OpReg = MI.getOperand(2).getReg();
int64_t OpStart = MI.getOperand(3).getImm();
int64_t OpSize = MRI.getType(OpReg).getSizeInBits();
uint64_t OpStart = MI.getOperand(3).getImm();
uint64_t OpSize = MRI.getType(OpReg).getSizeInBits();
for (int i = 0; i < NumParts; ++i) {
unsigned DstStart = i * NarrowSize;
@ -409,7 +410,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
// OpSegStart is where this destination segment would start in OpReg if it
// extended infinitely in both directions.
int64_t ExtractOffset, InsertOffset, SegSize;
int64_t ExtractOffset, InsertOffset;
uint64_t SegSize;
if (OpStart < DstStart) {
InsertOffset = 0;
ExtractOffset = DstStart - OpStart;
@ -443,6 +445,14 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
// NarrowSize.
if (SizeOp0 % NarrowSize != 0)
return UnableToLegalize;
const auto &MMO = **MI.memoperands_begin();
// This implementation doesn't work for atomics. Give up instead of doing
// something invalid.
if (MMO.getOrdering() != AtomicOrdering::NotAtomic ||
MMO.getFailureOrdering() != AtomicOrdering::NotAtomic)
return UnableToLegalize;
int NumParts = SizeOp0 / NarrowSize;
LLT OffsetTy = LLT::scalar(
MRI.getType(MI.getOperand(1).getReg()).getScalarSizeInBits());
@ -453,12 +463,16 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
unsigned SrcReg = 0;
unsigned Adjustment = i * NarrowSize / 8;
MachineMemOperand *SplitMMO = MIRBuilder.getMF().getMachineMemOperand(
MMO.getPointerInfo().getWithOffset(Adjustment), MMO.getFlags(),
NarrowSize / 8, i == 0 ? MMO.getAlignment() : NarrowSize / 8,
MMO.getAAInfo(), MMO.getRanges(), MMO.getSyncScopeID(),
MMO.getOrdering(), MMO.getFailureOrdering());
MIRBuilder.materializeGEP(SrcReg, MI.getOperand(1).getReg(), OffsetTy,
Adjustment);
// TODO: This is conservatively correct, but we probably want to split the
// memory operands in the future.
MIRBuilder.buildLoad(DstReg, SrcReg, **MI.memoperands_begin());
MIRBuilder.buildLoad(DstReg, SrcReg, *SplitMMO);
DstRegs.push_back(DstReg);
}
@ -472,6 +486,14 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
// NarrowSize.
if (SizeOp0 % NarrowSize != 0)
return UnableToLegalize;
const auto &MMO = **MI.memoperands_begin();
// This implementation doesn't work for atomics. Give up instead of doing
// something invalid.
if (MMO.getOrdering() != AtomicOrdering::NotAtomic ||
MMO.getFailureOrdering() != AtomicOrdering::NotAtomic)
return UnableToLegalize;
int NumParts = SizeOp0 / NarrowSize;
LLT OffsetTy = LLT::scalar(
MRI.getType(MI.getOperand(1).getReg()).getScalarSizeInBits());
@ -483,12 +505,16 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
unsigned DstReg = 0;
unsigned Adjustment = i * NarrowSize / 8;
MachineMemOperand *SplitMMO = MIRBuilder.getMF().getMachineMemOperand(
MMO.getPointerInfo().getWithOffset(Adjustment), MMO.getFlags(),
NarrowSize / 8, i == 0 ? MMO.getAlignment() : NarrowSize / 8,
MMO.getAAInfo(), MMO.getRanges(), MMO.getSyncScopeID(),
MMO.getOrdering(), MMO.getFailureOrdering());
MIRBuilder.materializeGEP(DstReg, MI.getOperand(1).getReg(), OffsetTy,
Adjustment);
// TODO: This is conservatively correct, but we probably want to split the
// memory operands in the future.
MIRBuilder.buildStore(SrcRegs[i], DstReg, **MI.memoperands_begin());
MIRBuilder.buildStore(SrcRegs[i], DstReg, *SplitMMO);
}
MI.eraseFromParent();
return Legalized;

View File

@ -42,11 +42,18 @@ cl::opt<bool> llvm::DisableGISelLegalityCheck(
cl::Hidden);
raw_ostream &LegalityQuery::print(raw_ostream &OS) const {
OS << Opcode << ", {";
OS << Opcode << ", Tys={";
for (const auto &Type : Types) {
OS << Type << ", ";
}
OS << "}, Opcode=";
OS << Opcode << ", MMOs={";
for (const auto &MMODescr : MMODescrs) {
OS << MMODescr.Size << ", ";
}
OS << "}";
return OS;
}
@ -330,7 +337,12 @@ LegalizerInfo::getAction(const MachineInstr &MI,
LLT Ty = getTypeFromTypeIdx(MI, MRI, i, TypeIdx);
Types.push_back(Ty);
}
return getAction({MI.getOpcode(), Types});
SmallVector<LegalityQuery::MemDesc, 2> MemDescrs;
for (const auto &MMO : MI.memoperands())
MemDescrs.push_back({MMO->getSize() /* in bytes */ * 8});
return getAction({MI.getOpcode(), Types, MemDescrs});
}
bool LegalizerInfo::isLegal(const MachineInstr &MI,

View File

@ -136,8 +136,15 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) {
.widenScalarToNextPow2(0);
getActionDefinitionsBuilder({G_LOAD, G_STORE})
.legalFor(
{{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}, {p0, p0}, {v2s32, p0}})
.legalForTypesWithMemSize({{s8, p0, 8},
{s16, p0, 16},
{s32, p0, 32},
{s64, p0, 64},
{p0, p0, 64},
{v2s32, p0, 64}})
// TODO: We could support sum-of-pow2's but the lowering code doesn't know
// how to do that yet.
.unsupportedIfMemSizeNotPow2()
.clampScalar(0, s8, s64)
.widenScalarToNextPow2(0)
.clampNumElements(0, v2s32, v2s32);

View File

@ -12,10 +12,10 @@ body: |
; CHECK-LABEL: name: test_extracts_1
; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x2
; CHECK: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load 16)
; CHECK: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load 8, align 16)
; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
; CHECK: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[COPY]], [[C]](s64)
; CHECK: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP]](p0) :: (load 16)
; CHECK: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP]](p0) :: (load 8)
; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY [[LOAD]](s64)
; CHECK: G_STORE [[COPY1]](s64), [[COPY]](p0) :: (store 8)
; CHECK: RET_ReallyLR
@ -37,10 +37,10 @@ body: |
; Low extraction wipes takes whole low register. High extraction is real.
; CHECK-LABEL: name: test_extracts_2
; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x2
; CHECK: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load 16)
; CHECK: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load 8, align 16)
; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
; CHECK: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[COPY]], [[C]](s64)
; CHECK: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP]](p0) :: (load 16)
; CHECK: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP]](p0) :: (load 8)
; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY [[LOAD]](s64)
; CHECK: [[EXTRACT:%[0-9]+]]:_(s32) = G_EXTRACT [[LOAD1]](s64), 0
; CHECK: [[COPY2:%[0-9]+]]:_(s32) = COPY [[EXTRACT]](s32)

View File

@ -62,10 +62,10 @@ body: |
%13:_(s64) = G_BITCAST %7
$x0 = COPY %13
; CHECK: [[LOAD0:%[0-9]+]]:_(s64) = G_LOAD %0(p0) :: (load 16 from %ir.addr)
; CHECK: [[LOAD0:%[0-9]+]]:_(s64) = G_LOAD %0(p0) :: (load 8 from %ir.addr, align 16)
; CHECK: [[OFFSET1:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
; CHECK: [[GEP1:%[0-9]+]]:_(p0) = G_GEP %0, [[OFFSET1]](s64)
; CHECK: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP1]](p0) :: (load 16 from %ir.addr)
; CHECK: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP1]](p0) :: (load 8 from %ir.addr + 8)
; CHECK: %8:_(s128) = G_MERGE_VALUES [[LOAD0]](s64), [[LOAD1]](s64)
%8(s128) = G_LOAD %0(p0) :: (load 16 from %ir.addr)
%14:_(s64) = G_TRUNC %8
@ -120,10 +120,10 @@ body: |
; CHECK: G_STORE %0(p0), %0(p0) :: (store 8 into %ir.addr)
G_STORE %0(p0), %0(p0) :: (store 8 into %ir.addr)
; CHECK: G_STORE %5(s64), %0(p0) :: (store 16 into %ir.addr)
; CHECK: G_STORE %5(s64), %0(p0) :: (store 8 into %ir.addr, align 16)
; CHECK: [[OFFSET1:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
; CHECK: [[GEP1:%[0-9]+]]:_(p0) = G_GEP %0, [[OFFSET1]](s64)
; CHECK: G_STORE %6(s64), [[GEP1]](p0) :: (store 16 into %ir.addr)
; CHECK: G_STORE %6(s64), [[GEP1]](p0) :: (store 8 into %ir.addr + 8)
%6(s64) = G_PTRTOINT %0(p0)
%7(s128) = G_MERGE_VALUES %5, %6
G_STORE %7, %0 :: (store 16 into %ir.addr)

View File

@ -95,10 +95,12 @@ body: |
; SOFT-ABI: [[SP1:%[0-9]+]]:_(p0) = COPY $sp
; SOFT-ABI: [[OFF1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
; SOFT-ABI: [[FI1:%[0-9]+]]:_(p0) = G_GEP [[SP1]], [[OFF1]](s32)
; SOFT-ABI: G_STORE [[Y0]](s32), [[FI1]](p0){{.*}}store 8 into stack
; FIXME: This ought to be align 8 but ARM's call lowering hardcodes it to 0
; SOFT-ABI: G_STORE [[Y0]](s32), [[FI1]](p0){{.*}}store 4 into stack, align 0)
; SOFT-ABI: [[OFF2:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
; SOFT-ABI: [[FI2:%[0-9]+]]:_(p0) = G_GEP [[FI1]], [[OFF2]](s32)
; SOFT-ABI: G_STORE [[Y1]](s32), [[FI2]](p0){{.*}}store 8 into stack
; SOFT-ABI: G_STORE [[Y1]](s32), [[FI2]](p0){{.*}}store 4 into stack + 4)
; SOFT-ABI: BL &fma, {{.*}}, implicit $r0, implicit $r1, implicit $r2, implicit $r3, implicit-def $r0, implicit-def $r1
; SOFT-ABI-DAG: [[R0:%[0-9]+]]:_(s32) = COPY $r0
; SOFT-ABI-DAG: [[R1:%[0-9]+]]:_(s32) = COPY $r1

View File

@ -96,14 +96,14 @@ body: |
; X64: G_STORE [[LOAD]](s64), [[DEF]](p0) :: (store 8)
; X32-LABEL: name: test_memop_s64
; X32: [[DEF:%[0-9]+]]:_(p0) = IMPLICIT_DEF
; X32: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[DEF]](p0) :: (load 8)
; X32: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[DEF]](p0) :: (load 4, align 8)
; X32: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
; X32: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[DEF]], [[C]](s32)
; X32: [[LOAD1:%[0-9]+]]:_(s32) = G_LOAD [[GEP]](p0) :: (load 8)
; X32: G_STORE [[LOAD]](s32), [[DEF]](p0) :: (store 8)
; X32: [[LOAD1:%[0-9]+]]:_(s32) = G_LOAD [[GEP]](p0) :: (load 4)
; X32: G_STORE [[LOAD]](s32), [[DEF]](p0) :: (store 4, align 8)
; X32: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
; X32: [[GEP1:%[0-9]+]]:_(p0) = G_GEP [[DEF]], [[C1]](s32)
; X32: G_STORE [[LOAD1]](s32), [[GEP1]](p0) :: (store 8)
; X32: G_STORE [[LOAD1]](s32), [[GEP1]](p0) :: (store 4)
%0(p0) = IMPLICIT_DEF
%1(s64) = G_LOAD %0(p0) :: (load 8)

View File

@ -14,38 +14,38 @@ body: |
; X64: [[DEF1:%[0-9]+]]:_(s8) = G_IMPLICIT_DEF
; X64: G_STORE [[DEF1]](s8), [[DEF]](p0) :: (store 1)
; X64: [[DEF2:%[0-9]+]]:_(s8) = G_IMPLICIT_DEF
; X64: G_STORE [[DEF2]](s8), [[DEF]](p0) :: (store 8)
; X64: G_STORE [[DEF2]](s8), [[DEF]](p0) :: (store 1)
; X64: [[DEF3:%[0-9]+]]:_(s16) = G_IMPLICIT_DEF
; X64: G_STORE [[DEF3]](s16), [[DEF]](p0) :: (store 16)
; X64: G_STORE [[DEF3]](s16), [[DEF]](p0) :: (store 2)
; X64: [[DEF4:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; X64: G_STORE [[DEF4]](s32), [[DEF]](p0) :: (store 32)
; X64: G_STORE [[DEF4]](s32), [[DEF]](p0) :: (store 4)
; X64: [[DEF5:%[0-9]+]]:_(s64) = G_IMPLICIT_DEF
; X64: G_STORE [[DEF5]](s64), [[DEF]](p0) :: (store 64)
; X64: G_STORE [[DEF5]](s64), [[DEF]](p0) :: (store 8)
; X32-LABEL: name: test_implicit_def
; X32: [[DEF:%[0-9]+]]:_(p0) = G_IMPLICIT_DEF
; X32: [[DEF1:%[0-9]+]]:_(s8) = G_IMPLICIT_DEF
; X32: G_STORE [[DEF1]](s8), [[DEF]](p0) :: (store 1)
; X32: [[DEF2:%[0-9]+]]:_(s8) = G_IMPLICIT_DEF
; X32: G_STORE [[DEF2]](s8), [[DEF]](p0) :: (store 8)
; X32: G_STORE [[DEF2]](s8), [[DEF]](p0) :: (store 1)
; X32: [[DEF3:%[0-9]+]]:_(s16) = G_IMPLICIT_DEF
; X32: G_STORE [[DEF3]](s16), [[DEF]](p0) :: (store 16)
; X32: G_STORE [[DEF3]](s16), [[DEF]](p0) :: (store 2)
; X32: [[DEF4:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; X32: G_STORE [[DEF4]](s32), [[DEF]](p0) :: (store 32)
; X32: G_STORE [[DEF4]](s32), [[DEF]](p0) :: (store 4)
; X32: [[DEF5:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; X32: [[DEF6:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; X32: G_STORE [[DEF5]](s32), [[DEF]](p0) :: (store 64)
; X32: G_STORE [[DEF5]](s32), [[DEF]](p0) :: (store 4, align 8)
; X32: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
; X32: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[DEF]], [[C]](s32)
; X32: G_STORE [[DEF6]](s32), [[GEP]](p0) :: (store 64)
; X32: G_STORE [[DEF6]](s32), [[GEP]](p0) :: (store 4)
%5:_(p0) = G_IMPLICIT_DEF
%0:_(s1) = G_IMPLICIT_DEF
G_STORE %0, %5 ::(store 1)
%1:_(s8) = G_IMPLICIT_DEF
G_STORE %1, %5 ::(store 8)
G_STORE %1, %5 ::(store 1)
%2:_(s16) = G_IMPLICIT_DEF
G_STORE %2, %5 ::(store 16)
G_STORE %2, %5 ::(store 2)
%3:_(s32) = G_IMPLICIT_DEF
G_STORE %3, %5 ::(store 32)
G_STORE %3, %5 ::(store 4)
%4:_(s64) = G_IMPLICIT_DEF
G_STORE %4, %5 ::(store 64)
G_STORE %4, %5 ::(store 8)
...