From d04c6e23f2e10eeb9936778d67f4a1c4a14cc4f6 Mon Sep 17 00:00:00 2001 From: Anders Carlsson Date: Tue, 27 Nov 2007 04:11:28 +0000 Subject: [PATCH] Add more semantic analysis for inline asm statements. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@44349 91177308-0d34-0410-b5e6-96231b3b80d8 --- Basic/TargetInfo.cpp | 124 ++++++++++++++++++++++++ Driver/Targets.cpp | 77 ++++++++++++++- Sema/SemaStmt.cpp | 39 +++++++- include/clang/AST/Stmt.h | 9 +- include/clang/Basic/DiagnosticKinds.def | 4 + include/clang/Basic/TargetInfo.h | 27 +++++- 6 files changed, 273 insertions(+), 7 deletions(-) diff --git a/Basic/TargetInfo.cpp b/Basic/TargetInfo.cpp index 3b20d3aa55..ec91bdd1a2 100644 --- a/Basic/TargetInfo.cpp +++ b/Basic/TargetInfo.cpp @@ -314,3 +314,127 @@ bool TargetInfo::isValidGCCRegisterName(const char *Name) const { return false; } + +const char *TargetInfo::getNormalizedGCCRegisterName(const char *Name) const +{ + assert(isValidGCCRegisterName(Name) && "Invalid register passed in"); + + const char * const *Names; + unsigned NumNames; + + PrimaryTarget->getGCCRegNames(Names, NumNames); + + // First, check if we have a number. + if (isdigit(Name[0])) { + char *End; + int n = (int)strtol(Name, &End, 0); + if (*End == 0) { + assert(n >= 0 && (unsigned)n < NumNames && + "Out of bounds register number!"); + return Names[n]; + } + } + + // Now check aliases. + const TargetInfoImpl::GCCRegAlias *Aliases; + unsigned NumAliases; + + PrimaryTarget->getGCCRegAliases(Aliases, NumAliases); + for (unsigned i = 0; i < NumAliases; i++) { + for (unsigned j = 0 ; j < llvm::array_lengthof(Aliases[i].Aliases); j++) { + if (!Aliases[i].Aliases[j]) + break; + if (strcmp(Aliases[i].Aliases[j], Name) == 0) + return Aliases[i].Register; + } + } + + return Name; +} + +bool TargetInfo::validateOutputConstraint(const char *Name, + ConstraintInfo &info) const +{ + // An output constraint must start with '=' or '+' + if (*Name != '=' && *Name != '+') + return false; + + if (*Name == '+') + info = CI_ReadWrite; + else + info = CI_None; + + Name++; + while (*Name) { + switch (*Name) { + default: + if (!PrimaryTarget->validateAsmConstraint(*Name, info)) { + // FIXME: This assert is in place temporarily + // so we can add more constraints as we hit it. + // Eventually, an unknown constraint should just be treated as 'g'. + assert(0 && "Unknown output constraint type!"); + } + case '&': // early clobber. + break; + case 'r': // general register. + info = (ConstraintInfo)(info|CI_AllowsRegister); + break; + case 'm': // memory operand. + info = (ConstraintInfo)(info|CI_AllowsMemory); + break; + case 'g': // general register, memory operand or immediate integer. + info = (ConstraintInfo)(info|CI_AllowsMemory|CI_AllowsRegister); + break; + } + + Name++; + } + + return true; +} + +bool TargetInfo::validateInputConstraint(const char *Name, + unsigned NumOutputs, + ConstraintInfo &info) const +{ + while (*Name) { + switch (*Name) { + default: + // Check if we have a matching constraint + if (*Name >= '0' && *Name <= '9') { + unsigned i = *Name - '0'; + + // Check if matching constraint is out of bounds. + if (i >= NumOutputs) + return false; + } else if (!PrimaryTarget->validateAsmConstraint(*Name, info)) { + // FIXME: This assert is in place temporarily + // so we can add more constraints as we hit it. + // Eventually, an unknown constraint should just be treated as 'g'. + assert(0 && "Unknown input constraint type!"); + } + case 'i': // immediate integer. + break; + case 'r': // general register. + info = (ConstraintInfo)(info|CI_AllowsRegister); + break; + case 'm': // memory operand. + info = (ConstraintInfo)(info|CI_AllowsMemory); + break; + case 'g': // general register, memory operand or immediate integer. + info = (ConstraintInfo)(info|CI_AllowsMemory|CI_AllowsRegister); + break; + } + + Name++; + } + + return true; +} + +const char *TargetInfo::getClobbers() const +{ + return PrimaryTarget->getClobbers(); +} + + diff --git a/Driver/Targets.cpp b/Driver/Targets.cpp index 7a51e175f1..5a99f7a925 100644 --- a/Driver/Targets.cpp +++ b/Driver/Targets.cpp @@ -397,8 +397,25 @@ namespace PPC { unsigned &NumAliases) { Aliases = GCCRegAliases; NumAliases = llvm::array_lengthof(GCCRegAliases); - } + } + + static bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) { + switch (c) { + default: return false; + case 'O': // Zero + return true; + case 'b': // Base register + case 'f': // Floating point register + info = (TargetInfo::ConstraintInfo)(info|TargetInfo::CI_AllowsRegister); + return true; + } + } + const char *getClobbers() { + return 0; + } + } // End namespace PPC @@ -454,6 +471,29 @@ namespace X86 { NumAliases = llvm::array_lengthof(GCCRegAliases); } + static bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) { + switch (c) { + default: return false; + case 'a': // eax. + case 'b': // ebx. + case 'c': // ecx. + case 'd': // edx. + case 'S': // esi. + case 'D': // edi. + case 'A': // edx:eax. + case 't': // top of floating point stack. + case 'u': // second from top of floating point stack. + case 'q': // a, b, c, d registers or any integer register in 64-bit. + info = (TargetInfo::ConstraintInfo)(info|TargetInfo::CI_AllowsRegister); + return true; + } + } + + const char *getClobbers() { + return "~{dirflag},~{fpsr},~{flags}"; + } + } // End namespace X86 //===----------------------------------------------------------------------===// @@ -483,6 +523,13 @@ public: unsigned &NumAliases) const { PPC::getGCCRegAliases(Aliases, NumAliases); } + virtual bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) const { + return PPC::validateAsmConstraint(c, info); + } + virtual const char *getClobbers() const { + return PPC::getClobbers(); + } }; } // end anonymous namespace. @@ -508,6 +555,13 @@ public: unsigned &NumAliases) const { PPC::getGCCRegAliases(Aliases, NumAliases); } + virtual bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) const { + return PPC::validateAsmConstraint(c, info); + } + virtual const char *getClobbers() const { + return PPC::getClobbers(); + } }; } // end anonymous namespace. @@ -533,6 +587,13 @@ public: unsigned &NumAliases) const { X86::getGCCRegAliases(Aliases, NumAliases); } + virtual bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) const { + return X86::validateAsmConstraint(c, info); + } + virtual const char *getClobbers() const { + return X86::getClobbers(); + } }; } // end anonymous namespace. @@ -558,6 +619,13 @@ public: unsigned &NumAliases) const { X86::getGCCRegAliases(Aliases, NumAliases); } + virtual bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) const { + return X86::validateAsmConstraint(c, info); + } + virtual const char *getClobbers() const { + return X86::getClobbers(); + } }; } // end anonymous namespace. @@ -589,6 +657,13 @@ public: unsigned &NumAliases) const { X86::getGCCRegAliases(Aliases, NumAliases); } + virtual bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) const { + return X86::validateAsmConstraint(c, info); + } + virtual const char *getClobbers() const { + return X86::getClobbers(); + } }; } // end anonymous namespace. diff --git a/Sema/SemaStmt.cpp b/Sema/SemaStmt.cpp index 7f5d038e71..d051f6b333 100644 --- a/Sema/SemaStmt.cpp +++ b/Sema/SemaStmt.cpp @@ -659,11 +659,26 @@ Sema::StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, SourceLocation RParenLoc) { Expr *E = (Expr *)AsmString; - // Check that the output exprs are valid lvalues. for (unsigned i = 0; i < NumOutputs; i++) { + StringLiteral *Literal = cast((Expr *)Constraints[i]); + assert(!Literal->isWide() && + "Output constraint strings should not be wide!"); + + std::string OutputConstraint(Literal->getStrData(), + Literal->getByteLength()); + + TargetInfo::ConstraintInfo info; + if (!Context.Target.validateOutputConstraint(OutputConstraint.c_str(), + info)) { + // FIXME: We currently leak memory here. + Diag(Literal->getLocStart(), + diag::err_invalid_output_constraint_in_asm); + return true; + } + + // Check that the output exprs are valid lvalues. Expr *OutputExpr = (Expr *)Exprs[i]; Expr::isLvalueResult Result = OutputExpr->isLvalue(); - if (Result != Expr::LV_Valid) { ParenExpr *PE = cast(OutputExpr); @@ -676,10 +691,26 @@ Sema::StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, } } - // Check that the input exprs aren't of type void. for (unsigned i = NumOutputs, e = NumOutputs + NumInputs; i != e; i++) { - Expr *InputExpr = (Expr *)Exprs[i]; + StringLiteral *Literal = cast((Expr *)Constraints[i]); + assert(!Literal->isWide() && + "Output constraint strings should not be wide!"); + + std::string InputConstraint(Literal->getStrData(), + Literal->getByteLength()); + + TargetInfo::ConstraintInfo info; + if (!Context.Target.validateInputConstraint(InputConstraint.c_str(), + NumOutputs, + info)) { + // FIXME: We currently leak memory here. + Diag(Literal->getLocStart(), + diag::err_invalid_input_constraint_in_asm); + return true; + } + // Check that the input exprs aren't of type void. + Expr *InputExpr = (Expr *)Exprs[i]; if (InputExpr->getType()->isVoidType()) { ParenExpr *PE = cast(InputExpr); diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 7e6f39dd77..448ad5319f 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -735,8 +735,11 @@ public: unsigned getNumOutputs() const { return NumOutputs; } const std::string &getOutputName(unsigned i) const { return Names[i]; } + const StringLiteral *getOutputConstraint(unsigned i) const + { return Constraints[i]; } StringLiteral *getOutputConstraint(unsigned i) { return Constraints[i]; } + const Expr *getOutputExpr(unsigned i) const { return Exprs[i]; } Expr *getOutputExpr(unsigned i) { return Exprs[i]; } unsigned getNumInputs() const { return NumInputs; } @@ -744,13 +747,17 @@ public: { return Names[i + NumOutputs]; } StringLiteral *getInputConstraint(unsigned i) { return Constraints[i + NumOutputs]; } + const StringLiteral *getInputConstraint(unsigned i) const + { return Constraints[i + NumOutputs]; } Expr *getInputExpr(unsigned i) { return Exprs[i + NumOutputs]; } - + const Expr *getInputExpr(unsigned i) const { return Exprs[i + NumOutputs]; } + const StringLiteral *getAsmString() const { return AsmStr; } StringLiteral *getAsmString() { return AsmStr; } unsigned getNumClobbers() const { return Clobbers.size(); } StringLiteral *getClobber(unsigned i) { return Clobbers[i]; } + const StringLiteral *getClobber(unsigned i) const { return Clobbers[i]; } virtual SourceRange getSourceRange() const { return SourceRange(AsmLoc, RParenLoc); diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index ffdf626993..f847453dd1 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -791,6 +791,10 @@ DIAG(err_pascal_string_too_long, ERROR, "Pascal string is too long") DIAG(err_invalid_lvalue_in_asm_output, ERROR, "invalid lvalue in asm output") +DIAG(err_invalid_output_constraint_in_asm, ERROR, + "invalid output constraint in asm") +DIAG(err_invalid_input_constraint_in_asm, ERROR, + "invalid input constraint in asm") DIAG(err_invalid_type_in_asm_input, ERROR, "invalid type '%0' in asm input") DIAG(err_unknown_register_name_in_asm, ERROR, diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 3a186a28be..a86023a637 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -191,7 +191,28 @@ public: /// is a valid register name according to GCC. This is used by Sema for /// inline asm statements. bool isValidGCCRegisterName(const char *Name) const; - + + // getNormalizedGCCRegisterName - Returns the "normalized" GCC register name. + // For example, on x86 it will return "ax" when "eax" is passed in. + const char *getNormalizedGCCRegisterName(const char *Name) const; + + enum ConstraintInfo { + CI_None = 0x00, + CI_AllowsMemory = 0x01, + CI_AllowsRegister = 0x02, + CI_ReadWrite = 0x03 + }; + + // validateOutputConstraint, validateInputConstraint - Checks that + // a constraint is valid and provides information about it. + // FIXME: These should return a real error instead of just true/false. + bool validateOutputConstraint(const char *Name, ConstraintInfo &Info) const; + bool validateInputConstraint (const char *Name, unsigned NumOutputs, + ConstraintInfo &info) const; + + // Returns a string of target-specific clobbers, in LLVM format. + const char *getClobbers() const; + ///===---- Some helper methods ------------------------------------------===// unsigned getCharWidth(SourceLocation Loc) { @@ -287,6 +308,10 @@ public: virtual void getGCCRegAliases(const GCCRegAlias *&Aliases, unsigned &NumAliases) const = 0; + virtual bool validateAsmConstraint(char c, + TargetInfo::ConstraintInfo &info) const= 0; + + virtual const char *getClobbers() const = 0; private: virtual void ANCHOR(); // out-of-line virtual method for class. }; -- 2.50.1