Skip to content
XS-Labs Coding Style Guide for C, C++, Objective-C and x86 Assembly
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
LICENSE.md
README.md

README.md

XS-Labs Coding Style Guide for C, C++, Objective-C and x86 Assembly

Build Status Issues Status License Contact
Donate-Patreon Donate-Gratipay Donate-Paypal

Table Of Contents

  1. About
  2. License
  3. C Style Guide
  4. C++ Style Guide
  5. Objective-C Style Guide
  6. x86 Assembly Style Guide

About

These are the coding conventions used for all recent XS-Labs projects.
Feel free to comment, raise issues, fork and/or adapt this document.

Note about whitespaces

XS-Labs' coding conventions make a heavy use of whitespaces.
It's not a common habit among coders, who usually prefers compact code.

This is just a personal taste.
After years of professional development, I like to see a lot of spaces in my own code. It's like letting the code breath.
Code is about rhythm, and readability. And I think taking time to carefully align operators, declarations, etc. improves the overall readability and the feeling you can get while reading code.

License

This style guide is published under the terms of the FreeBSD documentation license.

C Style Guide

  1. Indentation
  2. Ending line
  3. Comments
  4. Maximum number of columns
  5. Includes
  6. Whitespace
    1. Operators
    2. Parenthesis and brackets
    3. Pointers
    4. Casts
    5. for loops
  7. Braces
  8. Alignment
    1. Assignments
    2. Variable declarations
    3. Single line conditionals
    4. Array subscripting operator
  9. Case and symbols naming
  10. Variable declaration
  11. Macros
  12. Structures and unions
  13. Enumerated types
  14. Typedefs
  15. New lines
  16. Header guards
  17. Functions prototypes
  18. Functions with no parameters
  19. Inline functions
  20. Dereferencing
  21. Conditionals
  22. Switch statements
  23. Long if/else if statements
  24. C++ compatibility
  25. Inline documentation
  26. Compilation

1. Indentation

Code should always be indented using four spaces. Never use tabulations for indentation.

Preprocessor directives should also be indented:

void foo( void )
{
    #ifdef FOO
    /* ... */
    #endif
}

Not:

void foo( void )
{
#ifdef FOO
    /* ... */
#endif
}

2. Ending line

Source and header files should always end with a single empty line.

3. Comments

