[SelectionDAG] Fold more offsets into GlobalAddresses

SelectionDAG previously missed opportunities to fold constants into
GlobalAddresses in several areas. For example, given `(add (add GA, c1), y)`, it
would often reassociate to `(add (add GA, y), c1)`, missing the opportunity to
create `(add GA+c, y)`. This isn't often visible on targets such as X86 which
effectively reassociate adds in their complex address-mode folding logic,
however it is currently visible on WebAssembly since it currently has very
simple address mode folding code that doesn't reassociate anything.

This patch fixes this by making SelectionDAG fold offsets into GlobalAddresses
at the same times that it folds constants together, so that it doesn't miss any
opportunities to perform such folding.

Differential Revision: http://reviews.llvm.org/D16090

llvm-svn: 258296
This commit is contained in:
Dan Gohman 2016-01-20 07:03:08 +00:00
parent e5d3c15d7d
commit edf98c5682
6 changed files with 809 additions and 84 deletions

View File

@ -1156,6 +1156,10 @@ public:
/// either of the specified value types. /// either of the specified value types.
SDValue CreateStackTemporary(EVT VT1, EVT VT2); SDValue CreateStackTemporary(EVT VT1, EVT VT2);
SDValue FoldSymbolOffset(unsigned Opcode, EVT VT,
const GlobalAddressSDNode *GA,
const SDNode *N2);
SDValue FoldConstantArithmetic(unsigned Opcode, SDLoc DL, EVT VT, SDValue FoldConstantArithmetic(unsigned Opcode, SDLoc DL, EVT VT,
SDNode *Cst1, SDNode *Cst2); SDNode *Cst1, SDNode *Cst2);
@ -1267,6 +1271,9 @@ public:
unsigned getEVTAlignment(EVT MemoryVT) const; unsigned getEVTAlignment(EVT MemoryVT) const;
/// Test whether the given value is a constant int or similar node.
SDNode *isConstantIntBuildVectorOrConstantInt(SDValue N);
private: private:
void InsertNode(SDNode *N); void InsertNode(SDNode *N);
bool RemoveNodeFromCSEMaps(SDNode *N); bool RemoveNodeFromCSEMaps(SDNode *N);

View File

