diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h
--- a/llvm/include/llvm/MC/MCAsmBackend.h
+++ b/llvm/include/llvm/MC/MCAsmBackend.h
@@ -129,10 +129,6 @@
                           uint64_t Value, bool IsResolved,
                           const MCSubtargetInfo *STI) const = 0;

-  /// Check whether the given target requires emitting differences of two
-  /// symbols as a set of relocations.
-  virtual bool requiresDiffExpressionRelocations() const { return false; }
-
   /// @}

   /// \name Target Relaxation Interfaces
diff --git a/llvm/include/llvm/MC/MCFixup.h b/llvm/include/llvm/MC/MCFixup.h
--- a/llvm/include/llvm/MC/MCFixup.h
+++ b/llvm/include/llvm/MC/MCFixup.h
@@ -41,16 +41,6 @@
   FK_SecRel_2,    ///< A two-byte section relative fixup.
   FK_SecRel_4,    ///< A four-byte section relative fixup.
   FK_SecRel_8,    ///< A eight-byte section relative fixup.
-  FK_Data_Add_1,  ///< A one-byte add fixup.
-  FK_Data_Add_2,  ///< A two-byte add fixup.
-  FK_Data_Add_4,  ///< A four-byte add fixup.
-  FK_Data_Add_8,  ///< A eight-byte add fixup.
-  FK_Data_Add_6b, ///< A six-bits add fixup.
-  FK_Data_Sub_1,  ///< A one-byte sub fixup.
-  FK_Data_Sub_2,  ///< A two-byte sub fixup.
-  FK_Data_Sub_4,  ///< A four-byte sub fixup.
-  FK_Data_Sub_8,  ///< A eight-byte sub fixup.
-  FK_Data_Sub_6b, ///< A six-bits sub fixup.

   FirstTargetFixupKind = 128,

@@ -105,28 +95,6 @@
     return FI;
   }

-  /// Return a fixup corresponding to the add half of a add/sub fixup pair for
-  /// the given Fixup.
-  static MCFixup createAddFor(const MCFixup &Fixup) {
-    MCFixup FI;
-    FI.Value = Fixup.getValue();
-    FI.Offset = Fixup.getOffset();
-    FI.Kind = getAddKindForKind(Fixup.getKind());
-    FI.Loc = Fixup.getLoc();
-    return FI;
-  }
-
-  /// Return a fixup corresponding to the sub half of a add/sub fixup pair for
-  /// the given Fixup.
-  static MCFixup createSubFor(const MCFixup &Fixup) {
-    MCFixup FI;
-    FI.Value = Fixup.getValue();
-    FI.Offset = Fixup.getOffset();
-    FI.Kind = getSubKindForKind(Fixup.getKind());
-    FI.Loc = Fixup.getLoc();
-    return FI;
-  }
-
   MCFixupKind getKind() const { return Kind; }

   unsigned getTargetKind() const { return Kind; }
@@ -172,32 +140,6 @@
     }
   }

-  /// Return the generic fixup kind for an addition with a given size. It
-  /// is an error to pass an unsupported size.
-  static MCFixupKind getAddKindForKind(MCFixupKind Kind) {
-    switch (Kind) {
-    default: llvm_unreachable("Unknown type to convert!");
-    case FK_Data_1: return FK_Data_Add_1;
-    case FK_Data_2: return FK_Data_Add_2;
-    case FK_Data_4: return FK_Data_Add_4;
-    case FK_Data_8: return FK_Data_Add_8;
-    case FK_Data_6b: return FK_Data_Add_6b;
-    }
-  }
-
-  /// Return the generic fixup kind for an subtraction with a given size. It
-  /// is an error to pass an unsupported size.
-  static MCFixupKind getSubKindForKind(MCFixupKind Kind) {
-    switch (Kind) {
-    default: llvm_unreachable("Unknown type to convert!");
-    case FK_Data_1: return FK_Data_Sub_1;
-    case FK_Data_2: return FK_Data_Sub_2;
-    case FK_Data_4: return FK_Data_Sub_4;
-    case FK_Data_8: return FK_Data_Sub_8;
-    case FK_Data_6b: return FK_Data_Sub_6b;
-    }
-  }
-
   SMLoc getLoc() const { return Loc; }
 };

diff --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp
--- a/llvm/lib/MC/MCAsmBackend.cpp
+++ b/llvm/lib/MC/MCAsmBackend.cpp
@@ -95,16 +95,7 @@
       {"FK_SecRel_2", 0, 16, 0},
       {"FK_SecRel_4", 0, 32, 0},
       {"FK_SecRel_8", 0, 64, 0},
