Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6928,6 +6928,10 @@ "constraint '%0' is already present here">; } + def error_inoutput_conflict_with_clobber : Error< + "asm-specifier for input or output variable conflicts with asm" + " clobber list">; + let CategoryName = "Semantic Issue" in { def err_invalid_conversion_between_vectors : Error< Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -590,9 +590,17 @@ /// \brief Returns the "normalized" GCC register name. /// - /// For example, on x86 it will return "ax" when "eax" is passed in. - StringRef getNormalizedGCCRegisterName(StringRef Name) const; + /// ReturnCannonical true will return the register name without any additions + /// such as "{}" or "%" in it's canonical form, for example: + /// ReturnCanonical = true and Name = "rax", will return "ax". + StringRef getNormalizedGCCRegisterName(StringRef Name, + bool ReturnCanonical = false) const; + virtual StringRef getConstraintRegister(const StringRef &Constraint, + const StringRef &Expression) const { + return ""; + } + struct ConstraintInfo { enum { CI_None = 0x00, Index: lib/Basic/TargetInfo.cpp =================================================================== --- lib/Basic/TargetInfo.cpp +++ lib/Basic/TargetInfo.cpp @@ -401,7 +401,8 @@ } StringRef -TargetInfo::getNormalizedGCCRegisterName(StringRef Name) const { +TargetInfo::getNormalizedGCCRegisterName(StringRef Name, +bool ReturnCanonical) const { assert(isValidGCCRegisterName(Name) && "Invalid register passed in"); // Get rid of any register prefix. @@ -426,7 +427,10 @@ // Make sure the register that the additional name is for is within // the bounds of the register names from above. if (AN == Name && ARN.RegNum < Names.size()) - return Name; + if (ReturnCanonical) + return Names[ARN.RegNum]; + else + return Name; } // Now check aliases. Index: lib/Basic/Targets.cpp =================================================================== --- lib/Basic/Targets.cpp +++ lib/Basic/Targets.cpp @@ -2711,6 +2711,41 @@ const char *getClobbers() const override { return "~{dirflag},~{fpsr},~{flags}"; } + virtual StringRef getConstraintRegister(const StringRef &Constraint, + const StringRef &Expression) const { + for (int i = 0; i < Constraint.size(); ++i) { + switch (Constraint[i]) { + // Ignore these + case '*': + case '?': + case '!': + // Will see this and the following in mult-alt constraints. + case '=': + case '+': + continue; + // For the register constraints, return the matching register name + case 'a': + return "ax"; + case 'b': + return "bx"; + case 'c': + return "cx"; + case 'd': + return "dx"; + case 'S': + return "si"; + case 'D': + return "di"; + // In case the constraint is 'r' we need to extract the register name + case 'r': + return Expression; + default: + // Default value if there is no constraint for the register + return ""; + } + } + return ""; + } void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const override; static void setSSELevel(llvm::StringMap &Features, X86SSEEnum Level, Index: lib/Headers/intrin.h =================================================================== --- lib/Headers/intrin.h +++ lib/Headers/intrin.h @@ -844,45 +844,37 @@ #if defined(__i386__) || defined(__x86_64__) static __inline__ void __DEFAULT_FN_ATTRS __movsb(unsigned char *__dst, unsigned char const *__src, size_t __n) { - __asm__("rep movsb" : : "D"(__dst), "S"(__src), "c"(__n) - : "%edi", "%esi", "%ecx"); + __asm__("rep movsb" : : "D"(__dst), "S"(__src), "c"(__n)); } static __inline__ void __DEFAULT_FN_ATTRS __movsd(unsigned long *__dst, unsigned long const *__src, size_t __n) { - __asm__("rep movsl" : : "D"(__dst), "S"(__src), "c"(__n) - : "%edi", "%esi", "%ecx"); + __asm__("rep movsl" : : "D"(__dst), "S"(__src), "c"(__n)); } static __inline__ void __DEFAULT_FN_ATTRS __movsw(unsigned short *__dst, unsigned short const *__src, size_t __n) { - __asm__("rep movsw" : : "D"(__dst), "S"(__src), "c"(__n) - : "%edi", "%esi", "%ecx"); + __asm__("rep movsw" : : "D"(__dst), "S"(__src), "c"(__n)); } static __inline__ void __DEFAULT_FN_ATTRS __stosb(unsigned char *__dst, unsigned char __x, size_t __n) { - __asm__("rep stosb" : : "D"(__dst), "a"(__x), "c"(__n) - : "%edi", "%ecx"); + __asm__("rep stosb" : : "D"(__dst), "a"(__x), "c"(__n)); } static __inline__ void __DEFAULT_FN_ATTRS __stosd(unsigned long *__dst, unsigned long __x, size_t __n) { - __asm__("rep stosl" : : "D"(__dst), "a"(__x), "c"(__n) - : "%edi", "%ecx"); + __asm__("rep stosl" : : "D"(__dst), "a"(__x), "c"(__n)); } static __inline__ void __DEFAULT_FN_ATTRS __stosw(unsigned short *__dst, unsigned short __x, size_t __n) { - __asm__("rep stosw" : : "D"(__dst), "a"(__x), "c"(__n) - : "%edi", "%ecx"); + __asm__("rep stosw" : : "D"(__dst), "a"(__x), "c"(__n)); } #endif #ifdef __x86_64__ static __inline__ void __DEFAULT_FN_ATTRS __movsq(unsigned long long *__dst, unsigned long long const *__src, size_t __n) { - __asm__("rep movsq" : : "D"(__dst), "S"(__src), "c"(__n) - : "%edi", "%esi", "%ecx"); + __asm__("rep movsq" : : "D"(__dst), "S"(__src), "c"(__n)); } static __inline__ void __DEFAULT_FN_ATTRS __stosq(unsigned __int64 *__dst, unsigned __int64 __x, size_t __n) { - __asm__("rep stosq" : : "D"(__dst), "a"(__x), "c"(__n) - : "%edi", "%ecx"); + __asm__("rep stosq" : "=D"(__dst) : "a"(__x), "c"(__n)); } #endif Index: lib/Sema/SemaStmtAsm.cpp =================================================================== --- lib/Sema/SemaStmtAsm.cpp +++ lib/Sema/SemaStmtAsm.cpp @@ -22,6 +22,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/MC/MCParser/MCAsmParser.h" using namespace clang; using namespace sema; @@ -137,6 +138,58 @@ return false; } +// Extracting the register name from the Expression value, +// if there is no register name to extract, returns "" +StringRef ExtractRegisterName(const Expr *Expression, const TargetInfo &Target) { + while (const ImplicitCastExpr *P = dyn_cast(Expression)) { + Expression = P->getSubExpr(); + } + if (const DeclRefExpr *AsmDeclRef = dyn_cast(Expression)) { + // Handle cases where the expression is a variable + const VarDecl *Variable = dyn_cast(AsmDeclRef->getDecl()); + if (Variable && Variable->getStorageClass() == SC_Register) { + if (AsmLabelAttr *Attr = Variable->getAttr()) + return Target.isValidGCCRegisterName(Attr->getLabel()) + ? Target.getNormalizedGCCRegisterName(Attr->getLabel(), true) + : ""; + } + } + return ""; +} + +// Checks if there is a conflict between the input and output lists with the +// clobbers list. If there's a conflict, returns the location of the +// conflicted clobber, else returns nullptr +SourceLocation* GetClobberConflictLocation(MultiExprArg Exprs, + StringLiteral **Constraints, StringLiteral **Clobbers, int NumClobbers, + 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 (int i = 0; i < Exprs.size(); ++i) { + StringRef Constraint = Constraints[i]->getString(); + StringRef InOutReg = Target.getConstraintRegister( + Constraint, ExtractRegisterName(Exprs[i], Target)); + if (InOutReg != "") + InOutVars.insert(InOutReg); + } + // 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(); + // We only check registers, therefore we don't check cc and memory clobbers + if (Clobber == "cc" || Clobber == "memory") + continue; + Clobber = Target.getNormalizedGCCRegisterName(Clobber, true); + // Go over the output's registers we collected + if (InOutVars.find(Clobber) != InOutVars.end()){ + SourceLocation Loc = Clobbers[i]->getLocStart(); + return &Loc; + } + } + return nullptr; +} + StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, unsigned NumInputs, IdentifierInfo **Names, @@ -543,6 +596,13 @@ return StmtError(); } + // Check for conflicts between clobber list and input or output lists + SourceLocation* ConstraintLoc = GetClobberConflictLocation(Exprs, + Constraints, Clobbers, NumClobbers, Context.getTargetInfo(), Context); + if (ConstraintLoc) { + return Diag(*ConstraintLoc, + diag::error_inoutput_conflict_with_clobber); + } return NS; } Index: test/Sema/asm.c =================================================================== --- test/Sema/asm.c +++ test/Sema/asm.c @@ -28,6 +28,16 @@ asm ("nop" : : : "204"); // expected-error {{unknown register name '204' in asm}} asm ("nop" : : : "-1"); // expected-error {{unknown register name '-1' in asm}} asm ("nop" : : : "+1"); // expected-error {{unknown register name '+1' in asm}} + register void *clobber_conflict asm ("%rcx"); + register void *no_clobber_conflict asm ("%rax"); + int a,b,c; + asm ("nop" : "=r" (no_clobber_conflict) : "r" (clobber_conflict) : "%rcx"); // expected-error {{asm-specifier for input or output variable conflicts with asm clobber list}} + asm ("nop" : "=r" (clobber_conflict) : "r" (no_clobber_conflict) : "%rcx"); // expected-error {{asm-specifier for input or output variable conflicts with asm clobber list}} + asm ("nop" : "=r" (clobber_conflict) : "r" (clobber_conflict) : "%rcx"); // expected-error {{asm-specifier for input or output variable conflicts with asm clobber list}} + asm ("nop" : "=c" (a) : "r" (no_clobber_conflict) : "%rcx"); // expected-error {{asm-specifier for input or output variable conflicts with asm clobber list}} + asm ("nop" : "=r" (no_clobber_conflict) : "c" (c) : "%rcx"); // expected-error {{asm-specifier for input or output variable conflicts with asm clobber list}} + asm ("nop" : "=r" (clobber_conflict) : "c" (c) : "%rcx"); // expected-error {{asm-specifier for input or output variable conflicts with asm clobber list}} + asm ("nop" : "=a" (a) : "b" (b) : "%rcx", "%rbx"); // expected-error {{asm-specifier for input or output variable conflicts with asm clobber list}} } // rdar://6094010