Framewave Development Guide
Chapter 2....... Maximum Reusability
Chapter 3....... C++ / Intrinsics/ Assembly
Chapter 4....... Platform Independence Issues
Chapter 5....... Stack Objects Versus Malloc/new
Chapter 6....... Const Correctness
Chapter 7....... Reference Pointer Versus Value Pointer
Chapter 8....... Const Reference Versus Pass By Value
Chapter 11..... Coding styles - extensions
Date |
Revision |
Description |
August 2007 |
0.01 |
Initial draft |
October 2007 |
0.02 |
Added coding styles |
This document captures best practice guidelines in order to promote consistency and quality of development throughout the performance library project.
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.
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.
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; |
char dir[MAX_PATH]; |
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; |
std::string dir; |
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.
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.
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.
};
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 );
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.
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.
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.
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)
class TableEntry;
struct FwImageErode8uNotInPlace;
typedef unsigned char Fw8u;
TableEntry tableEntry;
FwImageErode8uNotInPlace erodeKernel;
Fw8u valueABC;
int g_libraryIndex;
static char *s_pName;
int *pIndex;
char **ppFirstLetter;
#define MAX_FW8U 255
void FilterSharpen();
int GetLength();
namespace CBL_Library {};
enum FeAlgorithm
{
ALG_1D,
ALG_2D
};
bool isAligned;
unsigned int nBytes;
Indentation and Spacing (3000-3999)
Spacing and indentation must be chosen so that the programming structure can be identified easily.
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.
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)
int *pSrc, *pDst;
int &rArea = area;
if( 8 == area ) {}
doubleQuantity = static_cast<double>(intQuantity);
#define ADD(x, y) ((x)+(y))
struct Splodge // DO NOT: typedef struct Splodge
{
int count;
char *pName;
char *pAlias;
};
if( 540.0f == floatValue ) {}
const float AREA_OF_CIRCLE = 540.0f;
#ifndef __HEADER_H__
#define __HEADER_H__
...
#endif // __HEADER_H__
namespace FwInternalFunctions
{
...
} // FwInternalFunctions
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.
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.