-
Notifications
You must be signed in to change notification settings - Fork 5
Contributing to Granary: Coding Conventions
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.
Spaces should always be used for both indentation and alignment, never tabs. Two spaces are used for indentation, not four, not eight.
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.
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 #define
s.
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.
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!
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 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) {
...
}
}
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.
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 }
).
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
Granary code is comprised of:
- C++
- C
- Python 2.7
- x86-64 assembly
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.
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.
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.