# Coding practices This chapter describes recommended coding practices when using the LLVM compilers. These practices typically result in the compiler generating more optimized code. ## Use int types for loop counters We strongly recommend using an `int` type for loop counters, which results in the compiler generating more efficient code. If the code uses a non-`int` type, the compiler must insert zero and sign-extensions to abide by C rules. For example, we do not recommend the following code: extern int A[30], B[30]; extern int n; for (short int ctr = 0; ctr < n30; ++ctr) { A[ctr] = B[ctr] + 55; } Copy to clipboard Use this code instead: extern int A[30], B[30]; extern int n; for (int ctr = 0; ctr < n30; ++ctr) { A[ctr] = B[ctr] + 55; } Copy to clipboard ## Mark function arguments as restrict (if possible) The LLVM compiler supports the `restrict` keyword for function arguments. For C++ programs, `__restrict__` is supported in a similar way. Using `restrict` on a pointer passed in as a function argument indicates to the compiler that the pointer will be used exclusively to dereference the address it points at. This allows the compiler to enable more aggressive optimizations on memory accesses. Note When using the `restrict` keyword, you must ensure that the restrict condition holds for all calls made to that function. If an argument is erroneously marked as `restrict`, the compiler might generate incorrect code. Since `restrict` only applies to function arguments, at times it is useful to outline sections of code to expose more opportunities for adding the keyword. Consider the following function. void func(..., int n) { uint8_t* in = ...; uint8_t* out = ...; for (int i = 0; i < n; ++i) { out[i] = ... in[i] ...; } } Copy to clipboard If it is known that the pointers in and out are safe with respect to each other, users can refactor this code to take advantage of the `restrict` keyword as in the following example. __attribute__(always_inline) void helper(uint8_t * restrict in, uint8_t *restrict out, int n) { for (int i = 0; i < n; ++i) { out[i] = ... in[i] ...; } } void func(..., int n) { uint8_t* in = ...; uint8_t* out = ...; helper(in, out n); } Copy to clipboard Note The use of the always\_inline attribute to ensure that the compiler will inline the call to the function for improved performance. ## Do not pass or return structures by value We strongly recommend that structures are passed to (and returned from) functions by reference and not by value. If a structure is passed to a function by value, the compiler must generate code that makes a copy of the structure during application runtime. This can be extremely inefficient, and will reduce the performance of the compiled code. For this reason, we recommend that structures be passed by pointer. For example, the following code is inefficient: struct S { int z; int y[50]; char *x; long int w[40]; }; int bar(struct S arg1) { ... } int baz() { struct S s; ... bar(s); } While this code is much more efficient: struct S { int z; int y[50]; char *x; long int w[40]; }; int bar(struct *S arg1) { /* Access z here using 'arg1->z' (instead of 'arg1.z') */ ... } int baz() { struct S s; ... bar(&s); } Copy to clipboard Alternatively, in C++, the efficient code can be simplified by using reference parameters: struct S { int z; int y[50]; char *x; long int w[40]; }; int bar(struct &S arg1) { ... } int baz() { struct S; ... populate elements of S ... bar(S); } Copy to clipboard ## Avoid control flow in inner-most loops When possible, avoid using conditional statements like `switch` or `if` within innermost loops. The software pipeliner only works on loops with no control flow in their bodies, so avoiding control flow in loops is an important component of achieving the best performance from compiled code. Vectorization of loops may also be improved by eliminating control flow. When writing vector code using intrinsics, try to use predicated intrinsics as much as possible to avoid introducing control flow. In cases where control flow cannot be avoided, try to keep it as simple as possible, with the only side effects being stores to memory. This increases the likelihood that the compiler will use predication instead of control flow, allowing the software pipeliner to try to optimize the loop. ## Avoid manual loop unrolling In most cases, it is best to avoid explicitly unrolling loops and instead rely on the compiler’s unrolling capabilities. This can lead to improved performance by enabling more optimization by the compiler, such as improved vectorization or software pipelining. Instead of unrolling loops by hand, first try using the command-line switches (like, `-funroll-loops`) and pragmas available to control unrolling on a per-loop basis. The full list of loop optimization `#pragmas` is at: [https://clang.llvm.org/docs/LanguageExtensions.html#extensions-for-loop-hint-optimizations](https://clang.llvm.org/docs/LanguageExtensions.html#extensions-for-loop-hint-optimizations). ## Avoid using inline assembly Using inline assembly snippets in C files is strongly discouraged for two reasons: - Inline assembly snippets are extremely difficult to write correctly. For instance, omitting the input, output, or clobber parameters frequently leads to incorrect code. The resulting failure can be extremely difficult to debug. - Inline assembly is not portable across processor versions. If you need to emit a specific assembly instruction, we recommend using a compiler intrinsic instead of inline assembly. Intrinsics are easy to insert in a C file, and they are portable across processor versions. If intrinsics are insufficient, add a new function written in assembly. The assembly function should be called from C code. Last Published: Oct 06, 2026 [Previous Topic Lightweight instrumentation](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/port-code-from-gcc.md) [Next Topic Language compatibility](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/language-compatibility.md)