From: Anders Carlsson Date: Tue, 5 Feb 2008 16:35:33 +0000 (+0000) Subject: Add code generation for inline asm statements. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fb1aeb804c08d5288a923fb278161783e6abdc66;p=clang Add code generation for inline asm statements. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@46750 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/CodeGen/CGStmt.cpp b/CodeGen/CGStmt.cpp index dfa212460b..77427fb63c 100644 --- a/CodeGen/CGStmt.cpp +++ b/CodeGen/CGStmt.cpp @@ -13,9 +13,12 @@ #include "CodeGenFunction.h" #include "clang/AST/AST.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" #include "llvm/Function.h" +#include "llvm/InlineAsm.h" +#include "llvm/ADT/StringExtras.h" using namespace clang; using namespace CodeGen; @@ -59,6 +62,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::SwitchStmtClass: EmitSwitchStmt(cast(*S)); break; case Stmt::DefaultStmtClass: EmitDefaultStmt(cast(*S)); break; case Stmt::CaseStmtClass: EmitCaseStmt(cast(*S)); break; + case Stmt::AsmStmtClass: EmitAsmStmt(cast(*S)); break; } } @@ -500,3 +504,201 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { SwitchInsn = SavedSwitchInsn; CaseRangeBlock = SavedCRBlock; } + +static inline std::string ConvertAsmString(const char *Start, + unsigned NumOperands) +{ + static unsigned AsmCounter = 0; + + AsmCounter++; + + std::string Result; + + while (*Start) { + switch (*Start) { + default: + Result += *Start; + break; + case '$': + Result += "$$"; + break; + case '%': + // Escaped character + Start++; + if (!*Start) { + // FIXME: This should be caught during Sema. + assert(0 && "Trailing '%' in asm string."); + } + + char EscapedChar = *Start; + if (EscapedChar == '%') { + // Escaped percentage sign. + Result += '%'; + } + else if (EscapedChar == '=') { + // Generate an unique ID. + Result += llvm::utostr(AsmCounter); + } else if (isdigit(EscapedChar)) { + // %n - Assembler operand n + char *End; + + unsigned long n = strtoul(Start, &End, 10); + if (Start == End) { + // FIXME: This should be caught during Sema. + assert(0 && "Missing operand!"); + } else if (n >= NumOperands) { + // FIXME: This should be caught during Sema. + assert(0 && "Operand number out of range!"); + } + + Result += '$' + llvm::utostr(n); + } else { + assert(0 && "Unhandled asm escaped character!"); + } + } + Start++; + } + + return Result; +} + +static std::string SimplifyConstraint(const char* Constraint) +{ + std::string Result; + + while (*Constraint) { + switch (*Constraint) { + default: + Result += *Constraint; + break; + // Ignore these + case '*': + case '?': + case '!': + break; + case 'g': + Result += "imr"; + break; + } + + Constraint++; + } + + return Result; +} + +void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { + std::string AsmString = + ConvertAsmString(std::string(S.getAsmString()->getStrData(), + S.getAsmString()->getByteLength()).c_str(), + S.getNumOutputs() + S.getNumInputs()); + + std::string Constraints; + + llvm::Value *ResultAddr = 0; + const llvm::Type *ResultType = llvm::Type::VoidTy; + + std::vector ArgTypes; + std::vector Args; + + for (unsigned i = 0, e = S.getNumOutputs(); i != e; i++) { + std::string OutputConstraint(S.getOutputConstraint(i)->getStrData(), + S.getOutputConstraint(i)->getByteLength()); + + TargetInfo::ConstraintInfo Info; + bool result = Target.validateOutputConstraint(OutputConstraint.c_str(), + Info); + assert(result && "Failed to parse output constraint"); + + // Simplify the output constraint. + OutputConstraint = SimplifyConstraint(OutputConstraint.c_str() + 1); + + LValue Dest = EmitLValue(S.getOutputExpr(i)); + const llvm::Type *DestValueType = + cast(Dest.getAddress()->getType())->getElementType(); + + // If the first output operand is not a memory dest, we'll + // make it the return value. + if (i == 0 && !(Info & TargetInfo::CI_AllowsMemory) && + DestValueType->isFirstClassType()) { + ResultAddr = Dest.getAddress(); + ResultType = DestValueType; + Constraints += "=" + OutputConstraint; + } else { + ArgTypes.push_back(Dest.getAddress()->getType()); + if (i != 0) + Constraints += ','; + Constraints += '*'; + Constraints += OutputConstraint; + } + } + + unsigned NumConstraints = S.getNumOutputs() + S.getNumInputs(); + + for (unsigned i = 0, e = S.getNumInputs(); i != e; i++) { + const Expr *InputExpr = S.getInputExpr(i); + + std::string InputConstraint(S.getInputConstraint(i)->getStrData(), + S.getInputConstraint(i)->getByteLength()); + + TargetInfo::ConstraintInfo Info; + bool result = Target.validateInputConstraint(InputConstraint.c_str(), + NumConstraints, + Info); + assert(result && "Failed to parse input constraint"); + + if (i != 0 || S.getNumOutputs() > 0) + Constraints += ','; + + // Simplify the input constraint. + InputConstraint = SimplifyConstraint(InputConstraint.c_str()); + + llvm::Value *Arg; + + if ((Info & TargetInfo::CI_AllowsRegister) || + !(Info & TargetInfo::CI_AllowsMemory)) { + if (ConvertType(InputExpr->getType())->isFirstClassType()) { + Arg = EmitScalarExpr(InputExpr); + } else { + assert(0 && "FIXME: Implement passing non first class types as inputs"); + } + } else { + LValue Dest = EmitLValue(InputExpr); + Arg = Dest.getAddress(); + Constraints += '*'; + } + + ArgTypes.push_back(Arg->getType()); + Args.push_back(Arg); + Constraints += InputConstraint; + } + + // Clobbers + for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) { + std::string Clobber(S.getClobber(i)->getStrData(), + S.getClobber(i)->getByteLength()); + + Clobber = Target.getNormalizedGCCRegisterName(Clobber.c_str()); + + if (i != 0) + Constraints += ','; + Constraints += Clobber; + } + + // Add machine specific clobbers + if (const char *C = Target.getClobbers()) { + if (!Constraints.empty()) + Constraints += ','; + Constraints += C; + } + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(ResultType, ArgTypes, false); + + llvm::InlineAsm *IA = + llvm::InlineAsm::get(FTy, AsmString, Constraints, + S.isVolatile() || S.getNumOutputs() == 0); + llvm::Value *Result = Builder.CreateCall(IA, Args.begin(), Args.end(), ""); + if (ResultAddr) + Builder.CreateStore(Result, ResultAddr); +} diff --git a/CodeGen/CodeGenFunction.h b/CodeGen/CodeGenFunction.h index 30dfaabd64..c741071d00 100644 --- a/CodeGen/CodeGenFunction.h +++ b/CodeGen/CodeGenFunction.h @@ -44,7 +44,8 @@ namespace clang { class CaseStmt; class DefaultStmt; class SwitchStmt; - + class AsmStmt; + class Expr; class DeclRefExpr; class StringLiteral; @@ -365,7 +366,8 @@ public: void EmitDefaultStmt(const DefaultStmt &S); void EmitCaseStmt(const CaseStmt &S); void EmitCaseStmtRange(const CaseStmt &S); - + void EmitAsmStmt(const AsmStmt &S); + //===--------------------------------------------------------------------===// // LValue Expression Emission //===--------------------------------------------------------------------===//