-      {"FK_Data_Add_1", 0, 8, 0},
-      {"FK_Data_Add_2", 0, 16, 0},
-      {"FK_Data_Add_4", 0, 32, 0},
-      {"FK_Data_Add_8", 0, 64, 0},
-      {"FK_Data_Add_6b", 0, 6, 0},
-      {"FK_Data_Sub_1", 0, 8, 0},
-      {"FK_Data_Sub_2", 0, 16, 0},
-      {"FK_Data_Sub_4", 0, 32, 0},
-      {"FK_Data_Sub_8", 0, 64, 0},
-      {"FK_Data_Sub_6b", 0, 6, 0}};
+  };

   assert((size_t)Kind <= array_lengthof(Builtins) && "Unknown fixup kind");
   return Builtins[Kind];
diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp
--- a/llvm/lib/MC/MCAssembler.cpp
+++ b/llvm/lib/MC/MCAssembler.cpp
@@ -792,26 +792,7 @@
     // The fixup was unresolved, we need a relocation. Inform the object
     // writer of the relocation, and give it an opportunity to adjust the
     // fixup value if need be.
-    if (Target.getSymA() && Target.getSymB() &&
-        getBackend().requiresDiffExpressionRelocations()) {
-      // The fixup represents the difference between two symbols, which the
-      // backend has indicated must be resolved at link time. Split up the fixup
-      // into two relocations, one for the add, and one for the sub, and emit
-      // both of these. The constant will be associated with the add half of the
-      // expression.
-      MCFixup FixupAdd = MCFixup::createAddFor(Fixup);
-      MCValue TargetAdd =
-          MCValue::get(Target.getSymA(), nullptr, Target.getConstant());
-      getWriter().recordRelocation(*this, Layout, &F, FixupAdd, TargetAdd,
-                                   FixedValue);
-      MCFixup FixupSub = MCFixup::createSubFor(Fixup);
-      MCValue TargetSub = MCValue::get(Target.getSymB());
-      getWriter().recordRelocation(*this, Layout, &F, FixupSub, TargetSub,
-                                   FixedValue);
-    } else {
-      getWriter().recordRelocation(*this, Layout, &F, Fixup, Target,
-                                   FixedValue);
-    }
+    getWriter().recordRelocation(*this, Layout, &F, Fixup, Target, FixedValue);
   }
   return std::make_tuple(Target, FixedValue, IsResolved);
 }
@@ -1113,28 +1094,8 @@
   raw_svector_ostream OSE(Data);
   DF.getFixups().clear();

-  if (!getBackend().requiresDiffExpressionRelocations()) {
-    MCDwarfLineAddr::Encode(Context, getDWARFLinetableParams(), LineDelta,
-                            AddrDelta, OSE);
-  } else {
-    uint32_t Offset;
-    uint32_t Size;
-    bool SetDelta;
-    std::tie(Offset, Size, SetDelta) =
-        MCDwarfLineAddr::fixedEncode(Context, LineDelta, AddrDelta, OSE);
-    // Add Fixups for address delta or new address.
-    const MCExpr *FixupExpr;
-    if (SetDelta) {
-      FixupExpr = &DF.getAddrDelta();
-    } else {
-      const MCBinaryExpr *ABE = cast<MCBinaryExpr>(&DF.getAddrDelta());
-      FixupExpr = ABE->getLHS();
-    }
-    DF.getFixups().push_back(
-        MCFixup::create(Offset, FixupExpr,
-                        MCFixup::getKindForSize(Size, false /*isPCRel*/)));
-  }
-
+  MCDwarfLineAddr::Encode(Context, getDWARFLinetableParams(), LineDelta,
+                          AddrDelta, OSE);
   return OldSize != Data.size();
 }

@@ -1151,20 +1112,7 @@
   raw_svector_ostream OSE(Data);
   DF.getFixups().clear();

-  if (getBackend().requiresDiffExpressionRelocations()) {
-    uint32_t Offset;
-    uint32_t Size;
-    MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OSE, &Offset,
-                                          &Size);
-    if (Size) {
-      DF.getFixups().push_back(MCFixup::create(
-          Offset, &DF.getAddrDelta(),
-          MCFixup::getKindForSizeInBits(Size /*In bits.*/, false /*isPCRel*/)));
-    }
-  } else {
-    MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OSE);
-  }
-
+  MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OSE);
   return OldSize != Data.size();
 }

@@ -1194,10 +1142,6 @@
   raw_svector_ostream OSE(Data);
   PF.getFixups().clear();

-  // Relocations should not be needed in general except on RISC-V which we are
-  // not targeted for now.
-  assert(!getBackend().requiresDiffExpressionRelocations() &&
-         "cannot relax relocations");
   // AddrDelta is a signed integer
   encodeSLEB128(AddrDelta, OSE, OldSize);
   return OldSize != Data.size();
diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp
--- a/llvm/lib/MC/MCExpr.cpp
+++ b/llvm/lib/MC/MCExpr.cpp
@@ -671,24 +671,6 @@
   }
 }

