[clang] 911b200 - [Clang] Constant Expressions inside of GCC' asm strings (#131003)

via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 17 12:10:50 PDT 2025


Author: cor3ntin
Date: 2025-03-17T20:10:46+01:00
New Revision: 911b200ce339ace2d55cd2827bb10ed6a494faae

URL: https://github.com/llvm/llvm-project/commit/911b200ce339ace2d55cd2827bb10ed6a494faae
DIFF: https://github.com/llvm/llvm-project/commit/911b200ce339ace2d55cd2827bb10ed6a494faae.diff

LOG: [Clang] Constant Expressions inside of GCC' asm strings (#131003)

Implements GCC's constexpr string ASM extension
https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html

Added: 
    clang/test/CodeGenCXX/gnu-asm-constexpr.cpp
    clang/test/SemaCXX/gnu-asm-constexpr.cpp

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/Expr.h
    clang/include/clang/AST/RecursiveASTVisitor.h
    clang/include/clang/AST/Stmt.h
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/Features.def
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/lib/AST/Stmt.cpp
    clang/lib/AST/StmtPrinter.cpp
    clang/lib/AST/StmtProfile.cpp
    clang/lib/CodeGen/CGStmt.cpp
    clang/lib/Parse/ParseStmtAsm.cpp
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaStmtAsm.cpp
    clang/lib/Sema/TreeTransform.h
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/test/Parser/asm.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index cc12ff5bad353..6d03e6de461e7 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1959,6 +1959,32 @@ references can be used instead of numeric references.
       return -1;
   }
 
+
+Constexpr strings in GNU ASM statememts
+=======================================
+
+In C++11 mode (and greater), Clang supports specifying the template,
+constraints, and clobber strings with a parenthesized constant expression
+producing an object with the following member functions
+
+.. code-block:: c++
+
+  constexpr const char* data() const;
+  constexpr size_t size() const;
+
+such as ``std::string``, ``std::string_view``, ``std::vector<char>``.
+This mechanism follow the same rules as ``static_assert`` messages in
+C++26, see ``[dcl.pre]/p12``.
+
+Query for this feature with ``__has_extension(gnu_asm_constexpr_strings)``.
+
+.. code-block:: c++
+
+   int foo() {
+      asm((std::string_view("nop")) ::: (std::string_view("memory")));
+   }
+
+
 Objective-C Features
 ====================
 

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 17128f15d345a..d9f1c95533c9c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -74,6 +74,15 @@ What's New in Clang |release|?
 C++ Language Changes
 --------------------
 
+- Similarly to GCC, Clang now supports constant expressions in
+  the strings of a GNU ``asm`` statement.
+
+  .. code-block:: c++
+
+    int foo() {
+      asm((std::string_view("nop")) ::: (std::string_view("memory")));
+    }
+
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index b81f3a403baf6..28f5eb283956d 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -787,6 +787,10 @@ class Expr : public ValueStmt {
                                  const Expr *PtrExpression, ASTContext &Ctx,
                                  EvalResult &Status) const;
 
+  bool EvaluateCharRangeAsString(APValue &Result, const Expr *SizeExpression,
+                                 const Expr *PtrExpression, ASTContext &Ctx,
+                                 EvalResult &Status) const;
+
   /// If the current Expr can be evaluated to a pointer to a null-terminated
   /// constant string, return the constant string (without the terminating
   /// null).

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index fac4c10987157..87a6c22b35ee8 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2410,15 +2410,15 @@ DEF_TRAVERSE_DECL(ImplicitConceptSpecializationDecl, {
   }
 
 DEF_TRAVERSE_STMT(GCCAsmStmt, {
-  TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmString());
+  TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmStringExpr());
   for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) {
-    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintLiteral(I));
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintExpr(I));
   }
   for (unsigned I = 0, E = S->getNumOutputs(); I < E; ++I) {
-    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintLiteral(I));
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintExpr(I));
   }
   for (unsigned I = 0, E = S->getNumClobbers(); I < E; ++I) {
-    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberStringLiteral(I));
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberExpr(I));
   }
   // children() iterates over inputExpr and outputExpr.
 })

