Blog 4 min read

Hacking on Clang to demystify the temporary objects

Share this article
Hacking on Clang to demystify the temporary objects

It is sometimes necessary for the C++ compiler to create temporary objects. They are used during:

  • Reference initialization.
  • Evaluation of expressions including standard type conversions.
  • Argument passing.
  • Function returns.
  • Evaluation of the throw expression.

For non-trivial classes, the creation and destruction of temporary objects can be expensive in terms of processing time and memory usage. In this case, you should minimize their introduction.  The C++ compiler does eliminate some temporary objects, but it cannot eliminate all of them.

It’s not always easy to detect where temporary objects are introduced, as Herb Sutter explains in his sample. The compiler has this information, but is it possible to get from the compiler the places where temporary objects are introduced? And if not, is it easy to modify it to report them?

In our case we will use Clang: it’s very flexible and provides many ways to customize its behavior. Indeed, a major design concept of Clang is its use of a library-based architecture. In this design, various parts of the front end can be cleanly divided into separate libraries, which can then be mixed and matched for different needs and uses. In addition, the library-based approach encourages good interfaces and makes it easier for new developers to get involved (because they only need to understand small pieces of the big picture).

The Clang compiler has three phases:

  • The front end, which parses source code, checks it for errors, and builds a language-specific Abstract Syntax Tree (AST) to represent the input code.
  • The optimizer: its goal is to perform some optimizations on the AST generated by the front end.
  • The back end, which generates the final code to be executed by the machine; it depends on the target.

In our case we will focus more on the front end phase: the goal is to get the Abstract Syntax Tree (AST) for a source file and check whether some useful data about temporary objects is reported.

Let’s explore the AST of this minimal source code:

ast2

To generate the AST, we can execute the Clang front end parser using the -cc1 switch.

clang -cc1 -ast-dump test.cpp

And here’s the AST generated for the GetTest function:

ast1

In this AST, two pieces of information are related to temporary objects: nrvo and elidable.

Named Return Value Optimization is a compiler optimization technique that involves eliminating the temporary object created to hold a function’s return value. NRVO eliminates the copy constructor and destructor of a stack-based return value. This optimizes out the redundant copy constructor and destructor calls and thus improves overall performance. For more details, you can refer to its wiki page.

And the copy elision refers to a compiler optimization technique that eliminates unnecessary copying of objects. For more details, you can refer to its wiki page.

It’s interesting that Clang tells us where an NRVO is applied, but it doesn’t explicitly report where temporary objects are created. Let’s go inside the AST dumper source code and try to report them.

How does ASTDumper work?

The compiler parses a program and represents the parsed program as an abstract syntax tree (AST). The AST has many different kinds of nodes, such as Assignment, Variable Reference, and Arithmetic Expression nodes. After generating the AST, Clang invokes some front end actions that traverse it and perform some processing; ASTDumpAction is one of them, and it dumps the AST to the console.

ASTDumper is declared like this

  class ASTDumper
      : public ConstDeclVisitor, public ConstStmtVisitor,
        public ConstCommentVisitor 

The visitor pattern is the recommended pattern when we need to traverse a structure and apply specific processing to each node of that structure.

Here are some methods invoked when the AST is traversed; each one is related to a specific AST node.

void VisitNamespaceDecl(const NamespaceDecl *D);
void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D);
void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D);
void VisitTypeAliasDecl(const TypeAliasDecl *D);
void VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D);
void VisitCXXRecordDecl(const CXXRecordDecl *D);
void VisitStaticAssertDecl(const StaticAssertDecl *D);

Hacking the Clang AST dumper

Our goal is to report the temporary objects created, so we have to track object creation and identify which AST node is concerned by the object construction.

In Clang, VisitCXXConstructExpr is invoked when a C++ object must be created; here’s its implementation:

ast5

As we can see, there’s no test for whether the created object is temporary or not, but the good news is that CXXConstructExpr has the IsTemporaryObject method, which determines whether the result of this expression is a temporary object of the given class type.

Let’s change the implementation to add another condition:

ast6

Here’s the new AST printed after the modification

ast3

After adding a few lines of code, the ASTDumper now explicitly reports where temporary objects are created.

Conclusion

The LLVM/Clang duo is not just a compiler, but a powerful infrastructure for developing your own C/C++/Objective-C tools. It’s not difficult to understand how it works, and it’s easy to customize as you like. Don’t hesitate to download the Clang source code, make some modifications and rebuild it — it will especially help students learn how compilers work.

Share this article