-static bool canFold(const MCAssembler *Asm, const MCSymbolRefExpr *A,
-                    const MCSymbolRefExpr *B, bool InSet) {
-  if (InSet)
-    return true;
-
-  if (!Asm->getBackend().requiresDiffExpressionRelocations())
-    return true;
-
-  const MCSymbol &CheckSym = A ? A->getSymbol() : B->getSymbol();
-  if (!CheckSym.isInSection())
-    return true;
-
-  if (!CheckSym.getSection().hasInstructions())
-    return true;
-
-  return false;
-}
-
 /// Evaluate the result of an add between (conceptually) two MCValues.
 ///
 /// This routine conceptually attempts to construct an MCValue:
@@ -725,11 +707,8 @@
   assert((!Layout || Asm) &&
          "Must have an assembler object if layout is given!");

-  // If we have a layout, we can fold resolved differences. Do not do this if
-  // the backend requires this to be emitted as individual relocations, unless
-  // the InSet flag is set to get the current difference anyway (used for
-  // example to calculate symbol sizes).
-  if (Asm && canFold(Asm, LHS_A, LHS_B, InSet)) {
+  // If we have a layout, we can fold resolved differences.
+  if (Asm) {
     // First, fold out any differences which are fully resolved. By
     // reassociating terms in
     //   Result = (LHS_A - LHS_B + LHS_Cst) + (RHS_A - RHS_B + RHS_Cst).
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -128,11 +128,8 @@
 // As a compile-time optimization, avoid allocating and evaluating an MCExpr
 // tree for (Hi - Lo) when Hi and Lo are offsets into the same fragment.
 static Optional<uint64_t>
-absoluteSymbolDiff(MCAssembler &Asm, const MCSymbol *Hi, const MCSymbol *Lo) {
+absoluteSymbolDiff(const MCSymbol *Hi, const MCSymbol *Lo) {
   assert(Hi && Lo);
-  if (Asm.getBackendPtr()->requiresDiffExpressionRelocations())
-    return None;
-
   if (!Hi->getFragment() || Hi->getFragment() != Lo->getFragment() ||
       Hi->isVariable() || Lo->isVariable())
     return None;
@@ -143,19 +140,17 @@
 void MCObjectStreamer::emitAbsoluteSymbolDiff(const MCSymbol *Hi,
                                               const MCSymbol *Lo,
                                               unsigned Size) {
-  if (Optional<uint64_t> Diff = absoluteSymbolDiff(getAssembler(), Hi, Lo)) {
-    emitIntValue(*Diff, Size);
-    return;
-  }
+  if (!getAssembler().getContext().getTargetTriple().isRISCV())
+    if (Optional<uint64_t> Diff = absoluteSymbolDiff(Hi, Lo))
+      return emitIntValue(*Diff, Size);
   MCStreamer::emitAbsoluteSymbolDiff(Hi, Lo, Size);
 }

 void MCObjectStreamer::emitAbsoluteSymbolDiffAsULEB128(const MCSymbol *Hi,
                                                        const MCSymbol *Lo) {
-  if (Optional<uint64_t> Diff = absoluteSymbolDiff(getAssembler(), Hi, Lo)) {
-    emitULEB128IntValue(*Diff);
-    return;
-  }
+  if (!getAssembler().getContext().getTargetTriple().isRISCV())
+    if (Optional<uint64_t> Diff = absoluteSymbolDiff(Hi, Lo))
+      return emitULEB128IntValue(*Diff);
   MCStreamer::emitAbsoluteSymbolDiffAsULEB128(Hi, Lo);
 }

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
@@ -42,20 +42,6 @@

   void setForceRelocs() { ForceRelocs = true; }

-  // Returns true if relocations will be forced for shouldForceRelocation by
-  // default. This will be true if relaxation is enabled or had previously
-  // been enabled.
-  bool willForceRelocations() const {
-    return ForceRelocs || STI.getFeatureBits()[RISCV::FeatureRelax];
-  }
-
-  // Generate diff expression relocations if the relax feature is enabled or had
-  // previously been enabled, otherwise it is safe for the assembler to
-  // calculate these internally.
-  bool requiresDiffExpressionRelocations() const override {
-    return willForceRelocations();
-  }
-
   // Return Size with extra Nop Bytes for alignment directive in code section.
   bool shouldInsertExtraNopBytesForCodeAlign(const MCAlignFragment &AF,
                                              unsigned &Size) override;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -70,7 +70,20 @@
       {"fixup_riscv_call", 0, 64, MCFixupKindInfo::FKF_IsPCRel},
       {"fixup_riscv_call_plt", 0, 64, MCFixupKindInfo::FKF_IsPCRel},
       {"fixup_riscv_relax", 0, 0, 0},
-      {"fixup_riscv_align", 0, 0, 0}};
+      {"fixup_riscv_align", 0, 0, 0},
+
+      {"fixup_riscv_add_8", 0, 0, 0},
+      {"fixup_riscv_sub_8", 0, 0, 0},
+
+      {"fixup_riscv_add_16", 0, 0, 0},
+      {"fixup_riscv_sub_16", 0, 0, 0},
+
+      {"fixup_riscv_add_32", 0, 0, 0},
+      {"fixup_riscv_sub_32", 0, 0, 0},
+
+      {"fixup_riscv_add_64", 0, 0, 0},
+      {"fixup_riscv_sub_64", 0, 0, 0},
+  };
   static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds,
                 "Not all fixup kinds added to Infos array");

@@ -227,6 +240,14 @@
   case RISCV::fixup_riscv_tls_got_hi20:
   case RISCV::fixup_riscv_tls_gd_hi20:
     llvm_unreachable("Relocation should be unconditionally forced\n");
+  case RISCV::fixup_riscv_add_8:
+  case RISCV::fixup_riscv_sub_8:
+  case RISCV::fixup_riscv_add_16:
+  case RISCV::fixup_riscv_sub_16:
+  case RISCV::fixup_riscv_add_32:
+  case RISCV::fixup_riscv_sub_32:
+  case RISCV::fixup_riscv_add_64:
+  case RISCV::fixup_riscv_sub_64:
   case FK_Data_1:
   case FK_Data_2:
   case FK_Data_4:
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
@@ -15,6 +15,8 @@
 #include "llvm/MC/MCObjectWriter.h"
 #include "llvm/Support/ErrorHandling.h"

+#include <utility>
+
 using namespace llvm;

 namespace {
@@ -86,6 +88,22 @@
       return ELF::R_RISCV_CALL;
     case RISCV::fixup_riscv_call_plt:
       return ELF::R_RISCV_CALL_PLT;
+    case RISCV::fixup_riscv_add_8:
+      return ELF::R_RISCV_ADD8;
+    case RISCV::fixup_riscv_sub_8:
+      return ELF::R_RISCV_SUB8;
+    case RISCV::fixup_riscv_add_16:
+      return ELF::R_RISCV_ADD16;
+    case RISCV::fixup_riscv_sub_16:
+      return ELF::R_RISCV_SUB16;
+    case RISCV::fixup_riscv_add_32:
+      return ELF::R_RISCV_ADD32;
+    case RISCV::fixup_riscv_sub_32:
+      return ELF::R_RISCV_SUB32;
+    case RISCV::fixup_riscv_add_64:
+      return ELF::R_RISCV_ADD64;
+    case RISCV::fixup_riscv_sub_64:
+      return ELF::R_RISCV_SUB64;
     }
   }