diff  --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 604ac51d478cf..e779f94d16b94 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -3193,7 +3193,7 @@ class AsmStmt : public Stmt {
   /// getOutputConstraint - Return the constraint string for the specified
   /// output operand.  All output constraints are known to be non-empty (either
   /// '=' or '+').
-  StringRef getOutputConstraint(unsigned i) const;
+  std::string getOutputConstraint(unsigned i) const;
 
   /// isOutputPlusConstraint - Return true if the specified output constraint
   /// is a "+" constraint (which is both an input and an output) or false if it
@@ -3214,14 +3214,14 @@ class AsmStmt : public Stmt {
 
   /// getInputConstraint - Return the specified input constraint.  Unlike output
   /// constraints, these can be empty.
-  StringRef getInputConstraint(unsigned i) const;
+  std::string getInputConstraint(unsigned i) const;
 
   const Expr *getInputExpr(unsigned i) const;
 
   //===--- Other ---===//
 
   unsigned getNumClobbers() const { return NumClobbers; }
-  StringRef getClobber(unsigned i) const;
+  std::string getClobber(unsigned i) const;
 
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == GCCAsmStmtClass ||
@@ -3302,21 +3302,20 @@ class GCCAsmStmt : public AsmStmt {
   friend class ASTStmtReader;
 
   SourceLocation RParenLoc;
-  StringLiteral *AsmStr;
+  Expr *AsmStr;
 
   // FIXME: If we wanted to, we could allocate all of these in one big array.
-  StringLiteral **Constraints = nullptr;
-  StringLiteral **Clobbers = nullptr;
+  Expr **Constraints = nullptr;
+  Expr **Clobbers = nullptr;
   IdentifierInfo **Names = nullptr;
   unsigned NumLabels = 0;
 
 public:
   GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple,
              bool isvolatile, unsigned numoutputs, unsigned numinputs,
-             IdentifierInfo **names, StringLiteral **constraints, Expr **exprs,
-             StringLiteral *asmstr, unsigned numclobbers,
-             StringLiteral **clobbers, unsigned numlabels,
-             SourceLocation rparenloc);
+             IdentifierInfo **names, Expr **constraints, Expr **exprs,
+             Expr *asmstr, unsigned numclobbers, Expr **clobbers,
+             unsigned numlabels, SourceLocation rparenloc);
 
   /// Build an empty inline-assembly statement.
   explicit GCCAsmStmt(EmptyShell Empty) : AsmStmt(GCCAsmStmtClass, Empty) {}
@@ -3326,9 +3325,11 @@ class GCCAsmStmt : public AsmStmt {
 
   //===--- Asm String Analysis ---===//
 
-  const StringLiteral *getAsmString() const { return AsmStr; }
-  StringLiteral *getAsmString() { return AsmStr; }
-  void setAsmString(StringLiteral *E) { AsmStr = E; }
+  const Expr *getAsmStringExpr() const { return AsmStr; }
+  Expr *getAsmStringExpr() { return AsmStr; }
+  void setAsmStringExpr(Expr *E) { AsmStr = E; }
+
+  std::string getAsmString() const;
 
   /// AsmStringPiece - this is part of a decomposed asm string specification
   /// (for use with the AnalyzeAsmString function below).  An asm string is
@@ -3397,14 +3398,12 @@ class GCCAsmStmt : public AsmStmt {
     return {};
   }
 
-  StringRef getOutputConstraint(unsigned i) const;
+  std::string getOutputConstraint(unsigned i) const;
 
-  const StringLiteral *getOutputConstraintLiteral(unsigned i) const {
-    return Constraints[i];
-  }
-  StringLiteral *getOutputConstraintLiteral(unsigned i) {
+  const Expr *getOutputConstraintExpr(unsigned i) const {
     return Constraints[i];
   }
+  Expr *getOutputConstraintExpr(unsigned i) { return Constraints[i]; }
 
   Expr *getOutputExpr(unsigned i);
 
@@ -3425,12 +3424,12 @@ class GCCAsmStmt : public AsmStmt {
     return {};
   }
 
-  StringRef getInputConstraint(unsigned i) const;
+  std::string getInputConstraint(unsigned i) const;
 
-  const StringLiteral *getInputConstraintLiteral(unsigned i) const {
+  const Expr *getInputConstraintExpr(unsigned i) const {
     return Constraints[i + NumOutputs];
   }
-  StringLiteral *getInputConstraintLiteral(unsigned i) {
+  Expr *getInputConstraintExpr(unsigned i) {
     return Constraints[i + NumOutputs];
   }
 
@@ -3441,6 +3440,8 @@ class GCCAsmStmt : public AsmStmt {
     return const_cast<GCCAsmStmt*>(this)->getInputExpr(i);
   }
 
+  static std::string ExtractStringFromGCCAsmStmtComponent(const Expr *E);
+
   //===--- Labels ---===//
 
   bool isAsmGoto() const {
@@ -3489,12 +3490,9 @@ class GCCAsmStmt : public AsmStmt {
 private:
   void setOutputsAndInputsAndClobbers(const ASTContext &C,
                                       IdentifierInfo **Names,
-                                      StringLiteral **Constraints,
-                                      Stmt **Exprs,
-                                      unsigned NumOutputs,
-                                      unsigned NumInputs,
-                                      unsigned NumLabels,
-                                      StringLiteral **Clobbers,
+                                      Expr **Constraints, Stmt **Exprs,
+                                      unsigned NumOutputs, unsigned NumInputs,
+                                      unsigned NumLabels, Expr **Clobbers,
                                       unsigned NumClobbers);
 
 public:
@@ -3505,12 +3503,10 @@ class GCCAsmStmt : public AsmStmt {
   /// This returns -1 if the operand name is invalid.
   int getNamedOperand(StringRef SymbolicName) const;
 
-  StringRef getClobber(unsigned i) const;
+  std::string getClobber(unsigned i) const;
 
-  StringLiteral *getClobberStringLiteral(unsigned i) { return Clobbers[i]; }
-  const StringLiteral *getClobberStringLiteral(unsigned i) const {
-    return Clobbers[i];
-  }
+  Expr *getClobberExpr(unsigned i) { return Clobbers[i]; }
+  const Expr *getClobberExpr(unsigned i) const { return Clobbers[i]; }
 
   SourceLocation getBeginLoc() const LLVM_READONLY { return AsmLoc; }
   SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; }

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 092a55f9e9e0c..4dc956f7ae6f7 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -336,7 +336,10 @@ def warn_cxx20_compat_label_end_of_compound_statement : Warning<
 def err_address_of_label_outside_fn : Error<
   "use of address-of-label extension outside of a function body">;
 def err_asm_operand_wide_string_literal : Error<
-  "cannot use %select{unicode|wide|an empty}0 string literal in 'asm'">;
+  "cannot use %select{unicode|wide}0 string literal in 'asm'">;
+
+def err_asm_expected_string : Error<
+  "expected string literal %select{or parenthesized constant expression |}0in 'asm'">;
 def err_expected_selector_for_method : Error<
   "expected selector for Objective-C method">;
 def err_expected_property_name : Error<"expected property name">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 627bebb31fc8d..a88af192fb2a4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1663,23 +1663,30 @@ def err_static_assert_requirement_failed : Error<
   "static assertion failed due to requirement '%0'%select{: %2|}1">;
 def note_expr_evaluates_to : Note<
   "expression evaluates to '%0 %1 %2'">;
-def err_static_assert_invalid_message : Error<
-  "the message in a static assertion must be a string literal or an "
+
+
+def subst_user_defined_msg : TextSubstitution<
+  "%select{the message|the expression}0 in "
+  "%select{a static assertion|this asm operand}0">;
+
+def err_user_defined_msg_invalid : Error<
+  "%sub{subst_user_defined_msg}0 must be a string literal or an "
   "object with 'data()' and 'size()' member functions">;
-def err_static_assert_missing_member_function : Error<
-  "the message object in this static assertion is missing %select{"
+def err_user_defined_msg_missing_member_function : Error<
+  "the %select{message|string}0 object in "
+  "%select{this static assertion|this asm operand}0 is missing %select{"
   "a 'size()' member function|"
   "a 'data()' member function|"
-  "'data()' and 'size()' member functions}0">;
-def err_static_assert_invalid_mem_fn_ret_ty : Error<
-  "the message in a static assertion must have a '%select{size|data}0()' member "
-  "function returning an object convertible to '%select{std::size_t|const char *}0'">;
-def warn_static_assert_message_constexpr : Warning<
-  "the message in this static assertion is not a "
-  "constant expression">,
+  "'data()' and 'size()' member functions}1">;
+def err_user_defined_msg_invalid_mem_fn_ret_ty : Error<
+  "%sub{subst_user_defined_msg}0 must have a '%select{size|data}1()' member "
+  "function returning an object convertible to '%select{std::size_t|const char *}1'">;
+def warn_user_defined_msg_constexpr : Warning<
+  "%select{the message|the expression}0 in "
+  "%select{this static assertion|this asm operand}0 is not a constant expression">,
   DefaultError, InGroup<DiagGroup<"invalid-static-assert-message">>;
-def err_static_assert_message_constexpr : Error<
-  "the message in a static assertion must be produced by a "
+def err_user_defined_msg_constexpr : Error<
+  "%sub{subst_user_defined_msg}0 must be produced by a "
   "constant expression">;
 
 def warn_consteval_if_always_true : Warning<
@@ -9517,6 +9524,9 @@ def warn_redefine_extname_not_applied : Warning<
 
 // inline asm.
 let CategoryName = "Inline Assembly Issue" in {
+  def err_asm_operand_empty_string : Error<
+     "cannot use an empty string literal in 'asm'">;
+
   def err_asm_pmf_through_constraint_not_permitted
     : Error<"cannot pass a pointer-to-member through register-constrained "
             "inline assembly parameter">;

diff  --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 92b1705d15227..05ce214935fad 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -306,6 +306,7 @@ EXTENSION(statement_attributes_with_gnu_syntax, true)
 EXTENSION(gnu_asm, LangOpts.GNUAsm)
 EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm)
 EXTENSION(gnu_asm_goto_with_outputs_full, LangOpts.GNUAsm)
+EXTENSION(gnu_asm_constexpr_strings, LangOpts.GNUAsm && LangOpts.CPlusPlus11)
 EXTENSION(matrix_types, LangOpts.MatrixTypes)
 EXTENSION(matrix_types_scalar_division, true)
 EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11)

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 657350fa843b9..fc3936d649320 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5585,9 +5585,15 @@ class Sema final : public SemaBase {
   void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
   void ActOnFinishDelayedMemberInitializers(Decl *Record);
 
-  bool EvaluateStaticAssertMessageAsString(Expr *Message, std::string &Result,
-                                           ASTContext &Ctx,
-                                           bool ErrorOnInvalidMessage);
+  enum class StringEvaluationContext { StaticAssert = 0, Asm = 1 };
+
+  bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx,
+                        StringEvaluationContext EvalContext,
+                        bool ErrorOnInvalidMessage);
+  bool EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx,
+                        StringEvaluationContext EvalContext,
+                        bool ErrorOnInvalidMessage);
+
   Decl *ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc,
                                      Expr *AssertExpr, Expr *AssertMessageExpr,
                                      SourceLocation RParenLoc);
@@ -11035,6 +11041,7 @@ class Sema final : public SemaBase {
   ///@{
 
 public:
+  ExprResult ActOnGCCAsmStmtString(Expr *Stm, bool ForAsmLabel);
   StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
                              bool IsVolatile, unsigned NumOutputs,
                              unsigned NumInputs, IdentifierInfo **Names,
@@ -15323,6 +15330,13 @@ void Sema::PragmaStack<Sema::AlignPackInfo>::Act(SourceLocation PragmaLocation,
                                                  PragmaMsStackAction Action,
                                                  llvm::StringRef StackSlotLabel,
                                                  AlignPackInfo Value);
+
+inline const StreamingDiagnostic &
+operator<<(const StreamingDiagnostic &DB, Sema::StringEvaluationContext Ctx) {
+  DB << llvm::to_underlying(Ctx);
+  return DB;
+}
+
 } // end namespace clang
 
 #endif

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 0d9b5afc4e4a6..3c15d7d9dcb52 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -6821,25 +6821,25 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
     Names.push_back(ToII);
   }
 
-  SmallVector<StringLiteral *, 4> Clobbers;
+  SmallVector<Expr *, 4> Clobbers;
   for (unsigned I = 0, E = S->getNumClobbers(); I != E; I++) {
-    if (auto ClobberOrErr = import(S->getClobberStringLiteral(I)))
+    if (auto ClobberOrErr = import(S->getClobberExpr(I)))
       Clobbers.push_back(*ClobberOrErr);
     else
       return ClobberOrErr.takeError();
 
   }
 
-  SmallVector<StringLiteral *, 4> Constraints;
+  SmallVector<Expr *, 4> Constraints;
   for (unsigned I = 0, E = S->getNumOutputs(); I != E; I++) {
-    if (auto OutputOrErr = import(S->getOutputConstraintLiteral(I)))
+    if (auto OutputOrErr = import(S->getOutputConstraintExpr(I)))
       Constraints.push_back(*OutputOrErr);
     else
       return OutputOrErr.takeError();
   }
 
   for (unsigned I = 0, E = S->getNumInputs(); I != E; I++) {
-    if (auto InputOrErr = import(S->getInputConstraintLiteral(I)))
+    if (auto InputOrErr = import(S->getInputConstraintExpr(I)))
       Constraints.push_back(*InputOrErr);
     else
       return InputOrErr.takeError();
@@ -6861,7 +6861,7 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) {
   ExpectedSLoc AsmLocOrErr = import(S->getAsmLoc());
   if (!AsmLocOrErr)
     return AsmLocOrErr.takeError();
-  auto AsmStrOrErr = import(S->getAsmString());
+  auto AsmStrOrErr = import(S->getAsmStringExpr());
   if (!AsmStrOrErr)
     return AsmStrOrErr.takeError();
   ExpectedSLoc RParenLocOrErr = import(S->getRParenLoc());

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f8e8aaddbfdbd..7803b1026aab9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17928,10 +17928,12 @@ std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const {
   return {};
 }
 
-bool Expr::EvaluateCharRangeAsString(std::string &Result,
-                                     const Expr *SizeExpression,
-                                     const Expr *PtrExpression, ASTContext &Ctx,
-                                     EvalResult &Status) const {
+template <typename T>
+static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result,
+                                          const Expr *SizeExpression,
+                                          const Expr *PtrExpression,
+                                          ASTContext &Ctx,
+                                          Expr::EvalResult &Status) {
   LValue String;
   EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
   Info.InConstantContext = true;
@@ -17943,6 +17945,13 @@ bool Expr::EvaluateCharRangeAsString(std::string &Result,
 
   uint64_t Size = SizeValue.getZExtValue();
 
+  // FIXME: better protect against invalid or excessive sizes
+  if constexpr (std::is_same_v<APValue, T>)
+    Result = APValue(APValue::UninitArray{}, Size, Size);
+  else {
+    if (Size < Result.max_size())
+      Result.reserve(Size);
+  }
   if (!::EvaluatePointer(PtrExpression, String, Info))
     return false;
 
@@ -17953,18 +17962,38 @@ bool Expr::EvaluateCharRangeAsString(std::string &Result,
                                         Char))
       return false;
 
-    APSInt C = Char.getInt();
-    Result.push_back(static_cast<char>(C.getExtValue()));
+    if constexpr (std::is_same_v<APValue, T>) {
+      Result.getArrayInitializedElt(I) = std::move(Char);
+    } else {
+      APSInt C = Char.getInt();
+
+      assert(C.getBitWidth() <= 8 &&
+             "string element not representable in char");
+
+      Result.push_back(static_cast<char>(C.getExtValue()));
+    }
+
     if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1))
       return false;
   }
-  if (!Scope.destroy())
-    return false;
 
-  if (!CheckMemoryLeaks(Info))
-    return false;
+  return Scope.destroy() && CheckMemoryLeaks(Info);
+}
 
-  return true;
+bool Expr::EvaluateCharRangeAsString(std::string &Result,
+                                     const Expr *SizeExpression,
+                                     const Expr *PtrExpression, ASTContext &Ctx,
+                                     EvalResult &Status) const {
+  return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression,
+                                       PtrExpression, Ctx, Status);
+}
+
+bool Expr::EvaluateCharRangeAsString(APValue &Result,
+                                     const Expr *SizeExpression,
+                                     const Expr *PtrExpression, ASTContext &Ctx,
+                                     EvalResult &Status) const {
+  return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression,
+                                       PtrExpression, Ctx, Status);
 }
 
 bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const {

diff  --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp
index c8ff2ecea20cc..be4ba6878bf0c 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -455,11 +455,11 @@ std::string AsmStmt::generateAsmString(const ASTContext &C) const {
   llvm_unreachable("unknown asm statement kind!");
 }
 
-StringRef AsmStmt::getOutputConstraint(unsigned i) const {
+std::string AsmStmt::getOutputConstraint(unsigned i) const {
   if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(this))
     return gccAsmStmt->getOutputConstraint(i);
   if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(this))
-    return msAsmStmt->getOutputConstraint(i);
+    return msAsmStmt->getOutputConstraint(i).str();
   llvm_unreachable("unknown asm statement kind!");
 }
 
