Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td	(revision 181721)
+++ include/clang/Basic/Attr.td	(working copy)
@@ -960,6 +960,14 @@
   let Spellings = [Keyword<"__ptr64">];
 }
 
+def SPtr : InheritableAttr {
+  let Spellings = [Keyword<"__sptr">];
+}
+
+def UPtr : InheritableAttr {
+  let Spellings = [Keyword<"__uptr">];
+}
+
 class MSInheritanceAttr : InheritableAttr;
 
 def SingleInheritance : MSInheritanceAttr {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 181721)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -1782,6 +1782,8 @@
   "nonnull attribute only applies to pointer arguments">;
 def err_attribute_pointers_only : Error<
   "'%0' attribute only applies to pointer arguments">;
+def err_attribute_no_member_pointers : Error<
+  "'%0' attribute cannot be used with pointers to members">;
 def err_attribute_invalid_implicit_this_argument : Error<
   "'%0' attribute is invalid for the implicit this argument">;
 def err_ownership_type : Error<
@@ -6165,6 +6167,9 @@
 def err_invalid_conversion_between_ext_vectors : Error<
   "invalid conversion between ext-vector type %0 and %1">;
 
+def warn_duplicate_attribute_exact : Warning<
+  "attribute %0 is already applied">, InGroup<IgnoredAttributes>;
+
 def warn_duplicate_attribute : Warning<
   "attribute %0 is already applied with different parameters">,
   InGroup<IgnoredAttributes>;
Index: include/clang/Basic/TokenKinds.def
===================================================================
--- include/clang/Basic/TokenKinds.def	(revision 181721)
+++ include/clang/Basic/TokenKinds.def	(working copy)
@@ -509,6 +509,8 @@
 // Microsoft extensions which should be disabled in strict conformance mode
 KEYWORD(__ptr64                       , KEYMS)
 KEYWORD(__ptr32                       , KEYMS)
+KEYWORD(__sptr                        , KEYMS)
+KEYWORD(__uptr                        , KEYMS)
 KEYWORD(__w64                         , KEYMS)
 KEYWORD(__uuidof                      , KEYMS | KEYBORLAND)
 KEYWORD(__try                         , KEYMS | KEYBORLAND)
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp	(revision 181721)
+++ lib/Parse/ParseDecl.cpp	(working copy)
@@ -601,7 +601,8 @@
   while (Tok.is(tok::kw___fastcall) || Tok.is(tok::kw___stdcall) ||
          Tok.is(tok::kw___thiscall) || Tok.is(tok::kw___cdecl)   ||
          Tok.is(tok::kw___ptr64) || Tok.is(tok::kw___w64) ||
-         Tok.is(tok::kw___ptr32) || Tok.is(tok::kw___unaligned)) {
+         Tok.is(tok::kw___ptr32) || Tok.is(tok::kw___unaligned) ||
+         Tok.is(tok::kw___sptr) || Tok.is(tok::kw___uptr)) {
     IdentifierInfo *AttrName = Tok.getIdentifierInfo();
     SourceLocation AttrNameLoc = ConsumeToken();
     attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
@@ -2711,6 +2712,8 @@
       break;
     }
 
+    case tok::kw___sptr:
+    case tok::kw___uptr:
     case tok::kw___ptr64:
     case tok::kw___ptr32:
     case tok::kw___w64:
@@ -4145,6 +4148,8 @@
   case tok::kw___fastcall:
   case tok::kw___thiscall:
   case tok::kw___w64:
+  case tok::kw___sptr:
+  case tok::kw___uptr:
   case tok::kw___ptr64:
   case tok::kw___ptr32:
   case tok::kw___forceinline:
@@ -4321,6 +4326,8 @@
       ParseOpenCLQualifiers(DS);
       break;
 
+    case tok::kw___sptr:
+    case tok::kw___uptr:
     case tok::kw___w64:
     case tok::kw___ptr64:
     case tok::kw___ptr32:
Index: lib/Parse/ParseTentative.cpp
===================================================================
--- lib/Parse/ParseTentative.cpp	(revision 181721)
+++ lib/Parse/ParseTentative.cpp	(working copy)
@@ -1122,6 +1122,8 @@
   case tok::kw___fastcall:
   case tok::kw___thiscall:
   case tok::kw___w64:
+  case tok::kw___sptr:
+  case tok::kw___uptr:
   case tok::kw___ptr64:
   case tok::kw___ptr32:
   case tok::kw___forceinline:
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp	(revision 181721)
+++ lib/Sema/SemaDeclAttr.cpp	(working copy)
@@ -4655,6 +4655,54 @@
                                       Attr.getAttributeSpellingListIndex()));
 }
 