@@ -106,26 +124,6 @@
     return ELF::R_RISCV_32;
   case FK_Data_8:
     return ELF::R_RISCV_64;
-  case FK_Data_Add_1:
-    return ELF::R_RISCV_ADD8;
-  case FK_Data_Add_2:
-    return ELF::R_RISCV_ADD16;
-  case FK_Data_Add_4:
-    return ELF::R_RISCV_ADD32;
-  case FK_Data_Add_8:
-    return ELF::R_RISCV_ADD64;
-  case FK_Data_Add_6b:
-    return ELF::R_RISCV_SET6;
-  case FK_Data_Sub_1:
-    return ELF::R_RISCV_SUB8;
-  case FK_Data_Sub_2:
-    return ELF::R_RISCV_SUB16;
-  case FK_Data_Sub_4:
-    return ELF::R_RISCV_SUB32;
-  case FK_Data_Sub_8:
-    return ELF::R_RISCV_SUB64;
-  case FK_Data_Sub_6b:
-    return ELF::R_RISCV_SUB6;
   case RISCV::fixup_riscv_hi20:
     return ELF::R_RISCV_HI20;
   case RISCV::fixup_riscv_lo12_i:
@@ -144,6 +142,22 @@
     return ELF::R_RISCV_RELAX;
   case RISCV::fixup_riscv_align:
     return ELF::R_RISCV_ALIGN;
+  case RISCV::fixup_riscv_add_8:
+    return ELF::R_RISCV_ADD8;
+  case RISCV::fixup_riscv_sub_8:
+    return ELF::R_RISCV_SUB8;
+  case RISCV::fixup_riscv_add_16:
+    return ELF::R_RISCV_ADD16;
+  case RISCV::fixup_riscv_sub_16:
+    return ELF::R_RISCV_SUB16;
+  case RISCV::fixup_riscv_add_32:
+    return ELF::R_RISCV_ADD32;
+  case RISCV::fixup_riscv_sub_32:
+    return ELF::R_RISCV_SUB32;
+  case RISCV::fixup_riscv_add_64:
+    return ELF::R_RISCV_ADD64;
+  case RISCV::fixup_riscv_sub_64:
+    return ELF::R_RISCV_SUB64;
   }
 }

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
@@ -104,5 +104,11 @@
   void emitDirectiveOptionRelax() override;
   void emitDirectiveOptionNoRelax() override;
 };
