Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -905,6 +905,9 @@ /// Whether this parameter is an ObjC method parameter or not. unsigned IsObjCMethodParam : 1; + /// Whether this parameter is known to be modified throughout a function. + unsigned IsArgumentKnownToBeModified : 1; + /// If IsObjCMethodParam, a Decl::ObjCDeclQualifier. /// Otherwise, the number of function parameter scopes enclosing /// the function parameter scope in which this parameter was @@ -1560,6 +1563,7 @@ assert(ParmVarDeclBits.DefaultArgKind == DAK_None); assert(ParmVarDeclBits.IsKNRPromoted == false); assert(ParmVarDeclBits.IsObjCMethodParam == false); + assert(ParmVarDeclBits.IsArgumentKnownToBeModified == false); setDefaultArg(DefArg); } @@ -1593,6 +1597,13 @@ return ParmVarDeclBits.IsObjCMethodParam; } + bool isArgumentKnownToBeModified() const { + return ParmVarDeclBits.IsArgumentKnownToBeModified; + } + void setIsArgumentKnownToBeModified() { + ParmVarDeclBits.IsArgumentKnownToBeModified = true; + } + unsigned getFunctionScopeDepth() const { if (ParmVarDeclBits.IsObjCMethodParam) return 0; return ParmVarDeclBits.ScopeDepthOrObjCQuals; Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -3778,6 +3778,12 @@ if (VD->isImplicit()) Flags |= llvm::DINode::FlagArtificial; + if (const auto *PD = dyn_cast(VD)) { + auto &CGOpts = CGM.getCodeGenOpts(); + if (CGOpts.EnableParamEntryValues && !PD->isArgumentKnownToBeModified()) + Flags |= llvm::DINode::FlagArgumentNotModified; + } + auto Align = getDeclAlignIfRequired(VD, CGM.getContext()); unsigned AddressSpace = CGM.getContext().getTargetAddressSpace(VD->getType()); Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" @@ -11276,6 +11277,15 @@ DiagnoseConstAssignment(S, E, Loc); } +/// Argument's value might be modified, so update the info. +static void EmitArgumentsValueModification(Expr *E) { + if (const auto *LHSDeclRef = dyn_cast(E->IgnoreParenCasts())) + if (const ValueDecl *Decl = LHSDeclRef->getDecl()) + if (const auto *PD = dyn_cast(Decl)) + if (!PD->isArgumentKnownToBeModified()) + const_cast(PD)->setIsArgumentKnownToBeModified(); +} + /// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not, /// emit an error and return true. If so, return false. static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { @@ -11283,6 +11293,12 @@ S.CheckShadowingDeclModification(E, Loc); + if (MemberExpr *ME = dyn_cast(E)) { + if (Expr *BaseExpr = ME->getBase()) + EmitArgumentsValueModification(BaseExpr); + } else + EmitArgumentsValueModification(E); + SourceLocation OrigLoc = Loc; Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context, &Loc); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1441,7 +1441,7 @@ PD->ParmVarDeclBits.HasInheritedDefaultArg = Record.readInt(); if (Record.readInt()) // hasUninstantiatedDefaultArg. PD->setUninstantiatedDefaultArg(Record.readExpr()); - + PD->ParmVarDeclBits.IsArgumentKnownToBeModified = Record.readInt(); // FIXME: If this is a redeclaration of a function from another module, handle // inheritance of default arguments. } Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1033,6 +1033,7 @@ Record.push_back(D->hasUninstantiatedDefaultArg()); if (D->hasUninstantiatedDefaultArg()) Record.AddStmt(D->getUninstantiatedDefaultArg()); + Record.push_back(D->isArgumentKnownToBeModified()); Code = serialization::DECL_PARM_VAR; assert(!D->isARCPseudoStrong()); // can be true of ImplicitParamDecl @@ -1046,6 +1047,7 @@ !D->isImplicit() && !D->isUsed(false) && !D->isInvalidDecl() && + !D->isArgumentKnownToBeModified() && !D->isReferenced() && D->getAccess() == AS_none && !D->isModulePrivate() && @@ -2011,6 +2013,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // KNRPromoted Abv->Add(BitCodeAbbrevOp(0)); // HasInheritedDefaultArg Abv->Add(BitCodeAbbrevOp(0)); // HasUninstantiatedDefaultArg + Abv->Add(BitCodeAbbrevOp(0)); // IsArgumentKnownToBeModified // Type Source Info Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // TypeLoc Index: test/CodeGen/dbginfo-var-change-templates.cpp =================================================================== --- /dev/null +++ test/CodeGen/dbginfo-var-change-templates.cpp @@ -0,0 +1,24 @@ +// RUN: %clang -femit-param-entry-values -emit-llvm -S -g %s -o - | FileCheck %s +// CHECK: !DILocalVariable(name: "a", arg: 1, scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}) +// CHECK: !DILocalVariable(name: "b", arg: 2, scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}, flags: DIFlagArgumentNotModified) + +template +__attribute__((noinline)) +T GetMin (T a, T b) { + T result; + ++a; + result = (a < b) ? a : b; + return result; +} + +int baa () { + int i=5, j=6, k; + k=GetMin(i,j); + + if (i == k) + ++k; + else + --k; + + return k; +} Index: test/CodeGen/debug-info-varchange.c =================================================================== --- /dev/null +++ test/CodeGen/debug-info-varchange.c @@ -0,0 +1,35 @@ +// RUN: %clang -femit-param-entry-values -emit-llvm -S -g %s -o - | FileCheck %s + +// CHECK: !DILocalVariable(name: "a", arg: 1, scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}, flags: DIFlagArgumentNotModified) +// CHECK: !DILocalVariable(name: "b", arg: 2, scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}) +// CHECK: !DILocalVariable(name: "test_s", arg: 3, scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}, flags: DIFlagArgumentNotModified) +// CHECK: !DILocalVariable(name: "test_s2", arg: 4, scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}) +// CHECK: !DILocalVariable(name: "x", scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}) +// CHECK: !DILocalVariable(name: "y", scope: {{.*}}, file: {{.*}}, line: {{.*}}, type: {{.*}}) + +typedef struct s { + int m; + int n; +}S; + +void foo (int a, int b, + S test_s, S test_s2) +{ + b++; + b = a + 1; + if (b>4) + test_s2.m = 434; +} + +int main() +{ + S test_s = {4, 5}; + + int x = 5; + int y = 6; + + foo(x , y, test_s, test_s); + + return 0; +} +