# Coding examples This chapter presents examples showing how the following operations can be coded in Hexagon assembly code: - Function prologs and epilogs - Function calls (direct and indirect) - Branches (direct and indirect) - Data access - Thread-local storage Most of the examples include separate coding solutions for two cases: - Absolute code (which must be loaded at a specific address) - Position-independent code (which can be loaded at an arbitrary address) Position-independent code is used in shared objects, which typically execute in the context of another object. Pay attention to the differences between the coding solutions for absolute and position-independent code. Note The examples are not intended as definitive coding solutions – they merely demonstrate how certain basic operations can be implemented. Optimal coding solutions can be derived from the examples. ## Register use The examples in this chapter use the following register conventions: - The absolute address of the GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N3040-23/topic/dynamic-linking.html#global-offset-table)) is stored in R24 - The absolute address of the application TLS area ([Thread-local storage access](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#thread-local-storage-access)) is stored in R25 Both R24 and R25 are initialized in the function prolog ([Function prologs and epilogs](https://docs.qualcomm.com/doc/80-N3040-23/topic/coding-examples.html#function-prologs-and-epilogs)). Note These register choices are arbitrary, and do not imply any standard coding convention for Hexagon assembly code. ## Assembler symbols **Special assembler symbols used in examples** | **Name** | **Description** | | --- | --- | | \_GLOBAL\_OFFSET\_TABLE\_ | Reference symbol in the global offset table ([Global offset table](https://docs.qualcomm.com/doc/80-N3040-23/topic/dynamic-linking.html#global-offset-table)). Implicit address of the
zero’th (index = 0) GOT entry. | | name@PCREL | Offset to position of the symbol name from the current location. | | name@GOT | Offset of GOT entry for the symbol name from
\_GLOBAL\_OFFSET\_TABLE\_. Instructs linker to create GOT entry
for the symbol name. | | name@GOTREL | Offset to location of the symbol name from
\_GLOBAL\_OFFSET\_TABLE\_. | | name@PLT | Offset to PLT entry for the symbol name from the current code
location. Instructs linker to create a PLT entry for the symbol
name. | | name@GDPLT | Offset to PLT entry for the function that returns the address of
a TLS symbol name from the current code location, using the GD method ([General dynamic (GD)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#general-dynamic)).
This function takes the address of a TLS\_index structure as its
argument ([Interface](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#interface)). | | name@GDGOT | Offset of first GOT entry for the TLS\_index structure for the
symbol name from \_GLOBAL\_OFFSET\_TABLE\_, using the GD method ([General dynamic (GD)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#general-dynamic)).
Instructs linker to create GOT entries for TLS\_index structure members ti\_moduleid and ti\_tlsoffset. | | name@LDPLT | Offset to PLT entry for the function that returns the address of
a TLS symbol name from the current code location, using the LD method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#local-dynamic)).
This function takes the address of a TLS\_index structure as its
argument ([Interface](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#interface)). | | name@LDGOT | Offset of first GOT entry for the TLS\_index structure for the
symbol name from \_GLOBAL\_OFFSET\_TABLE\_, using the LD method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#local-dynamic)).
Instructs linker to create GOT entries for TLS\_index structure
members ti\_moduleid and ti\_tlsoffset, with the latter zeroed. | | name@DTPREL | Offset to position of the symbol name from base of TLS template,
using the LD method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#local-dynamic)). | | name@IE | Address of the GOT entry for the offset into the TLS area for
the symbol name, using the IE method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#local-dynamic)).
Instructs linker to create a GOT entry for the offset into the
TLS area for the symbol name. | | name@IEGOT | Offset of the GOT entry for the offset into the TLS area for the
symbol name from
\_GLOBAL\_OFFSET\_TABLE\_, using the IE method ([Initial executable (IE)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#initial-executable)).
Instructs linker to create a GOT entry for the offset into the
TLS area for the symbol name. | | name@TPREL | Offset to position of the symbol name from base of the TLS area,
using the LE method ([Local executable (LE)](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#local-executable)). | ## Addressing constraints The Hexagon processor architecture has certain addressing constraints which affect how data accesses are coded in Hexagon assembly language. The constraints depend on the instruction and data size: - Some branching instructions use a 7- to 22-bit operand as a signed offset from the current program-counter (PC). - When accessing data in the small data area (using GP-relative addressing), a 16-bit value is used as an unsigned offset from the GP register. This value is shifted left before it is added to the GP register – the shift amount is determined by the size of the accessed data: - 0 for bytes - 1 for half-words - 2 for words - 3 for double-words This limits the range that GP-relative addressing can access: 64K for bytes, 128K for half- words, 256K for words and 512K for double-words. - When indexing an entry in the GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N3040-23/topic/dynamic-linking.html#global-offset-table)), instead of using a larger and slower 32-bit index, a program can restrict itself to just 16 bits for indexing a GOT entry. If the GOT size grows larger than 64KB, then 32-bit indexes must be used. ## Function prologs and epilogs Function *prologs* and *epilogs* are standard code sections that appear at the beginning and end of a function body. Their purpose is to manage the use of certain Hexagon processor registers within the function ([Register use](https://docs.qualcomm.com/doc/80-N3040-23/topic/coding-examples.html#register-use)). This section presents examples showing how function prologs and epilogs can be coded in Hexagon assembly language. ### Absolute If a function calls another function, it is defined as a *non-leaf* function. In this case, in order to save the link register, a stack frame must be allocated even if no local variables are allocated from the stack. If a function does not call another function, no stack frame is necessary (though it might be useful for traversing previous stack frames during debugging). **Function prolog and epilog (absolute)** | **C** | **Hexagon assembly language** | | --- | --- | | void foo (void)
{
...
}
Copy to clipboard | foo:
allocframe (#...)
...
dealloc_return
Copy to clipboard | When using the GD or LD method for accessing TLS ([Thread-local storage access](https://docs.qualcomm.com/doc/80-N3040-23/topic/thread-local-storage.html#thread-local-storage-access)), the absolute address of the GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N3040-23/topic/dynamic-linking.html#global-offset-table)) can be stored in a register for convenience. **Function prolog and epilog (absolute – GD or LD access)** | **C** | **Hexagon assembly language** | | --- | --- | | void foo ()
{
...
}
Copy to clipboard | foo:
allocframe (#8)
memw(r29+#4) = r24
r24 = add(pc,##_GLOBAL_OFFSET_TABLE_@PCREL)
...
dealloc_return
Copy to clipboard | When using the IE or LE method for accessing TLS, the UGP register ([Special registers](https://docs.qualcomm.com/doc/80-N3040-23/topic/process-initialization.html#special-registers)) can be copied to a general-purpose register for convenience. **Function prolog and epilog (absolute – IE or LE access)** | **C** | **Hexagon assembly language** | | --- | --- | | void foo (void)
{
...
}
Copy to clipboard | foo:
allocframe (#...)
r25 = ugp
...
dealloc_return
Copy to clipboard | ## Direct function call This section presents examples showing how direct function calls can be coded in Hexagon assembly language. ### Absolute Direct function calls are performed with the CALL instruction, which uses the PC-relative addressing mode to specify the function address. **Direct function call (absolute)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern void foo (void *);
foo (NULL);
Copy to clipboard | .extern foo
r0 = #0
call foo
Copy to clipboard | R_HEX_B22_PCREL
Copy to clipboard | ### Position-independent In external function calls, the function address is not known at build time. In this case the address must be stored in a specific data structure at runtime ([Dynamic linking](https://docs.qualcomm.com/doc/80-N3040-23/topic/dynamic-linking.html#dynamic-linking)). Assuming that the relevant data structures have been initialized, external functions are normally called through stubs contained in the procedure linkage table. **Direct function call (position-independent)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern void foo (void *);
foo (NULL);
Copy to clipboard | .extern foo
r0 = #0
call foo@PLT a
Copy to clipboard | R_HEX_PLT_B22_PCREL
Copy to clipboard | ^a^ The @PLT suffix is optional in this symbol reference. Note Local function calls use the same code sequence for both absolute and position- independent code. ## Indirect function call This section presents examples showing how indirect function calls can be coded in Hexagon assembly language. ### Absolute Indirect function calls are performed with the CALLR instruction. **Indirect function call (absolute)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);

fun = foo;


fun (bar);
Copy to clipboard | .extern foo
.local bar

r1 = ##foo

memw(#fun) = r1

r0 = ##bar

callr r1
Copy to clipboard | R_HEX_32_6_X + R_HEX_16_X

R_HEX_GPREL_2

R_HEX_32_6_X + R_HEX_16_X
Copy to clipboard | If the calling function does not have access to a small data area ([Special registers](https://docs.qualcomm.com/doc/80-N3040-23/topic/process-initialization.html#special-registers)), the function addresses must be loaded from regular memory, which requires a less-efficient code sequence. **Indirect function call (absolute – no small data)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);

fun = foo;


fun (bar);
Copy to clipboard | .extern foo
.local bar

r0 = ##fun

r1 = ##foo

memw(r0+#0) = r1

r0 = ##fun

r1 = memw(r0+#0)

r0 = ##bar

callr r1
Copy to clipboard | R_HEX_32_6_X | R_HEX_16_X
R_HEX_32_6_X | R_HEX_16_X

R_HEX_32_6_X | R_HEX_16_X

R_HEX_32_6_X | R_HEX_16_X
Copy to clipboard | ### Position-independent In position-independent code, indirect function calls are also performed with the CALLR instruction. However, it is necessary to calculate at runtime the absolute address of the function. **Indirect function call (position-independent)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);

fun = foo;


fun (bar);
Copy to clipboard | .extern foo
.local bar

r20 = memw (r24 + ##foo@GOT) a

r0 = add (pc, ##bar@PCREL)

callr r20
Copy to clipboard | R_HEX_GOT_32_6_X b
R_HEX_GOT_11_X

R_HEX_6_PCREL_X
Copy to clipboard | ^a^ If GOT is known to be smaller than 64 KB, the immediate extension can be omitted with the consequent changes to the appropriate relocation type. ^b^ Relocation created for immediate constant extender. If the GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N3040-23/topic/dynamic-linking.html#global-offset-table)) is known to be smaller than 64 KB, then a missing small data area requires fewer loads from memory because the callee function address value fits in 16 bits. **Indirect function call (position-independent, no small data)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);

fun = foo;


fun (bar);
Copy to clipboard | .extern foo
.local bar

r24 = add(pc,##_GLOBAL_OFFSET_TABLE_@PCREL)

r1 = add(r24, #foo@GOT)

r1 = memw(r1)

r0 = add(pc,##bar@PCREL)

callr r1
Copy to clipboard | R_HEX_GOT_16


R_HEX_6_PCREL_X
Copy to clipboard | Note The absolute address of the GOT is assumed to be stored in R24 ([Register use](https://docs.qualcomm.com/doc/80-N3040-23/topic/coding-examples.html#register-use)). Because the address of the function is referenced as a data object, it is resolved along with the other data object when an object file is loaded, effectively bypassing the lazy-binding mechanism ([Procedure linkage table](https://docs.qualcomm.com/doc/80-N3040-23/topic/dynamic-linking.html#linkage-table)). ## Direct branch This section presents an example showing how direct branches can be coded in Hexagon assembly language. ### Absolute and position-independent Direct branches are performed with the JUMP instruction, which uses the PC-relative addressing mode to specify the branch address. **Direct branch (absolute and position-independent)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | bar:

goto bar;
Copy to clipboard | .Lbar:

jump .lbar
Copy to clipboard | R_HEX_B22_PCREL
Copy to clipboard | ## Indirect branch This section presents examples showing how indirect branches can be coded in Hexagon assembly language. ### Absolute Indirect branches are performed with the JUMPR instruction. For instance, the following example uses an indirect branch to implement a jump table. r22 = const32 (#.Lbar) r23 = addasl (r22, r21, #2) r23 = memw (r23) jumpr r23 ... .L1: ... .L2: ... .section .rodata, “a”, @progbits .Lbar: .word .L1 .word .L2 ... Copy to clipboard If the code performing the branch does not have access to a small data area ([Special registers](https://docs.qualcomm.com/doc/80-N3040-23/topic/process-initialization.html#special-registers)), the branch address must be loaded from regular memory, which requires a less-efficient code sequence. r22 = ##.Lbar r23 = addasl (r22, r21, #2) r23 = memw (r23) jumpr r23 ... .L1: ... .L2: ... .section .rodata, “a”, @progbits .Lbar: .word .L1 .word .L2 ... Copy to clipboard ### Position-independent In position-independent code, indirect branches are also performed with the JUMPR instruction. However, it is necessary to calculate at runtime the absolute address of the branch destination. For instance, the following example loads the branch address from regular memory. r22 = add (pc, ##.Lbar@PCREL) r23 = addasl (r22, r21, #2) r23 = memw (r23) r23 = add (r23, r24) jumpr r23 ... .L1: ... .L2: ... .section .data, “aw”, @progbits .Lbar: .word .L1 .word .L2 ... Copy to clipboard ## Data access This section presents examples showing how data accesses can be coded in Hexagon assembly language. ### Absolute Data accesses are performed using the Hexagon load and store instructions. **Data access (absolute)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern int src;
static int dst;
auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .extern src
.lcomm dst, 4

r23 = const32 (#dst)

r22 = memw (#src)

memw (r23) = r22
Copy to clipboard | R_HEX_GPREL16_2 + R_HEX_32 a

R_HEX_GPREL16_2
Copy to clipboard | ^a^ Relocation created in small data area. If the code performing the data access does not have access to a small data area ([Special registers](https://docs.qualcomm.com/doc/80-N3040-23/topic/process-initialization.html#special-registers)), the data address must be loaded from regular memory, which requires a less-efficient code sequence. **Data access (absolute – no small data)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern int src;
static int dst;
auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .extern src
.lcomm dst, 4

r23 = ##dst

r21 = memw (##src)

memw (r23) = r21
Copy to clipboard | R_HEX_HI16
R_HEX_LO16

R_HEX_HI16
R_HEX_LO16
Copy to clipboard | ### Position-independent In position-independent code, data accesses are also performed with the Hexagon load and store instructions. However, it is necessary to calculate at runtime the absolute address of the data. **Data access (position-independent – no small data)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern int src;
static int dst;
auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .extern src
.lcomm dst, 4

r24 = add(pc, ##_GLOBAL_OFFSET_TABLE_@PCREL)

r1 = memw(r24 + ##ptr@GOT)

memw(r1+#0) = r0

r1 = memw(r24 + ##src@GOT)

r16 = memw(r1+#0)

r0 = add(pc, ##dst@PCREL)

memw(r0+#0) = r16
Copy to clipboard | R_HEX_GOTREL_HI16
R_HEX_GOTREL_LO16

R_HEX_GOT_HI16
R_HEX_GOT_LO16
Copy to clipboard | ## Thread-local storage This section presents examples showing how data accesses to thread-local storage (TLS) can be coded in version-specific Hexagon assembly language. Note The following examples for Hexagon V4 also apply to Hexagon V5x and V6x. ### Absolute This section presents code examples using the following TLS access methods: - GD - LD - IE - LE GD TLS data accesses in V4 use 32-bit constant extenders to make the code sequence more efficient. **Thread-local storage (V4 absolute – GD access)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern __thread int src;


extern __thread int dst;


auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object

.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4

.text
r0 = add (r24, ##dst@GDGOT) a

call dst@GDPLT
r23 = r0

r0 = add (r24, ##src@GDGOT) a

call src@GDPLT
r22 = memw (r0)
memw (r23) = r22
Copy to clipboard | R_HEX_GD_GOT_32_6_X b +
R_HEX_GD_GOT_16_X +
R_HEX_DTPMOD_32 c +
R_HEX_DTPREL_32 c

R_HEX_GD_PLT_B22_PCREL


R_HEX_GD_GOT_32_6_X b +
R_HEX_GD_GOT_16_X +
R_HEX_DTPMOD_32 c +
R_HEX_DTPREL_32 c

R_HEX_GD_PLT_B22_PCREL
Copy to clipboard | ^a^ If GOT is known to be smaller than 64 KB, the immediate extension can be omitted with the consequent changes to the appropriate relocation type. ^b^ Relocation created for immediate constant extender. ^c^ Relocation created in GOT. LD TLS data accesses in the V4 processor use 32-bit constant extenders to make the code sequence more efficient. **Thread-local storage (V4 absolute – LD access)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern __thread int src;


extern __thread int dst;


auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .section .tbss, “awT”, @nobits
.type src, @tls_object
src: .skip 4

.type src, @tls_object
dst: .skip 4

.text
r0 = add (r24, ##dst@LDGOT) a

call dst@LDPLT
r23 = add (r0, ##dst@DTPREL) d

r22 = add (r0, ##src@DTPREL) d

memw (r23) = r22
Copy to clipboard | R_HEX_LD_GOT_32_6_X b +
R_HEX_LD_GOT_16_X +
R_HEX_DTPMOD_32 c

R_HEX_LD_PLT_B22_PCREL
R_HEX_DTPREL_32_6_X b +
R_HEX_DTPREL_16_X

R_HEX_DTPREL_32_6_X b +
R_HEX_DTPREL_16_X
Copy to clipboard | ^a^ If GOT is known to be smaller than 64 KB, the immediate extension can be omitted with the consequent changes to the appropriate relocation type. ^b^ Relocation created for immediate constant extender. ^c^ Relocation created in GOT. - ^d^ If TLS template is known to be smaller than 64 KB, the immediate - extension can be omitted with consequent changes to the appropriate relocation type. IE TLS data accesses in the V4 processor use indirect-with-register-offset addressing mode and 32-bit constant extenders to make the code sequence more efficient. **Thread-local storage (V4 absolute – IE access)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern __thread int src;


extern __thread int dst;


auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object

.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4

.text
r23 = memw (##dst@IE)


r22 = memw (##src@IE)

r22 = memw (r25 + r22 << #1)
memw (r25 + r23 << #1) = r22
Copy to clipboard | R_HEX_IE_32_6_X a +
R_HEX_IE_16_X +
R_HEX_TPREL_32 b

R_HEX_IE_32_6_X a+
R_HEX_IE_16_X +
R_HEX_TPREL_32 b
Copy to clipboard | ^a^ Relocation created for immediate constant extender. ^b^ Relocation created in GOT. LE TLS data accesses in the V4 processor use 32-bit constant extenders to make the code sequence more efficient. **Thread-local storage (V4 absolute – LE access)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern __thread int src;


extern __thread int dst;


auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object

.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4

.text
r23 = add (r25, ##dst@TPREL) a


r22 = memw (r25 + ##src@TPREL)

memw (r23) = r22
Copy to clipboard | R_HEX_TPREL_32_6_X b +
R_HEX_TPREL_16_X

R_HEX_TPREL_32_6_X b +
R_HEX_TPREL_11_X
Copy to clipboard | ^a^ If TLS area is known to be smaller than 64 KB, the immediate extension can be omitted with consequent changes to the appropriate relocation type. ^b^ Relocation created for immediate constant extender. ### Position-independent This section presents code examples using the following TLS access methods: - IE Position-independent IE TLS data accesses in the V4 processor use indirect-with-register-offset addressing mode and 32-bit constant extenders to make the code sequence more efficient. **Thread-local storage (V4 position-independent – IE access)** | **C** | **Hexagon assembly language** | **Relocation** | | --- | --- | --- | | extern __thread int src;


extern __thread int dst;


auto int *ptr;

ptr = &dst;


*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object

.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4

.text
r23 = memw (r24 + ##dst@IEGOT) a


r22 = memw (r24 + ##src@IEGOT) a

r22 = memw (r25 + r22 << #1)
memw (r25 + r23 << #1) = r22
Copy to clipboard | R_HEX_IE_GOT_32_6_X b +
R_HEX_IE_GOT_11_X +
R_HEX_TPREL_32 c

R_HEX_IE_GOT_32_6_X b+
R_HEX_IE_GOT_11_X +
R_HEX_TPREL_32 b
Copy to clipboard | ^a^ If GOT is known to be smaller than 64 KB, the immediate extension can be omitted with consequent changes to the appropriate relocation type. ^b^ Relocation created for immediate constant extender. ^c^ Relocation created in GOT. Last Published: Oct 06, 2026 [Previous Topic Local executable (LE)](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/thread-local-storage.md) [Next Topic Hexagon V75 Programmer’s Reference Manual](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/index_80-n2040-57_v75_prm.md)