+
+MCELFStreamer *createRISCVELFStreamer(MCContext &C,
+                                      std::unique_ptr<MCAsmBackend> MAB,
+                                      std::unique_ptr<MCObjectWriter> MOW,
+                                      std::unique_ptr<MCCodeEmitter> MCE,
+                                      bool RelaxAll);
 }
 #endif
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -15,7 +15,10 @@
 #include "RISCVBaseInfo.h"
 #include "RISCVMCTargetDesc.h"
 #include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCCodeEmitter.h"
 #include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCObjectWriter.h"
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/Support/LEB128.h"
@@ -167,3 +170,94 @@
   }
   return Result;
 }
+
+namespace {
+class RISCVELFStreamer : public MCELFStreamer {
+  static std::pair<unsigned, unsigned> getRelocPairForSize(unsigned Size) {
+    switch (Size) {
+    default: llvm_unreachable("unsupported fixup size");
+    case 1:
+      return std::make_pair(RISCV::fixup_riscv_add_8, RISCV::fixup_riscv_sub_8);
+    case 2:
+      return std::make_pair(RISCV::fixup_riscv_add_16,
+                            RISCV::fixup_riscv_sub_16);
+    case 4:
+      return std::make_pair(RISCV::fixup_riscv_add_32,
+                            RISCV::fixup_riscv_sub_32);
+    case 8:
+      return std::make_pair(RISCV::fixup_riscv_add_64,
+                            RISCV::fixup_riscv_sub_64);
+    }
+  }
+
+  static bool UseLinkerRelaxation(const MCExpr *Value,
+                                  const MCSymbolRefExpr *&LHS,
+                                  const MCSymbolRefExpr *&RHS) {
+    const auto *MBE = dyn_cast<MCBinaryExpr>(Value);
+    if (MBE == nullptr)
+      return false;
+
+    LHS = dyn_cast<MCSymbolRefExpr>(MBE->getLHS());
+    RHS = dyn_cast<MCSymbolRefExpr>(MBE->getRHS());
+
+    // Unwrap A ± B ± C
+    if (RHS == nullptr && isa<MCConstantExpr>(MBE->getRHS())) {
+      if (const auto *E = dyn_cast<MCBinaryExpr>(MBE->getLHS())) {
+        LHS = dyn_cast<MCSymbolRefExpr>(E->getLHS());
+        RHS = dyn_cast<MCSymbolRefExpr>(E->getRHS());
+      }
+    }
+
+    if (LHS == nullptr || RHS == nullptr)
+      return false;
+
+    const auto &S = LHS->getSymbol();
+    const auto &T = RHS->getSymbol();
+    return (S.isInSection() ? S.getSection().hasInstructions()
+                            : !S.getName().empty()) ||
+           (T.isInSection() ? T.getSection().hasInstructions()
+                            : !T.getName().empty());
+  }
+
+public:
+  RISCVELFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> MAB,
+                   std::unique_ptr<MCObjectWriter> MOW,
+                   std::unique_ptr<MCCodeEmitter> MCE)
+      : MCELFStreamer(C, std::move(MAB), std::move(MOW), std::move(MCE)) {}
+
+  void emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) override {
+    const MCSymbolRefExpr *A, *B;
+    if (!UseLinkerRelaxation(Value, A, B))
+      return MCELFStreamer::emitValueImpl(Value, Size, Loc);
+
+    MCStreamer::emitValueImpl(Value, Size, Loc);
+
+    MCDataFragment *DF = getOrCreateDataFragment();
+    flushPendingLabels(DF, DF->getContents().size());
+    MCDwarfLineEntry::make(this, getCurrentSectionOnly());
+
+    unsigned add, sub;
+    std::tie(add, sub) = getRelocPairForSize(Size);
+
+    DF->getFixups().push_back(MCFixup::create(
+        DF->getContents().size(), A, static_cast<MCFixupKind>(add), Loc));
+    DF->getFixups().push_back(MCFixup::create(
+        DF->getContents().size(), B, static_cast<MCFixupKind>(sub), Loc));
+
+    DF->getContents().resize(DF->getContents().size() + Size, 0);
+  }
+};
+}
+
+namespace llvm {
+MCELFStreamer *createRISCVELFStreamer(MCContext &C,
+                                      std::unique_ptr<MCAsmBackend> MAB,
+                                      std::unique_ptr<MCObjectWriter> MOW,
+                                      std::unique_ptr<MCCodeEmitter> MCE,
+                                      bool RelaxAll) {
+  RISCVELFStreamer *S =
+      new RISCVELFStreamer(C, std::move(MAB), std::move(MOW), std::move(MCE));
+  S->getAssembler().setRelaxAll(RelaxAll);
+  return S;
+}
+}
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
@@ -82,6 +82,18 @@
   // relaxation.
   fixup_riscv_align,

