diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -298,6 +298,9 @@
 - Added support for MSVC's ``#pragma alloc_text``. The pragma names the code
   section functions are placed in. The pragma only applies to functions with
   C linkage.
+- Added support for MSVC's ``#pragma optimize``. The pragma takes a list of
+  optimizations to turn on or off which applies to all functions following the
+  pragma.
 
 - ...
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1117,6 +1117,13 @@
   "expected 'compiler', 'lib', 'user', or a string literal for the section name in '#pragma %0' - ignored">,
   InGroup<IgnoredPragmas>;
 
+def warn_pragma_unsupported_char_msvc_optimize : Warning<
+  "unsupported character '%0' in optimization list for '#pragma optimize'">;
+def warn_pragma_invalid_char_msvc_optimize : Warning<
+  "invalid character '%0' in optimization list for '#pragma optimize'">;
+def warn_pragma_deprecated_char_msvc_optimize : Warning<
+  "deprecated character '%0' in optimization list for '#pragma optimize'">;
+
 def err_pragma_expected_integer : Error<"expected an integer argument in '#pragma %0'">;
 def warn_pragma_expected_integer : Warning<
   "expected integer between %0 and %1 inclusive in '#pragma %2' - ignored">,
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -728,6 +728,8 @@
                               SourceLocation PragmaLocation);
   bool HandlePragmaMSAllocText(StringRef PragmaName,
                                SourceLocation PragmaLocation);
+  bool HandlePragmaMSOptimize(StringRef PragmaName,
+                              SourceLocation PragmaLocation);
 
   /// Handle the annotation token produced for
   /// #pragma align...
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -758,6 +758,15 @@
   /// optimizations are currently "on", this is set to an invalid location.
   SourceLocation OptimizeOffPragmaLocation;
 
+  /// The list of valid optimization parameters passed by \#pragma optimize.
+  std::string MSPragmaOptimizeValidParams;
+  /// The "on" or "off" argument passed by \#pragma optimize, that denotes
+  /// whether the optimizations in the list above should be turned off or on.
+  /// This boolean is true by default because command line options are honored
+  /// when `#pragma optimize("", on)`.
+  /// (i.e. `ModifyFnAttributeMSPragmaOptimze()` does nothing)
+  bool MSPragmaOptimizeIsOn = true;
+
   /// Set of no-builtin functions listed by \#pragma function.
   llvm::SmallSetVector<StringRef, 4> MSFunctionNoBuiltins;
 
@@ -10334,6 +10343,10 @@
   /// Called on well formed \#pragma clang optimize.
   void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc);
 
+  /// #pragma optimize("[optimization-list]", on | off).
+  void ActOnPragmaMSOptimize(SourceLocation Loc, bool IsOn,
+                             std::string ValidOptimizationList);
+
   /// Call on well formed \#pragma function.
   void
   ActOnPragmaMSFunction(SourceLocation Loc,
@@ -10360,6 +10373,11 @@
   /// attribute to be added (usually because of a pragma).
   void AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, SourceLocation Loc);
 
+  /// Only called on function definitions; if there is a MSVC #pragma optimize
+  /// in scope, consider changing the function's attributes based on the
+  /// optimization list passed to the pragma.
+  void ModifyFnAttributesMSPragmaOptimize(FunctionDecl *FD);
+
   /// Only called on function definitions; if there is a pragma in scope
   /// with the effect of a range-based no_builtin, consider marking the function
   /// with attribute no_builtin.
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -255,12 +255,6 @@
                     Token &FirstToken) override;
 };
 
