Framewave Development Guide


Contents

Contents. 2

Revision History. 3

Chapter 1....... Introduction. 4

Chapter 2....... Maximum Reusability. 5

Chapter 3....... C++ / Intrinsics/ Assembly. 6

Chapter 4....... Platform Independence Issues. 7

4.1...... Platform Issues. 7

Chapter 5....... Stack Objects Versus Malloc/new.. 9

Chapter 6....... Const Correctness. 10

Chapter 7....... Reference Pointer Versus Value Pointer. 11

Chapter 8....... Const Reference Versus  Pass By Value. 12

Chapter 9....... Code Layout 13

Chapter 10..... Documentation. 14

Chapter 11..... Coding styles - extensions. 15

 

Revision History

Date

Revision

Description

August 2007

0.01

Initial draft

October 2007

0.02

Added coding styles


Chapter 1                                  Introduction

This document captures best practice guidelines in order to promote consistency and quality of development throughout the performance library project.

Chapter 2                                  Maximum Reusability

It is critical that developers identify and exploit overlaps in functionality. Framewave provides several containers for common, reusable code.

Namespace

File

Use

Const

fwdev.h

Generic constants (ie. max/min, pi, etc…)

FW_REF

FwSharedCode.h

Various routines consisting of converting between float to integer, converting between different data formats, and generic math functions.

FW_SSE2

FwSharedCode_SSE2.h

Commonly used SSE2 load/store modules and pack/unpack instructions.

fe

fe.h

Robust pixel engine for image & signal processing routines.

 

shortcut.h

Redefinition of Framewave data types for easy coding.

 

shuffle.h

Complex shuffle routines.

 

SSE2Intrinsics.h

Efficient inlined SSE2 math routines. (ie. Rearranging the contents of XMM registers)

Sys

system.h

ALL operating system specific code

 

In order to improve the reusability of SSE intrinsic functions, use the XMM128 data type in common code. This allows the data to be easily reinterpreted for use with non SSE instructions.

Many common composite instructions are also available in the Framewave source code. All files contain the prefix “cbl_”. This consists of many complex, efficient data processing routines, such as load/store modules, arithmetic, logical, transpose, and pack and unpack routines.

Chapter 3                                  C++ / Intrinsics/ Assembly

The majority of the project should consist of C++ and SSE Intrinsics code: C++ as the dominant language, and intrinsics wherever performance is required. Because Framewave is compiled for many platforms, assembly code will be used as a last resort, where no other viable alternative exists.

Chapter 4                                  Platform Independence Issues

Framewave is compatible with a number of system platforms. Platform-specific functions should be wrapped around macros or in the Sys namespace as the following:

Bad:

Linux

Windows

char * pSrc;
pSrc = get_current_dir_name();

char dir[MAX_PATH];
::GetCurrentDirectory( MAX_PATH, dir);

Better:

// system.h

// Windows Section

   const std::string CurrentDir(){

char dir[MAX_PATH];

::GetCurrentDirectory( MAX_PATH, dir);

return dir;

}

//Linux Section

   const std::string CurrentDir(){

return get_current_dir_name();

}

}

Linux

Windows

std::string dir;
dir = Sys::CurrentDir();

std::string dir;
dir = Sys::CurrentDir();

4.1                        Platform Issues

1.      Always insert a new line at the end of your file!!!!.

2.      Wrap all Framewave function definitions with “PREFIX_OPT(OPT_PREFIX, myFWFn )” to allow multipass builds.

3.      Use MIN and MAX instead of min and max macros.

4.      Do not use any macros or functions that are Windows-specific. If you want to use one, write an equivalent implementation for Linux and Solaris and write a wrapper macro/function. i.e.,

   #if defined( WINDOWS_SYSTEM )

       #define My_Macro Windows_Macro

          … My_Function ( Windows_Function … )

   #else #if defined( LINUX_SYSTEM )

       #define My_Macro Linux_Macro

          … My_Function ( Linux_Function … )

   #else

       #define My_Macro Linux_Macro

          … My_Function ( Solaris_Function … )

#endif

5.      After this, use the My_Macro or My_Function in your implementations. The AlignedMalloc function in system.h is a good example. It is different in Windows, Linux, and Solaris.  See arithmetic.h on how it has been wrapped for a common interface.

6.      Be consistent with filenames. If you named a file “add.h”, don’t include it by saying #include “Add.h”, do #include “add.h”

7.      g++ doesn’t like huge decimal numbers (greater than 2^31), if possible represent them in hex.

8.      when converting from a higher precision data type to lower one, be explicit. E.g., float a=5.5; int b = static_cast<int>(a); // rather than int b = a;