@ -390,6 +390,9 @@ namespace {
/// consecutive chains. /// consecutive chains.
bool findBetterNeighborChains(StoreSDNode *St); bool findBetterNeighborChains(StoreSDNode *St);
/// Match "(X shl/srl V1) & V2" where V2 may not be present.
bool MatchRotateHalf(SDValue Op, SDValue &Shift, SDValue &Mask);
/// Holds a pointer to an LSBaseSDNode as well as information on where it /// Holds a pointer to an LSBaseSDNode as well as information on where it
/// is located in a sequence of memory operations connected by a chain. /// is located in a sequence of memory operations connected by a chain.
struct MemOpLink { struct MemOpLink {
@ -763,16 +766,6 @@ static bool isConstantSplatVector(SDNode *N, APInt& SplatValue) {
EltVT.getSizeInBits() >= SplatBitSize); EltVT.getSizeInBits() >= SplatBitSize);
} }
// \brief Returns the SDNode if it is a constant integer BuildVector
// or constant integer.
static SDNode *isConstantIntBuildVectorOrConstantInt(SDValue N) {
if (isa<ConstantSDNode>(N))
return N.getNode();
if (ISD::isBuildVectorOfConstantSDNodes(N.getNode()))
return N.getNode();
return nullptr;
}
// \brief Returns the SDNode if it is a constant float BuildVector // \brief Returns the SDNode if it is a constant float BuildVector
// or constant float. // or constant float.
static SDNode *isConstantFPBuildVectorOrConstantFP(SDValue N) { static SDNode *isConstantFPBuildVectorOrConstantFP(SDValue N) {
@ -825,8 +818,8 @@ SDValue DAGCombiner::ReassociateOps(unsigned Opc, SDLoc DL,
SDValue N0, SDValue N1) { SDValue N0, SDValue N1) {
EVT VT = N0.getValueType(); EVT VT = N0.getValueType();
if (N0.getOpcode() == Opc) { if (N0.getOpcode() == Opc) {
if (SDNode *L = isConstantIntBuildVectorOrConstantInt(N0.getOperand(1))) { if (SDNode *L = DAG.isConstantIntBuildVectorOrConstantInt(N0.getOperand(1))) {
if (SDNode *R = isConstantIntBuildVectorOrConstantInt(N1)) { if (SDNode *R = DAG.isConstantIntBuildVectorOrConstantInt(N1)) {
// reassoc. (op (op x, c1), c2) -> (op x, (op c1, c2)) // reassoc. (op (op x, c1), c2) -> (op x, (op c1, c2))
if (SDValue OpNode = DAG.FoldConstantArithmetic(Opc, DL, VT, L, R)) if (SDValue OpNode = DAG.FoldConstantArithmetic(Opc, DL, VT, L, R))
return DAG.getNode(Opc, DL, VT, N0.getOperand(0), OpNode); return DAG.getNode(Opc, DL, VT, N0.getOperand(0), OpNode);
@ -845,8 +838,8 @@ SDValue DAGCombiner::ReassociateOps(unsigned Opc, SDLoc DL,
} }
if (N1.getOpcode() == Opc) { if (N1.getOpcode() == Opc) {
if (SDNode *R = isConstantIntBuildVectorOrConstantInt(N1.getOperand(1))) { if (SDNode *R = DAG.isConstantIntBuildVectorOrConstantInt(N1.getOperand(1))) {
if (SDNode *L = isConstantIntBuildVectorOrConstantInt(N0)) { if (SDNode *L = DAG.isConstantIntBuildVectorOrConstantInt(N0)) {
// reassoc. (op c2, (op x, c1)) -> (op x, (op c1, c2)) // reassoc. (op c2, (op x, c1)) -> (op x, (op c1, c2))
if (SDValue OpNode = DAG.FoldConstantArithmetic(Opc, DL, VT, R, L)) if (SDValue OpNode = DAG.FoldConstantArithmetic(Opc, DL, VT, R, L))
return DAG.getNode(Opc, DL, VT, N1.getOperand(0), OpNode); return DAG.getNode(Opc, DL, VT, N1.getOperand(0), OpNode);
@ -1657,27 +1650,20 @@ SDValue DAGCombiner::visitADD(SDNode *N) {
return N0; return N0;
if (N1.getOpcode() == ISD::UNDEF) if (N1.getOpcode() == ISD::UNDEF)
return N1; return N1;
// fold (add c1, c2) -> c1+c2 if (DAG.isConstantIntBuildVectorOrConstantInt(N0)) {
ConstantSDNode *N0C = getAsNonOpaqueConstant(N0);
ConstantSDNode *N1C = getAsNonOpaqueConstant(N1);
if (N0C && N1C)
return DAG.FoldConstantArithmetic(ISD::ADD, SDLoc(N), VT, N0C, N1C);
// canonicalize constant to RHS // canonicalize constant to RHS
if (isConstantIntBuildVectorOrConstantInt(N0) && if (!DAG.isConstantIntBuildVectorOrConstantInt(N1))
!isConstantIntBuildVectorOrConstantInt(N1))
return DAG.getNode(ISD::ADD, SDLoc(N), VT, N1, N0); return DAG.getNode(ISD::ADD, SDLoc(N), VT, N1, N0);
// fold (add c1, c2) -> c1+c2
return DAG.FoldConstantArithmetic(ISD::ADD, SDLoc(N), VT,
N0.getNode(), N1.getNode());
}
// fold (add x, 0) -> x // fold (add x, 0) -> x
if (isNullConstant(N1)) if (isNullConstant(N1))
return N0; return N0;
// fold (add Sym, c) -> Sym+c
if (GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(N0))
if (!LegalOperations && TLI.isOffsetFoldingLegal(GA) && N1C &&
GA->getOpcode() == ISD::GlobalAddress)
return DAG.getGlobalAddress(GA->getGlobal(), SDLoc(N1C), VT,
GA->getOffset() +
(uint64_t)N1C->getSExtValue());
// fold ((c1-A)+c2) -> (c1+c2)-A // fold ((c1-A)+c2) -> (c1+c2)-A
if (N1C && N0.getOpcode() == ISD::SUB) if (ConstantSDNode *N1C = getAsNonOpaqueConstant(N1)) {
if (N0.getOpcode() == ISD::SUB)
if (ConstantSDNode *N0C = getAsNonOpaqueConstant(N0.getOperand(0))) { if (ConstantSDNode *N0C = getAsNonOpaqueConstant(N0.getOperand(0))) {
SDLoc DL(N); SDLoc DL(N);
return DAG.getNode(ISD::SUB, DL, VT, return DAG.getNode(ISD::SUB, DL, VT,
@ -1685,6 +1671,7 @@ SDValue DAGCombiner::visitADD(SDNode *N) {
N0C->getAPIntValue(), DL, VT), N0C->getAPIntValue(), DL, VT),
N0.getOperand(1)); N0.getOperand(1));
} }
}
// reassociate add // reassociate add
if (SDValue RADD = ReassociateOps(ISD::ADD, SDLoc(N), N0, N1)) if (SDValue RADD = ReassociateOps(ISD::ADD, SDLoc(N), N0, N1))
return RADD; return RADD;
@ -1879,11 +1866,14 @@ SDValue DAGCombiner::visitSUB(SDNode *N) {
// FIXME: Refactor this and xor and other similar operations together. // FIXME: Refactor this and xor and other similar operations together.
if (N0 == N1) if (N0 == N1)
return tryFoldToZero(SDLoc(N), TLI, VT, DAG, LegalOperations, LegalTypes); return tryFoldToZero(SDLoc(N), TLI, VT, DAG, LegalOperations, LegalTypes);
if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
DAG.isConstantIntBuildVectorOrConstantInt(N1)) {
// fold (sub c1, c2) -> c1-c2 // fold (sub c1, c2) -> c1-c2
return DAG.FoldConstantArithmetic(ISD::SUB, SDLoc(N), VT,
N0.getNode(), N1.getNode());
}
ConstantSDNode *N0C = getAsNonOpaqueConstant(N0); ConstantSDNode *N0C = getAsNonOpaqueConstant(N0);
ConstantSDNode *N1C = getAsNonOpaqueConstant(N1); ConstantSDNode *N1C = getAsNonOpaqueConstant(N1);
if (N0C && N1C)
return DAG.FoldConstantArithmetic(ISD::SUB, SDLoc(N), VT, N0C, N1C);
// fold (sub x, c) -> (add x, -c) // fold (sub x, c) -> (add x, -c)
if (N1C) { if (N1C) {
SDLoc DL(N); SDLoc DL(N);
@ -2047,8 +2037,8 @@ SDValue DAGCombiner::visitMUL(SDNode *N) {
N0.getNode(), N1.getNode()); N0.getNode(), N1.getNode());
// canonicalize constant to RHS (vector doesn't have to splat) // canonicalize constant to RHS (vector doesn't have to splat)
if (isConstantIntBuildVectorOrConstantInt(N0) && if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
!isConstantIntBuildVectorOrConstantInt(N1)) !DAG.isConstantIntBuildVectorOrConstantInt(N1))
return DAG.getNode(ISD::MUL, SDLoc(N), VT, N1, N0); return DAG.getNode(ISD::MUL, SDLoc(N), VT, N1, N0);
// fold (mul x, 0) -> 0 // fold (mul x, 0) -> 0
if (N1IsConst && ConstValue1 == 0) if (N1IsConst && ConstValue1 == 0)
@ -2125,9 +2115,9 @@ SDValue DAGCombiner::visitMUL(SDNode *N) {
} }
// fold (mul (add x, c1), c2) -> (add (mul x, c2), c1*c2) // fold (mul (add x, c1), c2) -> (add (mul x, c2), c1*c2)
if (isConstantIntBuildVectorOrConstantInt(N1) && if (DAG.isConstantIntBuildVectorOrConstantInt(N1) &&
N0.getOpcode() == ISD::ADD && N0.getOpcode() == ISD::ADD &&
isConstantIntBuildVectorOrConstantInt(N0.getOperand(1)) && DAG.isConstantIntBuildVectorOrConstantInt(N0.getOperand(1)) &&
isMulAddWithConstProfitable(N, N0, N1)) isMulAddWithConstProfitable(N, N0, N1))
return DAG.getNode(ISD::ADD, SDLoc(N), VT, return DAG.getNode(ISD::ADD, SDLoc(N), VT,
DAG.getNode(ISD::MUL, SDLoc(N0), VT, DAG.getNode(ISD::MUL, SDLoc(N0), VT,
@ -2698,8 +2688,8 @@ SDValue DAGCombiner::visitIMINMAX(SDNode *N) {
return DAG.FoldConstantArithmetic(N->getOpcode(), SDLoc(N), VT, N0C, N1C); return DAG.FoldConstantArithmetic(N->getOpcode(), SDLoc(N), VT, N0C, N1C);
// canonicalize constant to RHS // canonicalize constant to RHS
if (isConstantIntBuildVectorOrConstantInt(N0) && if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
!isConstantIntBuildVectorOrConstantInt(N1)) !DAG.isConstantIntBuildVectorOrConstantInt(N1))
return DAG.getNode(N->getOpcode(), SDLoc(N), VT, N1, N0); return DAG.getNode(N->getOpcode(), SDLoc(N), VT, N1, N0);
return SDValue(); return SDValue();
@ -3045,8 +3035,8 @@ SDValue DAGCombiner::visitAND(SDNode *N) {
if (N0C && N1C && !N1C->isOpaque()) if (N0C && N1C && !N1C->isOpaque())
return DAG.FoldConstantArithmetic(ISD::AND, SDLoc(N), VT, N0C, N1C); return DAG.FoldConstantArithmetic(ISD::AND, SDLoc(N), VT, N0C, N1C);
// canonicalize constant to RHS // canonicalize constant to RHS
if (isConstantIntBuildVectorOrConstantInt(N0) && if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
!isConstantIntBuildVectorOrConstantInt(N1)) !DAG.isConstantIntBuildVectorOrConstantInt(N1))
return DAG.getNode(ISD::AND, SDLoc(N), VT, N1, N0); return DAG.getNode(ISD::AND, SDLoc(N), VT, N1, N0);
// fold (and x, -1) -> x // fold (and x, -1) -> x
if (isAllOnesConstant(N1)) if (isAllOnesConstant(N1))
@ -3760,8 +3750,8 @@ SDValue DAGCombiner::visitOR(SDNode *N) {
if (N0C && N1C && !N1C->isOpaque()) if (N0C && N1C && !N1C->isOpaque())
return DAG.FoldConstantArithmetic(ISD::OR, SDLoc(N), VT, N0C, N1C); return DAG.FoldConstantArithmetic(ISD::OR, SDLoc(N), VT, N0C, N1C);
// canonicalize constant to RHS // canonicalize constant to RHS
if (isConstantIntBuildVectorOrConstantInt(N0) && if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
!isConstantIntBuildVectorOrConstantInt(N1)) !DAG.isConstantIntBuildVectorOrConstantInt(N1))
return DAG.getNode(ISD::OR, SDLoc(N), VT, N1, N0); return DAG.getNode(ISD::OR, SDLoc(N), VT, N1, N0);
// fold (or x, 0) -> x // fold (or x, 0) -> x
if (isNullConstant(N1)) if (isNullConstant(N1))
@ -3817,9 +3807,9 @@ SDValue DAGCombiner::visitOR(SDNode *N) {
} }
/// Match "(X shl/srl V1) & V2" where V2 may not be present. /// Match "(X shl/srl V1) & V2" where V2 may not be present.
static bool MatchRotateHalf(SDValue Op, SDValue &Shift, SDValue &Mask) { bool DAGCombiner::MatchRotateHalf(SDValue Op, SDValue &Shift, SDValue &Mask) {
if (Op.getOpcode() == ISD::AND) { if (Op.getOpcode() == ISD::AND) {
if (isConstantIntBuildVectorOrConstantInt(Op.getOperand(1))) { if (DAG.isConstantIntBuildVectorOrConstantInt(Op.getOperand(1))) {
Mask = Op.getOperand(1); Mask = Op.getOperand(1);
Op = Op.getOperand(0); Op = Op.getOperand(0);
} else { } else {
@ -4106,8 +4096,8 @@ SDValue DAGCombiner::visitXOR(SDNode *N) {
if (N0C && N1C) if (N0C && N1C)
return DAG.FoldConstantArithmetic(ISD::XOR, SDLoc(N), VT, N0C, N1C); return DAG.FoldConstantArithmetic(ISD::XOR, SDLoc(N), VT, N0C, N1C);
// canonicalize constant to RHS // canonicalize constant to RHS
if (isConstantIntBuildVectorOrConstantInt(N0) && if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
!isConstantIntBuildVectorOrConstantInt(N1)) !DAG.isConstantIntBuildVectorOrConstantInt(N1))
return DAG.getNode(ISD::XOR, SDLoc(N), VT, N1, N0); return DAG.getNode(ISD::XOR, SDLoc(N), VT, N1, N0);
// fold (xor x, 0) -> x // fold (xor x, 0) -> x
if (isNullConstant(N1)) if (isNullConstant(N1))
@ -4916,7 +4906,7 @@ SDValue DAGCombiner::visitBSWAP(SDNode *N) {
EVT VT = N->getValueType(0); EVT VT = N->getValueType(0);
// fold (bswap c1) -> c2 // fold (bswap c1) -> c2
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::BSWAP, SDLoc(N), VT, N0); return DAG.getNode(ISD::BSWAP, SDLoc(N), VT, N0);
// fold (bswap (bswap x)) -> x // fold (bswap (bswap x)) -> x
if (N0.getOpcode() == ISD::BSWAP) if (N0.getOpcode() == ISD::BSWAP)
@ -4929,7 +4919,7 @@ SDValue DAGCombiner::visitCTLZ(SDNode *N) {
EVT VT = N->getValueType(0); EVT VT = N->getValueType(0);
// fold (ctlz c1) -> c2 // fold (ctlz c1) -> c2
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::CTLZ, SDLoc(N), VT, N0); return DAG.getNode(ISD::CTLZ, SDLoc(N), VT, N0);
return SDValue(); return SDValue();
} }
@ -4939,7 +4929,7 @@ SDValue DAGCombiner::visitCTLZ_ZERO_UNDEF(SDNode *N) {
EVT VT = N->getValueType(0); EVT VT = N->getValueType(0);
// fold (ctlz_zero_undef c1) -> c2 // fold (ctlz_zero_undef c1) -> c2
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::CTLZ_ZERO_UNDEF, SDLoc(N), VT, N0); return DAG.getNode(ISD::CTLZ_ZERO_UNDEF, SDLoc(N), VT, N0);
return SDValue(); return SDValue();
} }
@ -4949,7 +4939,7 @@ SDValue DAGCombiner::visitCTTZ(SDNode *N) {
EVT VT = N->getValueType(0); EVT VT = N->getValueType(0);
// fold (cttz c1) -> c2 // fold (cttz c1) -> c2
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::CTTZ, SDLoc(N), VT, N0); return DAG.getNode(ISD::CTTZ, SDLoc(N), VT, N0);
return SDValue(); return SDValue();
} }
@ -4959,7 +4949,7 @@ SDValue DAGCombiner::visitCTTZ_ZERO_UNDEF(SDNode *N) {
EVT VT = N->getValueType(0); EVT VT = N->getValueType(0);
// fold (cttz_zero_undef c1) -> c2 // fold (cttz_zero_undef c1) -> c2
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::CTTZ_ZERO_UNDEF, SDLoc(N), VT, N0); return DAG.getNode(ISD::CTTZ_ZERO_UNDEF, SDLoc(N), VT, N0);
return SDValue(); return SDValue();
} }
@ -4969,7 +4959,7 @@ SDValue DAGCombiner::visitCTPOP(SDNode *N) {
EVT VT = N->getValueType(0); EVT VT = N->getValueType(0);
// fold (ctpop c1) -> c2 // fold (ctpop c1) -> c2
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::CTPOP, SDLoc(N), VT, N0); return DAG.getNode(ISD::CTPOP, SDLoc(N), VT, N0);
return SDValue(); return SDValue();
} }
@ -6902,7 +6892,7 @@ SDValue DAGCombiner::visitSIGN_EXTEND_INREG(SDNode *N) {
return DAG.getUNDEF(VT); return DAG.getUNDEF(VT);
// fold (sext_in_reg c1) -> c1 // fold (sext_in_reg c1) -> c1
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::SIGN_EXTEND_INREG, SDLoc(N), VT, N0, N1); return DAG.getNode(ISD::SIGN_EXTEND_INREG, SDLoc(N), VT, N0, N1);
// If the input is already sign extended, just drop the extension. // If the input is already sign extended, just drop the extension.
@ -7021,7 +7011,7 @@ SDValue DAGCombiner::visitTRUNCATE(SDNode *N) {
if (N0.getValueType() == N->getValueType(0)) if (N0.getValueType() == N->getValueType(0))
return N0; return N0;
// fold (truncate c1) -> c1 // fold (truncate c1) -> c1
if (isConstantIntBuildVectorOrConstantInt(N0)) if (DAG.isConstantIntBuildVectorOrConstantInt(N0))
return DAG.getNode(ISD::TRUNCATE, SDLoc(N), VT, N0); return DAG.getNode(ISD::TRUNCATE, SDLoc(N), VT, N0);
// fold (truncate (truncate x)) -> (truncate x) // fold (truncate (truncate x)) -> (truncate x)
if (N0.getOpcode() == ISD::TRUNCATE) if (N0.getOpcode() == ISD::TRUNCATE)
@ -8868,7 +8858,7 @@ SDValue DAGCombiner::visitSINT_TO_FP(SDNode *N) {
EVT OpVT = N0.getValueType(); EVT OpVT = N0.getValueType();
// fold (sint_to_fp c1) -> c1fp // fold (sint_to_fp c1) -> c1fp
if (isConstantIntBuildVectorOrConstantInt(N0) && if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
// ...but only if the target supports immediate floating-point values // ...but only if the target supports immediate floating-point values
(!LegalOperations || (!LegalOperations ||
TLI.isOperationLegalOrCustom(llvm::ISD::ConstantFP, VT))) TLI.isOperationLegalOrCustom(llvm::ISD::ConstantFP, VT)))
@ -8922,7 +8912,7 @@ SDValue DAGCombiner::visitUINT_TO_FP(SDNode *N) {
EVT OpVT = N0.getValueType(); EVT OpVT = N0.getValueType();
// fold (uint_to_fp c1) -> c1fp // fold (uint_to_fp c1) -> c1fp
if (isConstantIntBuildVectorOrConstantInt(N0) && if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
// ...but only if the target supports immediate floating-point values // ...but only if the target supports immediate floating-point values
(!LegalOperations || (!LegalOperations ||
TLI.isOperationLegalOrCustom(llvm::ISD::ConstantFP, VT))) TLI.isOperationLegalOrCustom(llvm::ISD::ConstantFP, VT)))
@ -10940,9 +10930,23 @@ struct BaseIndexOffset {
} }
/// Parses tree in Ptr for base, index, offset addresses. /// Parses tree in Ptr for base, index, offset addresses.
static BaseIndexOffset match(SDValue Ptr) { static BaseIndexOffset match(SDValue Ptr, SelectionDAG &DAG) {
bool IsIndexSignExt = false; bool IsIndexSignExt = false;
// Split up a folded GlobalAddress+Offset into its component parts.
if (GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Ptr))
if (GA->getOpcode() == ISD::GlobalAddress && GA->getOffset() != 0) {
return BaseIndexOffset(DAG.getGlobalAddress(GA->getGlobal(),
SDLoc(GA),
GA->getValueType(0),
/*Offset=*/0,
/*isTargetGA=*/false,
GA->getTargetFlags()),
SDValue(),
GA->getOffset(),
IsIndexSignExt);
}
// We only can pattern match BASE + INDEX + OFFSET. If Ptr is not an ADD // We only can pattern match BASE + INDEX + OFFSET. If Ptr is not an ADD
// instruction, then it could be just the BASE or everything else we don't // instruction, then it could be just the BASE or everything else we don't
// know how to handle. Just use Ptr as BASE and give up. // know how to handle. Just use Ptr as BASE and give up.
@ -11063,7 +11067,7 @@ bool DAGCombiner::isMulAddWithConstProfitable(SDNode *MulNode,
// multiply (CONST * A) after we also do the same transformation // multiply (CONST * A) after we also do the same transformation
// to the "t2" instruction. // to the "t2" instruction.
if (OtherOp->getOpcode() == ISD::ADD && if (OtherOp->getOpcode() == ISD::ADD &&
isConstantIntBuildVectorOrConstantInt(OtherOp->getOperand(1)) && DAG.isConstantIntBuildVectorOrConstantInt(OtherOp->getOperand(1)) &&
OtherOp->getOperand(0).getNode() == MulVar) OtherOp->getOperand(0).getNode() == MulVar)
return true; return true;
} }
@ -11215,7 +11219,7 @@ void DAGCombiner::getStoreMergeAndAliasCandidates(
SmallVectorImpl<LSBaseSDNode*> &AliasLoadNodes) { SmallVectorImpl<LSBaseSDNode*> &AliasLoadNodes) {
// This holds the base pointer, index, and the offset in bytes from the base // This holds the base pointer, index, and the offset in bytes from the base
// pointer. // pointer.
BaseIndexOffset BasePtr = BaseIndexOffset::match(St->getBasePtr()); BaseIndexOffset BasePtr = BaseIndexOffset::match(St->getBasePtr(), DAG);
// We must have a base and an offset. // We must have a base and an offset.
if (!BasePtr.Base.getNode()) if (!BasePtr.Base.getNode())
@ -11253,7 +11257,7 @@ void DAGCombiner::getStoreMergeAndAliasCandidates(
if (OtherST->getMemoryVT() != MemVT) if (OtherST->getMemoryVT() != MemVT)
continue; continue;
BaseIndexOffset Ptr = BaseIndexOffset::match(OtherST->getBasePtr()); BaseIndexOffset Ptr = BaseIndexOffset::match(OtherST->getBasePtr(), DAG);
if (Ptr.equalBaseIndex(BasePtr)) if (Ptr.equalBaseIndex(BasePtr))
StoreNodes.push_back(MemOpLink(OtherST, Ptr.Offset, Seq++)); StoreNodes.push_back(MemOpLink(OtherST, Ptr.Offset, Seq++));
@ -11269,7 +11273,7 @@ void DAGCombiner::getStoreMergeAndAliasCandidates(
break; break;
// Find the base pointer and offset for this memory node. // Find the base pointer and offset for this memory node.
BaseIndexOffset Ptr = BaseIndexOffset::match(Index->getBasePtr()); BaseIndexOffset Ptr = BaseIndexOffset::match(Index->getBasePtr(), DAG);
// Check that the base pointer is the same as the original one. // Check that the base pointer is the same as the original one.
if (!Ptr.equalBaseIndex(BasePtr)) if (!Ptr.equalBaseIndex(BasePtr))
@ -11557,7 +11561,7 @@ bool DAGCombiner::MergeConsecutiveStores(StoreSDNode* St) {
if (Ld->getMemoryVT() != MemVT) if (Ld->getMemoryVT() != MemVT)
break; break;
BaseIndexOffset LdPtr = BaseIndexOffset::match(Ld->getBasePtr()); BaseIndexOffset LdPtr = BaseIndexOffset::match(Ld->getBasePtr(), DAG);
// If this is not the first ptr that we check. // If this is not the first ptr that we check.
if (LdBasePtr.Base.getNode()) { if (LdBasePtr.Base.getNode()) {
// The base ptr must be the same. // The base ptr must be the same.
@ -14716,7 +14720,7 @@ SDValue DAGCombiner::FindBetterChain(SDNode *N, SDValue OldChain) {
bool DAGCombiner::findBetterNeighborChains(StoreSDNode* St) { bool DAGCombiner::findBetterNeighborChains(StoreSDNode* St) {
// This holds the base pointer, index, and the offset in bytes from the base // This holds the base pointer, index, and the offset in bytes from the base
// pointer. // pointer.
BaseIndexOffset BasePtr = BaseIndexOffset::match(St->getBasePtr()); BaseIndexOffset BasePtr = BaseIndexOffset::match(St->getBasePtr(), DAG);
// We must have a base and an offset. // We must have a base and an offset.
if (!BasePtr.Base.getNode()) if (!BasePtr.Base.getNode())
@ -14742,7 +14746,7 @@ bool DAGCombiner::findBetterNeighborChains(StoreSDNode* St) {
break; break;
// Find the base pointer and offset for this memory node. // Find the base pointer and offset for this memory node.
BaseIndexOffset Ptr = BaseIndexOffset::match(Index->getBasePtr()); BaseIndexOffset Ptr = BaseIndexOffset::match(Index->getBasePtr(), DAG);
// Check that the base pointer is the same as the original one. // Check that the base pointer is the same as the original one.
if (!Ptr.equalBaseIndex(BasePtr)) if (!Ptr.equalBaseIndex(BasePtr))

View File

@ -3263,6 +3263,26 @@ SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, SDLoc DL, EVT VT,
return getConstant(Folded.first, DL, VT); return getConstant(Folded.first, DL, VT);
} }
SDValue SelectionDAG::FoldSymbolOffset(unsigned Opcode, EVT VT,
const GlobalAddressSDNode *GA,
const SDNode *N2) {
if (GA->getOpcode() != ISD::GlobalAddress)
return SDValue();
if (!TLI->isOffsetFoldingLegal(GA))
return SDValue();
const ConstantSDNode *Cst2 = dyn_cast<ConstantSDNode>(N2);
if (!Cst2)
return SDValue();
int64_t Offset = Cst2->getSExtValue();
switch (Opcode) {
case ISD::ADD: break;
case ISD::SUB: Offset = -uint64_t(Offset); break;
default: return SDValue();
}
return getGlobalAddress(GA->getGlobal(), SDLoc(Cst2), VT,
GA->getOffset() + uint64_t(Offset));
}
SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, SDLoc DL, EVT VT, SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, SDLoc DL, EVT VT,
SDNode *Cst1, SDNode *Cst2) { SDNode *Cst1, SDNode *Cst2) {
// If the opcode is a target-specific ISD node, there's nothing we can // If the opcode is a target-specific ISD node, there's nothing we can
@ -3289,6 +3309,12 @@ SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, SDLoc DL, EVT VT,
} }
} }
// fold (add Sym, c) -> Sym+c
if (GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Cst1))
return FoldSymbolOffset(Opcode, VT, GA, Cst2);
if (GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Cst2))
return FoldSymbolOffset(Opcode, VT, GA, Cst1);
// For vectors extract each constant element into Inputs so we can constant // For vectors extract each constant element into Inputs so we can constant
// fold them individually. // fold them individually.
BuildVectorSDNode *BV1 = dyn_cast<BuildVectorSDNode>(Cst1); BuildVectorSDNode *BV1 = dyn_cast<BuildVectorSDNode>(Cst1);
@ -7322,6 +7348,22 @@ bool ShuffleVectorSDNode::isSplatMask(const int *Mask, EVT VT) {
return true; return true;
} }
// \brief Returns the SDNode if it is a constant integer BuildVector
// or constant integer.
SDNode *SelectionDAG::isConstantIntBuildVectorOrConstantInt(SDValue N) {
if (isa<ConstantSDNode>(N))
return N.getNode();
if (ISD::isBuildVectorOfConstantSDNodes(N.getNode()))
return N.getNode();
// Treat a GlobalAddress supporting constant offset folding as a
// constant integer.
if (GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(N))
if (GA->getOpcode() == ISD::GlobalAddress &&
TLI->isOffsetFoldingLegal(GA))
return GA;
return nullptr;
}
#ifndef NDEBUG #ifndef NDEBUG
static void checkForCyclesHelper(const SDNode *N, static void checkForCyclesHelper(const SDNode *N,
SmallPtrSetImpl<const SDNode*> &Visited, SmallPtrSetImpl<const SDNode*> &Visited,

View File

@ -0,0 +1,672 @@
; RUN: llc < %s -asm-verbose=false | FileCheck %s
; Test folding constant offsets and symbols into load and store addresses under
; a variety of circumstances.
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
@g = external global [0 x i32], align 4
; CHECK-LABEL: load_test0:
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 0{{$}}
; CHECK-NEXT: i32.load $push1=, g+40($pop0){{$}}
; CHECK-NEXT: return $pop1{{$}}
define i32 @load_test0() {
%t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
ret i32 %t
}
; CHECK-LABEL: load_test0_noinbounds:
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 0{{$}}
; CHECK-NEXT: i32.load $push1=, g+40($pop0){{$}}
; CHECK-NEXT: return $pop1{{$}}
define i32 @load_test0_noinbounds() {
%t = load i32, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
ret i32 %t
}
; CHECK-LABEL: load_test1:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test1(i32 %n) {
%add = add nsw i32 %n, 10
%arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test2:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test2(i32 %n) {
%add = add nsw i32 10, %n
%arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test3:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test3(i32 %n) {
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
%t = load i32, i32* %add.ptr1, align 4
ret i32 %t
}
; CHECK-LABEL: load_test4:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test4(i32 %n) {
%add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n
%t = load i32, i32* %add.ptr, align 4
ret i32 %t
}
; CHECK-LABEL: load_test5:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test5(i32 %n) {
%add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n
%t = load i32, i32* %add.ptr, align 4
ret i32 %t
}
; CHECK-LABEL: load_test6:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test6(i32 %n) {
%add = add nsw i32 %n, 10
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
%t = load i32, i32* %add.ptr, align 4
ret i32 %t
}
; CHECK-LABEL: load_test7:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test7(i32 %n) {
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
%t = load i32, i32* %add.ptr1, align 4
ret i32 %t
}
; CHECK-LABEL: load_test8:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test8(i32 %n) {
%add = add nsw i32 10, %n
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
%t = load i32, i32* %add.ptr, align 4
ret i32 %t
}
; CHECK-LABEL: load_test9:
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 0{{$}}
; CHECK-NEXT: i32.load $push1=, g-40($pop0){{$}}
; CHECK-NEXT: return $pop1{{$}}
define i32 @load_test9() {
%t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4
ret i32 %t
}
; CHECK-LABEL: load_test10:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.const $push2=, g-40{{$}}
; CHECK-NEXT: i32.add $push3=, $pop1, $pop2{{$}}
; CHECK-NEXT: i32.load $push4=, 0($pop3){{$}}
; CHECK-NEXT: return $pop4{{$}}
define i32 @load_test10(i32 %n) {
%add = add nsw i32 %n, -10
%arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test11:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.load $push0=, 40($0){{$}}
; CHECK-NEXT: return $pop0{{$}}
define i32 @load_test11(i32* %p) {
%arrayidx = getelementptr inbounds i32, i32* %p, i32 10
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test11_noinbounds:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 40{{$}}
; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, 0($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test11_noinbounds(i32* %p) {
%arrayidx = getelementptr i32, i32* %p, i32 10
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test12:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}}
; CHECK-NEXT: return $pop5{{$}}
define i32 @load_test12(i32* %p, i32 %n) {
%add = add nsw i32 %n, 10
%arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test13:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}}
; CHECK-NEXT: return $pop5{{$}}
define i32 @load_test13(i32* %p, i32 %n) {
%add = add nsw i32 10, %n
%arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test14:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}}
; CHECK-NEXT: return $pop3{{$}}
define i32 @load_test14(i32* %p, i32 %n) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
%t = load i32, i32* %add.ptr1, align 4
ret i32 %t
}
; CHECK-LABEL: load_test15:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}}
; CHECK-NEXT: return $pop5{{$}}
define i32 @load_test15(i32* %p, i32 %n) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 10
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n
%t = load i32, i32* %add.ptr1, align 4
ret i32 %t
}
; CHECK-LABEL: load_test16:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}}
; CHECK-NEXT: return $pop5{{$}}
define i32 @load_test16(i32* %p, i32 %n) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 10
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n
%t = load i32, i32* %add.ptr1, align 4
ret i32 %t
}
; CHECK-LABEL: load_test17:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}}
; CHECK-NEXT: return $pop5{{$}}
define i32 @load_test17(i32* %p, i32 %n) {
%add = add nsw i32 %n, 10
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %add
%t = load i32, i32* %add.ptr, align 4
ret i32 %t
}
; CHECK-LABEL: load_test18:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}}
; CHECK-NEXT: return $pop3{{$}}
define i32 @load_test18(i32* %p, i32 %n) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
%t = load i32, i32* %add.ptr1, align 4
ret i32 %t
}
; CHECK-LABEL: load_test19:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}}
; CHECK-NEXT: return $pop5{{$}}
define i32 @load_test19(i32* %p, i32 %n) {
%add = add nsw i32 10, %n
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %add
%t = load i32, i32* %add.ptr, align 4
ret i32 %t
}
; CHECK-LABEL: load_test20:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, -40{{$}}
; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.load $push2=, 0($pop1){{$}}
; CHECK-NEXT: return $pop2{{$}}
define i32 @load_test20(i32* %p) {
%arrayidx = getelementptr inbounds i32, i32* %p, i32 -10
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: load_test21:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: result i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, -40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}}
; CHECK-NEXT: return $pop5{{$}}
define i32 @load_test21(i32* %p, i32 %n) {
%add = add nsw i32 %n, -10
%arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
%t = load i32, i32* %arrayidx, align 4
ret i32 %t
}
; CHECK-LABEL: store_test0:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: i32.const $push0=, 0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop0), $0{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test0(i32 %i) {
store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
ret void
}
; CHECK-LABEL: store_test0_noinbounds:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: i32.const $push0=, 0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop0), $0{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test0_noinbounds(i32 %i) {
store i32 %i, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
ret void
}
; CHECK-LABEL: store_test1:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test1(i32 %n, i32 %i) {
%add = add nsw i32 %n, 10
%arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test2:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test2(i32 %n, i32 %i) {
%add = add nsw i32 10, %n
%arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test3:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test3(i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
store i32 %i, i32* %add.ptr1, align 4
ret void
}
; CHECK-LABEL: store_test4:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test4(i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n
store i32 %i, i32* %add.ptr, align 4
ret void
}
; CHECK-LABEL: store_test5:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test5(i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n
store i32 %i, i32* %add.ptr, align 4
ret void
}
; CHECK-LABEL: store_test6:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test6(i32 %n, i32 %i) {
%add = add nsw i32 %n, 10
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
store i32 %i, i32* %add.ptr, align 4
ret void
}
; CHECK-LABEL: store_test7:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test7(i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
store i32 %i, i32* %add.ptr1, align 4
ret void
}
; CHECK-LABEL: store_test8:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, g+40($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test8(i32 %n, i32 %i) {
%add = add nsw i32 10, %n
%add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
store i32 %i, i32* %add.ptr, align 4
ret void
}
; CHECK-LABEL: store_test9:
; CHECK-NEXT: param i32{{$}}
; CHECK-NEXT: i32.const $push0=, 0{{$}}
; CHECK-NEXT: i32.store $discard=, g-40($pop0), $0{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test9(i32 %i) {
store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4
ret void
}
; CHECK-LABEL: store_test10:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.const $push2=, g-40{{$}}
; CHECK-NEXT: i32.add $push3=, $pop1, $pop2{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop3), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test10(i32 %n, i32 %i) {
%add = add nsw i32 %n, -10
%arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test11:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.store $discard=, 40($0), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test11(i32* %p, i32 %i) {
%arrayidx = getelementptr inbounds i32, i32* %p, i32 10
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test11_noinbounds:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 40{{$}}
; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test11_noinbounds(i32* %p, i32 %i) {
%arrayidx = getelementptr i32, i32* %p, i32 10
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test12:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop4), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test12(i32* %p, i32 %n, i32 %i) {
%add = add nsw i32 %n, 10
%arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test13:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop4), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test13(i32* %p, i32 %n, i32 %i) {
%add = add nsw i32 10, %n
%arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test14:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.store $discard=, 40($pop2), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test14(i32* %p, i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
store i32 %i, i32* %add.ptr1, align 4
ret void
}
; CHECK-LABEL: store_test15:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop4), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test15(i32* %p, i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 10
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n
store i32 %i, i32* %add.ptr1, align 4
ret void
}
; CHECK-LABEL: store_test16:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop4), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test16(i32* %p, i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 10
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n
store i32 %i, i32* %add.ptr1, align 4
ret void
}
; CHECK-LABEL: store_test17:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop4), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test17(i32* %p, i32 %n, i32 %i) {
%add = add nsw i32 %n, 10
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %add
store i32 %i, i32* %add.ptr, align 4
ret void
}
; CHECK-LABEL: store_test18:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}}
; CHECK-NEXT: i32.store $discard=, 40($pop2), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test18(i32* %p, i32 %n, i32 %i) {
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %n
%add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
store i32 %i, i32* %add.ptr1, align 4
ret void
}
; CHECK-LABEL: store_test19:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, 40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop4), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test19(i32* %p, i32 %n, i32 %i) {
%add = add nsw i32 10, %n
%add.ptr = getelementptr inbounds i32, i32* %p, i32 %add
store i32 %i, i32* %add.ptr, align 4
ret void
}
; CHECK-LABEL: store_test20:
; CHECK-NEXT: param i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, -40{{$}}
; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop1), $1{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test20(i32* %p, i32 %i) {
%arrayidx = getelementptr inbounds i32, i32* %p, i32 -10
store i32 %i, i32* %arrayidx, align 4
ret void
}
; CHECK-LABEL: store_test21:
; CHECK-NEXT: param i32, i32, i32{{$}}
; CHECK-NEXT: i32.const $push0=, 2{{$}}
; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}}
; CHECK-NEXT: i32.add $push2=, $pop1, $0{{$}}
; CHECK-NEXT: i32.const $push3=, -40{{$}}
; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}}
; CHECK-NEXT: i32.store $discard=, 0($pop4), $2{{$}}
; CHECK-NEXT: return{{$}}
define void @store_test21(i32* %p, i32 %n, i32 %i) {
%add = add nsw i32 %n, -10
%arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
store i32 %i, i32* %arrayidx, align 4
ret void
}