@@ -471,11 +471,11 @@ const Expr *AsmStmt::getOutputExpr(unsigned i) const {
   llvm_unreachable("unknown asm statement kind!");
 }
 
-StringRef AsmStmt::getInputConstraint(unsigned i) const {
+std::string AsmStmt::getInputConstraint(unsigned i) const {
   if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(this))
     return gccAsmStmt->getInputConstraint(i);
   if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(this))
-    return msAsmStmt->getInputConstraint(i);
+    return msAsmStmt->getInputConstraint(i).str();
   llvm_unreachable("unknown asm statement kind!");
 }
 
@@ -487,11 +487,11 @@ const Expr *AsmStmt::getInputExpr(unsigned i) const {
   llvm_unreachable("unknown asm statement kind!");
 }
 
-StringRef AsmStmt::getClobber(unsigned i) const {
+std::string AsmStmt::getClobber(unsigned i) const {
   if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(this))
     return gccAsmStmt->getClobber(i);
   if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(this))
-    return msAsmStmt->getClobber(i);
+    return msAsmStmt->getClobber(i).str();
   llvm_unreachable("unknown asm statement kind!");
 }
 
@@ -510,8 +510,32 @@ char GCCAsmStmt::AsmStringPiece::getModifier() const {
   return isLetter(Str[0]) ? Str[0] : '\0';
 }
 
-StringRef GCCAsmStmt::getClobber(unsigned i) const {
-  return getClobberStringLiteral(i)->getString();
+std::string GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(const Expr *E) {
+  if (auto *SL = llvm::dyn_cast<StringLiteral>(E))
+    return SL->getString().str();
+  assert(E->getDependence() == ExprDependence::None &&
+         "cannot extract a string from a dependent expression");
+  auto *CE = cast<ConstantExpr>(E);
+  APValue Res = CE->getAPValueResult();
+  assert(Res.isArray() && "expected an array");
+
+  std::string Out;
+  Out.reserve(Res.getArraySize());
+  for (unsigned I = 0; I < Res.getArraySize(); ++I) {
+    APValue C = Res.getArrayInitializedElt(I);
+    assert(C.isInt());
+    auto Ch = static_cast<char>(C.getInt().getExtValue());
+    Out.push_back(Ch);
+  }
+  return Out;
+}
+
+std::string GCCAsmStmt::getAsmString() const {
+  return ExtractStringFromGCCAsmStmtComponent(getAsmStringExpr());
+}
+
+std::string GCCAsmStmt::getClobber(unsigned i) const {
+  return ExtractStringFromGCCAsmStmtComponent(getClobberExpr(i));
 }
 
 Expr *GCCAsmStmt::getOutputExpr(unsigned i) {
@@ -521,8 +545,8 @@ Expr *GCCAsmStmt::getOutputExpr(unsigned i) {
 /// getOutputConstraint - Return the constraint string for the specified
 /// output operand.  All output constraints are known to be non-empty (either
 /// '=' or '+').
-StringRef GCCAsmStmt::getOutputConstraint(unsigned i) const {
-  return getOutputConstraintLiteral(i)->getString();
+std::string GCCAsmStmt::getOutputConstraint(unsigned i) const {
+  return ExtractStringFromGCCAsmStmtComponent(getOutputConstraintExpr(i));
 }
 
 Expr *GCCAsmStmt::getInputExpr(unsigned i) {
@@ -543,19 +567,14 @@ StringRef GCCAsmStmt::getLabelName(unsigned i) const {
 
 /// getInputConstraint - Return the specified input constraint.  Unlike output
 /// constraints, these can be empty.
-StringRef GCCAsmStmt::getInputConstraint(unsigned i) const {
-  return getInputConstraintLiteral(i)->getString();
-}
-
-void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C,
-                                                IdentifierInfo **Names,
-                                                StringLiteral **Constraints,
-                                                Stmt **Exprs,
-                                                unsigned NumOutputs,
-                                                unsigned NumInputs,
-                                                unsigned NumLabels,
-                                                StringLiteral **Clobbers,
-                                                unsigned NumClobbers) {
+std::string GCCAsmStmt::getInputConstraint(unsigned i) const {
+  return ExtractStringFromGCCAsmStmtComponent(getInputConstraintExpr(i));
+}
+
+void GCCAsmStmt::setOutputsAndInputsAndClobbers(
+    const ASTContext &C, IdentifierInfo **Names, Expr **Constraints,
+    Stmt **Exprs, unsigned NumOutputs, unsigned NumInputs, unsigned NumLabels,
+    Expr **Clobbers, unsigned NumClobbers) {
   this->NumOutputs = NumOutputs;
   this->NumInputs = NumInputs;
   this->NumClobbers = NumClobbers;
@@ -573,11 +592,11 @@ void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C,
 
   unsigned NumConstraints = NumOutputs + NumInputs;
   C.Deallocate(this->Constraints);
-  this->Constraints = new (C) StringLiteral*[NumConstraints];
+  this->Constraints = new (C) Expr *[NumConstraints];
   std::copy(Constraints, Constraints + NumConstraints, this->Constraints);
 
   C.Deallocate(this->Clobbers);
-  this->Clobbers = new (C) StringLiteral*[NumClobbers];
+  this->Clobbers = new (C) Expr *[NumClobbers];
   std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers);
 }
 
@@ -609,9 +628,10 @@ int GCCAsmStmt::getNamedOperand(StringRef SymbolicName) const {
 /// true, otherwise return false.
 unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces,
                                 const ASTContext &C, unsigned &DiagOffs) const {
-  StringRef Str = getAsmString()->getString();
-  const char *StrStart = Str.begin();
-  const char *StrEnd = Str.end();
+
+  std::string Str = getAsmString();
+  const char *StrStart = Str.data();
+  const char *StrEnd = Str.data() + Str.size();
   const char *CurPtr = StrStart;
 
   // "Simple" inline asms have no constraints or operands, just convert the asm
@@ -739,15 +759,20 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces,
 
       // Str contains "x4" (Operand without the leading %).
       std::string Str(Begin, CurPtr - Begin);
-
       // (BeginLoc, EndLoc) represents the range of the operand we are currently
       // processing. Unlike Str, the range includes the leading '%'.
-      SourceLocation BeginLoc = getAsmString()->getLocationOfByte(
-          Percent - StrStart, SM, LO, TI, &LastAsmStringToken,
-          &LastAsmStringOffset);
-      SourceLocation EndLoc = getAsmString()->getLocationOfByte(
-          CurPtr - StrStart, SM, LO, TI, &LastAsmStringToken,
-          &LastAsmStringOffset);
+      SourceLocation BeginLoc, EndLoc;
+      if (auto *SL = dyn_cast<StringLiteral>(getAsmStringExpr())) {
+        BeginLoc =
+            SL->getLocationOfByte(Percent - StrStart, SM, LO, TI,
+                                  &LastAsmStringToken, &LastAsmStringOffset);
+        EndLoc =
+            SL->getLocationOfByte(CurPtr - StrStart, SM, LO, TI,
+                                  &LastAsmStringToken, &LastAsmStringOffset);
+      } else {
+        BeginLoc = getAsmStringExpr()->getBeginLoc();
+        EndLoc = getAsmStringExpr()->getEndLoc();
+      }
 
       Pieces.emplace_back(N, std::move(Str), BeginLoc, EndLoc);
       continue;
@@ -778,12 +803,18 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces,
 
       // (BeginLoc, EndLoc) represents the range of the operand we are currently
       // processing. Unlike Str, the range includes the leading '%'.
-      SourceLocation BeginLoc = getAsmString()->getLocationOfByte(
-          Percent - StrStart, SM, LO, TI, &LastAsmStringToken,
-          &LastAsmStringOffset);
-      SourceLocation EndLoc = getAsmString()->getLocationOfByte(
-          NameEnd + 1 - StrStart, SM, LO, TI, &LastAsmStringToken,
-          &LastAsmStringOffset);
+      SourceLocation BeginLoc, EndLoc;
+      if (auto *SL = dyn_cast<StringLiteral>(getAsmStringExpr())) {
+        BeginLoc =
+            SL->getLocationOfByte(Percent - StrStart, SM, LO, TI,
+                                  &LastAsmStringToken, &LastAsmStringOffset);
+        EndLoc =
+            SL->getLocationOfByte(NameEnd + 1 - StrStart, SM, LO, TI,
+                                  &LastAsmStringToken, &LastAsmStringOffset);
+      } else {
+        BeginLoc = getAsmStringExpr()->getBeginLoc();
+        EndLoc = getAsmStringExpr()->getEndLoc();
+      }
 
       Pieces.emplace_back(N, std::move(Str), BeginLoc, EndLoc);
 
@@ -863,13 +894,12 @@ void MSAsmStmt::setInputExpr(unsigned i, Expr *E) {
 GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc,
                        bool issimple, bool isvolatile, unsigned numoutputs,
                        unsigned numinputs, IdentifierInfo **names,
-                       StringLiteral **constraints, Expr **exprs,
-                       StringLiteral *asmstr, unsigned numclobbers,
-                       StringLiteral **clobbers, unsigned numlabels,
-                       SourceLocation rparenloc)
+                       Expr **constraints, Expr **exprs, Expr *asmstr,
+                       unsigned numclobbers, Expr **clobbers,
+                       unsigned numlabels, SourceLocation rparenloc)
     : AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs,
               numinputs, numclobbers),
-              RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) {
+      RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) {
   unsigned NumExprs = NumOutputs + NumInputs + NumLabels;
 
   Names = new (C) IdentifierInfo*[NumExprs];
@@ -879,10 +909,10 @@ GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc,
   std::copy(exprs, exprs + NumExprs, Exprs);
 
   unsigned NumConstraints = NumOutputs + NumInputs;
-  Constraints = new (C) StringLiteral*[NumConstraints];
+  Constraints = new (C) Expr *[NumConstraints];
   std::copy(constraints, constraints + NumConstraints, Constraints);
 
-  Clobbers = new (C) StringLiteral*[NumClobbers];
+  Clobbers = new (C) Expr *[NumClobbers];
   std::copy(clobbers, clobbers + NumClobbers, Clobbers);
 }
 

diff  --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index e0063ec5f25eb..fe9784a15b76b 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -507,7 +507,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
     OS << "goto ";
 
   OS << "(";
-  VisitStringLiteral(Node->getAsmString());
+  Visit(Node->getAsmStringExpr());
 
   // Outputs
   if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 ||
@@ -524,7 +524,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
       OS << "] ";
     }
 
-    VisitStringLiteral(Node->getOutputConstraintLiteral(i));
+    Visit(Node->getOutputConstraintExpr(i));
     OS << " (";
     Visit(Node->getOutputExpr(i));
     OS << ")";
@@ -545,7 +545,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
       OS << "] ";
     }
 
-    VisitStringLiteral(Node->getInputConstraintLiteral(i));
+    Visit(Node->getInputConstraintExpr(i));
     OS << " (";
     Visit(Node->getInputExpr(i));
     OS << ")";
@@ -559,7 +559,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) {
     if (i != 0)
       OS << ", ";
 
-    VisitStringLiteral(Node->getClobberStringLiteral(i));
+    Visit(Node->getClobberExpr(i));
   }
 
   // Labels