9.      g++ complains if it sees full template specialization of a templatized function. Instead, wrap it with a struct and make the function static.

10.  When doing partial template specialization, template parameters that are being specialized should be at the end of the list of template arguments.

Chapter 5                                  Stack Objects Versus Malloc/new

Use stack objects rather than malloc/new within the Framewave functions.  Manual memory allocation/de-allocation is the source of a tremendous number of bugs.  Objects allocated on the stack avoid many of these problems.

Fair:

int * array = new int[256];

Better:

std::vector< int > array( 256 );

Note:   The first case requires an explicit delete[]() call to avoid a memory leak. The user must handle all cases where the code might exit before calling delete[]().In the second case, no explicit cleanup code is required.

Chapter 6                                  Const Correctness

The C++ const keyword has a number of uses. Several are listed below.

const int width = 45;                 // ‘width’ is always 45.

const char *       name = “Framewave”;  // ”Framewave” string cannot change.

      char * const name = “Framewave”;      // ‘name’ cannot point to another string.

const char * const name = “Framewave”;      // Both of above.

struct s{

   int data;

   int Zero()const  { return 0;   }  // Function cannot modify struct data.

   int &Data(){ return data;}        // Return value may be used to modify ‘data’.

   const int &Data(){ return data;}  // Return value may not be used to modify ‘data’.

   const int &Data()const            // Return value may not be used to modify ‘data’.

   { return data;}  //  -AND- Function can not modify struct data.

};

Chapter 7                                  Reference Pointer Versus
Value Pointer

Reference pointers offer several advantages over value pointers:

·        better type safety

·        it can only point to a single object

·        manipulation in a function does not require a dereference

·        less data is pushed onto the stack

Value pointers also have advantages over reference pointers:

·        it can be set to 0 to indicate that the data is invalid

·        it can be reinterpreted

In the common case, a reference pointer is preferred over value pointers when passing data to functions.

Fair:

void Add(Object * dst, Object src1, Object src2 )

{

   *dst = src1 + src2;

}

Object dst, src1(2), src(3);

Add( &dst, src1, src2 );

Better:

void Add(Object & dst, Object src1, Object src2 )

{

   dst = src1 + src2;

}

Object dst, src1(2), src(3);

Add( dst, src1, src2 );

Chapter 8                                  Const Reference Versus
Pass By Value

Notice that the src1 and src2 parameters in the examples of Shown in Sectin 6 are passed by value. It is generally preferable to pass large objects by reference or pointer in order to avoid the additional memory copy. A better implementation is shown below.

void Add(Object & dst, const Object & src1, const Object & src2 )

{

   dst = src1 + src2;

}

Object dst, src1(2), src(3);

Add( dst, src1, src2 );

Note: Much like pass by value, this implementation guarantees that the called function cannot modify the source parameters. It also guarantees that src1 and src2 are valid objects.

Chapter 9                                  Code Layout

The large size of Framewave code base can make it difficult to find a given function. Where possible, arrange functions by volume, chapter, then alphabetically. Where necessary some other criteria, such as data type, may be used to sort the functions.

Also, to make the source code portable and retain format settings, please use 4 spaces instead of tabs.

Chapter 10                           Documentation

Framewave code should be well commented, and where necessary should include a brief preamble to explain the purpose of the function. This is especially important in FW_SSE2 and FW_REF namespaces which is intended for reuse by other developers.

The cbl prefixed functions should be documented as follows.

Source:

SYS_INLINE void Clone_x3_16(   XMM128 &d0,   //!< = XXXX | XXXX -----> 0001 | 1122

                               XMM128 &sd1,  //!< = 0123 | 4567 -----> 2333 | 4445

                               XMM128 &d2 )  //!< = XXXX | XXXX -----> 5566 | 6777

{ … }

Documentation:

SYS_INLINE void Util::Pack::Clone_x3_16

(

XMM128 &

d0,

 

 

XMM128 &

sd1,

 

 

XMM128 &

d2

 

)

 

Parameters:

d0

= XXXX | XXXX -----> 0001 | 1122

sd1

= 0123 | 4567 -----> 2333 | 4445

d2

= XXXX | XXXX -----> 5566 | 6777

Source:

//! Convert 16x8 bit unsigned integers inTo 8x16 bit signed integers in 2 registers

SYS_INLINE void Unpack8UTo16( XMM128 &srcDstLo, XMM128 &dstHi )

{ … }

Documentation:

SYS_INLINE void CBL_SSE2::Unpack8UTo16

(

XMM128 &

srcDstLo,

 

 

XMM128 &

dstHi

 

)

 

Convert 16x8 bit unsigned integers inTo 8x16 bit signed integers in 2 registers.

 

 

Chapter 11                           Coding styles - extensions