-struct PragmaMSOptimizeHandler : public PragmaHandler {
-  PragmaMSOptimizeHandler() : PragmaHandler("optimize") {}
-  void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
-                    Token &FirstToken) override;
-};
-
 // "\#pragma fenv_access (on)".
 struct PragmaMSFenvAccessHandler : public PragmaHandler {
   PragmaMSFenvAccessHandler() : PragmaHandler("fenv_access") {}
@@ -449,12 +443,12 @@
     PP.AddPragmaHandler(MSFunction.get());
     MSAllocText = std::make_unique<PragmaMSPragma>("alloc_text");
     PP.AddPragmaHandler(MSAllocText.get());
+    MSOptimize = std::make_unique<PragmaMSPragma>("optimize");
+    PP.AddPragmaHandler(MSOptimize.get());
     MSRuntimeChecks = std::make_unique<PragmaMSRuntimeChecksHandler>();
     PP.AddPragmaHandler(MSRuntimeChecks.get());
     MSIntrinsic = std::make_unique<PragmaMSIntrinsicHandler>();
     PP.AddPragmaHandler(MSIntrinsic.get());
-    MSOptimize = std::make_unique<PragmaMSOptimizeHandler>();
-    PP.AddPragmaHandler(MSOptimize.get());
     MSFenvAccess = std::make_unique<PragmaMSFenvAccessHandler>();
     PP.AddPragmaHandler(MSFenvAccess.get());
   }
@@ -923,7 +917,8 @@
           .Case("section", &Parser::HandlePragmaMSSection)
           .Case("init_seg", &Parser::HandlePragmaMSInitSeg)
           .Case("function", &Parser::HandlePragmaMSFunction)
-          .Case("alloc_text", &Parser::HandlePragmaMSAllocText);
+          .Case("alloc_text", &Parser::HandlePragmaMSAllocText)
+          .Case("optimize", &Parser::HandlePragmaMSOptimize);
 
   if (!(this->*Handler)(PragmaName, PragmaLocation)) {
     // Pragma handling failed, and has been diagnosed.  Slurp up the tokens
@@ -3653,57 +3648,73 @@
 }
 
 // #pragma optimize("gsty", on|off)
-void PragmaMSOptimizeHandler::HandlePragma(Preprocessor &PP,
-                                           PragmaIntroducer Introducer,
-                                           Token &Tok) {
-  SourceLocation StartLoc = Tok.getLocation();
-  PP.Lex(Tok);
-
-  if (Tok.isNot(tok::l_paren)) {
-    PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) << "optimize";
-    return;
-  }
-  PP.Lex(Tok);
+bool Parser::HandlePragmaMSOptimize(StringRef PragmaName,
+                                    SourceLocation PragmaLocation) {
+  Token FirstTok = Tok;
+  if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+                       PragmaName))
+    return false;
 
   if (Tok.isNot(tok::string_literal)) {
-    PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_string) << "optimize";
-    return;
+    PP.Diag(PragmaLocation, diag::warn_pragma_expected_string) << PragmaName;
+    return false;
+  }
+  ExprResult StringResult = ParseStringLiteralExpression();
+  if (StringResult.isInvalid())
+    return false; // Already diagnosed.
+  StringLiteral *OptimizationList = cast<StringLiteral>(StringResult.get());
+  if (OptimizationList->getCharByteWidth() != 1) {
+    PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
+        << PragmaName;
+    return false;
   }
-  // We could syntax check the string but it's probably not worth the effort.
-  PP.Lex(Tok);
 
-  if (Tok.isNot(tok::comma)) {
-    PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_comma) << "optimize";
-    return;
+  std::string ValidOptimizationList;
+  for (auto &c : OptimizationList->getString()) {
+    // Deprecated parameters: g
+    if (c == 'g')
+      PP.Diag(PragmaLocation, diag::warn_pragma_deprecated_char_msvc_optimize)
+          << std::string(1, c);
+    // Unsupported parameters (supported by MSVC): y
+    else if (c == 'y')
+      PP.Diag(PragmaLocation, diag::warn_pragma_unsupported_char_msvc_optimize)
+          << std::string(1, c);
+    // Supported parameters: s, t
+    else if (c != 's' || c != 't')
+      PP.Diag(PragmaLocation, diag::warn_pragma_invalid_char_msvc_optimize)
+          << std::string(1, c);
+    else
+      ValidOptimizationList.push_back(c);
   }
-  PP.Lex(Tok);
 