Comments should always use the /* */ notation.
Single line C++ style comments (//) are strictly prohibited.

A line of comment should whenever possible be no more that 80 columns.

When a comment consists of a single line, place the /* */ on the same line.
If the comments consists of multiple lines, place the /* */ on a new line:

/* Single line comment */

/*
 * Multiple
 * line
 * comment
 */

When using multiple line comments, always align the * signs, as in the above example.

4. Maximum number of columns

The number of columns for a single line is not limited.
However, try whenever possible to wrap long lines in order to improve the overall readability.

5. Includes

Include directives should always come first, before any other declaration:

#include <stdio.h>

int x;

Not:

int x;

#include <stdio.h>

An exception is made when a header needs a specific macro to be set before inclusion:

#define FOOBAR 1 /* Permitted */

#include <foobar.h>

6. Whitespace

6.1 Operators

A single whitespace character should always be used around all operators except unary operators:

x = 1 + 2 + 3;

x++;
~x;

if( !x )
{
    /* .... */
}

Not:

x=1+2+3;

x ++;
~ x;

if( ! x )
{
    /* .... */
}

6.2 Parenthesis and brackets

A single whitespace character should always be used inside parenthesis and brackets, but never before:

x[ 0 ] = 0;

foo( x );

if( y == 0 )
{
    /*  */
}

Not:

x[0] = 0;

foo (x);

if (y == 0)
{
    /*  */
}

6.3 Pointers

The pointer sign should alway have a leading and trailing space:

int * x;

Not:

int* x;
int *x;

6.4 Casts

No whitespace should be added after a cast. A single whitespace should be used after the opening parenthesis and before the closing one:

x = ( char * )y;

Not:

x = ( char * ) y;

6.5 for loops

When using for loops, a single whitespace character should be used after the semicolons:

for( i = 0; i < 10; i++ )
{
	/* ... */
}

Not:

for( i = 0;i < 10;i++ )
{
	/* ... */
}

7. Braces

Braces should always be placed on an empty line.
This apply for all constructs (functions, conditions, loops, etc.).
Code inside braces should be indented by four spaces:

void foo( void )
{
    if( ... )
    {
    	/* ... */
    }
    else if( ... )
    {
    	/* ... */
    }
    else
    {
    	/* ... */
    }
    
    for( ... )
    {
    	/* ... */
    }
    
    while( ... )
    {
    	/* ... */
    }
    
    do
    {
    	/* ... */
    }
    while( ... );
}

An exceptions can be made for very simple constructs:

     if( ... ) { x = 1; }
else if( ... ) { x = 2; }

8. Alignment

8.1 Assignments

Always align consecutive assignments:

x       = 1;
foo     = 2;
foobar += 2;

Not:

x = 1;
foo = 2;
foobar += 2;

If using multiple lines in an assignment, aligns the extra lines to the equal sign:

x      = 1;
foobar = x
       + 1
       + 2;

When using conditional assignment, aligns the ? and : signs whenever possible.
The ? sign should be aligned by adding whitespaces before the closing parenthesis:

x      = 1;
foobar = ( x      ) ? 2      : x + 3;
foo    = ( foobar ) ? foobar : x;

Not:

x      = 1;
foobar = ( x ) ? 2 : x + 3;
foo    = ( foobar ) ? foobar : x;

8.2. Variable declarations

Always aligns the names of variables:

int           x;
unsigned long y;
float         z;

Not:

int x;
unsigned long y;
float z;

When using pointers, place the pointer sign next to the variable name:

int           * x;
unsigned long * y;

8.3. Single line conditionals

If using single line conditional statements (see above), align the if/else statements, as well as the opening/closing braces and comparison operators:

     if( x      == 1 ) { foobar = 1;          }
else if( foobar == 1 ) { x      = 0xFFFFFFFF; }
else				   { x      = 0;          }

Not:

if( x == 1 ) { foobar = 1; }
else if( foobar == 1 ) { x = 0xFFFFFFFF; }
else { x = 0; }

8.4. Array subscripting operator

Always align the closing brackets when using the array subscripting operator. Indexes should be indented in a logical manner:

x[   1 ] = 0;
x[ 100 ] = 0;

Not:

x[ 1 ]   = 0;
x[ 100 ] = 0;

9. Case and symbols naming

Local variables should never start with an underscore, and should always start with a lowercase letter.
Lower camel-case is recommended.

For global symbols (variables and functions), upper camel-case is usually recommended.
Underscores may be used to simulate namespaces.

void SomePackage_SomePublicFunction( void );

extern int SomeInteger;

Symbols shall never start with two underscores, or with an underscore followed by an uppercase letter.

10. Variable declaration

Local variables should be declared without a value, and before any other statement:

void foo( void )
{
    int x;
    int y;
    
    x = 0;
    x = 1;
    
    foo();
    foobar();
}

Not:

void foo( void )
{
    bar();
    
    int x = 0;
    
    foobar();
    
    int y = 0;
}

The same applies for for loops:

int i;

for( i = 0; i < 10; i++ )
{
    /* ... */
}

Not:

for( int i = 0; i < 10; i++ )
{
    /* ... */
}

11. Macros

Macros should always be in uppercase.

#define FOO         1

If a macro takes parameters, the parameters names should begin and end with a single underscore:

#define BAR( _x_ )  ( ( _x_ ) + 1 )

As in the above example, parenthesis should always be used around a macro parameter.

12. Structures and unions

Members of structures and unions should be properly aligned, as mentioned before:

struct foo
{
	int           x;
	unsigned long y;
};

union bar
{
	int           x;
	unsigned long y;
};

When manually padding a struct, use a leading underscore for the member name, and a trailing number, prefixed with a single underscore.
Always use a char array to manually pad a structure:

struct foo
{
	char s;
	char _pad_0[ 3 ];
	int  x;
};

13. Enumerated types

Enum values should be properly aligned, as mentioned before.
Explicit values are preferred, as well as hexadecimal notation:

enum
{
	Foo    = 0x01,
	Bar    = 0x02,
	Foobar = 0x10
};

Not:

enum
{
	Foo,
	Bar,
	Foobar = 0x10
};

14. Typedefs

Simple typedefs are declared on a single line:

typedef int foo;

With structures, unions and enumrated types place the type name on a new line:

typedef struct
{
    int x;
    int y;
}
foo;

For enumrated types, each value should be prefixed by the type name:

typedef enum
{
    FooX = 0,
    FooY = 1,
    FooZ = 2
}
Foo;

Not:

typedef enum
{
    X = 0,
    Y = 1,
    Z = 2
}
Foo;

15. New lines

An empty line should be used to separate logical parts of the code, as well as to separate function calls and assignments:

x = 0;
y = 0;

foo();

z = 2;

Not:

x = 0;
y = 0;
foo();
y = 2;

16. Header guards

All headers should be properly guarded:

#ifndef FOO_H
#define FOO_H

/* ... */

#endif /* FOO_H */

The name of the macro used as header guard should always be in uppercase.
It should consist of the name of the header file (optionally with directories prefixes, separated by a single underscore), and a trailing _H.

For instance, for include/foo/bar/foobar.h, this should be FOO_BAR_FOOBAR_H.

17. Functions prototypes

Function prototypes should always declare the parameters names.
A single whitespace character should be used after the coma separating parameters.
The return type should be place on the same line as the function's name:

void foo( int x, int y );

Not:

void
foo( int,int );

18. Functions with no parameters

Functions without parameters should always be declared as taking void:

void foo( void );

Not:

void foo();

19. Inline functions

Inline functions should generally be avoided, unless there's a very good and specific reason to make them inline.

20. Dereferencing

When using the dereference operator *, always use an extra set of parenthesis:

*( x ) = 1;
y      = *( x );

Not:

*x = 1;
y  = *x;

21. Conditionals

Always use braces with conditionals:

if( x == 1 )
{
    x = 2;
}

Not:

if( x == 1 )
    x = 2;

Don't use else clauses when not necessary:

if( x == 0 )
{
    return true;
}

return false;

Not:

if( x == 0 )
{
    return true;
}
else
{
	return false;
}

Always prefer testing with ==, even with boolean values:

if( b == true )
{
    /* ... */
}

Not:

if( b )
{
    /* ... */
}

Also avoid using ! in a conditional:

if( b == false || p == null )
{
    /* ... */
}

Not:

if( !b || !p )
{
    /* ... */
}

22. Switch statements

When using switch statements, separate each case with an empty line and adds an empty line after the case.
The break statement should be indented, in regard to the case statement.

switch( x )
{
    case 1:
        
        /* ... */
        break;
        
    default:
        
        /* ... */
        break;
}

An exception can be made for very simple switch statements:

switch( x )
{
    case 1:  y      = 0;          break;
    default: foobar = 0xFFFFFFFF; break;
}

In such a case, break statements should be aligned, as well as assignment operators, if any.

23. Long if/else if statements

Very long if/else if statements should be wrapped the following way:

if
(
       x      == 1
    && y      == 2
    && foobar == 3
)
{
   /* ... */
}

Parenthesis are placed on a new line. Variable names and comparison operators should be aligned.

When using extra parenthesis, apply the same rules:

if
(
       x == 1
    && y == 2
    &&
    (
          z      == 3
       || foobar == 4
    )
)
{
   /* ... */
}

24. C++ compatibility

All headers should be compatible with C++, using extern "C":

#ifdef __cplusplus
extern "C" {
#endif

/* ... */

#ifdef __cplusplus
}
#endif

25. Inline documentation

Documented code should prefer Apple's HeaderDoc syntax rather than JavaDoc.

26. Compilation

Always compiles your code with -Werror or similar, and always use the highest possible error reporting level.

When function parameters are not used, cast them to void to prevent a warning:

void foo( int unused )
{
    ( void )unused;
}

C++ Style Guide

All rules from the C Style Guide applies here, with a few exceptions and additions described hereafter.

  1. Files and files extensions
  2. Comments
  3. Namespaces
  4. Classes 1. Naming 2. Inheritance 3. Members 4. Method definitions 5. Destructors
  5. Templates
  6. Using
  7. Variable declaration
  8. Function/method arguments
  9. Lambdas
  10. Enumerated types
  11. Includes and forward declarations
  12. Using C functions

1. Files and files extensions

C++ source files should end with the .cpp extension.
C++ headers should end with the .hpp extension.

Source and header files for classes should be named as the classes they declare/define.
Namespaces should be represented as directories.

2. Comments

Single line C++ comments are allowed, even if the /* */ notation is usually preferred, especially when the comment consists of multiple lines.

3. Namespaces

Namespaces should always follow the upper camel-case rule.

4. Classes

4.1. Naming

C++ classes should always follow the upper camel-case rule.
Properties should always follow the lower camel-case rule.
Methods should follow either the upper camel-case rule or the lower camel-case rule, but mixing both in the same project is not allowed.

Private members should always have a leading underscore.

4.2. Inheritance

The : sign should immediately follow the class name and should be followed by a single whitespace character.
When inheriting from multiple classes, a single whitespace character should be used after the comma:

class FooBar: Foo, Bar
{
	
};

4.3. Members

Public members should be declared first, followed by protected and private members.
Always group members with the same visibility (properties and methods), unless it is not possible.

Static members should also be declared first, inside the visibility group, followed by methods and properties.
Separate static methods, methods and properties by an empty line.

The public, protected and private keywords should be indented by four spaces and an empty line should be placed directly after.
Members should be indented by four more spaces:

class Foo
{
    public:
        
        static void staticMethod( void );
        
        Foo( void );
        
        int           x;
        unsigned long y;
        
    private:
        
        void _bar( void );
        
        int _z;
};

4.4. Method definitions

Except for templates, method should never be defined in the header files.

4.5. Destructors

Destructors should always be declared as virtual, unless there's a very good and specific reason not to do so.

5. Templates

Template parameters should be surrounded by a single whitespace character.
A single whitespace character should be used after the comma.
The < sign should immediately follow the class name:

template class Foo< int x, int y >
{
    
};

6. Using

The using keyword is usually discouraged for namespaces, except for very long namespaces.
It's strictly prohibited for the std namespace - the full notation should always be used.

std::vector< int > v;

Not:

using std;

vector< int > v;

7. Variable declaration

Variables may be declared with a value.
In such a case, parenthesis should be preferred over the equal sign.

int x( 0 );

Over:

int x = 0;

Using braces is also allowed.
In such a case, always add a leading space before the opening brace:

int x {};
int y { 42 };

Not:

int x{};
int y{ 42 };

8. Function/method arguments

Except for out parameters, primitive or integral types should always be passed by value.
For other types, passing const references is preferred:

void foo( int value );
void bar( const std::string & value );

Over:

void foo( const int & value );
void bar( std::string value );

9. Lambdas

When using lambdas, a single space should be placed before and after any capture.
A single space should be placed after the comma in the capture list.
When there is no capture, no space should be placed inside the brackets:

auto a = []( void )            {};
auto b = [ & ]( void )         {};
auto c = [ this ]( void )      {};
auto c = [ this, foo ]( void ) {};

Not:

auto a = [ ]( void )        {};
auto b = [&]( void )        {};
auto c = [this]( void )     {};
auto c = [this,foo]( void ) {};

No space should be placed between the capture brackets and the opening parenthesis for arguments.
When the lambda doesn't take arguments, always use ( void ), as for functions:

auto a = []( int x ) {};
auto b = []( void )  {};

Not:

auto a = [](int x) {};
auto b = []        {};

The return type may be omitted.
If specified, a leading and trailing space should be placed around ->:

auto a = []( void ) -> int {};

Not:

auto a = []( void )->int {};

10. Enumerated types

Scoped enumerations should always be preferred over unscoped enumerations:

enum class Foo
{
	X
	Y
};

Over:

enum Foo
{
	FooX
	FooY
};

11. Includes and forward declarations

The number of included files contained in the headers should be limited.
Always use forward declarations when possible:

class Foo;

class Bar
{
    public:
        
        Bar( Foo * f );
};

Not:

#include "Foo.h"

class Bar
{
    public:
        
        Bar( Foo * f );
};

12. Using C functions

The use of C functions is generally discouraged unless there's no C++ equivalent, or if there's a specific reason not to use a C++ equivalent.

C library headers should not be used directly. Always use the C++ variants instead, when available:

#include <cstdio>

Not:

#include <stdio.h>

Objective-C Style Guide

All rules from the C Style Guide applies here, with a few exceptions and additions described hereafter.

  1. Files
  2. Primitive datatypes
  3. Bracket notation
  4. Literals
  5. Constants
  6. Enumerated types
  7. NULL, nil and Nil
  8. Blocks
  9. Classes
    1. Naming
    2. Interface declaration
    3. Instance variables
    4. Properties
    5. Properties atomicity
    6. Methods
    7. Imports and forward declarations
    8. Private methods
    9. Categories
    10. Protocols
  10. Singletons/Shared instances
  11. NSLog
  12. Multithreading
  13. Compilation

1. Files

Source and header files for classes should be named as the classes they declare/define.
Directories should be used to separate logical groups of source files.

For categories, the source and header files should be name as the class, followed by a + and the category name (SomeClass+SomeCategory.m).

2. Primitive datatypes

Unless there's a specific need to do so, never use the C primitive datatypes.
The Objective-C equivalent should always be preferred:

  • NSInteger instead of int or long
  • NSUInteger instead of unsigned int or unsigned long
  • CGFloat instead of float or double

Or use the types from stdint.h when needed.

3. Bracket notation

When sending messages, a single whitespace should be used before and after the brackets:

a = [ [ NSArray alloc ] init ];

Not:

a = [[NSArray alloc]init];

Long lines should be wrapped the following way:

a = [ [ NSDictionary alloc ] initWithObjects: obj1,
                                              obj2,
                                              obj3,
                                              nil
                             forKeys:         @"key1",
                                              @"key2",
                                              @"key3",
                                              nil
    ];

The closing bracket is aligned with the opening one.
Parts of the method name are aligned to the left, as well as arguments.

5. Literals

The use of literals is generally encouraged, as well as subscripting:

array      = @[ obj1, obj2 ];
dictionary = @{ @"key1" : obj1, @"key2" : obj2 };
number     = @42;

Instead of:

array      = [ NSArray arrayWithObjects: obj1, obj2, nil ];
dictionary = [ NSDictionary dictionaryWithObjectsAndKeys: obj1, @"key1", obj2, @"key2", nil ];
number     = [ NSNumber numberWithInteger: 42 ];

Long declarations for array or dictionaries literals should be wrapped the following way:

array      = @[
                  obj1,
                  obj2
              ];
dictionary = @{
                  @"key1"   : obj1,
                  @"key2"   : obj2,
                  @"foobar" : obj2
              };

The opening and closing brackets/braces are aligned.
Contained objects are placed on their own line, indented by four spaces.

Fo dictionaries, the : signs are aligned.

6. Constants

The use of the k prefix for constants is prohibited.
When related to a class, constants should be prefixed by the class name.

The name of constants should follow the upper camel-case rule.

The use of the extern keyword is strictly prohibited for constants. Use the FOUNDATION_EXPORT macro instead:

FOUNDATION_EXPORT NSString * const SomeClassConstantName;

Not

extern NSString * const kConstantName;

7. Enumerated types

The use of the NSEnum and NSOptions macros is encouraged, while not mandatory.

8. NULL, nil and Nil

NULL, nil and Nil should not be used interchangeably.

nil should always be used for instances, while Nilshould be used for classes.
For any other pointer type, NULL should be used.

9. Blocks

The declaration of a block should follow the same rules as the declaration of a function pointer:

NSUInteger ( ^ blockName )( NSUInteger x );

The ^ sign is surrounded by a single whitespace character.
For complex blocks, a typedef is recommended:

typedef NSUInteger ( ^ blockTypeName )( NSUInteger x );

The definition of blocks should follow the function's definition style.
No whitespace should be placed after the ^ sign.
Block's code should be indented by four spaces.

block = ^( void )
{
    /* ... */
};

Blocks without arguments should always be defined as taking void.

9. Classes

  1. Naming
  2. Interface declaration
  3. Instance variables
  4. Properties
  5. Properties atomicity
  6. Methods
  7. Imports and forward declarations
  8. Private methods
  9. Categories
  10. Protocols

9.1. Naming

The name of classes should follow the upper camel-case rule.

9.2. Interface declaration

The interface declaration should follow the following rules:

Instance variabes are prohibited in the interface (see 9.3. Instance variables).

Properties and methods should not be indented.
Properties should come first, followed by methods.

The : sign after the class name should have a trailing whitespace character, but never a leading one.

If protocols are implemented, the opening < sign should have a leading and trailing whitespace character.
The closing > sign should have a leading whitespace character.
A single whitespace character should be placed after the comma, when implementing multiple protocols.

Example:

@interface Foobar: NSObject < Foo, Bar >

@property( atomic, readonly ) NSInteger x;

- ( void )foo;

@end

9.3. Instance variables

Instance variables should follow the lower camel-case rule and start with a single leading underscore.

Using instance variables should be avoided avoided whenever possible.
Use properties instead.

Instance variables are not allowed in the public interface.
Use a private class extension in the implementation instead.

Except when using headerdoc comments, the name of the instance variables should be aligned, as mentioned in the alignment topic of the C style guide:

@interface Foo()
{
    NSUInteger     _x;
    NSArray      * _array;
    NSDictionary * _dict;
}

Not:

@interface Foo()
{    
    NSUInteger _x;
    NSArray * _array;
    NSDictionary * _dict;
}

9.4. Properties

Properties variables should follow the lower camel-case rule.

Properties should always declare their full attributes:

@property( nonatomic, readwrite, assign ) NSUInteger x;

Not:

@property( assign ) NSUInteger x;

9.5. Properties atomicity

Properties should generally be declared as atomic, unless there's a specific reason not to do so.

An exception is made for IBOutlet properties, which should always be nonatomic.

9.6. Methods

Methods should follow the lower camel-case rule.
The use of a leading underscore is strictly prohibited.

Empty parameter names are discouraged.

A trailing whitespace should be used after the + or - sign.
The method's return type and argument's types should follow the same rule as casts, as mentioned in the C style guide.
A trailing whitespace character should be used after the : sign, but not before:

- ( void )methodWithObject1: ( id )object object2: ( id )object;

Not:

-( void ) methodWithObject1 :( id ) object object2 :( id ) object;

Parameter names should be as explicit as possible.

The use of a the or a prefix for a parameter name is strictly prohibited.

( id )object

Not:

( id )anObject

9.7. Imports and forward declarations

The number of included files contained in the headers should be limited.
Always use forward declarations when possible:

@class Foo;

@interface Bar

- ( id )initWithFoo: ( Foo * )foo;

@end

Not:

#import "Foo.h"

@interface Bar

- ( id )initWithFoo: ( Foo * )foo;

@end

9.8. Private methods

Private methods should be declared and defined in a class extension, in the main implementation.
Declaration may be avoided, but is usually preferred:

#import "Foo.h"

@interface Foo()

- ( void )bar;

@end

@implementation Foo()

- ( void )bar
{}

@end

9.9. Categories

Each category should have its own header and source file.

Declaration should be as follow, with a single whitespace character around the category name, and no leading whitespace before the ( sign:

@interface Foo( CategoryName )

Not:

@interface Foo (CategoryName)

9.10. Protocols

If the protocol conformance is not intended to be public, the main interface file should not declare it.

For instance, a Foo class implementing NSTableViewDelegate (for internal use).

Foo.h:

@interface Foo: NSObject

@end

Foo.m:

#import "Foo.h"

@interface Foo() < NSTableViewDelegate >

@end

10. Singletons/Shared instances

Singletons or shared instances should always be created with the dispatch_once pattern.
For pure singletons, allocWithZone: should be overriden to return the shared instance:

+ ( instancetype )sharedInstance
{
    static dispatch_once_t once;
    static id              instance = nil;
    
    dispatch_once
    (
        &once,
        ^( void )
        {
            instance = [ [ super allocWithZone: nil ] init ];
        }
    );
    
    return instance;
}

+ ( id )allocWithZone: ( NSZone * )zone
{
    ( void )zone;
    
    return [ self sharedInstance ];
}

11. NSLog

NSLog should be used carefully.
It's recommended to use a macro to disable logging for release builds.

12. Multithreading

The use of libdispatch should always be preferred to standard NSThread approach, unless there's a specific reason to use NSThread, like ensuring the thread is detached immediately.

13. Compilation

GCC should never be used to compile Objective-C. Use Clang/LLVM instead.

x86 Assembly Style Guide

  1. Syntax
    1. Direction of Operands
    2. Prefixes
    3. Suffixes
    4. Memory operands
  2. Local labels
  3. Indentation
  4. Alignment
  5. Whitespace
  6. Comments
  7. Grouping
  8. Procedures comments
  9. Optimisation
    1. Zeroing
    2. Comparing with zero
    3. Incrementing and decrementing
    4. Branching
    5. Loops unrolling

1. Syntax

The Intel syntax should always be preferred, when possible, to the AT&T syntax, for clarity.
An exception is made for inline assembly, when compiling C code with Clang or GCC.

The basic differences are the following:

1.1. Direction of Operands

The direction of the operands in the Intel syntax is the opposite of AT&T syntax.
In the Intel syntax, the destination operand comes first, followed by the source operand:

    instr dest, src  ; Intel
    instr src,  dest # AT&T

1.2. Prefixes

The Intel syntax doesn't use prefixes for register names or immediate operands, while the AT&T syntax uses the % prefix for registers and $ for immediate operands:

    mov  rax, 1    ; Intel
    movq $1,  %rax # AT&T

1.3. Suffixes

The Intel syntax doesn't use suffixes for mnemonics, while the AT&T syntax uses b, w, l and q.
The size of the operands is automatically assumed when using registers.
For memory operands, similar directives can be used (BYTE, WORD, DWORD, QWORD):

    ; Intel
	mov  rax, 1
	mov  eax, 1
    # AT&T
	movq $1, %rax
	movl $1, %eax

1.4. Memory operands

The Intel syntax uses [] for memory operands, while the AT&T syntax uses ():

    mov  rax, [ rdi + 8 ] ; Intel
    movq 8( %rdi ), %rax  # AT&T

The index, scale, displacement and segment also use a different notation:

mov  rax, segment:[ base + index * scale + displacement ] ; Intel
movq %segment:displacement( base, index, scale ), %rax    # AT&T

2. Local labels

Local labels inside a procedure should always start with a dot.
For instance:

procedure:
    
    .label1:
        
        ; ...
        
    .label2:
        
        ; ...
        
    ret

Label names should be meaningful.
Don't use compiler style label names, like L1:.

3. Indentation

Code should always be indented using four spaces. Never use tabulations for indentation.

4. Alignment

Mnemonics and operands should be aligned, in order to improve the code's readability, and a decent amount of spaces should be placed between the mnemonics and the operands:

xor      rax,       rax
mov      al,        1
pxor     xmm0,      xmm0
movdqa   xmm1,      [ rsi ]
movdqa   [ rdi ],   xmm1

Not this:

xor rax, rax
mov al, 1
pxor xmm0, xmm0
movdqa xmm1, [rsi]
movdqa [rdi], xmm1

5. Whitespace

When using memory operands, inserts a white space between the brackets:

mov rax, [ rdi ]

Not:

mov rax, [rdi]

Also inserts a a whitespace around arithmetic operators:

mov rax, [ rdi + 8 ]

Not:

mov rax, [rdi+8]

6. Comments

Comments should be placed on a new line and should not exceed 80 columns in width:

; This is a comment
; with another line...
xor rax, rax

Not:

xor rax, rax ; This is a comment

Comments should be as meaningful as possible.
Don't simply describe what you are doing, but also why you are doing it.

7. Grouping

Instructions should be grouped in a logical manner, with a newline between groups:

; Comment for instruction group 1
xor     rax,     rax
xor     rcx,     rcx
mov     al,      2
mov     cl,      8
mul     rcx

; Comment for instruction group 2
pxor     xmm0,   xmm0
movdqa   xmm1,   [ rdi ]

8. Procedures comments

All procedures should start with a standard comment, describing the procedure, the input and return registers, as well as killed registers, if any:

;-------------------------------------------------------------------------------
; Short description of the procedure (single line)
; 
; Long description of the procedure
; (can be multi-line)
; 
; Input registers:
;       
;       - RDI:      Parameter description
;       - RSI:      Parameter description
; 
; Return registers:
;       
;       - RAX:      Return value description
; 
; Killed registers:
;       
;       - RCX
;       - RDX
;------------------------------------------------------------------------------- 

When no register is used as input or as return, or when no register is killed:

;-------------------------------------------------------------------------------
; Short description of the procedure (single line)
; 
; Long description of the procedure
; (can be multi-line)
;
; Input registers:
;       
;       None
; 
; Return registers:
;       
;       None
; 
; Killed registers:
;       
;       None
;------------------------------------------------------------------------------- 

9. Optimisation

The following is not mandatory, but it's strongly advised to follow these recommendations, when writing performance-critical code.

9.1. Zeroing

Always use xor to zero a register, instead of mov:

xor rax, rax

Not:

mov rax, 0

9.2. Comparing with zero

Always use test when comparing with zero, instead of cmp:

test rax, rax
jz   .label

Not:

cmp  rax, 0
je   .label

9.3. Incrementing and decrementing

Always use add and sub when incrementing or decrementing a register, instead of inc or dec:

add rax, 1
sub rbx, 1

Not:

inc rax
dec rbx

While inc and dec are shorter, some processors have performance issues with those mnemonics.

9.4. Branching

When using conditional jumps, the following rules should be observed in order to increase the overall performances (due to the CPUs branch prediction algorithm).

Predict forward conditional branches to be not taken:
test rax, rax
jz   .label

; Fallthrough - Most likely

.label:
    
    ; Forward branch - Most unlikely
Predict backward conditional branches to be taken:
.label:
    
    ; Backward branch - Most likely
    
    test rax, rax
    jz   .label
    
; Fallthrough - Most unlikely

And of course, eliminate branches whenever possible.

9.5. Loops unrolling

Whenever possible, unroll loops:

.loop:
    
    mov [ rdi      ], [ rsi      ]
    mov [ rdi +  8 ], [ rsi +  8 ]
    mov [ rdi + 16 ], [ rsi + 16 ]
    mov [ rdi + 24 ], [ rsi + 24 ]
    
    add  rdi,         32
    add  rsi,         32
    sub  rcx,         32
    
    test rcx,         rcx
    jnz .loop

Instead of:

.loop:
    
    mov [ rdi ],      [ rsi ]
    
    add  rdi,         8
    add  rsi,         8
    sub  rcx,         8
        
    test rcx,         rcx
    jnz .loop

Repository Infos

Owner:          Jean-David Gadina - XS-Labs
Web:            www.xs-labs.com
Blog:           www.noxeos.com
Twitter:        @macmade
GitHub:         github.com/macmade
LinkedIn:       ch.linkedin.com/in/macmade/
StackOverflow:  stackoverflow.com/users/182676/macmade
You can’t perform that action at this time.