diff  --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index bf72de3854f4b..96852858b3b9e 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -328,20 +328,20 @@ void StmtProfiler::VisitGCCAsmStmt(const GCCAsmStmt *S) {
   VisitStmt(S);
   ID.AddBoolean(S->isVolatile());
   ID.AddBoolean(S->isSimple());
-  VisitStringLiteral(S->getAsmString());
+  VisitExpr(S->getAsmStringExpr());
   ID.AddInteger(S->getNumOutputs());
   for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) {
     ID.AddString(S->getOutputName(I));
-    VisitStringLiteral(S->getOutputConstraintLiteral(I));
+    VisitExpr(S->getOutputConstraintExpr(I));
   }
   ID.AddInteger(S->getNumInputs());
   for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) {
     ID.AddString(S->getInputName(I));
-    VisitStringLiteral(S->getInputConstraintLiteral(I));
+    VisitExpr(S->getInputConstraintExpr(I));
   }
   ID.AddInteger(S->getNumClobbers());
   for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I)
-    VisitStringLiteral(S->getClobberStringLiteral(I));
+    VisitExpr(S->getClobberExpr(I));
   ID.AddInteger(S->getNumLabels());
   for (auto *L : S->labels())
     VisitDecl(L->getLabel());

diff  --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 99b6f563d7c82..9860f23dc9e28 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -2586,11 +2586,14 @@ static void UpdateAsmCallInst(llvm::CallBase &Result, bool HasSideEffect,
 
   // Slap the source location of the inline asm into a !srcloc metadata on the
   // call.
-  if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S))
-    Result.setMetadata("srcloc",
-                       getAsmSrcLocInfo(gccAsmStmt->getAsmString(), CGF));
-  else {
-    // At least put the line number on MS inline asm blobs.
+  const StringLiteral *SL;
+  if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S);
+      gccAsmStmt &&
+      (SL = dyn_cast<StringLiteral>(gccAsmStmt->getAsmStringExpr()))) {
+    Result.setMetadata("srcloc", getAsmSrcLocInfo(SL, CGF));
+  } else {
+    // At least put the line number on MS inline asm blobs and GCC asm constexpr
+    // strings.
     llvm::Constant *Loc =
         llvm::ConstantInt::get(CGF.Int64Ty, S.getAsmLoc().getRawEncoding());
     Result.setMetadata("srcloc",
@@ -2705,9 +2708,9 @@ static void EmitHipStdParUnsupportedAsm(CodeGenFunction *CGF,
                                         const AsmStmt &S) {
   constexpr auto Name = "__ASM__hipstdpar_unsupported";
 
-  StringRef Asm;
+  std::string Asm;
   if (auto GCCAsm = dyn_cast<GCCAsmStmt>(&S))
-    Asm = GCCAsm->getAsmString()->getString();
+    Asm = GCCAsm->getAsmString();
 
   auto &Ctx = CGF->CGM.getLLVMContext();
 
@@ -3050,7 +3053,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
 
   // Clobbers
   for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) {
-    StringRef Clobber = S.getClobber(i);
+    std::string Clobber = S.getClobber(i);
 
     if (Clobber == "memory")
       ReadOnly = ReadNone = false;
@@ -3071,7 +3074,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
         if (Constraints.find("=&A") != std::string::npos)
           continue;
         std::string::size_type position1 =
-            Constraints.find("={" + Clobber.str() + "}");
+            Constraints.find("={" + Clobber + "}");
         if (position1 != std::string::npos) {
           Constraints.insert(position1 + 1, "&");
           continue;

diff  --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp
index 04c3a8700c10c..e32094bdd99b8 100644
--- a/clang/lib/Parse/ParseStmtAsm.cpp
+++ b/clang/lib/Parse/ParseStmtAsm.cpp
@@ -809,7 +809,7 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
       ConsumeToken();
     }
     // Parse the asm-string list for clobbers if present.
-    if (!AteExtraColon && isTokenStringLiteral()) {
+    if (!AteExtraColon && (isTokenStringLiteral() || Tok.is(tok::l_paren))) {
       while (true) {
         ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
 
@@ -884,8 +884,8 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) {
 bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names,
                                  SmallVectorImpl<Expr *> &Constraints,
                                  SmallVectorImpl<Expr *> &Exprs) {
-  // 'asm-operands' isn't present?
-  if (!isTokenStringLiteral() && Tok.isNot(tok::l_square))
+  // 'asm-operands' isn't present
+  if (Tok.isOneOf(tok::colon, tok::coloncolon, tok::r_paren))
     return false;
 
   while (true) {

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 0710542f5e938..83dd7b17c73b8 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -19,6 +19,7 @@
 #include "clang/Basic/FileManager.h"
 #include "clang/Parse/RAIIObjectsForParser.h"
 #include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/SemaCodeCompletion.h"
@@ -1668,28 +1669,40 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) {
 ///         string-literal
 ///
 ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) {
-  if (!isTokenStringLiteral()) {
-    Diag(Tok, diag::err_expected_string_literal)
-      << /*Source='in...'*/0 << "'asm'";
-    return ExprError();
-  }
 
-  ExprResult AsmString(ParseStringLiteralExpression());
-  if (!AsmString.isInvalid()) {
+  ExprResult AsmString;
+  if (isTokenStringLiteral()) {
+    AsmString = ParseStringLiteralExpression();
+    if (AsmString.isInvalid())
+      return AsmString;
+
     const auto *SL = cast<StringLiteral>(AsmString.get());
     if (!SL->isOrdinary()) {
       Diag(Tok, diag::err_asm_operand_wide_string_literal)
-        << SL->isWide()
-        << SL->getSourceRange();
+          << SL->isWide() << SL->getSourceRange();
       return ExprError();
     }
-    if (ForAsmLabel && SL->getString().empty()) {
-      Diag(Tok, diag::err_asm_operand_wide_string_literal)
-          << 2 /* an empty */ << SL->getSourceRange();
+  } else if (!ForAsmLabel && getLangOpts().CPlusPlus11 &&
+             Tok.is(tok::l_paren)) {
+    ParenParseOption ExprType = SimpleExpr;
+    SourceLocation RParenLoc;
+    ParsedType CastTy;
+
+    EnterExpressionEvaluationContext ConstantEvaluated(
+        Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+    AsmString = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false,
+                                     CastTy, RParenLoc);
+    if (!AsmString.isInvalid())
+      AsmString = Actions.ActOnConstantExpression(AsmString);
+
+    if (AsmString.isInvalid())
       return ExprError();
-    }
+  } else {
+    Diag(Tok, diag::err_asm_expected_string) << /*and expression=*/(
+        (getLangOpts().CPlusPlus11 && !ForAsmLabel) ? 0 : 1);
   }
-  return AsmString;
+
+  return Actions.ActOnGCCAsmStmtString(AsmString.get(), ForAsmLabel);
 }
 
 /// ParseSimpleAsm

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 8850161ab01c7..20533961a2217 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17289,17 +17289,34 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
   }
 }
 
-bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,
-                                               std::string &Result,
-                                               ASTContext &Ctx,
-                                               bool ErrorOnInvalidMessage) {
+template <typename ResultType>
+static bool EvaluateAsStringImpl(Sema &SemaRef, Expr *Message,
+                                 ResultType &Result, ASTContext &Ctx,
+                                 Sema::StringEvaluationContext EvalContext,
+                                 bool ErrorOnInvalidMessage) {
+
   assert(Message);
   assert(!Message->isTypeDependent() && !Message->isValueDependent() &&
          "can't evaluate a dependant static assert message");
 
   if (const auto *SL = dyn_cast<StringLiteral>(Message)) {
     assert(SL->isUnevaluated() && "expected an unevaluated string");
-    Result.assign(SL->getString().begin(), SL->getString().end());
+    if constexpr (std::is_same_v<APValue, ResultType>) {
+      Result =
+          APValue(APValue::UninitArray{}, SL->getLength(), SL->getLength());
+      const ConstantArrayType *CAT =
+          SemaRef.getASTContext().getAsConstantArrayType(SL->getType());
+      assert(CAT && "string literal isn't an array");
+      QualType CharType = CAT->getElementType();
+      llvm::APSInt Value(SemaRef.getASTContext().getTypeSize(CharType),
+                         CharType->isUnsignedIntegerType());
+      for (unsigned I = 0; I < SL->getLength(); I++) {
+        Value = SL->getCodeUnit(I);
+        Result.getArrayInitializedElt(I) = APValue(Value);
+      }
+    } else {
+      Result.assign(SL->getString().begin(), SL->getString().end());
+    }
     return true;
   }
 
@@ -17307,16 +17324,14 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,
   QualType T = Message->getType().getNonReferenceType();
   auto *RD = T->getAsCXXRecordDecl();
   if (!RD) {
-    Diag(Loc, diag::err_static_assert_invalid_message);
+    SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid) << EvalContext;
     return false;
   }
 
-  auto FindMember = [&](StringRef Member, bool &Empty,
-                        bool Diag = false) -> std::optional<LookupResult> {
-    DeclarationName DN = PP.getIdentifierInfo(Member);
-    LookupResult MemberLookup(*this, DN, Loc, Sema::LookupMemberName);
-    LookupQualifiedName(MemberLookup, RD);
-    Empty = MemberLookup.empty();
+  auto FindMember = [&](StringRef Member) -> std::optional<LookupResult> {
+    DeclarationName DN = SemaRef.PP.getIdentifierInfo(Member);
+    LookupResult MemberLookup(SemaRef, DN, Loc, Sema::LookupMemberName);
+    SemaRef.LookupQualifiedName(MemberLookup, RD);
     OverloadCandidateSet Candidates(MemberLookup.getNameLoc(),
                                     OverloadCandidateSet::CSK_Normal);
     if (MemberLookup.empty())
@@ -17324,67 +17339,63 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,
     return std::move(MemberLookup);
   };
 
-  bool SizeNotFound, DataNotFound;
-  std::optional<LookupResult> SizeMember = FindMember("size", SizeNotFound);
-  std::optional<LookupResult> DataMember = FindMember("data", DataNotFound);
-  if (SizeNotFound || DataNotFound) {
-    Diag(Loc, diag::err_static_assert_missing_member_function)
-        << ((SizeNotFound && DataNotFound) ? 2
-            : SizeNotFound                 ? 0
-                                           : 1);
-    return false;
-  }
-
+  std::optional<LookupResult> SizeMember = FindMember("size");
+  std::optional<LookupResult> DataMember = FindMember("data");
   if (!SizeMember || !DataMember) {
-    if (!SizeMember)
-      FindMember("size", SizeNotFound, /*Diag=*/true);
-    if (!DataMember)
-      FindMember("data", DataNotFound, /*Diag=*/true);
+    SemaRef.Diag(Loc, diag::err_user_defined_msg_missing_member_function)
+        << EvalContext
+        << ((!SizeMember && !DataMember) ? 2
+            : !SizeMember                ? 0
+                                         : 1);
     return false;
   }
 
   auto BuildExpr = [&](LookupResult &LR) {
-    ExprResult Res = BuildMemberReferenceExpr(
+    ExprResult Res = SemaRef.BuildMemberReferenceExpr(
         Message, Message->getType(), Message->getBeginLoc(), false,
         CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr);
     if (Res.isInvalid())
       return ExprError();
-    Res = BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, false, true);
+    Res = SemaRef.BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr,
+                                false, true);
     if (Res.isInvalid())
       return ExprError();
     if (Res.get()->isTypeDependent() || Res.get()->isValueDependent())
       return ExprError();
-    return TemporaryMaterializationConversion(Res.get());
+    return SemaRef.TemporaryMaterializationConversion(Res.get());
   };
 
   ExprResult SizeE = BuildExpr(*SizeMember);
   ExprResult DataE = BuildExpr(*DataMember);
 
-  QualType SizeT = Context.getSizeType();
-  QualType ConstCharPtr =
-      Context.getPointerType(Context.getConstType(Context.CharTy));
+  QualType SizeT = SemaRef.Context.getSizeType();
+  QualType ConstCharPtr = SemaRef.Context.getPointerType(
+      SemaRef.Context.getConstType(SemaRef.Context.CharTy));
 
   ExprResult EvaluatedSize =
-      SizeE.isInvalid() ? ExprError()
-                        : BuildConvertedConstantExpression(
-                              SizeE.get(), SizeT, CCEK_StaticAssertMessageSize);
+      SizeE.isInvalid()
+          ? ExprError()
+          : SemaRef.BuildConvertedConstantExpression(
+                SizeE.get(), SizeT, Sema::CCEK_StaticAssertMessageSize);
   if (EvaluatedSize.isInvalid()) {
-    Diag(Loc, diag::err_static_assert_invalid_mem_fn_ret_ty) << /*size*/ 0;
+    SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty)
+        << EvalContext << /*size*/ 0;
     return false;
   }
 
   ExprResult EvaluatedData =
       DataE.isInvalid()
           ? ExprError()
-          : BuildConvertedConstantExpression(DataE.get(), ConstCharPtr,
-                                             CCEK_StaticAssertMessageData);
+          : SemaRef.BuildConvertedConstantExpression(
+                DataE.get(), ConstCharPtr, Sema::CCEK_StaticAssertMessageData);
   if (EvaluatedData.isInvalid()) {
-    Diag(Loc, diag::err_static_assert_invalid_mem_fn_ret_ty) << /*data*/ 1;
+    SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty)
+        << EvalContext << /*data*/ 1;
     return false;
   }
 
   if (!ErrorOnInvalidMessage &&
-      Diags.isIgnored(diag::warn_static_assert_message_constexpr, Loc))
+      SemaRef.Diags.isIgnored(diag::warn_user_defined_msg_constexpr, Loc))
     return true;
 
   Expr::EvalResult Status;
@@ -17393,16 +17404,31 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,
   if (!Message->EvaluateCharRangeAsString(Result, EvaluatedSize.get(),
                                           EvaluatedData.get(), Ctx, Status) ||
       !Notes.empty()) {
-    Diag(Message->getBeginLoc(),
-         ErrorOnInvalidMessage ? diag::err_static_assert_message_constexpr
-                               : diag::warn_static_assert_message_constexpr);
+    SemaRef.Diag(Message->getBeginLoc(),
+                 ErrorOnInvalidMessage ? diag::err_user_defined_msg_constexpr
+                                       : diag::warn_user_defined_msg_constexpr)
+        << EvalContext;
     for (const auto &Note : Notes)
-      Diag(Note.first, Note.second);
+      SemaRef.Diag(Note.first, Note.second);
     return !ErrorOnInvalidMessage;
   }
   return true;
 }
 
+bool Sema::EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx,
+                            StringEvaluationContext EvalContext,
+                            bool ErrorOnInvalidMessage) {
+  return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext,
+                              ErrorOnInvalidMessage);
+}
+
+bool Sema::EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx,
+                            StringEvaluationContext EvalContext,
+                            bool ErrorOnInvalidMessage) {
+  return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext,
+                              ErrorOnInvalidMessage);
+}
+
 Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
                                          Expr *AssertExpr, Expr *AssertMessage,
                                          SourceLocation RParenLoc,
