diff --git a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
--- a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
@@ -47,6 +47,12 @@
                            MachineBasicBlock::iterator MBBI,
                            MachineBasicBlock::iterator &NextMBBI,
                            unsigned FlagsHi, unsigned SecondOpcode);
+  bool expandLoad(MachineBasicBlock &MBB,
+                 MachineBasicBlock::iterator MBBI,
+                 MachineBasicBlock::iterator &NextMBBI);
+  bool expandStore(MachineBasicBlock &MBB,
+                  MachineBasicBlock::iterator MBBI,
+                  MachineBasicBlock::iterator &NextMBBI);
   bool expandLoadLocalAddress(MachineBasicBlock &MBB,
                               MachineBasicBlock::iterator MBBI,
                               MachineBasicBlock::iterator &NextMBBI);
@@ -96,6 +102,19 @@
   // expanded instructions for each pseudo is correct in the Size field of the
   // tablegen definition for the pseudo.
   switch (MBBI->getOpcode()) {
+  case RISCV::PseudoLB:
+  case RISCV::PseudoLBU:
+  case RISCV::PseudoLH:
+  case RISCV::PseudoLHU:
+  case RISCV::PseudoLW:
+  case RISCV::PseudoLWU:
+  case RISCV::PseudoLD:
+    return expandLoad(MBB, MBBI, NextMBBI);
+  case RISCV::PseudoSB:
+  case RISCV::PseudoSH:
+  case RISCV::PseudoSW:
+  case RISCV::PseudoSD:
+    return expandStore(MBB, MBBI, NextMBBI);
   case RISCV::PseudoLLA:
     return expandLoadLocalAddress(MBB, MBBI, NextMBBI);
   case RISCV::PseudoLA:
@@ -196,6 +215,42 @@
   return true;
 }
 
+bool RISCVExpandPseudo::expandLoad(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
+    MachineBasicBlock::iterator &NextMBBI) {
+  MachineInstr &MI = *MBBI;
+  unsigned SecondOpcode;
+  switch (MI.getOpcode()) {
+    case RISCV::PseudoLB:  SecondOpcode = RISCV::LB; break;
+    case RISCV::PseudoLBU: SecondOpcode = RISCV::LBU; break;
+    case RISCV::PseudoLH:  SecondOpcode = RISCV::LH; break;
+    case RISCV::PseudoLHU: SecondOpcode = RISCV::LHU; break;
+    case RISCV::PseudoLW:  SecondOpcode = RISCV::LW; break;
+    case RISCV::PseudoLWU: SecondOpcode = RISCV::LWU; break;
+    case RISCV::PseudoLD:  SecondOpcode = RISCV::LD; break;
+    default:
+      llvm_unreachable("Unexpected opcode");
+  }
+  return expandAuipcInstPair(MBB, MBBI, NextMBBI, RISCVII::MO_PCREL_HI,
+                             SecondOpcode);
+}
+
+bool RISCVExpandPseudo::expandStore(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
+    MachineBasicBlock::iterator &NextMBBI) {
+  MachineInstr &MI = *MBBI;
+  unsigned SecondOpcode;
+  switch (MI.getOpcode()) {
+    case RISCV::PseudoSW:
+      SecondOpcode = RISCV::SW;
+      break;
+    default:
+      llvm_unreachable("Unexpected opcode");
+  }
+  MI.eraseFromParent(); // TODO
+  return true;
+}
+
 bool RISCVExpandPseudo::expandLoadLocalAddress(
     MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
     MachineBasicBlock::iterator &NextMBBI) {
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
@@ -2019,11 +2019,23 @@
 
   SDValue Base = N->getOperand(BaseOpIdx);
 
-  // If the base is an ADDI, we can merge it in to the load/store.
-  if (!Base.isMachineOpcode() || Base.getMachineOpcode() != RISCV::ADDI)
+  if (!Base.isMachineOpcode())
     return false;
 
-  SDValue ImmOperand = Base.getOperand(1);
+  // If the base is an ADDI or PseudoLLA, we can merge it in to the load/store.
+  bool BaseIsAddi;
+  switch (Base.getMachineOpcode()) {
+    default:
+      return false;
+    case RISCV::ADDI:
+      BaseIsAddi = true;
+      break;
+    case RISCV::PseudoLLA:
+      BaseIsAddi = false;
+      break;
+  }
+
+  SDValue ImmOperand = Base.getOperand(BaseIsAddi ? 1 : 0);
   uint64_t Offset2 = N->getConstantOperandVal(OffsetOpIdx);
 
   if (auto *Const = dyn_cast<ConstantSDNode>(ImmOperand)) {
@@ -2061,20 +2073,60 @@
     return false;
   }
 
-  LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase:    ");
+  if (BaseIsAddi)
+    LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase:    ");
+  else
+    LLVM_DEBUG(dbgs() << "Folding load address into mem-op:\nBase:    ");
   LLVM_DEBUG(Base->dump(CurDAG));
   LLVM_DEBUG(dbgs() << "\nN: ");
   LLVM_DEBUG(N->dump(CurDAG));
   LLVM_DEBUG(dbgs() << "\n");
 
-  // Modify the offset operand of the load/store.
-  if (BaseOpIdx == 0) // Load
-    CurDAG->UpdateNodeOperands(N, Base.getOperand(0), ImmOperand,
-                               N->getOperand(2));
-  else // Store
-    CurDAG->UpdateNodeOperands(N, N->getOperand(0), Base.getOperand(0),
-                               ImmOperand, N->getOperand(3));
-
+  if (BaseIsAddi) {
+    // Modify the offset operand of the load/store.
+    if (BaseOpIdx == 0) // Load
+      CurDAG->UpdateNodeOperands(N, Base.getOperand(0), ImmOperand,
+                                N->getOperand(2));
+    else // Store
+      CurDAG->UpdateNodeOperands(N, N->getOperand(0), Base.getOperand(0),
+                                ImmOperand, N->getOperand(3));
+  } else {
+    unsigned Opc;
+    switch (N->getMachineOpcode()) {
+    default:
+      return false;
+    case RISCV::LB:  Opc = RISCV::PseudoLB; break;
+    case RISCV::LBU: Opc = RISCV::PseudoLBU; break;
+    case RISCV::LH:  Opc = RISCV::PseudoLH; break;
+    case RISCV::LHU: Opc = RISCV::PseudoLHU; break;
+    case RISCV::LW:  Opc = RISCV::PseudoLW; break;
+    case RISCV::LWU: Opc = RISCV::PseudoLWU; break;
+    case RISCV::LD:  Opc = RISCV::PseudoLD; break;
+    // case RISCV::FLH:
+    // case RISCV::FLW:
+    // case RISCV::FLD:
+    case RISCV::SB: Opc = RISCV::PseudoSB; break;
+    case RISCV::SH: Opc = RISCV::PseudoSH; break;
+    case RISCV::SW: Opc = RISCV::PseudoSW; break;
+    case RISCV::SD: Opc = RISCV::PseudoSD; break;
+    // case RISCV::FSH:
+    // case RISCV::FSW:
+    // case RISCV::FSD:
+    }
+    if (BaseOpIdx == 0) { // Load
+      SDNode *PseudoLoad =
+          CurDAG->getMachineNode(Opc, SDLoc(ImmOperand),
+                                N->getValueType(0), MVT::Other, ImmOperand, N->getOperand(2));
+      ReplaceUses(N, PseudoLoad);
+    } else {
+      MVT XLenVT = Subtarget->getXLenVT();
+      auto TmpOp = CurDAG->getRegister(RISCV::X0, XLenVT);
+      SDNode *PseudoStore =
+          CurDAG->getMachineNode(Opc, SDLoc(ImmOperand),
+                                N->getValueType(0), MVT::Other, TmpOp, ImmOperand, N->getOperand(3));
+      ReplaceUses(N, PseudoStore);
+    }
+  }
   return true;
 }
 