+  fixup_riscv_add_8,
+  fixup_riscv_sub_8,
+
+  fixup_riscv_add_16,
+  fixup_riscv_sub_16,
+
+  fixup_riscv_add_32,
+  fixup_riscv_sub_32,
+
+  fixup_riscv_add_64,
+  fixup_riscv_sub_64,
+
   // fixup_riscv_invalid - used as a sentinel and a marker, must be last fixup
   fixup_riscv_invalid,
   NumTargetFixupKinds = fixup_riscv_invalid - FirstTargetFixupKind
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
@@ -92,14 +92,18 @@
 bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res,
                                             const MCAsmLayout *Layout,
                                             const MCFixup *Fixup) const {
-  if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup))
-    return false;
+  bool IsSymbolicDifference = false;
+  if (const auto *MBE = dyn_cast<MCBinaryExpr>(getSubExpr())) {
+    if (isa<MCBinaryExpr>(MBE->getLHS()) && isa<MCConstantExpr>(MBE->getRHS()))
+      MBE = cast<MCBinaryExpr>(MBE->getLHS());
+    IsSymbolicDifference = isa<MCSymbolRefExpr>(MBE->getLHS()) &&
+                           isa<MCSymbolRefExpr>(MBE->getRHS());
+  }

   // Some custom fixup types are not valid with symbol difference expressions
-  if (Res.getSymA() && Res.getSymB()) {
+  if (IsSymbolicDifference) {
     switch (getKind()) {
-    default:
-      return true;
+    default: break;
     case VK_RISCV_LO:
     case VK_RISCV_HI:
     case VK_RISCV_PCREL_LO:
@@ -113,8 +117,7 @@
       return false;
     }
   }
-
-  return true;
+  return getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup);
 }

 void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const {
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
@@ -18,10 +18,13 @@
 #include "RISCVTargetStreamer.h"
 #include "TargetInfo/RISCVTargetInfo.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/MC/MCAsmBackend.h"
 #include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
 #include "llvm/MC/MCInstrAnalysis.h"
 #include "llvm/MC/MCInstrInfo.h"
 #include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -138,6 +141,17 @@
   return new RISCVMCInstrAnalysis(Info);
 }