@@ -17448,8 +17474,9 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
     // the message is grammatically valid without evaluating it.
     if (!Failed && AssertMessage && Cond.getBoolValue()) {
       std::string Str;
-      EvaluateStaticAssertMessageAsString(AssertMessage, Str, Context,
-                                          /*ErrorOnInvalidMessage=*/false);
+      EvaluateAsString(AssertMessage, Str, Context,
+                       StringEvaluationContext::StaticAssert,
+                       /*ErrorOnInvalidMessage=*/false);
     }
 
     // CWG2518
@@ -17464,10 +17491,10 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
       bool HasMessage = AssertMessage;
       if (AssertMessage) {
         std::string Str;
-        HasMessage =
-            EvaluateStaticAssertMessageAsString(
-                AssertMessage, Str, Context, /*ErrorOnInvalidMessage=*/true) ||
-            !Str.empty();
+        HasMessage = EvaluateAsString(AssertMessage, Str, Context,
+                                      StringEvaluationContext::StaticAssert,
+                                      /*ErrorOnInvalidMessage=*/true) ||
+                     !Str.empty();
         Msg << Str;
       }
       Expr *InnerCond = nullptr;

diff  --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp
index a0b203fbdfec2..4507a21a4c111 100644
--- a/clang/lib/Sema/SemaStmtAsm.cpp
+++ b/clang/lib/Sema/SemaStmtAsm.cpp
@@ -17,6 +17,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Sema/Ownership.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -203,15 +204,15 @@ static StringRef extractRegisterName(const Expr *Expression,
 // clobbers list. If there's a conflict, returns the location of the
 // conflicted clobber, else returns nullptr
 static SourceLocation
-getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints,
-                           StringLiteral **Clobbers, int NumClobbers,
-                           unsigned NumLabels,
+getClobberConflictLocation(MultiExprArg Exprs, Expr **Constraints,
+                           Expr **Clobbers, int NumClobbers, unsigned NumLabels,
                            const TargetInfo &Target, ASTContext &Cont) {
   llvm::StringSet<> InOutVars;
   // Collect all the input and output registers from the extended asm
   // statement in order to check for conflicts with the clobber list
   for (unsigned int i = 0; i < Exprs.size() - NumLabels; ++i) {
-    StringRef Constraint = Constraints[i]->getString();
+    std::string Constraint =
+        GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraints[i]);
     StringRef InOutReg = Target.getConstraintRegister(
         Constraint, extractRegisterName(Exprs[i], Target));
     if (InOutReg != "")
@@ -220,7 +221,8 @@ getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints,
   // Check for each item in the clobber list if it conflicts with the input
   // or output
   for (int i = 0; i < NumClobbers; ++i) {
-    StringRef Clobber = Clobbers[i]->getString();
+    std::string Clobber =
+        GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Clobbers[i]);
     // We only check registers, therefore we don't check cc and memory
     // clobbers
     if (Clobber == "cc" || Clobber == "memory" || Clobber == "unwind")
@@ -233,6 +235,37 @@ getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints,
   return SourceLocation();
 }
 
+ExprResult Sema::ActOnGCCAsmStmtString(Expr *Expr, bool ForAsmLabel) {
+  if (!Expr)
+    return ExprError();
+
+  if (auto *SL = dyn_cast<StringLiteral>(Expr)) {
+    assert(SL->isOrdinary());
+    if (ForAsmLabel && SL->getString().empty()) {
+      Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string)
+          << SL->getSourceRange();
+    }
+    return SL;
+  }
+  if (DiagnoseUnexpandedParameterPack(Expr))
+    return ExprError();
+  if (Expr->getDependence() != ExprDependence::None)
+    return Expr;
+  APValue V;
+  if (!EvaluateAsString(Expr, V, getASTContext(), StringEvaluationContext::Asm,
+                        /*ErrorOnInvalid=*/true))
+    return ExprError();
+
+  if (ForAsmLabel && V.getArrayInitializedElts() == 0) {
+    Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string);
+  }
+
+  ConstantExpr *Res = ConstantExpr::Create(getASTContext(), Expr,
+                                           ConstantResultStorageKind::APValue);
+  Res->SetResult(V, getASTContext());
+  return Res;
+}
+
 StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
                                  bool IsVolatile, unsigned NumOutputs,
                                  unsigned NumInputs, IdentifierInfo **Names,