diff --git a/llvm/test/CodeGen/RISCV/codemodel-lowering.ll b/llvm/test/CodeGen/RISCV/codemodel-lowering.ll
--- a/llvm/test/CodeGen/RISCV/codemodel-lowering.ll
+++ b/llvm/test/CodeGen/RISCV/codemodel-lowering.ll
@@ -18,8 +18,7 @@
 ; RV32I-MEDIUM:       # %bb.0:
 ; RV32I-MEDIUM-NEXT:  .LBB0_1: # Label of block must be emitted
 ; RV32I-MEDIUM-NEXT:    auipc a0, %pcrel_hi(G)
-; RV32I-MEDIUM-NEXT:    addi a0, a0, %pcrel_lo(.LBB0_1)
-; RV32I-MEDIUM-NEXT:    lw a0, 0(a0)
+; RV32I-MEDIUM-NEXT:    lw a0, %pcrel_lo(.LBB0_1)(a0)
 ; RV32I-MEDIUM-NEXT:    ret
   %1 = load volatile i32, i32* @G
   ret i32 %1
@@ -39,11 +38,6 @@
 ;
 ; RV32I-MEDIUM-LABEL: lower_blockaddress:
 ; RV32I-MEDIUM:       # %bb.0:
-; RV32I-MEDIUM-NEXT:  .LBB1_1: # Label of block must be emitted
-; RV32I-MEDIUM-NEXT:    auipc a0, %pcrel_hi(addr)
-; RV32I-MEDIUM-NEXT:    addi a0, a0, %pcrel_lo(.LBB1_1)
-; RV32I-MEDIUM-NEXT:    li a1, 1
-; RV32I-MEDIUM-NEXT:    sw a1, 0(a0)
 ; RV32I-MEDIUM-NEXT:    ret
   store volatile i8* blockaddress(@lower_blockaddress, %block), i8** @addr
   ret void