-  if (Tok.is(tok::eod) || Tok.is(tok::r_paren)) {
-    PP.Diag(Tok.getLocation(), diag::warn_pragma_missing_argument)
-        << "optimize" << /*Expected=*/true << "'on' or 'off'";
-    return;
+  if (ExpectAndConsume(tok::comma, diag::warn_pragma_expected_comma,
+                       PragmaName))
+    return false;
+
+  if (Tok.is(tok::eof) || Tok.is(tok::r_paren)) {
+    PP.Diag(PragmaLocation, diag::warn_pragma_missing_argument)
+        << PragmaName << /*Expected=*/true << "'on' or 'off'";
+    return false;
   }
   IdentifierInfo *II = Tok.getIdentifierInfo();
   if (!II || (!II->isStr("on") && !II->isStr("off"))) {
-    PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
-        << PP.getSpelling(Tok) << "optimize" << /*Expected=*/true
+    PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument)
+        << PP.getSpelling(Tok) << PragmaName << /*Expected=*/true
         << "'on' or 'off'";
-    return;
+    return false;
   }
+  bool IsOn = II->isStr("on");
   PP.Lex(Tok);
 
-  if (Tok.isNot(tok::r_paren)) {
-    PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) << "optimize";
-    return;
-  }
-  PP.Lex(Tok);
+  if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
+                       PragmaName) ||
+      ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
+                       PragmaName))
+    return false;
 
-  if (Tok.isNot(tok::eod)) {
-    PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
-        << "optimize";
-    return;
-  }
-  PP.Diag(StartLoc, diag::warn_pragma_optimize);
+  Actions.ActOnPragmaMSOptimize(FirstTok.getLocation(), IsOn,
+                                ValidOptimizationList);
+  return true;
 }
 
 void PragmaForceCUDAHostDeviceHandler::HandlePragma(
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1096,6 +1096,17 @@
     OptimizeOffPragmaLocation = PragmaLoc;
 }
 
+void Sema::ActOnPragmaMSOptimize(SourceLocation Loc, bool IsOn,
+                                 std::string ValidOptimizationList) {
+  if (!CurContext->getRedeclContext()->isFileContext()) {
+    Diag(Loc, diag::err_pragma_expected_file_scope) << "optimize";
+    return;
+  }
+
+  MSPragmaOptimizeValidParams = ValidOptimizationList;
+  MSPragmaOptimizeIsOn = IsOn;
+}
+
 void Sema::ActOnPragmaMSFunction(
     SourceLocation Loc, const llvm::SmallVectorImpl<StringRef> &NoBuiltins) {
   if (!CurContext->getRedeclContext()->isFileContext()) {
@@ -1129,6 +1140,33 @@
   }
 }
 
+void Sema::ModifyFnAttributesMSPragmaOptimize(FunctionDecl *FD) {
+  if (MSPragmaOptimizeValidParams.empty())
+    // Don't modify the function attributes if it's "on". "on" resets the
+    // optimizations to the ones listed on the command line
+    if (!MSPragmaOptimizeIsOn) {
+      AddOptnoneAttributeIfNoConflicts(FD, FD->getBeginLoc());
+      return;
+    }
+
+  for (auto &c : MSPragmaOptimizeValidParams) {
+    FD->dropAttr<OptimizeAttr>();
+    if (MSPragmaOptimizeIsOn) {
+      if (c == 's') {
+        FD->addAttr(
+            OptimizeAttr::CreateImplicit(Context, "", OptimizeAttr::Os));
+      } else if (c == 't') {
+        // OptimizeAttr::O2 should be a noop
+        FD->addAttr(
+            OptimizeAttr::CreateImplicit(Context, "", OptimizeAttr::O2));
+      }
+    } else {
+      // If turning off, add optnone for both "s" and "t" cases
+      AddOptnoneAttributeIfNoConflicts(FD, FD->getBeginLoc());
+    }
+  }
+}
+
 void Sema::AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD,
                                             SourceLocation Loc) {
   // Don't add a conflicting attribute. No diagnostic is needed.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10192,6 +10192,7 @@
     AddRangeBasedOptnone(NewFD);
     AddImplicitMSFunctionNoBuiltinAttr(NewFD);
     AddSectionMSAllocText(NewFD);
+    ModifyFnAttributesMSPragmaOptimize(NewFD);
   }
 
   // If this is the first declaration of an extern C variable, update