@@ -241,38 +274,45 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
                                  unsigned NumLabels,
                                  SourceLocation RParenLoc) {
   unsigned NumClobbers = clobbers.size();
-  StringLiteral **Constraints =
-    reinterpret_cast<StringLiteral**>(constraints.data());
-  StringLiteral *AsmString = cast<StringLiteral>(asmString);
-  StringLiteral **Clobbers = reinterpret_cast<StringLiteral**>(clobbers.data());
 
   SmallVector<TargetInfo::ConstraintInfo, 4> OutputConstraintInfos;
 
-  // The parser verifies that there is a string literal here.
-  assert(AsmString->isOrdinary());
-
   FunctionDecl *FD = dyn_cast<FunctionDecl>(getCurLexicalContext());
   llvm::StringMap<bool> FeatureMap;
   Context.getFunctionFeatureMap(FeatureMap, FD);
 
-  for (unsigned i = 0; i != NumOutputs; i++) {
-    StringLiteral *Literal = Constraints[i];
-    assert(Literal->isOrdinary());
+  auto CreateGCCAsmStmt = [&] {
+    return new (Context)
+        GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs,
+                   Names, constraints.data(), Exprs.data(), asmString,
+                   NumClobbers, clobbers.data(), NumLabels, RParenLoc);
+  };
+
+  if (asmString->getDependence() != ExprDependence::None ||
+      llvm::any_of(
+          constraints,
+          [](Expr *E) { return E->getDependence() != ExprDependence::None; }) ||
+      llvm::any_of(clobbers, [](Expr *E) {
+        return E->getDependence() != ExprDependence::None;
+      }))
+    return CreateGCCAsmStmt();
 
+  for (unsigned i = 0; i != NumOutputs; i++) {
+    Expr *Constraint = constraints[i];
     StringRef OutputName;
     if (Names[i])
       OutputName = Names[i]->getName();
 
-    TargetInfo::ConstraintInfo Info(Literal->getString(), OutputName);
+    std::string ConstraintStr =
+        GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint);
+
+    TargetInfo::ConstraintInfo Info(ConstraintStr, OutputName);
     if (!Context.getTargetInfo().validateOutputConstraint(Info) &&
         !(LangOpts.HIPStdPar && LangOpts.CUDAIsDevice)) {
-      targetDiag(Literal->getBeginLoc(),
+      targetDiag(Constraint->getBeginLoc(),
                  diag::err_asm_invalid_output_constraint)
           << Info.getConstraintStr();
-      return new (Context)
-          GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
-                     NumInputs, Names, Constraints, Exprs.data(), AsmString,
-                     NumClobbers, Clobbers, NumLabels, RParenLoc);
+      return CreateGCCAsmStmt();
     }
 
     ExprResult ER = CheckPlaceholderExpr(Exprs[i]);
@@ -335,35 +375,34 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
 
     unsigned Size = Context.getTypeSize(OutputExpr->getType());
     if (!Context.getTargetInfo().validateOutputSize(
-            FeatureMap, Literal->getString(), Size)) {
+            FeatureMap,
+            GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint),
+            Size)) {
       targetDiag(OutputExpr->getBeginLoc(), diag::err_asm_invalid_output_size)
           << Info.getConstraintStr();
-      return new (Context)
-          GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
-                     NumInputs, Names, Constraints, Exprs.data(), AsmString,
-                     NumClobbers, Clobbers, NumLabels, RParenLoc);
+      return CreateGCCAsmStmt();
     }
   }
 
   SmallVector<TargetInfo::ConstraintInfo, 4> InputConstraintInfos;
 
   for (unsigned i = NumOutputs, e = NumOutputs + NumInputs; i != e; i++) {
-    StringLiteral *Literal = Constraints[i];
-    assert(Literal->isOrdinary());
+    Expr *Constraint = constraints[i];
 
     StringRef InputName;
     if (Names[i])
       InputName = Names[i]->getName();
 
-    TargetInfo::ConstraintInfo Info(Literal->getString(), InputName);
+    std::string ConstraintStr =
+        GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint);
+
+    TargetInfo::ConstraintInfo Info(ConstraintStr, InputName);
     if (!Context.getTargetInfo().validateInputConstraint(OutputConstraintInfos,
                                                          Info)) {
-      targetDiag(Literal->getBeginLoc(), diag::err_asm_invalid_input_constraint)
+      targetDiag(Constraint->getBeginLoc(),
+                 diag::err_asm_invalid_input_constraint)
           << Info.getConstraintStr();
-      return new (Context)
-          GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
-                     NumInputs, Names, Constraints, Exprs.data(), AsmString,
-                     NumClobbers, Clobbers, NumLabels, RParenLoc);
+      return CreateGCCAsmStmt();
     }
 
     ExprResult ER = CheckPlaceholderExpr(Exprs[i]);
@@ -448,8 +487,8 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
         return StmtError();
 
     unsigned Size = Context.getTypeSize(Ty);
-    if (!Context.getTargetInfo().validateInputSize(FeatureMap,
-                                                   Literal->getString(), Size))
+    if (!Context.getTargetInfo().validateInputSize(FeatureMap, ConstraintStr,
+                                                   Size))
       return targetDiag(InputExpr->getBeginLoc(),
                         diag::err_asm_invalid_input_size)
              << Info.getConstraintStr();
@@ -459,46 +498,47 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
 
   // Check that the clobbers are valid.
   for (unsigned i = 0; i != NumClobbers; i++) {
-    StringLiteral *Literal = Clobbers[i];
-    assert(Literal->isOrdinary());
+    Expr *ClobberExpr = clobbers[i];
 
-    StringRef Clobber = Literal->getString();
+    std::string Clobber =
+        GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(ClobberExpr);
 
     if (!Context.getTargetInfo().isValidClobber(Clobber)) {
-      targetDiag(Literal->getBeginLoc(), diag::err_asm_unknown_register_name)
+      targetDiag(ClobberExpr->getBeginLoc(),
+                 diag::err_asm_unknown_register_name)
           << Clobber;
-      return new (Context)
-          GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
-                     NumInputs, Names, Constraints, Exprs.data(), AsmString,
-                     NumClobbers, Clobbers, NumLabels, RParenLoc);
+      return new (Context) GCCAsmStmt(
+          Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs, Names,
+          constraints.data(), Exprs.data(), asmString, NumClobbers,
+          clobbers.data(), NumLabels, RParenLoc);
     }
 
     if (Clobber == "unwind") {
-      UnwindClobberLoc = Literal->getBeginLoc();
+      UnwindClobberLoc = ClobberExpr->getBeginLoc();
     }
   }
 
   // Using unwind clobber and asm-goto together is not supported right now.
   if (UnwindClobberLoc && NumLabels > 0) {
     targetDiag(*UnwindClobberLoc, diag::err_asm_unwind_and_goto);
-    return new (Context)
-        GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs,
-                   Names, Constraints, Exprs.data(), AsmString, NumClobbers,
-                   Clobbers, NumLabels, RParenLoc);
+    return CreateGCCAsmStmt();
   }
 