Audience

This document is intended for the developers of Framewave.

Introduction

The purpose of this style guide is to present a set of C++ coding style rules (with respect to layout, indentation, spacing, naming convention, commenting etc) for Framewave project with the ultimate goal of making Framewave code easy to read and understand. The most important thing to keep in mind is that exceptions can be made wherever necessary to improve readability rather than blindly following the style guide. If such decisions are made and if it affects significant amount of code, then they have to be discussed, formalized and added to this document.

There are numerous style guidelines that are in use today and the reason why we need a new one for Framewave is because many of them are general and does not target a specific kind of project. This document summarizes some important rules that would make Framewave code have a consistent pattern and easy to maintain and extend.

 Naming Files (1000-1999)

1.      [1010*] Source files should have the extension of .cpp and header files should have .h extension. All files should have mixed case with the starting letter in lower case.  Acronyms are all uppercase. If acronyms are used an underscore should be prefixed and/or suffixed as necessary.

trigonometric.h

FW_SharedCode.h

fe.h

arithmeticImage.cpp

linear_DCT.cpp

 

Naming Conventions (2000-2999)

  1. [2010] In general the name of any entity should be chosen such that it gives a good idea of what it is and how it is intended to be used. For example, a variable for temporary accumulation can be named ‘accumulator’ rather than something like ‘tmp’. Do not use ‘_’ or ‘__’ prefix in the names of functions, types or variables.
  2. [2020*] Data types should have mixed case with the starting letter in upper case. Acronyms are all uppercase.

class TableEntry;

struct FwImageErode8uNotInPlace;

typedef unsigned char Fw8u;

  1. [2030*] Variables and object names should have mixed case with the starting letter in lower case. Acronyms are all uppercase.

TableEntry tableEntry;

FwImageErode8uNotInPlace erodeKernel;

Fw8u valueABC;

  1. [2040] Global variables must be avoided as much as possible. For naming, they should have mixed case with a prefix of ‘g_’.

int g_libraryIndex;

  1. [2050*]  Static variables must be avoided as much as possible. They will produce hard to find bugs when the code gets threaded. For naming, they should have mixed case with a prefix of ‘s_’.

static char *s_pName;

  1. [2060*] Pointer variables should have mixed case with starting letter as ‘p’. Double pointers may start with ‘pp’.

int *pIndex;

char **ppFirstLetter;

  1. [2070*] Preprocessor macros should be all upper case with ‘_’ delimiters.

#define MAX_FW8U 255

  1. [2080*] Function names should have mixed case and start with upper case.

void FilterSharpen();

int GetLength();

  1. [2090*] Namespaces should have mixed case with uppercase starting letter.

namespace CBL_Library {};

  1. [2100*] Enumerated values should have a common prefix and all upper case.

enum FeAlgorithm

{

   ALG_1D,

   ALG_2D

};

  1. [2110*] Boolean values should have ‘is’ as their prefix.

bool isAligned;

  1. [2120] Avoid abbreviations. ‘Copy’ is better than ‘Cpy’.
  2. [2130] Use ‘i’ as the iteration variable name. Use ‘j’ and ‘k’ for nested loops.
  3. [2140] Use ‘n’ as the prefix for variables holding number of units.

unsigned int nBytes;

Indentation and Spacing (3000-3999)

Spacing and indentation must be chosen so that the programming structure can be identified easily.

  1. [3020*] Always use ‘spacebar’ white spaces. TAB spaces are evil.
  2. [3030] Vertical alignment will increase readability and so use spaces generously to create that.
  3. [3040] Indentation after every level should include 4 spacebar spaces.
  4. [3050*] It is better to have one statement per line i.e., one semicolon per line except in for loop etc. But very closely related statements can be put in the same line.
  5. [3060] Include space between operators, comma, semicolon etc. Exceptions are ‘.’, ‘->’, ‘[]’ etc.

Examples:

pBox->func();

 

q = (hLength + vLength) * d;

 

while (true) {}

 

ComputeArea(a, b, c, d);

 

for (i = 0; i < nBytes; i++) {}

 

template <U32 bytes, DispatchType dt, IsAlign ia, IsStream is>

struct MEM

{

    ISV Load ( XMM128 *reg, const void *src ){ reg; src; }

    ISV Store( XMM128 *reg,       void *dst ){ reg; dst; }

};

 

switch(ft)

