Skip to content
This repository has been archived by the owner on Jun 28, 2022. It is now read-only.

Contributing to Granary: Coding Conventions

pgoodman edited this page Dec 11, 2014 · 4 revisions

Code Style

Granary's code base follows the Google C++ Style Guide. All code is checked for stylistic issues using the cpplint.py linter tool. The linter can be disabled using by specifying GRANARY_LINT_CODE=0 when building Granary.

Indentation

Spaces should always be used for both indentation and alignment, never tabs. Two spaces are used for indentation, not four, not eight.

Namespaces

Granary has the following namespaces:

granary
├── internal
├── detail
├── arch
└── os

Most Granary code fits under the granary namespace. OS-specific code fits under the granary::os namespace, and architecture-specific code fits under the granary::arch namespace. The granary::detail and granary::internal namespaces are less obvious. granary::detail is used to prevent types / variables related to the implementation of some client-shared data structure from polluting the top-level granary namespace. granary::internal is similar, in that it contains types / variables that must be shared with client code in order for some client-shared data structures to function, but where the bare minimum about the implementation is revealed.

Anonymous namespaces are widely used within Granary. They can be nested within any other namespace, and help prevent symbol clashes. A good rule of thumb is that if you have a .cc file with a static variable or function, then those symbols belong within an anonymous namespace.

Special Naming Conventions

Most functions in Granary follow Pascal Case. For example, a function should be named as FooBar, not fooBar, Foobar, foo_bar or any other variant of this.

Global variables should be named as foo_bar if they are mutable, kFooBar or FOO_BAR if they are constant, and FOO_BAR if they can be defined as compile-time enumeration constants. Always prefer enumeration constants over C preprocessor #defines.

An exception to the function naming rule is global functions or variables that are exported to C and Assembly, or are meant to be queried from GDB commands. In this case, functions are named as granary_foo_bar. The addition of granary_ is to prevent name clashes.

Comparison of two values

If two values are being compared, then the constant or r-value should always be placed on the left-hand side of the comparison operator in order to avoid potential mistakes related to writing = instead of ==.

For example:

if (a == 10) {  // BAD!
if (10 == a) {  // GOOD!
Global Variables

It is important to be careful and considerate when defining global variables. If the global variable does not need to be visible outside of its translation unit, then it should be defined as static and embedded within an anonymous namespace.

If the variable is trivially constructible, then no special considerations are needed. However, if the type of the variable defines a non-trivial constructor, then the variable should be marked with the GRANARY_GLOBAL macro. For example:

namespace {
static FooBar y GRANARY_GLOBAL;
};  // namespace

The GRANARY_GLOBAL helps to ensure that your global variable gets initialized, but only after certain critical internal Granary global variables are initialized.

Note: Constructors of global variables should not depend on FLAG_* variables or on heap allocation.

If the constructor of a global variable depends on features like dynamic memory allocation, then the Container template should be used, and then the variable should be manually constructed as a well-defined point. For example:

#include <granary.h>
using namespace granary;

static Container<FooBar> y;
...
GRANARY_ON_CLIENT_INIT() {
  y.Construct();
})

If a Container is used, then marking the container as a GRANARY_GLOBAL is not necessary.

Static Local Variables

Static local variables of any kind are strictly forbidden. For example, the following code should never be written:

void FooBarInit(void) {
  static bool is_initialized = false;
  if (!is_initialized) {
    ...
  }
}

Instead, the above code should be re-factored into a use of a global static variable that is embedded within an anonymous namespace. For example:

namespace {
static bool foo_bar_is_initialized = false;
}  // namespace
void FooBarInit(void) {
  if (!foo_bar_is_initialized) {
    ...
  }
}

Client-shared Code

Some Granary code and data-structures are exported to clients. This code is amalgamated into a single header file, which is auto-generated as part of Granary's build process. For example, the #include <granary.h> at the top of a client .cc file in a debug build will refer to a file like generated/debug_linux_user/granary.h.

Some Granary code is explicitly prevented from being shared with clients. Two examples of this are the internal fields of BasicBlock and Instruction objects. Granary code is prevented from being exported to client code in one of two ways: in-line declaration constraints, and block-level contraints.

1. In-line Declaration Constraints

Individual declarations can be excluded from the shared client header file by using the GRANARY_INTERNAL_DEFINITION macro. For example:

class Instruction {
  ...
  GRANARY_INTERNAL_DEFINITION std::unique_ptr<Instruction> UnsafeUnlink(void);
  ...
};

In the above example, GRANARY_INTERNAL_DEFINITION applies at a line granularity to the declaration following the GRANARY_INTERNAL_DEFINITION up to the next matching ; that is correctly nested with braces ({ and }).

2. Block-level Export Constraints

Code is prevented from being visible to clients at a block level by using the C preprocessor's #ifdef command. For example:

#ifdef GRANARY_INTERNAL
// internal code here.
#endif  // GRANARY_INTERNAL

Non-client Granary .h files that should never be exported to clients should be guarded by the following macro check at the beginning of the file:

#ifndef GRANARY_INTERNAL
# error "This code is internal to Granary."
#endif

Languages and Libraries

Granary code is comprised of:

  • C++
  • C
  • Python 2.7
  • x86-64 assembly
C++

C++11 is the standard C++ used. C++11 comes with several convenient features: limited type inference via the auto storage class, and range-based for loops. Both of these concepts are used extensively within Granary.

Several C++ features are forbidden:

  • Run-time type information (RTTI).
  • Exceptions.
  • C++ standard library containers.

The reasons for these restrictions is that all of the above C++ features require run-time support that is absent in both user- and kernel-space (e.g. in user space, Granary does not link against libc or libc++/libstdc++, nor does it link against crt*.o).

C++ header files are named as file.h, and source files are named as file.cc. Header and source files are typically placed in the same directory, except where the header file exposes a generic interface to one and only one implementation of that interface. For example, the header files in the arch directory have their corresponding implementations in arch/x86-64 for the x86-64 architecture.

C

Functions provided by libc must not be used, unless a custom implementation is given. For example, Granary defines its own memset function. Granary also defines custom versions of various system calls.

x86-64 Assembly

Granary uses x86-64 assembly. Assembly files are named as file.asm, and can use all of the features of the C pre-processor. Assembly can be written using either AT&T or Intel syntax. To write the file using AT&T's assembly syntax, begin with file with START_FILE_ATT. Similarly, to write the file using Intel's assembly syntax, begin the file with START_FILE_INTEL. Intel syntax is the preferred syntax.