View File

@ -34,12 +34,12 @@ sw.bb.2: ; preds = %entry
sw.epilog: ; preds = %sw.bb.2, %sw.bb.1, %entry sw.epilog: ; preds = %sw.bb.2, %sw.bb.1, %entry
ret void ret void
; CHECK-LABEL: test1: ; CHECK-LABEL: test1:
; CHECK: leaq (%rdi,%rdi,2), [[REG1:%[a-z]+]] ; CHECK: shlq $2, [[REG1:%[a-z]+]]
; CHECK: movl arr1(,[[REG1]],4), {{.*}} ; CHECK: movl arr1([[REG1]],[[REG1]],2), {{.*}}
; CHECK: leaq arr1+4(,[[REG1]],4), [[REG2:%[a-z]+]] ; CHECK: leaq arr1+4([[REG1]],[[REG1]],2), [[REG2:%[a-z]+]]
; CHECK: subl arr1+4(,[[REG1]],4), {{.*}} ; CHECK: subl arr1+4([[REG1]],[[REG1]],2), {{.*}}
; CHECK: leaq arr1+8(,[[REG1]],4), [[REG3:%[a-z]+]] ; CHECK: leaq arr1+8([[REG1]],[[REG1]],2), [[REG3:%[a-z]+]]
; CHECK: addl arr1+8(,[[REG1]],4), {{.*}} ; CHECK: addl arr1+8([[REG1]],[[REG1]],2), {{.*}}
; CHECK: movl ${{[1-4]+}}, ([[REG2]]) ; CHECK: movl ${{[1-4]+}}, ([[REG2]])
; CHECK: movl ${{[1-4]+}}, ([[REG3]]) ; CHECK: movl ${{[1-4]+}}, ([[REG3]])
; CHECK: movl ${{[1-4]+}}, ([[REG2]]) ; CHECK: movl ${{[1-4]+}}, ([[REG2]])
@ -74,11 +74,11 @@ sw.bb.2: ; preds = %entry
sw.epilog: ; preds = %sw.bb.2, %sw.bb.1, %entry sw.epilog: ; preds = %sw.bb.2, %sw.bb.1, %entry
ret void ret void
; CHECK-LABEL: test2: ; CHECK-LABEL: test2:
; CHECK: leaq (%rdi,%rdi,2), [[REG1:%[a-z]+]] ; CHECK: shlq $2, [[REG1:%[a-z]+]]
; CHECK: leaq arr1+4(,[[REG1]],4), [[REG2:%[a-z]+]] ; CHECK: leaq arr1+4([[REG1]],[[REG1]],2), [[REG2:%[a-z]+]]
; CHECK: movl -4([[REG2]]), {{.*}} ; CHECK: movl -4([[REG2]]), {{.*}}
; CHECK: subl ([[REG2]]), {{.*}} ; CHECK: subl ([[REG2]]), {{.*}}
; CHECK: leaq arr1+8(,[[REG1]],4), [[REG3:%[a-z]+]] ; CHECK: leaq arr1+8([[REG1]],[[REG1]],2), [[REG3:%[a-z]+]]
; CHECK: addl ([[REG3]]), {{.*}} ; CHECK: addl ([[REG3]]), {{.*}}
; CHECK: movl ${{[1-4]+}}, ([[REG2]]) ; CHECK: movl ${{[1-4]+}}, ([[REG2]])
; CHECK: movl ${{[1-4]+}}, ([[REG3]]) ; CHECK: movl ${{[1-4]+}}, ([[REG3]])

