diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 7ede9ce323..d1f7d667f3 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -1328,7 +1328,8 @@ public: } Expr *getInputExpr(unsigned i); - + void setInputExpr(unsigned i, Expr *E); + const Expr *getInputExpr(unsigned i) const { return const_cast(this)->getInputExpr(i); } diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 7e73f02e67..8a80275aa1 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -218,6 +218,10 @@ unsigned AsmStmt::getNumPlusOperands() const { Expr *AsmStmt::getInputExpr(unsigned i) { return cast(Exprs[i + NumOutputs]); } +void AsmStmt::setInputExpr(unsigned i, Expr *E) { + Exprs[i + NumOutputs] = E; +} + /// getInputConstraint - Return the specified input constraint. Unlike output /// constraints, these can be empty. diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 6801dd4c2e..3815deae58 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1542,8 +1542,9 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, if (!Info.hasTiedOperand()) continue; unsigned TiedTo = Info.getTiedOperand(); + unsigned InputOpNo = i+NumOutputs; Expr *OutputExpr = Exprs[TiedTo]; - Expr *InputExpr = Exprs[i+NumOutputs]; + Expr *InputExpr = Exprs[InputOpNo]; QualType InTy = InputExpr->getType(); QualType OutTy = OutputExpr->getType(); if (Context.hasSameType(InTy, OutTy)) @@ -1588,7 +1589,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, // If this is a reference to the input and if the input was the smaller // one, then we have to reject this asm. - if (isOperandMentioned(i+NumOutputs, Pieces)) { + if (isOperandMentioned(InputOpNo, Pieces)) { // This is a use in the asm string of the smaller operand. Since we // codegen this by promoting to a wider value, the asm will get printed // "wrong". @@ -1607,6 +1608,19 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, OutputConstraintInfos[TiedTo].allowsRegister()) continue; + // Either both of the operands were mentioned or the smaller one was + // mentioned. One more special case that we'll allow: if the tied input is + // integer, unmentioned, and is a constant, then we'll allow truncating it + // down to the size of the destination. + if (InputDomain == AD_Int && OutputDomain == AD_Int && + !isOperandMentioned(InputOpNo, Pieces) && + InputExpr->isEvaluatable(Context)) { + ImpCastExprToType(InputExpr, OutTy, CK_IntegralCast); + Exprs[InputOpNo] = InputExpr; + NS->setInputExpr(i, InputExpr); + continue; + } + Diag(InputExpr->getLocStart(), diag::err_asm_tying_incompatible_types) << InTy << OutTy << OutputExpr->getSourceRange() diff --git a/test/CodeGen/asm-inout.c b/test/CodeGen/asm-inout.c index 5b0a5f7ef1..29142f7c55 100644 --- a/test/CodeGen/asm-inout.c +++ b/test/CodeGen/asm-inout.c @@ -29,3 +29,12 @@ asm( : "edi" ); } + +// PR8959 - This should implicitly truncate the immediate to a byte. +int test4(volatile int *addr) { + unsigned char oldval; + __asm__ ("frob %0" : "=r"(oldval) : "0"(0xff)); + return (int)oldval; +// CHECK: call i8 asm "frob $0", "=r,0{{.*}}"(i8 -1) +} +