-  GCCAsmStmt *NS =
-    new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs,
-                             NumInputs, Names, Constraints, Exprs.data(),
-                             AsmString, NumClobbers, Clobbers, NumLabels,
-                             RParenLoc);
+  GCCAsmStmt *NS = CreateGCCAsmStmt();
   // Validate the asm string, ensuring it makes sense given the operands we
   // have.
+
+  auto GetLocation = [this](const Expr *Str, unsigned Offset) {
+    if (auto *SL = dyn_cast<StringLiteral>(Str))
+      return getLocationOfStringLiteralByte(SL, Offset);
+    return Str->getBeginLoc();
+  };
+
   SmallVector<GCCAsmStmt::AsmStringPiece, 8> Pieces;
   unsigned DiagOffs;
   if (unsigned DiagID = NS->AnalyzeAsmString(Pieces, Context, DiagOffs)) {
-    targetDiag(getLocationOfStringLiteralByte(AsmString, DiagOffs), DiagID)
-        << AsmString->getSourceRange();
+    targetDiag(GetLocation(asmString, DiagOffs), DiagID)
+        << asmString->getSourceRange();
     return NS;
   }
 
@@ -529,7 +569,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
     }
 
     // Now that we have the right indexes go ahead and check.
-    StringLiteral *Literal = Constraints[ConstraintIdx];
+    Expr *Constraint = constraints[ConstraintIdx];
     const Type *Ty = Exprs[ConstraintIdx]->getType().getTypePtr();
     if (Ty->isDependentType() || Ty->isIncompleteType())
       continue;
@@ -537,8 +577,8 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
     unsigned Size = Context.getTypeSize(Ty);
     std::string SuggestedModifier;
     if (!Context.getTargetInfo().validateConstraintModifier(
-            Literal->getString(), Piece.getModifier(), Size,
-            SuggestedModifier)) {
+            GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint),
+            Piece.getModifier(), Size, SuggestedModifier)) {
       targetDiag(Exprs[ConstraintIdx]->getBeginLoc(),
                  diag::warn_asm_mismatched_size_modifier);
 
@@ -546,8 +586,11 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
         auto B = targetDiag(Piece.getRange().getBegin(),
                             diag::note_asm_missing_constraint_modifier)
                  << SuggestedModifier;
-        SuggestedModifier = "%" + SuggestedModifier + Piece.getString();
-        B << FixItHint::CreateReplacement(Piece.getRange(), SuggestedModifier);
+        if (isa<StringLiteral>(Constraint)) {
+          SuggestedModifier = "%" + SuggestedModifier + Piece.getString();
+          B << FixItHint::CreateReplacement(Piece.getRange(),
+                                            SuggestedModifier);
+        }
       }
     }
   }
@@ -707,10 +750,9 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
   }
 
   // Check for conflicts between clobber list and input or output lists
-  SourceLocation ConstraintLoc =
-      getClobberConflictLocation(Exprs, Constraints, Clobbers, NumClobbers,
-                                 NumLabels,
-                                 Context.getTargetInfo(), Context);
+  SourceLocation ConstraintLoc = getClobberConflictLocation(
+      Exprs, constraints.data(), clobbers.data(), NumClobbers, NumLabels,
+      Context.getTargetInfo(), Context);
   if (ConstraintLoc.isValid())
     targetDiag(ConstraintLoc, diag::error_inoutput_conflict_with_clobber);
 

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index a09f623ff8fbf..b5de98e3989ea 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -8556,21 +8556,34 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
   SmallVector<Expr*, 8> Exprs;
   SmallVector<IdentifierInfo *, 4> Names;
 
-  ExprResult AsmString;
   SmallVector<Expr*, 8> Clobbers;
 
   bool ExprsChanged = false;
 
+  auto RebuildString = [&](Expr *E) {
+    ExprResult Result = getDerived().TransformExpr(E);
+    if (!Result.isUsable())
+      return Result;
+    if (Result.get() != E) {
+      ExprsChanged = true;
+      Result = SemaRef.ActOnGCCAsmStmtString(Result.get(), /*ForLabel=*/false);
+    }
+    return Result;
+  };
+
   // Go through the outputs.
   for (unsigned I = 0, E = S->getNumOutputs(); I != E; ++I) {
     Names.push_back(S->getOutputIdentifier(I));
 
-    // No need to transform the constraint literal.
-    Constraints.push_back(S->getOutputConstraintLiteral(I));
+    ExprResult Result = RebuildString(S->getOutputConstraintExpr(I));
+    if (Result.isInvalid())
+      return StmtError();
+
+    Constraints.push_back(Result.get());
 
     // Transform the output expr.
     Expr *OutputExpr = S->getOutputExpr(I);
-    ExprResult Result = getDerived().TransformExpr(OutputExpr);
+    Result = getDerived().TransformExpr(OutputExpr);
     if (Result.isInvalid())
       return StmtError();
 
@@ -8583,12 +8596,15 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
   for (unsigned I = 0, E = S->getNumInputs(); I != E; ++I) {
     Names.push_back(S->getInputIdentifier(I));
 
-    // No need to transform the constraint literal.
-    Constraints.push_back(S->getInputConstraintLiteral(I));
+    ExprResult Result = RebuildString(S->getInputConstraintExpr(I));
+    if (Result.isInvalid())
+      return StmtError();
+
+    Constraints.push_back(Result.get());
 
     // Transform the input expr.
     Expr *InputExpr = S->getInputExpr(I);
-    ExprResult Result = getDerived().TransformExpr(InputExpr);
+    Result = getDerived().TransformExpr(InputExpr);
     if (Result.isInvalid())
       return StmtError();
 
@@ -8607,15 +8623,22 @@ TreeTransform<Derived>::TransformGCCAsmStmt(GCCAsmStmt *S) {
     ExprsChanged |= Result.get() != S->getLabelExpr(I);
     Exprs.push_back(Result.get());
   }
-  if (!getDerived().AlwaysRebuild() && !ExprsChanged)
-    return S;
 
   // Go through the clobbers.
-  for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I)
-    Clobbers.push_back(S->getClobberStringLiteral(I));
+  for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I) {
+    ExprResult Result = RebuildString(S->getClobberExpr(I));
+    if (Result.isInvalid())
+      return StmtError();
+    Clobbers.push_back(Result.get());
+  }
+
+  ExprResult AsmString = RebuildString(S->getAsmStringExpr());
+  if (AsmString.isInvalid())
+    return StmtError();
+
+  if (!getDerived().AlwaysRebuild() && !ExprsChanged)
+    return S;
 
-  // No need to transform the asm string literal.
-  AsmString = S->getAsmString();
   return getDerived().RebuildGCCAsmStmt(S->getAsmLoc(), S->isSimple(),
                                         S->isVolatile(), S->getNumOutputs(),
                                         S->getNumInputs(), Names.data(),

diff  --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 48f9f89bd6e4c..1e13bcfe60b47 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -381,7 +381,7 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) {
   VisitAsmStmt(S);
   S->NumLabels = Record.readInt();
   S->setRParenLoc(readSourceLocation());
-  S->setAsmString(cast_or_null<StringLiteral>(Record.readSubStmt()));
+  S->setAsmStringExpr(cast_or_null<Expr>(Record.readSubStmt()));
 
   unsigned NumOutputs = S->getNumOutputs();
   unsigned NumInputs = S->getNumInputs();
@@ -390,18 +390,18 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) {
 
   // Outputs and inputs
   SmallVector<IdentifierInfo *, 16> Names;
-  SmallVector<StringLiteral*, 16> Constraints;
+  SmallVector<Expr *, 16> Constraints;
   SmallVector<Stmt*, 16> Exprs;
   for (unsigned I = 0, N = NumOutputs + NumInputs; I != N; ++I) {
     Names.push_back(Record.readIdentifier());
-    Constraints.push_back(cast_or_null<StringLiteral>(Record.readSubStmt()));
+    Constraints.push_back(cast_or_null<Expr>(Record.readSubStmt()));
     Exprs.push_back(Record.readSubStmt());
   }
 
   // Constraints
-  SmallVector<StringLiteral*, 16> Clobbers;
+  SmallVector<Expr *, 16> Clobbers;
   for (unsigned I = 0; I != NumClobbers; ++I)
-    Clobbers.push_back(cast_or_null<StringLiteral>(Record.readSubStmt()));
+    Clobbers.push_back(cast_or_null<Expr>(Record.readSubStmt()));
 
   // Labels
   for (unsigned I = 0, N = NumLabels; I != N; ++I) {

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index aa5a7854394a0..32211ced4ff15 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -361,25 +361,25 @@ void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) {
   VisitAsmStmt(S);
   Record.push_back(S->getNumLabels());
   Record.AddSourceLocation(S->getRParenLoc());
-  Record.AddStmt(S->getAsmString());
+  Record.AddStmt(S->getAsmStringExpr());
 
   // Outputs
   for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) {
     Record.AddIdentifierRef(S->getOutputIdentifier(I));
-    Record.AddStmt(S->getOutputConstraintLiteral(I));
+    Record.AddStmt(S->getOutputConstraintExpr(I));
     Record.AddStmt(S->getOutputExpr(I));
   }
 
   // Inputs
   for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) {
     Record.AddIdentifierRef(S->getInputIdentifier(I));
-    Record.AddStmt(S->getInputConstraintLiteral(I));
+    Record.AddStmt(S->getInputConstraintExpr(I));
     Record.AddStmt(S->getInputExpr(I));
   }
 
   // Clobbers
   for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I)
