From 810f6d5d6223adaab0ccf0139f40de6484ad1bb5 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 13 Mar 2009 17:38:01 +0000 Subject: [PATCH] introduce a new -fheinous-gnu-extensions flag that enables really really horrible extensions that are disabled by default but that can be accepted by -fheinous-gnu-extensions (but which always emit a warning when enabled). As our first instance of this, implement PR3788/PR3794, which allows non-lvalues in inline asms in contexts where lvalues are required. bleh. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66910 91177308-0d34-0410-b5e6-96231b3b80d8 --- Driver/clang.cpp | 7 ++++ include/clang/Basic/DiagnosticSemaKinds.def | 7 ++++ include/clang/Basic/LangOptions.h | 3 ++ lib/CodeGen/CGStmt.cpp | 7 +++- lib/Sema/SemaStmt.cpp | 39 +++++++++++++++++---- test/Sema/heinous-extensions-off.c | 10 ++++++ test/Sema/heinous-extensions-on.c | 10 ++++++ 7 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 test/Sema/heinous-extensions-off.c create mode 100644 test/Sema/heinous-extensions-on.c diff --git a/Driver/clang.cpp b/Driver/clang.cpp index 171e4ecb31..b33f3443e5 100644 --- a/Driver/clang.cpp +++ b/Driver/clang.cpp @@ -479,6 +479,11 @@ EnableBlocks("fblocks", llvm::cl::desc("enable the 'blocks' language feature"), llvm::cl::ValueDisallowed, llvm::cl::AllowInverse, llvm::cl::ZeroOrMore); +static llvm::cl::opt +EnableHeinousExtensions("fheinous-gnu-extensions", + llvm::cl::desc("enable GNU extensions that you really really shouldn't use"), + llvm::cl::ValueDisallowed, llvm::cl::Hidden); + static llvm::cl::opt ObjCNonFragileABI("fobjc-nonfragile-abi", llvm::cl::desc("enable objective-c's nonfragile abi")); @@ -647,6 +652,8 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, if (Freestanding) Options.Freestanding = 1; + if (EnableHeinousExtensions) + Options.HeinousExtensions = 1; Options.MathErrno = MathErrno; diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 0267cfc10b..8938a7ae63 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -1205,6 +1205,13 @@ DIAG(err_asm_invalid_type_in_input, ERROR, "invalid type %0 in asm input for constraint '%1'") DIAG(err_asm_unknown_register_name, ERROR, "unknown register name '%0' in asm") +DIAG(err_invalid_asm_cast_lvalue, ERROR, + "invalid use of a cast in a inline asm context requiring an l-value:" + " remove the cast or build with -fheinous-gnu-extensions") +DIAG(warn_invalid_asm_cast_lvalue, WARNING, + "invalid use of a cast in a inline asm context requiring an l-value:" + " accepted due to -fheinous-gnu-extensions, but clang may remove support" + " for this in the future") DIAG(err_invalid_conversion_between_vectors, ERROR, "invalid conversion between vector type %0 and %1 of different size") diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index fcfe0ca1e9..f0a583c3d3 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -57,6 +57,8 @@ public: unsigned MathErrno : 1; // Math functions must respect errno // (modulo the platform support). + unsigned HeinousExtensions : 1; // Extensions that we really don't like and + // may be ripped out at any time. private: unsigned GC : 2; // Objective-C Garbage Collection modes. We declare // this enum as unsigned because MSVC insists on making enums @@ -75,6 +77,7 @@ public: CXXOperatorNames = PascalStrings = Boolean = WritableStrings = 0; Exceptions = NeXTRuntime = Freestanding = 0; LaxVectorConversions = 1; + HeinousExtensions = 0; // FIXME: The default should be 1. ThreadsafeStatics = 0; diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index fddc547fd3..91aa88569e 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -730,6 +730,7 @@ llvm::Value* CodeGenFunction::EmitAsmInput(const AsmStmt &S, if (Ty->isSingleValueType()) { Arg = EmitScalarExpr(InputExpr); } else { + InputExpr = InputExpr->IgnoreParenNoopCasts(getContext()); LValue Dest = EmitLValue(InputExpr); uint64_t Size = CGM.getTargetData().getTypeSizeInBits(Ty); @@ -744,6 +745,7 @@ llvm::Value* CodeGenFunction::EmitAsmInput(const AsmStmt &S, } } } else { + InputExpr = InputExpr->IgnoreParenNoopCasts(getContext()); LValue Dest = EmitLValue(InputExpr); Arg = Dest.getAddress(); ConstraintStr += '*'; @@ -799,7 +801,10 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { // Simplify the output constraint. OutputConstraint = SimplifyConstraint(OutputConstraint.c_str() + 1, Target); - LValue Dest = EmitLValue(S.getOutputExpr(i)); + const Expr *OutExpr = S.getOutputExpr(i); + OutExpr = OutExpr->IgnoreParenNoopCasts(getContext()); + + LValue Dest = EmitLValue(OutExpr); const llvm::Type *DestValueType = cast(Dest.getAddress()->getType())->getElementType(); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 96e59f0f82..39f21f853a 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -641,9 +641,7 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, return StmtError(Diag(VD->getLocation(), diag::err_non_variable_decl_in_for)); } else { - Expr::isLvalueResult lval = cast(First)->isLvalue(Context); - - if (lval != Expr::LV_Valid) + if (cast(First)->isLvalue(Context) != Expr::LV_Valid) return StmtError(Diag(First->getLocStart(), diag::err_selector_element_not_lvalue) << First->getSourceRange()); @@ -833,6 +831,36 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) { return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp)); } +/// CheckAsmLValue - GNU C has an extremely ugly extension whereby they silently +/// ignore "noop" casts in places where an lvalue is required by an inline asm. +/// We emulate this behavior when -fheinous-gnu-extensions is specified, but +/// provide a strong guidance to not use it. +/// +/// This method checks to see if the argument is an acceptable l-value and +/// returns false if it is a case we can handle. +static bool CheckAsmLValue(const Expr *E, Sema &S) { + if (E->isLvalue(S.Context) == Expr::LV_Valid) + return false; // Cool, this is an lvalue. + + // Okay, this is not an lvalue, but perhaps it is the result of a cast that we + // are supposed to allow. + const Expr *E2 = E->IgnoreParenNoopCasts(S.Context); + if (E != E2 && E2->isLvalue(S.Context) == Expr::LV_Valid) { + if (!S.getLangOptions().HeinousExtensions) + S.Diag(E2->getLocStart(), diag::err_invalid_asm_cast_lvalue) + << E->getSourceRange(); + else + S.Diag(E2->getLocStart(), diag::warn_invalid_asm_cast_lvalue) + << E->getSourceRange(); + // Accept, even if we emitted an error diagnostic. + return false; + } + + // None of the above, just randomly invalid non-lvalue. + return true; +} + + Sema::OwningStmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, @@ -875,8 +903,7 @@ Sema::OwningStmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, // Check that the output exprs are valid lvalues. ParenExpr *OutputExpr = cast(Exprs[i]); - Expr::isLvalueResult Result = OutputExpr->isLvalue(Context); - if (Result != Expr::LV_Valid) { + if (CheckAsmLValue(OutputExpr, *this)) { return StmtError(Diag(OutputExpr->getSubExpr()->getLocStart(), diag::err_asm_invalid_lvalue_in_output) << OutputExpr->getSubExpr()->getSourceRange()); @@ -909,7 +936,7 @@ Sema::OwningStmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, // Only allow void types for memory constraints. if ((info & TargetInfo::CI_AllowsMemory) && !(info & TargetInfo::CI_AllowsRegister)) { - if (InputExpr->isLvalue(Context) != Expr::LV_Valid) + if (CheckAsmLValue(InputExpr, *this)) return StmtError(Diag(InputExpr->getSubExpr()->getLocStart(), diag::err_asm_invalid_lvalue_in_input) << InputConstraint << InputExpr->getSubExpr()->getSourceRange()); diff --git a/test/Sema/heinous-extensions-off.c b/test/Sema/heinous-extensions-off.c new file mode 100644 index 0000000000..2b15f29bf4 --- /dev/null +++ b/test/Sema/heinous-extensions-off.c @@ -0,0 +1,10 @@ +// RUN: clang %s -verify + +int foo() { + int a; + // PR3788 + asm("nop" : : "m"((int)(a))); // expected-error {{cast in a inline asm context requiring an l-value}} + // PR3794 + asm("nop" : "=r"((unsigned)a)); // expected-error {{cast in a inline asm context requiring an l-value}} +} + diff --git a/test/Sema/heinous-extensions-on.c b/test/Sema/heinous-extensions-on.c new file mode 100644 index 0000000000..6b831f14d1 --- /dev/null +++ b/test/Sema/heinous-extensions-on.c @@ -0,0 +1,10 @@ +// RUN: clang %s -verify -fheinous-gnu-extensions + +int foo() { + int a; + // PR3788 + asm("nop" : : "m"((int)(a))); // expected-warning {{cast in a inline asm context requiring an l-value}} + // PR3794 + asm("nop" : "=r"((unsigned)a)); // expected-warning {{cast in a inline asm context requiring an l-value}} +} + -- 2.40.0