diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp --- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp @@ -1903,10 +1903,38 @@ SDLoc DL(Addr); MVT VT = Addr.getSimpleValueType(); + if (Addr.getOpcode() == RISCVISD::ADD_LO) { + Base = Addr.getOperand(0); + Offset = Addr.getOperand(1); + return true; + } + if (CurDAG->isBaseWithConstantOffset(Addr)) { int64_t CVal = cast(Addr.getOperand(1))->getSExtValue(); if (isInt<12>(CVal)) { Base = Addr.getOperand(0); + if (Base.getOpcode() == RISCVISD::ADD_LO) { + SDValue LoOperand = Base.getOperand(1); + if (auto *GA = dyn_cast(LoOperand)) { + // If the Lo in (ADD_LO hi, lo) is a global variable's address + // (its low part, really), then we can rely on the alignment of that + // variable to provide a margin of safety before low part can overflow + // the 12 bits of the load/store offset. Check if CVal falls within + // that margin; if so (low part + CVal) can't overflow. + const DataLayout &DL = CurDAG->getDataLayout(); + Align Alignment = commonAlignment( + GA->getGlobal()->getPointerAlignment(DL), GA->getOffset()); + if (CVal == 0 || Alignment > CVal) { + int64_t CombinedOffset = CVal + GA->getOffset(); + Base = Base.getOperand(0); + Offset = CurDAG->getTargetGlobalAddress( + GA->getGlobal(), SDLoc(LoOperand), LoOperand.getValueType(), + CombinedOffset, GA->getTargetFlags()); + return true; + } + } + } + if (auto *FIN = dyn_cast(Base)) Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), VT); Offset = CurDAG->getTargetConstant(CVal, DL, VT);