[PATCH] Fixing a bug where debug info for a local variable gets emitted at file scope

Yunzhong Gao Yunzhong_Gao at playstation.sony.com
Fri Aug 9 15:06:54 PDT 2013

ygao added you to the CC list for the revision "Fixing a bug where debug info for a local variable gets emitted at file scope".

When compiling the following test file with "-g -O0" and then debug with gdb:
/* test.cpp */
const int d = 100;

extern int foo();

int main()
 const int d = 4;
 const float e = 4;
 const char* f = "Woopy";

 return d + foo(); // the value of d inside main() is 4.

int foo()
 return d; // the value of d inside foo() is 100.
/* end of test.cpp */

$ clang -g -O0 test.cpp -o a.out
$ gdb a.out
(gdb) b foo
(gdb) r
(gdb) print d
/* the expected output is 100; but gdb prints 4 instead */

The current behavior is that Clang will try to defer generation of global
variable definitions until its first use. At that point, tryEmitAsConstant()
in CGExpr.cpp calls EmitValueDeclDbgValue(), which then calls a version of
EmitGlobalVariable (to be discussed in more details below) to emit debug info
for the deferred global variable or enum constant. There are two problems in
the current implementation:

1. When tryEmitAsConstant() generates debug info for the deferred global
variables, it does not check that they are indeed global variables. As a
result, some of the local variables have their debug info emitted again.
A fix to this problem is straight-forward; we just need to add a check for
global variables.

2. The version of EmitGlobalVariable() for deferred global variables always
prints out their debug info at file scope (referred to as "the third version"
in the paragraph below). One unpleasant result is that a namespace-scoped
variable will have its debug info emitted without its enclosing namespace info.
Another unpleasant result is that, combined with the first problem mentioned
above, a local variable can have its debug info emitted at file scope.

There are three versions of EmitGlobalVariable() in CGDebugInfo.cpp. The
first version (in the order they appear in CGDebugInfo.cpp) handles all C/C++
non-deferred global variables; the second version handles objective-c
interfaces; and the third version handles enumerator constants and deferred
global variables. I may refer to these functions as "the first version", "the
second version" or "the third version" in later descriptions.

To fix this second problem, the version of EmitGlobalVariable() for deferred
global variables should behave like the version for non-deferred variables
in terms of computing proper enclosing scopes and maybe attaching the proper
mangled names as well. I feel that if we want the two functions to behave
in similar ways, then we should try to use one function instead of having
duplicate codes in two separate functions. So my proposed fix is to pull out
the enumerator constant part from the third version of EmitGlobalVariable()
into its own function, and then merge the remaining bits into the first
version of EmitGlobalVariable().

I am leaving the objective-c version unchanged only because I am not familiar
with objective-c interface. There are a few subtle differences between the
objective-c version and the other two versions, and I am not confident to merge
all three versions together.

I try to give some line-by-line explanations about my changes:

==== CGDebugInfo.cpp: ====
Line#3114: a new function EmitEnumConstant() is created to handle the
  enumerator constant part in the third version (see discussion above) of
Line#3043-3046: the first version of EmitGlobalVariable() is modified to
  handle both deferred and non-deferred global variables. The deferred global
  variables will have initializers so the parameter type is changed from
  llvm::GlobalVariable to llvm::Value; the parameter name is also changed
  to be more clear.
Line#3052: deferred global variables have internal linkage because global
  variables with external linkage are not deferred.
==== CodeGenFunction.cpp ====
Line#1382-1403: check whether the declaration is VarDecl (global variable) or
  EnumConstantDecl (enumerator constant), and call EmitGlobalVariable() or
  EmitEnumConstant() accordingly. DeclRefExpr->getDecl() is passed in as
  parameter instead of DeclRefExpr itself; the function is renamed from
  EmitDeclRefExprDbgValue to EmitValueDeclDbgValue to match the change in
  parameter type.

  The type checking codes are moved here from CGExpr.cpp. There is one
  difference on line#1394 where two new conditions are added:
    if (VD->isFileVarDecl() && !getLangOpts().EmitAllDecls &&

 The first condition "isFileVarDecl()" is to make sure that local variables
 do not get their debug info emitted again. The function name is a little bit
 of a misnomer because if you look at isFileContext() in DeclBase.h (which is
 called by isFileVarDecl), you see that both file-scope and namespace-scope
 variables are considered global variables.

 -femit-all-decls is an option that disables global variables' deferring.
 If this option is turned on, then do not need to emit debug info for global
 variables when we see them being used, because their debug info is already
 emitted at declaration time.
=== CGExpr.cpp ===
All the type-checking and dispatching codes are moved to CodeGenFunction.cpp,
so tryEmitAsConstant() only needs to call EmitValueDeclDbgValue().
Also, I propose adding the following two tests to the
debuginfo-tests repository.

Could someone review this for me? Both the fix and the tests

Many thanks,
- Gao.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D1281.3.patch
Type: text/x-patch
Size: 11594 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130809/178db8f9/attachment.bin>

More information about the cfe-commits mailing list