-    Record.AddStmt(S->getClobberStringLiteral(I));
+    Record.AddStmt(S->getClobberExpr(I));
 
   // Labels
   for (unsigned I = 0, N = S->getNumLabels(); I != N; ++I) {

diff  --git a/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp
new file mode 100644
index 0000000000000..d9c633ea69283
--- /dev/null
+++ b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp
@@ -0,0 +1,52 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -triple x86_64 %s -S -o /dev/null -Werror -verify
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
+
+struct string_view {
+    int S;
+    const char* D;
+    constexpr string_view() : S(0), D(0){}
+    constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
+    constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
+    constexpr int size() const {
+        return S;
+    }
+    constexpr const char* data() const {
+        return D;
+    }
+};
+
+int func() {return 0;};
+
+void f() {
+
+    asm((string_view("")) ::(string_view("r"))(func()));
+    // CHECK: %[[CALL:.*]] = call noundef i32 @_Z4funcv
+    // CHECK: call void asm sideeffect "", "r,~{dirflag},~{fpsr},~{flags}"
+    asm("" :::(string_view("memory")));
+    // CHECK: call void asm sideeffect "", "~{memory},~{dirflag},~{fpsr},~{flags}"
+}
+
+void foo(unsigned long long addr, unsigned long long a0) {
+    register unsigned long long result asm("rax");
+    register unsigned long long b0 asm("rdi");
+
+    b0 = a0;
+
+    asm((string_view("call *%1")) : (string_view("=r")) (result)
+        : (string_view("r"))(addr), (string_view("r")) (b0) : (string_view("memory")));
+
+    // CHECK:{{.*}} call i64 asm "call *$1", "={rax},r,{rdi},~{memory},~{dirflag},~{fpsr},~{flags}"
+}
+
+
+void test_srcloc() {
+    asm((string_view( // expected-error {{invalid instruction mnemonic 'nonsense'}} \
+                      // expected-error {{invalid instruction mnemonic 'foobar'}} \
+                      // expected-note at 1 {{instantiated into assembly here}} \
+                      // expected-note at 2 {{instantiated into assembly here}}
+        R"o(nonsense
+        foobar)o")
+
+    ) ::(string_view("r"))(func()));
+  }

diff  --git a/clang/test/Parser/asm.cpp b/clang/test/Parser/asm.cpp
index de156877ec470..4d590992f6b09 100644
--- a/clang/test/Parser/asm.cpp
+++ b/clang/test/Parser/asm.cpp
@@ -12,3 +12,48 @@ void f() {
   [[]] asm("");
   [[gnu::deprecated]] asm(""); // expected-warning {{'deprecated' attribute ignored}}
 }
+
+
+#if !__has_extension(gnu_asm_constexpr_strings)
+#error Extension 'gnu_asm_constexpr_strings' should be available by default
+#endif
+
+struct string_view {
+  int S;
+  const char* D;
+  constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
+  constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
+  constexpr int size() const {
+      return S;
+  }
+  constexpr const char* data() const {
+      return D;
+  }
+};
+
+// Neither gcc nor clang support expressions in label
+int foo1 asm ((string_view("test"))); // expected-error {{expected string literal in 'asm'}}
+int func() asm ((string_view("test"))); // expected-error {{expected string literal in 'asm'}}
+
+
+void f2() {
+  asm(string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
+  asm("" : string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
+  asm("" : : string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
+  asm("" : : : string_view("")); // expected-error {{expected ')'}}
+  asm("" :: string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
+  asm(::string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}}
+
+  int i;
+
+  asm((string_view("")));
+  asm((::string_view("")));
+  asm("" : (::string_view("+g")) (i));
+  asm("" : (::string_view("+g"))); // expected-error {{expected '(' after 'asm operand'}}
+  asm("" : (::string_view("+g")) (i) : (::string_view("g")) (0));
+  asm("" : (::string_view("+g")) (i) : (::string_view("g"))); // expected-error {{expected '(' after 'asm operand'}}
+  asm("" : (::string_view("+g")) (i) : (::string_view("g")) (0) : (string_view("memory")));
+
+
+  asm((0)); // expected-error {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}}
+}

diff  --git a/clang/test/SemaCXX/gnu-asm-constexpr.cpp b/clang/test/SemaCXX/gnu-asm-constexpr.cpp
new file mode 100644
index 0000000000000..8813b873fab0c
--- /dev/null
+++ b/clang/test/SemaCXX/gnu-asm-constexpr.cpp
@@ -0,0 +1,127 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -triple x86_64-gnu-linux
+
+template <bool Leak>
+struct RAIIBase {
+    constexpr RAIIBase(const char* in) {
+        s = __builtin_strlen(in);
+        d = new char[s + 1]; // expected-note 4{{allocation performed here was not deallocated}}
+        for(int i = 0 ; i < s; i++)
+            d[i] = in[i];
+    }
+    int s;
+    char* d;
+    constexpr unsigned long size() const {
+        return s;
+    }
+    constexpr const char* data() const {
+        return d;
+    }
+    constexpr ~RAIIBase() {
+        if constexpr(!Leak)
+            delete[] d;
+    }
+};
+
+using RAII = RAIIBase<false>;
+using RAIILeak = RAIIBase<true>;
+
+void test_leaks(int i) {
+    asm((RAII("nop")) : (RAII("+ir")) (i) : (RAII("g")) (i) : (RAII("memory")));
+    asm((RAIILeak("nop"))); // expected-error {{the expression in this asm operand must be produced by a constant expression}}
+    asm((RAII("nop"))
+        : (RAIILeak("+ir")) (i) // expected-error {{the expression in this asm operand must be produced by a constant expression}}
+        ::
+    );
+    asm((RAII("nop"))
+        : (RAII("+ir")) (i)
+        : (RAIILeak("g")) (i) // expected-error {{the expression in this asm operand must be produced by a constant expression}}
+        :
+    );
+    asm((RAII("nop"))
+        : (RAII("+ir")) (i)
+        : (RAII("g")) (i)
+        : (RAIILeak("memory")) // expected-error {{the expression in this asm operand must be produced by a constant expression}}
+    );
+}
+
+struct NotAString{};
+struct MessageInvalidSize {
+    constexpr unsigned long size(int) const; // expected-note {{'size' declared here}}
+    constexpr const char* data() const;
+};
+struct MessageInvalidData {
+    constexpr unsigned long size() const;
+    constexpr const char* data(int) const; // expected-note {{'data' declared here}}
+};
+
+
+struct WMessage {
+    constexpr unsigned long long size() const {return 0;};
+    constexpr const wchar_t* data() const {return L"";}
+};
+
+struct string_view {
+  int S;
+  const char* D;
+  constexpr string_view() : S(0), D(0){}
+  constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
+  constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
+  constexpr int size() const {
+      return S;
+  }
+  constexpr const char* data() const {
+      return D;
+  }
+};
+
+
+void f() {
+    asm(("")); // expected-error {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}}
+    asm((NotAString{})); // expected-error {{the string object in this asm operand is missing 'data()' and 'size()' member functions}}
+    asm((MessageInvalidData{})); // expected-error {{the expression in this asm operand must have a 'data()' member function returning an object convertible to 'const char *'}} \
+                                 // expected-error {{too few arguments to function call, expected 1, have 0}}
+    asm((MessageInvalidSize{})); // expected-error {{the expression in this asm operand must have a 'size()' member function returning an object convertible to 'std::size_t'}} \
+                                 // expected-error {{too few arguments to function call, expected 1, have 0}}
+
+    asm((WMessage{})); // expected-error {{value of type 'const wchar_t *' is not implicitly convertible to 'const char *'}} \
+                       // expected-error {{the expression in this asm operand must have a 'data()' member function returning an object convertible to 'const char *'}}
+}
+
+template <typename... U>
+void test_packs() {
+    asm((U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
+    asm("" : (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
+    asm("" :: (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
+    asm("" ::: (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}}
+}
+
+template <typename T>
+void test_dependent1(int i) {
+    asm((T{})); // #err-int
+    asm("" : (T{"+g"})(i)); // #err-int2
+    asm("" :: (T{"g"})(i)); // #err-int3
+    asm("" ::: (T{"memory"})); // #err-int4
+}
+
+template void test_dependent1<int>(int);
+// expected-note at -1 {{in instantiation of function template specialization}}
+// expected-error@#err-int {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}}
+// expected-error@#err-int2 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[3]'}}
+// expected-error@#err-int3 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[2]'}}
+// expected-error@#err-int4 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[7]'}}
+
+template void test_dependent1<string_view>(int);
+
+
+template <typename T>
+void test_dependent2(int i) {
+    asm("" : (T{"g"})(i)); // #err-invalid1
+    asm("" :: (T{"+g"})(i)); // #err-invalid2
+    asm("" ::: (T{"foo"})); // #err-invalid3
+}
+template void test_dependent2<string_view>(int);
+// expected-note at -1 {{in instantiation of function template specialization}}
+// expected-error@#err-invalid1 {{invalid output constraint 'g' in asm}}
+// expected-error@#err-invalid2 {{invalid input constraint '+g' in asm}}
+// expected-error@#err-invalid3 {{unknown register name 'foo' in asm}}
+


        


More information about the cfe-commits mailing list