diff --git a/clang/test/CodeGen/pragma-msvc-optimize.c b/clang/test/CodeGen/pragma-msvc-optimize.c
new file mode 100644
--- /dev/null
+++ b/clang/test/CodeGen/pragma-msvc-optimize.c
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -O2 -emit-llvm -fms-extensions -o - %s | FileCheck %s -check-prefix CHECK-O2
+// RUN: %clang_cc1 -O0 -emit-llvm -fms-extensions -o - %s | FileCheck %s -check-prefix CHECK-O0
+// RUN: %clang_cc1 -Os -emit-llvm -fms-extensions -o - %s | FileCheck %s -check-prefix CHECK-Os
+
+#pragma optimize("s", on)
+
+// CHECK-O2: define{{.*}} void @f0(){{.*}} #[[OPTSIZE:[0-9]+]]
+void f0() {}
+
+#pragma optimize("y", on)
+
+// CHECK-O2: define{{.*}} void @f1(){{.*}} #[[OPTSIZE_FRAMEPTR_NONLEAF:[0-9]+]]
+void f1() {}
+
+#pragma optimize("t", on)
+
+// CHECK-O2: define{{.*}} void @f2(){{.*}} #[[OPTSIZE_FRAMEPTR_NONLEAF]]
+// CHECK-O0: define{{.*}} void @f2(){{.*}} #[[NO_OPTNONE:[0-9]+]]
+void f2() {}
+
+#pragma optimize("s", off)
+
+// CHECK-O2: define{{.*}} void @f3(){{.*}} #[[FRAMEPTR_NONLEAF:[0-9]+]]
+// CHECK-Os: define{{.*}} void @f3(){{.*}} #[[FRAMEPTR_NONLEAF:[0-9]+]]
+void f3() {}
+
+#pragma optimize("y", off)
+
+// CHECK-O2: define{{.*}} void @f4(){{.*}} #[[FRAMEPTR_NONE:[0-9]+]]
+void f4() {}
+
+#pragma optimize("t", off)
+
+// CHECK-O2: define{{.*}} void @f5(){{.*}} #[[OPTNONE_FRAMEPTR_NONE:[0-9]+]]
+void f5() {}
+
+// CHECK-O2:     attributes #[[OPTSIZE]] = {{{.*}}optsize{{.*}}}
+// CHECK-O2-NOT: attributes #[[OPTSIZE]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}}
+// CHECK-O2:     attributes #[[OPTSIZE_FRAMEPTR_NONLEAF]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}}
+// CHECK-O2:     attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}"frame-pointer"="non-leaf"{{.*}}}
+// CHECK-O2-NOT: attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}}
+// CHECK-O2:     attributes #[[FRAMEPTR_NONE]] = {{{.*}}"frame-pointer"="none"{{.*}}}
+// CHECK-O2:     attributes #[[OPTNONE_FRAMEPTR_NONE]] = {{{.*}}optnone{{.*}}"frame-pointer"="none"{{.*}}}
+
+// CHECK-O0-NOT: attributes #[[NO_OPTNONE]] = {{{.*}}optnone{{.*}}}
+
+// CHECK-Os:     attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}"frame-pointer"="non-leaf"{{.*}}}
+// CHECK-Os-NOT: attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}}
diff --git a/clang/test/Preprocessor/pragma_microsoft.c b/clang/test/Preprocessor/pragma_microsoft.c
--- a/clang/test/Preprocessor/pragma_microsoft.c
+++ b/clang/test/Preprocessor/pragma_microsoft.c
@@ -228,7 +228,12 @@
 #pragma optimize("g"      // expected-warning{{expected ',' in '#pragma optimize'}}
 #pragma optimize("g",     // expected-warning{{missing argument to '#pragma optimize'; expected 'on' or 'off'}}
 #pragma optimize("g",xyz  // expected-warning{{unexpected argument 'xyz' to '#pragma optimize'; expected 'on' or 'off'}}
-#pragma optimize("g",on)  // expected-warning{{#pragma optimize' is not supported}}
+#pragma optimize("g",on)  // no-warning
+#pragma optimize("g", on) asdf // expected-warning{{extra tokens at end of '#pragma optimize'}}
+
+void pragma_optimize_foo() {
+#pragma optimize("g", on) // expected-error {{'#pragma optimize' can only appear at file scope}}
+}
 
 #pragma execution_character_set                 // expected-warning {{expected '('}}
 #pragma execution_character_set(                // expected-warning {{expected 'push' or 'pop'}}