{

    case TL_FT_INIT_ALLOC: return "sFFTInitAlloc_C_64fc";

    case TL_FT_FREE:       return "sFFTFree_C_64fc";

    case TL_FT_GETSIZE:    return "sFFTGetSize_C_64fc";

    case TL_FT_INIT:       return "sFFTInit_C_64fc";

    case TL_FT_GETBUFSIZE: return "sFFTGetBufSize_C_64fc";

    case TL_FT_FWD_NIP:    return "sFFTFwd_CToC_64fc";

    case TL_FT_FWD_IP:     return "sFFTFwd_CToC_64fc_I";

    case TL_FT_INV_NIP:    return "sFFTInv_CToC_64fc";

    case TL_FT_INV_IP:     return "sFFTInv_CToC_64fc_I";

    default:               return "";

}

 

for( ; pDst < pEnd; pDst += dstStep, pSrc += srcStep )

{

    const Fw8u *ps = pSrc;

          Fw8u *pd = pDst;

 

    U32 width      = dstRoiSize.width;

    U32 midWidth   = width & 0xFFFFFFF0;

 

    if( width < 16 )

    {

        midWidth = 0;

    }

 

    ...

}

 

Line Width (4000-4999)

Any line of code must not contain more than 125 characters. The code is subject to be viewed/edited by different tools and a number of editors do not support long lines. Wrapped lines are hard to read. It is difficult to maintain good vertical alignment and have short lines of code. In that case weigh line width over vertical structure.

If lines have to be split, use these general guidelines.

  1. [4010] Break after a comma.
  2. [4020] Break after an operator.
  3. [4030] Align the new line with the beginning of the expression on the previous line.

sum = aVal + bVal + cVal +

      dVal + eVal;

 

Filter(pSrc, pDst,

       nBytes);

 

Message("This is a long line and has to be split. "

         "The continuation is here.");

 

for (int i = 0; i < nField;

     i += fieldStep)

{

}

 

Code Statements (5000-5999)

  1. [5010*] Associate the ‘*’ in pointer declarations with the variable and not with the type. Follow the same with references.

int *pSrc, *pDst;

int &rArea = area;

  1. [5020] Put the constant in if statements before the equality check. This prevents inadvertently using the ‘=’ sign in if statements.

if( 8 == area ) {}

  1. [5030] Explicitly cast between different data types. Implicit data type conversion causes a lot of subtle bugs.

doubleQuantity = static_cast<double>(intQuantity);

  1. [5040*] Always be const correct wherever you can. DO NOT cast away constness.
  2. [5050] DO NOT use macros as simple functions. Instead use inline template functions. If you cannot avoid macro functions, use parentheses around macro parameters.

#define ADD(x, y) ((x)+(y))

  1. [5060*] Structures may NOT be typedef’d when they are declared. This is old practice and ‘C’ related. It must be avoided as much as possible.

struct Splodge // DO NOT: typedef struct Splodge

{

    int  count;

    char *pName;

    char *pAlias;

};

  1. [5070] Use ‘struct’ to declare plain data structures and ‘class’ to declare types for true objects and template classes etc.
  2. [5080] The ‘goto’, ‘break’ and ‘continue’ statements should be used only if it helps performance and/or produces better code construct. Else, avoid them as much as you can. DO NOT use ‘goto’ for backward jumps.
  3. [5090] Use floating point number representation for floats when comparing against constants even though it can be represented as integer.

if( 540.0f == floatValue ) {}

  1. [5100*] Do not use magic numbers in code. Use const keywords to declare them before.

const float AREA_OF_CIRCLE = 540.0f;

  1. [5110] Uses include guards in header files. Avoid including unnecessary headers.

#ifndef __HEADER_H__

#define __HEADER_H__

...

#endif // __HEADER_H__

  1. [5120] Always have a default clause in all switch statements.
  2. [5130] Put all OS and compiler dependent parts of code in one place.
  3. [5140*] Use 0 instead of NULL.
  4. [5160] At the end of a logical block of code, put a comment to specify what is being ended.

namespace FwInternalFunctions

{

...

} // FwInternalFunctions

  1. [5170] Do not perform C style conversions between unrelated pointer types. Exercise caution when going from arrays to pointers.

 

Fw8u value[16];

__m128i *pVal = (__m128i *)value; // WRONG

 

Commenting (6000-6999)

Always put comments in code as you write it. These comments should enable the reader to understand the structure and detail of your algorithm. Follow the following rules to insert comments.

  1. [6010] As much as possible use the ‘//’ line comment, even if comments span multiple lines.
  2. [6020] Add comments after the end of a code statement or in a new line without affecting the readability.
  3. [6030*] Add any copyright comments at the start of the files.

File Sizes (7000-7999)

The number of lines in a file should not exceed a preset limit. We define this threshold to be 5000 lines.

License statements

The current license statement that needs to go into all source code is the following:

 

Copyright © 2006-2008 Advanced Micro Devices, Inc. All Rights Reserved.

This software is subject to the Apache v2.0 License.