+static void handleMSPointerTypeQualifierAttr(Sema &S, Decl *D,
+                                             const AttributeList &Attr) {
+  if (S.LangOpts.MicrosoftExt) {
+    // Pointer type qualifiers can only operate on pointer types, but not
+    // pointer-to-member types.
+    ValueDecl *VD = dyn_cast<ValueDecl>(D);
+    if (VD) {
+      QualType T = VD->getType();
+      if (!T->isPointerType() || T->isMemberPointerType()) {
+        S.Diag(Attr.getLoc(), T->isMemberPointerType() ?
+                                diag::err_attribute_no_member_pointers :
+                                diag::err_attribute_pointers_only)
+          << Attr.getName();
+        return;
+      }
+    } else
+      S.Diag(Attr.getLoc(),
+             diag::err_attribute_can_be_applied_only_to_value_decl)
+                << Attr.getName();
+    
+    AttributeList::Kind Kind = Attr.getKind();
+
+    // You cannot have both __sptr and __uptr on the same declaration, nor can
+    // you duplicate the attributes.
+    bool HasUPtr = D->hasAttr<UPtrAttr>(), HasSPtr = D->hasAttr<SPtrAttr>();
+    if ((HasUPtr && Kind == AttributeList::AT_SPtr) ||
+        (HasSPtr && Kind == AttributeList::AT_UPtr)) {
+      S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible)
+        << S.Context.Idents.get("'__sptr'").getName()
+        << S.Context.Idents.get("'__uptr'").getName();
+      return;
+    } else if ((HasUPtr && Kind == AttributeList::AT_UPtr) ||
+               (HasSPtr && Kind == AttributeList::AT_SPtr)) {
+      S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute_exact)
+        << Attr.getName();
+      return;
+    }
+
+    if (Kind == AttributeList::AT_SPtr)
+      D->addAttr(::new (S.Context) SPtrAttr(Attr.getRange(), S.Context,
+                                        Attr.getAttributeSpellingListIndex()));
+    if (Kind == AttributeList::AT_UPtr)
+      D->addAttr(::new (S.Context) UPtrAttr(Attr.getRange(), S.Context,
+                                        Attr.getAttributeSpellingListIndex()));
+  } else
+    S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName();
+}
+
 static void handlePortabilityAttr(Sema &S, Decl *D, const AttributeList &Attr) {
   if (S.LangOpts.MicrosoftExt) {
     AttributeList::Kind Kind = Attr.getKind();
@@ -4906,6 +4954,10 @@
   case AttributeList::AT_Ptr64:
     handlePortabilityAttr(S, D, Attr);
     break;
+  case AttributeList::AT_SPtr:
+  case AttributeList::AT_UPtr:
+    handleMSPointerTypeQualifierAttr(S, D, Attr);
+    break;
   case AttributeList::AT_ForceInline:
     handleForceInlineAttr(S, D, Attr);
     break;
Index: test/Parser/MicrosoftExtensions.c
===================================================================
--- test/Parser/MicrosoftExtensions.c	(revision 181721)
+++ test/Parser/MicrosoftExtensions.c	(working copy)
@@ -105,3 +105,11 @@
   struct S7 s;
   int i = s.t;	/* expected-warning {{'t' is deprecated}} */
 }
+
+int * __sptr psp;
+int * __uptr pup;
+/* Either ordering is acceptable */
+int * __ptr32 __sptr psp32;
+int * __ptr32 __uptr pup32;
+int * __sptr __ptr64 psp64;
+int * __uptr __ptr64 pup64;
Index: test/Sema/MicrosoftCompatibility.cpp
===================================================================
--- test/Sema/MicrosoftCompatibility.cpp	(revision 181721)
+++ test/Sema/MicrosoftCompatibility.cpp	(working copy)
@@ -2,3 +2,9 @@
 
 // PR15845
 int foo(xxx); // expected-error{{unknown type name}}
+
+struct cls {
+  char *m;
+};
+
+char * cls::* __uptr wrong2 = &cls::m; // expected-error {{''__uptr'' attribute cannot be used with pointers to members}}
Index: test/Sema/MicrosoftExtensions.c
===================================================================
--- test/Sema/MicrosoftExtensions.c	(revision 181721)
+++ test/Sema/MicrosoftExtensions.c	(working copy)
@@ -102,3 +102,7 @@
 
 	enum DE1 no;	// no warning because E1 is not deprecated
 }
+
+int __sptr wrong1; // expected-error {{''__sptr'' attribute only applies to pointer arguments}}
+int * __sptr __uptr wrong2; // expected-error {{'__sptr' and '__uptr' attributes are not compatible}}
+int * __sptr __sptr wrong3; // expected-warning {{attribute '__sptr' is already applied}}