+namespace {
+MCStreamer *createRISCVELFStreamer(const Triple &T, MCContext &Context,
+                                   std::unique_ptr<MCAsmBackend> &&MAB,
+                                   std::unique_ptr<MCObjectWriter> &&MOW,
+                                   std::unique_ptr<MCCodeEmitter> &&MCE,
+                                   bool RelaxAll) {
+  return createRISCVELFStreamer(Context, std::move(MAB), std::move(MOW),
+                                std::move(MCE), RelaxAll);
+}
+}
+
 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTargetMC() {
   for (Target *T : {&getTheRISCV32Target(), &getTheRISCV64Target()}) {
     TargetRegistry::RegisterMCAsmInfo(*T, createRISCVMCAsmInfo);
@@ -147,6 +161,7 @@
     TargetRegistry::RegisterMCCodeEmitter(*T, createRISCVMCCodeEmitter);
     TargetRegistry::RegisterMCInstPrinter(*T, createRISCVMCInstPrinter);
     TargetRegistry::RegisterMCSubtargetInfo(*T, createRISCVMCSubtargetInfo);
+    TargetRegistry::RegisterELFStreamer(*T, createRISCVELFStreamer);
     TargetRegistry::RegisterObjectTargetStreamer(
         *T, createRISCVObjectTargetStreamer);
     TargetRegistry::RegisterMCInstrAnalysis(*T, createRISCVInstrAnalysis);
diff --git a/llvm/test/CodeGen/RISCV/fixups-diff.ll b/llvm/test/CodeGen/RISCV/fixups-diff.ll
--- a/llvm/test/CodeGen/RISCV/fixups-diff.ll
+++ b/llvm/test/CodeGen/RISCV/fixups-diff.ll
@@ -1,10 +1,10 @@
 ; RUN: llc -filetype=obj -mtriple=riscv32 -mattr=+relax %s -o - \
-; RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELAX %s
+; RUN:     | llvm-readobj -r - | FileCheck %s
 ; RUN: llc -filetype=obj -mtriple=riscv32 -mattr=-relax %s -o - \
-; RUN:     | llvm-readobj -r - | FileCheck -check-prefix=NORELAX %s
+; RUN:     | llvm-readobj -r - | FileCheck %s

 ; Check that a difference between two symbols in the same fragment
-; causes relocations to be emitted if and only if relaxation is enabled.
+; causes relocations to be emitted.
 ;
 ; This specific test is checking that the size of the function in
 ; the debug information is represented by a relocation. This isn't
@@ -22,11 +22,18 @@
   ret i32 0
 }

-; RELAX: 0x22 R_RISCV_ADD32 - 0x0
-; RELAX: 0x22 R_RISCV_SUB32 - 0x0
-; RELAX: 0x2B R_RISCV_ADD32 - 0x0
-; RELAX: 0x2B R_RISCV_SUB32 - 0x0
-; NORELAX-NOT: R_RISCV_ADD32
+; CHECK: Section {{.*}} .rela.debug_info {
+; CHECK: 0x22 R_RISCV_ADD32 - 0x0
+; CHECK: 0x22 R_RISCV_SUB32 - 0x0
+; CHECK: 0x2B R_RISCV_ADD32 - 0x0
+; CHECK: 0x2B R_RISCV_SUB32 - 0x0
+; CHECK: }
+
+; CHECK: Section {{.*}} .rela.eh_frame {
+; CHECK: 0x1C R_RISCV_32_PCREL - 0x0
+; CHECK: 0x20 R_RISCV_ADD32 - 0x0
+; CHECK: 0x20 R_RISCV_SUB32 - 0x0
+; CHECK: }

 !llvm.dbg.cu = !{!0}
 !llvm.module.flags = !{!3, !4, !5}
diff --git a/llvm/test/CodeGen/RISCV/fixups-relax-diff.ll b/llvm/test/CodeGen/RISCV/fixups-relax-diff.ll
--- a/llvm/test/CodeGen/RISCV/fixups-relax-diff.ll
+++ b/llvm/test/CodeGen/RISCV/fixups-relax-diff.ll
@@ -1,12 +1,11 @@
 ; RUN: llc -filetype=obj -mtriple=riscv32 -mattr=+relax %s -o - \
 ; RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELAX %s
 ; RUN: llc -filetype=obj -mtriple=riscv32 -mattr=-relax %s -o - \
-; RUN:     | llvm-readobj -r - | FileCheck -check-prefix=NORELAX %s
+; RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELAX %s

-; This test checks that a diff inserted via inline assembly only causes
-; relocations when relaxation is enabled. This isn't an assembly test
-; as the assembler takes a different path through LLVM, which is
-; already covered by the fixups-expr.s test.
+; This test checks that a diff inserted via inline assembly always causes
+; relocations. This isn't an assembly test as the assembler takes a different
+; path through LLVM, which is already covered by the fixups-expr.s test.

 define i32 @main() nounwind {
 entry:
@@ -14,7 +13,6 @@
   store i32 0, i32* %retval, align 4
   ; RELAX: R_RISCV_ADD64 b
   ; RELAX: R_RISCV_SUB64 a
-  ; NORELAX-NOT: R_RISCV_ADD
   call void asm sideeffect "a:\0Ab:\0A.dword b-a", ""()
   ret i32 0
 }
diff --git a/llvm/test/MC/RISCV/fde-reloc.s b/llvm/test/MC/RISCV/fde-reloc.s
--- a/llvm/test/MC/RISCV/fde-reloc.s
+++ b/llvm/test/MC/RISCV/fde-reloc.s
@@ -1,19 +1,18 @@
 # RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+relax < %s \
-# RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELAX-RELOC %s
+# RUN:     | llvm-readobj -r - | FileCheck %s
 # RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=-relax < %s \
-# RUN:     | llvm-readobj -r - | FileCheck -check-prefix=NORELAX-RELOC %s
+# RUN:     | llvm-readobj -r - | FileCheck %s
+
+# Ensure that the eh_frame records the symbolic difference with the paired
+# relocations always.

 func:
        .cfi_startproc
   ret
        .cfi_endproc

-# RELAX-RELOC:   Section (4) .rela.eh_frame {
-# RELAX-RELOC-NEXT:   0x1C R_RISCV_32_PCREL - 0x0
-# RELAX-RELOC-NEXT:   0x20 R_RISCV_ADD32 - 0x0
-# RELAX-RELOC-NEXT:   0x20 R_RISCV_SUB32 - 0x0
-# RELAX-RELOC-NEXT: }
-
-# NORELAX-RELOC:        Section (4) .rela.eh_frame {
-# NORELAX-RELOC-NEXT:    0x1C R_RISCV_32_PCREL - 0x0
-# NORELAX-RELOC-NEXT:  }
+# CHECK:   Section (4) .rela.eh_frame {
+# CHECK-NEXT:   0x1C R_RISCV_32_PCREL - 0x0
+# CHECK-NEXT:   0x20 R_RISCV_ADD32 - 0x0
+# CHECK-NEXT:   0x20 R_RISCV_SUB32 - 0x0
+# CHECK-NEXT: }
diff --git a/llvm/test/MC/RISCV/fixups-expr.s b/llvm/test/MC/RISCV/fixups-expr.s
--- a/llvm/test/MC/RISCV/fixups-expr.s
+++ b/llvm/test/MC/RISCV/fixups-expr.s
@@ -1,15 +1,14 @@
 # RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s \
-# RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELAX %s
+# RUN:     | llvm-readobj -r - | FileCheck -check-prefix RELAX %s
 # RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s \
-# RUN:     | llvm-readobj -r - | FileCheck -check-prefix=NORELAX %s
+# RUN:     | llvm-readobj -r - | FileCheck -check-prefix RELAX %s

 # RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s \
-# RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELAX %s
+# RUN:     | llvm-readobj -r - | FileCheck -check-prefix RELAX %s
 # RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax %s \
-# RUN:     | llvm-readobj -r - | FileCheck -check-prefix=NORELAX %s
+# RUN:     | llvm-readobj -r - | FileCheck -check-prefix RELAX %s

-# Check that subtraction expressions are emitted as two relocations
-# only when relaxation is enabled
+# Check that subtraction expressions are emitted as two relocations always.

 .globl G1
 .globl G2
@@ -44,4 +43,3 @@
 # RELAX: 0x1C R_RISCV_SUB8 .L1 0x0
 # RELAX: 0x1D R_RISCV_ADD8 G2 0x0
 # RELAX: 0x1D R_RISCV_SUB8 G1 0x0
-# NORELAX-NOT: R_RISCV
diff --git a/llvm/test/MC/RISCV/hilo-constaddr-expr.s b/llvm/test/MC/RISCV/hilo-constaddr-expr.s
--- a/llvm/test/MC/RISCV/hilo-constaddr-expr.s
+++ b/llvm/test/MC/RISCV/hilo-constaddr-expr.s
@@ -1,9 +1,5 @@
-# RUN: not llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s 2>&1 \
-# RUN:     | FileCheck %s -check-prefix=CHECK-RELAX
-# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s 2>&1 \
-# RUN:     | llvm-objdump -d - | FileCheck %s --check-prefix=CHECK-INSTR
-# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s 2>&1 \
-# RUN:     | llvm-objdump -r - | FileCheck %s --check-prefix=CHECK-REL
+# RUN: not llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s 2>&1 | FileCheck %s
+# RUN: not llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s 2>&1 | FileCheck %s

 # Check the assembler rejects hi and lo expressions with constant expressions
 # involving labels when diff expressions are emitted as relocation pairs.
@@ -13,19 +9,13 @@
   # Emit zeros so that difference between tmp1 and tmp3 is 0x30124 bytes.
   .fill 0x30124-8
 tmp2:
-# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
   lui t0, %hi(tmp3-tmp1)
-# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
   lw ra, %lo(tmp3-tmp1)(t0)
-# CHECK-INSTR: lui t0, 48
-# CHECK-INSTR: lw ra, 292(t0)

 tmp3:
-# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
   lui t1, %hi(tmp2-tmp3)
-# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression
   lw sp, %lo(tmp2-tmp3)(t1)
-# CHECK-INSTR: lui t1, 0
-# CHECK-INSTR: lw sp, -8(t1)
-
-# CHECK-REL-NOT: R_RISCV
\ No newline at end of file
diff --git a/llvm/test/MC/RISCV/scoped-relaxation.s b/llvm/test/MC/RISCV/scoped-relaxation.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/RISCV/scoped-relaxation.s
@@ -0,0 +1,30 @@
+# RUN: llvm-mc -mattr -relax -triple riscv64-unknown-none-elf -filetype obj %s -o - | llvm-readobj -d -r - | FileCheck %s
+
+.global function
+
+# Unrelaxed reference, this would normally fail, but the subsequent scoped
+# relaxation forces relaxation on the file.
+.dword function - .
+
+# CHECK: 0x0 R_RISCV_ADD64 function 0x0
+# CHECK: 0x0 R_RISCV_SUB64 - 0x0
+
+# Relaxed reference, this will resolve to a pair of `RISCV_ADD64` and
+# `RISCV_SUB64` relocation.
+.option push
+.option relax
+.dword function - .
+.option pop
+
+# CHECK: 0x8 R_RISCV_ADD64 function 0x0
+# CHECK: 0x8 R_RISCV_SUB64 - 0x0
+
+# Unrelaxed reference, this will resolve to a pair of `RISCV_ADD64` and
+# `RISCV_SUB64` relocation due to relaxation being sticky to the file.
+.option push
+.option norelax
+.dword function - .
+.option pop
+
+# CHECK: 0x10 R_RISCV_ADD64 function 0x0
+# CHECK: 0x10 R_RISCV_SUB64 - 0x0