[RISCV] Lower extern_weak symbols using the GOT for the medany model
Such symbols may be undefined at link time and thus resolve to 0, which
may be further than 2GiB away from PC, causing the immediate to be out
of range for PC-relative addressing. Using the GOT avoids this, and is
the approach taken by AArch64.
Reviewed By: asb, MaskRay, arichardson
Differential Revision: https://reviews.llvm.org/D107280
GitOrigin-RevId: a178ba9fbd0a27057dc2fa4cb53c76caa013caac
diff --git a/lib/Target/RISCV/RISCVISelLowering.cpp b/lib/Target/RISCV/RISCVISelLowering.cpp
index 6662d37..5e02f37 100644
--- a/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -5261,7 +5261,7 @@
template <class NodeTy>
SDValue RISCVTargetLowering::getAddr(NodeTy *N, SelectionDAG &DAG,
- bool IsLocal) const {
+ bool IsLocal, bool IsExternWeak) const {
SDLoc DL(N);
EVT Ty = getPointerTy(DAG.getDataLayout());
@@ -5304,10 +5304,28 @@
return DAG.getNode(RISCVISD::ADD_LO, DL, Ty, MNHi, AddrLo);
}
case CodeModel::Medium: {
+ SDValue Addr = getTargetNode(N, DL, Ty, DAG, 0);
+ if (IsExternWeak) {
+ // An extern weak symbol may be undefined, i.e. have value 0, which may
+ // not be within 2GiB of PC, so use GOT-indirect addressing to access the
+ // symbol. This generates the pattern (PseudoLGA sym), which expands to
+ // (ld (addi (auipc %got_pcrel_hi(sym)) %pcrel_lo(auipc))).
+ MachineFunction &MF = DAG.getMachineFunction();
+ MachineMemOperand *MemOp = MF.getMachineMemOperand(
+ MachinePointerInfo::getGOT(MF),
+ MachineMemOperand::MOLoad | MachineMemOperand::MODereferenceable |
+ MachineMemOperand::MOInvariant,
+ LLT(Ty.getSimpleVT()), Align(Ty.getFixedSizeInBits() / 8));
+ SDValue Load =
+ DAG.getMemIntrinsicNode(RISCVISD::LGA, DL,
+ DAG.getVTList(Ty, MVT::Other),
+ {DAG.getEntryNode(), Addr}, Ty, MemOp);
+ return Load;
+ }
+
// Generate a sequence for accessing addresses within any 2GiB range within
// the address space. This generates the pattern (PseudoLLA sym), which
// expands to (addi (auipc %pcrel_hi(sym)) %pcrel_lo(auipc)).
- SDValue Addr = getTargetNode(N, DL, Ty, DAG, 0);
return DAG.getNode(RISCVISD::LLA, DL, Ty, Addr);
}
}
@@ -5317,7 +5335,8 @@
SelectionDAG &DAG) const {
GlobalAddressSDNode *N = cast<GlobalAddressSDNode>(Op);
assert(N->getOffset() == 0 && "unexpected offset in global node");
- return getAddr(N, DAG, N->getGlobal()->isDSOLocal());
+ const GlobalValue *GV = N->getGlobal();
+ return getAddr(N, DAG, GV->isDSOLocal(), GV->hasExternalWeakLinkage());
}
SDValue RISCVTargetLowering::lowerBlockAddress(SDValue Op,
diff --git a/lib/Target/RISCV/RISCVISelLowering.h b/lib/Target/RISCV/RISCVISelLowering.h
index 6c66982..fb67ed5 100644
--- a/lib/Target/RISCV/RISCVISelLowering.h
+++ b/lib/Target/RISCV/RISCVISelLowering.h
@@ -766,7 +766,8 @@
RISCVCCAssignFn Fn) const;
template <class NodeTy>
- SDValue getAddr(NodeTy *N, SelectionDAG &DAG, bool IsLocal = true) const;
+ SDValue getAddr(NodeTy *N, SelectionDAG &DAG, bool IsLocal = true,
+ bool IsExternWeak = false) const;
SDValue getStaticTLSAddr(GlobalAddressSDNode *N, SelectionDAG &DAG,
bool UseGOT) const;
SDValue getDynamicTLSAddr(GlobalAddressSDNode *N, SelectionDAG &DAG) const;
diff --git a/test/CodeGen/RISCV/codemodel-lowering.ll b/test/CodeGen/RISCV/codemodel-lowering.ll
index 38752b8..1a88a25 100644
--- a/test/CodeGen/RISCV/codemodel-lowering.ll
+++ b/test/CodeGen/RISCV/codemodel-lowering.ll
@@ -152,8 +152,9 @@
; RV32I-MEDIUM-LABEL: lower_extern_weak:
; RV32I-MEDIUM: # %bb.0:
; RV32I-MEDIUM-NEXT: .Lpcrel_hi3:
-; RV32I-MEDIUM-NEXT: auipc a0, %pcrel_hi(W)
+; RV32I-MEDIUM-NEXT: auipc a0, %got_pcrel_hi(W)
; RV32I-MEDIUM-NEXT: lw a0, %pcrel_lo(.Lpcrel_hi3)(a0)
+; RV32I-MEDIUM-NEXT: lw a0, 0(a0)
; RV32I-MEDIUM-NEXT: ret
%1 = load volatile i32, ptr @W
ret i32 %1