diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index a3e2edf14531..aab7448eb0af 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -3951,7 +3951,7 @@ SDValue AArch64TargetLowering::LowerGlobalAddress(SDValue Op,
   }
   EVT PtrVT = getPointerTy(DAG.getDataLayout());
   SDLoc DL(GN);
-  if (GV->hasDLLImportStorageClass())
+  if (OpFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB))
     Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result,
                          MachinePointerInfo::getGOT(DAG.getMachineFunction()));
   return Result;
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index 72d80ba61b1d..1152d3799f13 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -1607,7 +1607,7 @@ bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
 
   if ((OpFlags & AArch64II::MO_GOT) != 0) {
     BuildMI(MBB, MI, DL, get(AArch64::LOADgot), Reg)
-        .addGlobalAddress(GV, 0, AArch64II::MO_GOT);
+        .addGlobalAddress(GV, 0, OpFlags);
     BuildMI(MBB, MI, DL, get(AArch64::LDRXui), Reg)
         .addReg(Reg, RegState::Kill)
         .addImm(0)
@@ -4842,6 +4842,7 @@ AArch64InstrInfo::getSerializableBitmaskMachineOperandTargetFlags() const {
   using namespace AArch64II;
 
   static const std::pair<unsigned, const char *> TargetFlags[] = {
+      {MO_COFFSTUB, "aarch64-coffstub"},
       {MO_GOT, "aarch64-got"},   {MO_NC, "aarch64-nc"},
       {MO_TLS, "aarch64-tls"},   {MO_DLLIMPORT, "aarch64-dllimport"}};
   return makeArrayRef(TargetFlags);
diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
index 6c0263585933..6e1824f632ee 100644
--- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
@@ -18,6 +18,7 @@
 #include "llvm/CodeGen/AsmPrinter.h"
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineModuleInfoImpls.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCExpr.h"
@@ -44,16 +45,31 @@ AArch64MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
   assert(TheTriple.isOSWindows() &&
          "Windows is the only supported COFF target");
 
-  bool IsIndirect = (TargetFlags & AArch64II::MO_DLLIMPORT);
+  bool IsIndirect = (TargetFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB));
   if (!IsIndirect)
     return Printer.getSymbol(GV);
 
   SmallString<128> Name;
-  Name = "__imp_";
+  if (TargetFlags & AArch64II::MO_DLLIMPORT)
+    Name = "__imp_";
+  else if (TargetFlags & AArch64II::MO_COFFSTUB)
+    Name = ".refptr.";
   Printer.TM.getNameWithPrefix(Name, GV,
                                Printer.getObjFileLowering().getMangler());
 
-  return Ctx.getOrCreateSymbol(Name);
+  MCSymbol *MCSym = Ctx.getOrCreateSymbol(Name);
+
+  if (TargetFlags & AArch64II::MO_COFFSTUB) {
+    MachineModuleInfoCOFF &MMICOFF =
+        Printer.MMI->getObjFileInfo<MachineModuleInfoCOFF>();
+    MachineModuleInfoImpl::StubValueTy &StubSym =
+        MMICOFF.getGVStubEntry(MCSym);
+
+    if (!StubSym.getPointer())
+      StubSym = MachineModuleInfoImpl::StubValueTy(Printer.getSymbol(GV), true);
+  }
+
+  return MCSym;
 }
 
 MCSymbol *
diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
index be655e3482c6..78eb3470e641 100644
--- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
@@ -196,8 +196,13 @@ AArch64Subtarget::ClassifyGlobalReference(const GlobalValue *GV,
   if (TM.getCodeModel() == CodeModel::Large && isTargetMachO())
     return AArch64II::MO_GOT;
 
-  unsigned Flags = GV->hasDLLImportStorageClass() ? AArch64II::MO_DLLIMPORT
-                                                  : AArch64II::MO_NO_FLAG;
+  unsigned Flags = AArch64II::MO_NO_FLAG;
+  if (GV->hasDLLImportStorageClass())
+    Flags = AArch64II::MO_DLLIMPORT;
+  else if (getTargetTriple().isWindowsGNUEnvironment() &&
+           !GV->isDSOLocal() && GV->isDeclarationForLinker() &&
+           isa<GlobalVariable>(GV))
+    Flags = AArch64II::MO_COFFSTUB | AArch64II::MO_GOT;
 
   if (!TM.shouldAssumeDSOLocal(*GV->getParent(), GV))
     return AArch64II::MO_GOT | Flags;
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
index 2874c4ab42ea..461a7dcf0904 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
@@ -507,7 +507,7 @@ namespace AArch64II {
 
     MO_NO_FLAG,
 
-    MO_FRAGMENT = 0xf,
+    MO_FRAGMENT = 0x7,
 
     /// MO_PAGE - A symbol operand with this flag represents the pc-relative
     /// offset of the 4K page containing the symbol.  This is used with the
@@ -540,6 +540,11 @@ namespace AArch64II {
     /// by-12-bits instruction.
     MO_HI12 = 7,
 
+    /// MO_COFFSTUB - On a symbol operand "FOO", this indicates that the
+    /// reference is actually to the ".refptrp.FOO" symbol.  This is used for
+    /// stub symbols on windows.
+    MO_COFFSTUB = 0x8,
+
     /// MO_GOT - This flag indicates that a symbol operand represents the
     /// address of the GOT entry for the symbol, rather than the address of
     /// the symbol itself.
diff --git a/llvm/test/CodeGen/AArch64/mingw-refptr.ll b/llvm/test/CodeGen/AArch64/mingw-refptr.ll
new file mode 100644
index 000000000000..3a76d8586270
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/mingw-refptr.ll
@@ -0,0 +1,97 @@
+; RUN: llc < %s -mtriple=aarch64-w64-mingw32 | FileCheck %s
+
+@var = external local_unnamed_addr global i32, align 4
+@dsolocalvar = external dso_local local_unnamed_addr global i32, align 4
+@localvar = dso_local local_unnamed_addr global i32 0, align 4
+@localcommon = common dso_local local_unnamed_addr global i32 0, align 4
+@extvar = external dllimport local_unnamed_addr global i32, align 4
+
+define dso_local i32 @getVar() {
+; CHECK-LABEL: getVar:
+; CHECK:    adrp x8, .refptr.var
+; CHECK:    ldr  x8, [x8, .refptr.var]
+; CHECK:    ldr  w0, [x8]
+; CHECK:    ret
+entry:
+  %0 = load i32, i32* @var, align 4
+  ret i32 %0
+}
+
+define dso_local i32 @getDsoLocalVar() {
+; CHECK-LABEL: getDsoLocalVar:
+; CHECK:    adrp x8, dsolocalvar
+; CHECK:    ldr  w0, [x8, dsolocalvar]
+; CHECK:    ret
+entry:
+  %0 = load i32, i32* @dsolocalvar, align 4
+  ret i32 %0
+}
+
+define dso_local i32 @getLocalVar() {
+; CHECK-LABEL: getLocalVar:
+; CHECK:    adrp x8, localvar
+; CHECK:    ldr  w0, [x8, localvar]
+; CHECK:    ret
+entry:
+  %0 = load i32, i32* @localvar, align 4
+  ret i32 %0
+}
+
+define dso_local i32 @getLocalCommon() {
+; CHECK-LABEL: getLocalCommon:
+; CHECK:    adrp x8, localcommon
+; CHECK:    ldr  w0, [x8, localcommon]
+; CHECK:    ret
+entry:
+  %0 = load i32, i32* @localcommon, align 4
+  ret i32 %0
+}
+
+define dso_local i32 @getExtVar() {
+; CHECK-LABEL: getExtVar:
+; CHECK:    adrp x8, __imp_extvar
+; CHECK:    ldr  x8, [x8, __imp_extvar]
+; CHECK:    ldr  w0, [x8]
+; CHECK:    ret
+entry:
+  %0 = load i32, i32* @extvar, align 4
+  ret i32 %0
+}
+
+define dso_local void @callFunc() {
+; CHECK-LABEL: callFunc:
+; CHECK:    b otherFunc
+entry:
+  tail call void @otherFunc()
+  ret void
+}
+
+declare dso_local void @otherFunc()
+
+define dso_local void @sspFunc() #0 {
+; CHECK-LABEL: sspFunc:
+; CHECK:    adrp x8, .refptr.__stack_chk_guard
+; CHECK:    ldr  x8, [x8, .refptr.__stack_chk_guard]
+; CHECK:    ldr  x8, [x8]
+entry:
+  %c = alloca i8, align 1
+  call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %c)
+  call void @ptrUser(i8* nonnull %c)
+  call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %c)
+  ret void
+}
+
+declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
+declare dso_local void @ptrUser(i8*) local_unnamed_addr #2
+declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
+
+attributes #0 = { sspstrong }
+
+; CHECK:        .section        .rdata$.refptr.__stack_chk_guard,"dr",discard,.refptr.__stack_chk_guard
+; CHECK:        .globl  .refptr.__stack_chk_guard
+; CHECK: .refptr.__stack_chk_guard:
+; CHECK:        .xword  __stack_chk_guard
+; CHECK:        .section        .rdata$.refptr.var,"dr",discard,.refptr.var
+; CHECK:        .globl  .refptr.var
+; CHECK: .refptr.var:
+; CHECK:        .xword  var