View File

@ -87,7 +87,7 @@ define i32* @f_tle() {
; CHECK: shl [[R0:r[0-9]]], r11, 3 ; CHECK: shl [[R0:r[0-9]]], r11, 3
; CHECK: ldaw [[R1:r[0-9]]], dp[tle] ; CHECK: ldaw [[R1:r[0-9]]], dp[tle]
; r0 = &tl + id*8 ; r0 = &tl + id*8
; CHECK: add r0, [[R1]], [[R0]] ; CHECK: add r0, [[R0]], [[R1]]
ret i32* getelementptr inbounds ([2 x i32], [2 x i32]* @tle, i32 0, i32 0) ret i32* getelementptr inbounds ([2 x i32], [2 x i32]* @tle, i32 0, i32 0)
} }
@ -96,7 +96,7 @@ define i32 @f_tlExpr () {
; CHECK: get r11, id ; CHECK: get r11, id
; CHECK: shl [[R0:r[0-9]]], r11, 3 ; CHECK: shl [[R0:r[0-9]]], r11, 3
; CHECK: ldaw [[R1:r[0-9]]], dp[tle] ; CHECK: ldaw [[R1:r[0-9]]], dp[tle]
; CHECK: add [[R2:r[0-9]]], [[R1]], [[R0]] ; CHECK: add [[R2:r[0-9]]], [[R0]], [[R1]]
; CHECK: add r0, [[R2]], [[R2]] ; CHECK: add r0, [[R2]], [[R2]]
ret i32 add( ret i32 add(
i32 ptrtoint( i32* getelementptr inbounds ([2 x i32], [2 x i32]* @tle, i32 0, i32 0) to i32), i32 ptrtoint( i32* getelementptr inbounds ([2 x i32], [2 x i32]* @tle, i32 0, i32 0) to i32),