Assembly Optimizer Directives

Assembly optimizer directives supply data for and control the assembly optimization process. The assembly optimizer optimizes linear assembly code that is contained within procedures; that is, code within the .proc and .endproc directives or within the .cproc and .endproc directives. If you do not use .cproc/.proc directives in your linear assembly file, your code will not be optimized by the assembly optimizer. This section describes these directives and others that you can use with the assembly optimizer.

Table 5-2 summarizes the assembly optimizer directives. It provides the syntax for each directive, a description of each directive, and any restrictions that you should keep in mind. See the specific directive topic for more detail.

In Table 5-2 and the detailed directive topics, the following terms for parameters are used:

Table 5-2 Assembly Optimizer Directives Summary

Syntax Description Restrictions
.call[ret_reg =] func_name (argument1,
    argument2, ...)
Calls a function Valid only within procedures
.circsymbol1/register1[,symbol2/register2] Declares circular addressing Must manually insert setup/teardown code for circular addressing. Valid only within procedures
label.cproc[argument1 [, argument2, …]] Start a C/C++ callable procedure Must use with .endproc
.endproc End a C/C++ callable procedure Must use with .cproc
.endproc[variable1 [, variable2,…]] End a procedure Must use with .proc
.mapsymbol1/register1[,symbol2/
    register2]
Assigns a symbol to a register Must use an actual machine register
.mdep[memref1[, memref2]] Indicates a memory dependence Valid only within procedures
.mptr{variable|memref}, base [+ offset]
     [, stride]
Avoid memory bank conflicts Valid only within procedures
.no_mdep No memory aliases in the function Valid only within procedures
.prefsymbol/register1[/register2/...] Assigns a symbol to a register in a set Must use actual machine registers
label .proc[variable1 [, variable2, …]] Start a procedure Must use with .endproc
.regsymbol1[, symbol2,…] Declare variables Valid only within procedures
.regasymbol1[, symbol2,…] Partition symbol to A-side register Valid only within procedures
.regbsymbol1[, symbol2,…] Partition symbol to B-side register Valid only within procedures
.reserve[register1 [,register2,…]] Prevents the compiler from allocating a register Valid only within procedures
.return[argument] Return a value to a procedure Valid only within .cproc procedures
label .tripmin Specify trip count value Valid only within procedures
.volatilememref1[,memref2,…] Designate memory reference volatile Use --interrupt_threshold=1 if reference may be modified during an interrupt
.call
Calls a Function
Syntax

            .call [ret_reg=] func_name ([argument1, argument2,...])

Description

Use the .call directive to call a function. Optionally, you can specify a register that is assigned the result of the call. The register can be a symbolic or machine register. The .call directive adheres to the same register and function calling conventions as the C/C++ compiler. For information, see Section 8.3 and Section 8.4. There is no support for alternative register or function calling conventions.

You cannot call a function that has a variable number of arguments, such as printf. No error checking is performed to ensure the correct number and/or type of arguments is passed. You cannot pass or return structures through the .call directive.

Following is a description of the .call directive parameters:

ret_reg (Optional) Symbolic/machine register that is assigned the result of the call. If not specified, the assembly optimizer presumes the call overwrites the registers A5 and A4 with a result.
func_name The name of the function to call, or the name of the symbolic/ machine register for indirect calls. A register pair is not allowed. The label of the called function must be defined in the file. If the code for the function is not in the file, the label must be defined with the .global or .ref directive (refer to the TMS320C6000 Assembly Language Tools User's Guide for details). If you are calling a C/C++ function, you must use the appropriate linkname of that function. See Section 7.12 for more information.
arguments (Optional) Symbolic/machine registers passed as an argument. The arguments are passed in this order and cannot be a constant, memory reference, or other expression.

By default, the compiler generates near calls and the linker utilizes trampolines if the near call will not reach its destination. To force a far call, you must explicitly load the address of the function into a register, and then issue an indirect call. For example:

MVK func,reg MVKH func,reg .call reg(op1) ; forcing a far call

If you want to use * for indirection, you must abide by C/C++ syntax rules, and use the following alternate syntax:

.call [ret_reg =] (* ireg)([arg1, arg2,...])

For example:

.call (*driver)(op1, op2) ; indirect call .reg driver .call driver(op1, op2) ; also an indirect call

Here are other valid examples that use the .call syntax.

.call fir(x, h, y) ; void function .call minimal( ) ; no arguments .call sum = vecsum(a, b) ; returns an int .call hi:lo = _atol(string) ; returns a long

Since you can use machine register names anywhere you can use symbolic registers, it may appear you can change the function calling convention. For example:

.call A6 = compute()

It appears that the result is returned in A6 instead of A4. This is incorrect. Using machine registers does not override the calling convention. After returning from the compute function with the returned result in A4, a MV instruction transfers the result to A6.

Example

Here is a complete .call example:

.global _main .global _puts, _rand, _ltoa .sect ".const" string1: .string "The random value returned is ", 0 string2: .string " ", 10, 0 ; '10' == newline .bss charbuf, 20 .text _main: .cproc .reg random_value, bufptr, ran_val_hi:ran_val_lo .call random_value = _rand() ; get a random value MVKL string1, bufptr ; load address of string1 MVKH string1, bufptr .call _puts(bufptr) ; print out string1 MV random_value, ran_val_lo SHR ran_val_lo, 31, ran_val_hi ; sign extend random value .call _ltoa(ran_val_hi:ran_val_lo, bufptr) ; convert it to a string .call _puts(bufptr) ; print out the random value MVKL string2, bufptr ; load address of string2 MVKH string2, bufptr .call _puts(bufptr) ; print out a newline .endproc
.circ
Declare Circular Registers
Syntax

            .circ symbol1/register1[, symbol2lregister2, ...]

Description

The .circ directive assigns a symbolic register name to a machine register and declares the symbolic register as available for circular addressing. The compiler then assigns the variable to the register and ensures that all code transformations are safe in this situation. You must insert setup/teardown code for circular addressing.

symbol A valid symbol name to be assigned to the register. The variable is up to 128 characters long and must begin with a letter. Remaining characters of the variable can be a combination of alphanumeric characters, the underscore (_), and the dollar sign ($).
register Name of the actual register to be assigned a variable.

The compiler assumes that it is safe to speculate any load using an explicitly declared circular addressing variable as the address pointer and may exploit this assumption to perform optimizations.

When a symbol is declared with the .circ directive, it is not necessary to declare that symbol with the .reg directive.

The .circ directive is equivalent to using .map with a circular declaration.

Example

Here the symbolic name Ri is assigned to actual machine register Mi and Ri is declared as potentially being used for circular addressing.

.CIRC R1/M1, R2/M2 ...
.cproc/.endproc
Define a C Callable Procedure
Syntax

label    .cproc [argument1 [, argument2, …]]

      .endproc

Description

Use the .cproc/.endproc directive pair to delimit a section of your code that you want the assembly optimizer to optimize and treat as a C/C++ callable function. This section is called a procedure. The .cproc directive is similar to the .proc directive in that you use .cproc at the beginning of a section and .endproc at the end of a section. In this way, you can set off sections of your assembly code that you want to be optimized, like functions. The directives must be used in pairs; do not use .cproc without the corresponding .endproc. Specify a label with the .cproc directive. You can have multiple procedures in a linear assembly file.

The .cproc directive differs from the .proc directive in that the compiler treats the .cproc region as a C/C++ callable function. The assembly optimizer performs some operations automatically in a .cproc region in order to make the function conform to the C/C++ calling conventions and to C/C++ register usage conventions.

These operations include the following:

  • When you use save-on-entry registers (A10 to A15 and B10 to B15), the assembly optimizer saves the registers on the stack and restores their original values at the end of the procedure.
  • If the compiler cannot allocate machine registers to symbolic register names specified with the .reg directive (see the .reg topic) it uses local temporary stack variables. With .cproc, the compiler manages the stack pointer and ensures that space is allocated on the stack for these variables.

For more information, see Section 8.3 and Section 8.4.

Use the optional argument to represent function parameters. The argument entries are very similar to parameters declared in a C/C++ function. The arguments to the .cproc directive can be of the following types:

  • Machine-register names. If you specify a machine-register name, its position in the argument list must correspond to the argument passing conventions for C (see Section 8.4). For example, the C/C++ compiler passes the first argument to a function in register A4. This means that the first argument in a .cproc directive must be A4 or a symbolic name. Up to ten arguments can be used with the .cproc directive.
  • Variable names.If you specify a variable name, then the assembly optimizer ensures that either the variable name is allocated to the appropriate argument passing register or the argument passing register is copied to the register allocated for the variable name. For example, the first argument in a C/C++ call is passed in register A4, so if you specify the following .cproc directive:
  • frame .cproc arg1

    The assembly optimizer either allocates arg1 to A4, or arg1 is allocated to a different register (such as B7) and an MV A4, B7 is automatically generated.

  • Register pairs. A register pair is specified as arghi:arglo and represents a 40-bit argument or a 64-bit type double argument.
  • For example, the .cproc defined as follows:

    _fcn: .cproc arg1, arg2hi:arg2lo, arg3, B6, arg5, B9:B8 ... .return res ... .endproc

    corresponds to a C function declared as:

    int fcn(int arg1, long arg2, int arg3, int arg4, int arg5, long arg6);

    In this example, the fourth argument of .cproc is register B6. This is allowed since the fourth argument in the C/C++ calling conventions is passed in B6. The sixth argument of .cproc is the actual register pair B9:B8. This is allowed since the sixth argument in the C/C++ calling conventions is passed in B8 or B9:B8 for longs.

  • Register quads (C6600 only). A register quad is specified as r3:r2:r1:r0 and represents a 128-bit type, __x128_t. See Example 4.

If you are calling a procedure from C++ source, you must use the appropriate linkname for the procedure label. Otherwise, you can force C naming conventions by using the extern C declaration. See Section 7.12 and Section 8.6 for more information.

When .endproc is used with a .cproc directive, it cannot have arguments. The live out set for a .cproc region is determined by any .return directives that appear in the .cproc region. (A value is live out if it has been defined before or within the procedure and is used as an output from the procedure.) Returning a value from a .cproc region is handled by the .return directive. The return branch is automatically generated in a .cproc region. See the .return topic for more information.

Only code within procedures is optimized. The assembly optimizer copies any code that is outside of procedures to the output file and does not modify it. See Section 5.4.1 for a list of instruction types that cannot appear in a .cproc region.

Example

Here is an example in which .cproc and .endproc are used:

_if_then: .cproc a, cword, mask, theta .reg cond, if, ai, sum, cntr MVK 32,cntr ; cntr = 32 ZERO sum ; sum = 0 LOOP: AND cword,mask,cond ; cond = codeword & mask [cond] MVK 1,cond ; !(!(cond)) CMPEQ theta,cond,if ; (theta == !(!(cond))) LDH *a++,ai ; a[i] [if] ADD sum,ai,sum ; sum += a[i] [!if] SUB sum,ai,sum ; sum -= a[i] SHL mask,1,mask ; mask = mask << 1 [cntr] ADD -1,cntr,cntr ; decrement counter [cntr] B LOOP ; for LOOP .return sum .endproc
.map
Assign a Variable to a Register
Syntax

            .map symbol1/register1[, symbol2/register2, ...]

Description

The .map directive assigns symbol names to machine registers. Symbols are stored in the substitution symbol table. The association between symbolic names and actual registers is wiped out at the beginning and end of each linear assembly function. The .map directive can be used in assembly and linear assembly files.

variable A valid symbol name to be assigned to the register. The substitution symbol is up to 128 characters long and must begin with a letter. Remaining characters of the variable can be a combination of alphanumeric characters, the underscore (_), and the dollar sign ($).
register Name of the actual register to be assigned a variable.

When a symbol is declared with the .map directive, it is not necessary to declare that symbol with the .reg directive.

Example

Here the .map directive is used to assign x to register A6 and y to register B7. The symbols are used with a move statement.

.map x/A6, y/B7 MV x, y ; equivalent to MV A6, B7
.mdep
Indicates a Memory Dependence
Syntax

            .mdep memref1,  memref2

Description

The .mdep directive identifies a specific memory dependence.

Following is a description of the .mdep directive parameters:

memref The symbol parameter is the name of the memory reference.

The symbol used to name a memory reference has the same syntax restrictions as any assembly symbol. (For more information about symbols, refer to the TMS320C6000 Assembly Language Tools User's Guide.) It is in the same space as the symbolic registers. You cannot use the same name for a symbolic register and annotating a memory reference.

The .mdep directive tells the assembly optimizer that there is a dependence between two memory references.

The .mdep directive is valid only within procedures; that is, within occurrences of the .proc and .endproc directive pair or the .cproc and .endproc directive pair.

Example

Here is an example in which .mdep is used to indicate a dependence between two memory references.

.mdep ld1, st1 LDW *p1++{ld1}, inp1 ;memory reference "ld1" ;other code ... STW outp2, *p2++{st1} ;memory reference "st1"
.mptr
Avoid Memory Bank Conflicts
Syntax

            .mptr {variable | memref}, base [+ offset] [, stride]

Description

The .mptr directive associates a register with the information that allows the assembly optimizer to determine automatically whether two memory operations have a memory bank conflict. If the assembly optimizer determines that two memory operations have a memory bank conflict, then it does not schedule them in parallel.

A memory bank conflict occurs when two accesses to a single memory bank in a given cycle result in a memory stall that halts all pipeline operation for one cycle while the second value is read from memory. For more information on memory bank conflicts, including how to use the .mptr directive to prevent them, see Section 5.5.

Following are descriptions of the .mptr directive parameters:

variable|memref The name of the register symbol or memory reference used to identify a load or store involved in a dependence.
base A symbolic address that associates related memory accesses
offset The offset in bytes from the starting base symbol. The offset is an optional parameter and defaults to 0.
stride The register loop increment in bytes. The stride is an optional parameter and defaults to 0.

The .mptr directive tells the assembly optimizer that when the symbol or memref is used as a memory pointer in an LD(B/BU)(H/HU)(W) or ST(B/H/W) instruction, it is initialized to point to base + offset and is incremented by stride each time through the loop.

The .mptr directive is valid within procedures only; that is, within occurrences of the .proc and .endproc directive pair or the .cproc and .endproc directive pair.

The symbolic addresses used for base symbol names are in a name space separate from all other labels. This means that a symbolic register or assembly label can have the same name as a memory bank base name. For example:

.mptr Darray,Darray
Example

Here is an example in which .mptr is used to avoid memory bank conflicts.

_blkcp: .cproc I .reg ptr1, ptr2, tmp1, tmp2 MVK 0x0, ptr1 ; ptr1 = address 0 MVK 0x8, ptr2 ; ptr2 = address 8 loop: .trip 50 .mptr ptr1, a+0, 4 .mptr foo, a+8, 4 ; potential conflict LDW *ptr1++, tmp1 ; load *0, bank 0 STW tmp1, *ptr2++{foo} ; store *8, bank 0 [I] ADD -1,i,i ; I-- [I] B loop ; if (!0) goto loop .endproc
.no_mdep
No Memory Aliases in the Function
Syntax

            .no_mdep

Description

The .no_mdep directive tells the assembly optimizer that no memory dependencies occur within that function, with the exception of any dependencies pointed to with the .mdep directive.

Example

Here is an example in which .no_mdep is used.

fn: .cproc dst, src, cnt .no_mdep ;no memory aliasing in this function ... .endproc
.pref
Assign a Variable to a Register in a Set
Syntax

            .pref symbol / register1[/register2...]

Description

The .pref directive communicates a preference to assign a variable to one of a list of registers. The preference is used only in the .cproc or .proc region the .pref directive is declared in and is valid only until the end of the region.

symbol A valid symbol name to be assigned to the register. The substitution symbol is up to 128 characters long and must begin with a letter. Remaining characters of the symbol can be a combination of alphanumeric characters, the underscore (_), and the dollar sign ($).
register List of actual registers to be assigned a variable.

There is no guarantee that the symbol will be assigned to any register in the specified group. The compiler may ignore the preference.

When a symbol is declared with the .pref directive, it is not necessary to declare that variable with the .reg directive.

Example

Here x is given a preference to be assigned to either A6 or B7. However, It would be correct for the compiler to assign x to B3 (for example) instead.

.PREF x/A6/B7 ; Preference to assign x to either A6 or B7
.proc/.endproc
Define a Procedure
Syntax

label    .proc [variable1 [, variable2, …]]

      .endproc [register1 [, register2, …]]

Description

Use the .proc/.endproc directive pair to delimit a section of your code that you want the assembly optimizer to optimize. This section is called a procedure. Use .proc at the beginning of the section and .endproc at the end of the section. In this way, you can set off sections of unscheduled assembly instructions that you want optimized by the compiler. The directives must be used in pairs; do not use .proc without the corresponding .endproc. Specify a label with the .proc directive. You can have multiple procedures in a linear assembly file.

Use the optional variable parameter in the .proc directive to indicate which registers are live in, and use the optional register parameter of the .endproc directive to indicate which registers are live out for each procedure. The variable can be an actual register or a symbolic name. For example:

.PROC x, A5, y, B7 ... .ENDPROC y

A value is live in if it has been defined before the procedure and is used as an input to the procedure. A value is live out if it has been defined before or within the procedure and is used as an output from the procedure. If you do not specify any registers with the .endproc directive, it is assumed that no registers are live out.

Only code within procedures is optimized. The assembly optimizer copies any code that is outside of procedures to the output file and does not modify it.

See Section 5.4.1 for a list of instruction types that cannot appear in a .proc region.

Example

Here is a block move example in which .proc and .endproc are used:

move .proc A4, B4, B0 .no_mdep loop: LDW *B4++, A1 MV A1, B1 STW B1, *A4++ ADD -4, B0, B0 [B0] B loop .endproc
.reg
Declare Symbolic Registers
Syntax

            .reg symbol1[, symbol2, …]

Description

The .reg directive allows you to use descriptive names for values that are stored in registers. The assembly optimizer chooses a register for you such that its use agrees with the functional units chosen for the instructions that operate on the value.

The .reg directive is valid within procedures only; that is, within occurrences of the .proc and .endproc directive pair or the .cproc and .endproc directive pair.

Declaring register pairs (or register quads for C6600) explicitly is optional. Doing so is only necessary if the registers should be allocated as a pair, but they are not used that way. It is a best practice to declare register pairs and register quads with the pair/quad syntax. Here is an example of declaring a register pair:

.reg A7:A6
Example 1

This example uses the same code as the block move example shown for .proc/.endproc but the .reg directive is used:

move .cproc dst, src, cnt .reg tmp1, tmp2 loop: LDW *src++, tmp1 MV tmp1, tmp2 STW tmp2, *dst++ ADD -4, cnt, cnt [cnt] B loop

Notice how this example differs from the .proc example: symbolic registers declared with .reg are allocated as machine registers.

Example 2

The code in the following example is invalid, because a variable defined by the .reg directive cannot be used outside of the defined procedure:

move .proc A4 .reg tmp LDW *A4++, top MV top, B5 .endproc MV top, B6 ; WRONG: top is invalid outside of the procedure
.rega/.regb
Partition Registers Directly
Syntax

            .rega symbol1[, symbol2, …]

            .regb symbol1[, symbol2, …]

Description

Registers can be directly partitioned through two directives. The .rega directive is used to constrain a symbol name to A-side registers. The .regb directive is used to constrain a symbol name to B-side registers. For example:

.REGA y .REGB u, v, w MV x, y LDW *u, v:w

The .rega and .regb directives are valid within procedures only; that is, within occurrences of the .proc and .endproc directive pair or the .cproc and .endproc directive pair.

When a symbol is declared with the .rega or .regb directive, it is not necessary to declare that symbol with the .reg directive.

The old method of partitioning registers indirectly by partitioning instructions can still be used. Side and functional unit specifiers can still be used on instructions. However, functional unit specifiers (.L/.S/.D/.M) and crosspath information are ignored. Side specifiers are translated into partitioning constraints on the corresponding symbol names, if any. For example:

MV .1X z, y ; translated to .REGA y LDW .D2T2 *u, v:w ; translated to .REGB u, v, w
.reserve
Reserve a Register
Syntax

             .reserve [register1 [, register2, …]]

Description

The .reserve directive prevents the assembly optimizer from using the specified register in a .proc or .cproc region.

If a .reserved register is explicitly assigned in a .proc or .cproc region, then the assembly optimizer can also use that register. For example, the variable tmp1 can be allocated to register A7, even though it is in the .reserve list, since A7 was explicitly defined in the ADD instruction:

.cproc .reserve a7 .reg tmp1 .... ADD a6, b4, a7 .... .endproc

NOTE

Reserving Registers A4 and A5

When inside of a .cproc region that contains a .call statement, A4 and A5 cannot be specified in a .reserve statement. The calling convention mandates that A4 and A5 are used as the return registers for a .call statement.

Example 1

The .reserve in this example guarantees that the assembly optimizer does not use A10 to A13 or B10 to B13 for the variables tmp1 to tmp5:

test .proc a4, b4 .reg tmp1, tmp2, tmp3, tmp4, tmp5 .reserve a10, a11, a12, a13, b10, b11, b12, b13 ..... .endproc a4
Example 2

The assembly optimizer may generate less efficient code if the available register pool is overly restricted. In addition, it is possible that the available register pool is constrained such that allocation is not possible and an error message is generated. For example, the following code generates an error since all of the conditional registers have been reserved, but a conditional register is required for the variable tmp:

.cproc ... .reserve a1,a2,b0,b1,b2 .reg tmp .... [tmp] .... .... .endproc
.return
Return a Value to a C callable Procedure
Syntax

            .return [argument]

Description

The .return directive function is equivalent to the return statement in C/C++ code. It places the optional argument in the appropriate register for a return value as per the C/C++ calling conventions (see Section 8.4).

The optional argument can have the following meanings:

  • Zero arguments implies a .cproc region that has no return value, similar to a void function in C/C++ code.
  • An argument implies a .cproc region that has a 32-bit return value, similar to an int function in C/C++ code.
  • A register pair of the format hi:lo implies a .cproc region that has a 40-bit long, a 64-bit long long, or a 64-bit type double return value; similar to a long/long long/double function in C/C++ code.

Arguments to the .return directive can be either symbolic register names or machine-register names.

All return statements in a .cproc region must be consistent in the type of the return value. It is not legal to mix a .return arg with a .return hi:lo in the same .cproc region.

The .return directive is unconditional. To perform a conditional .return, simply use a conditional branch around a .return. The assembly optimizer removes the branch and generates the appropriate conditional code. For example, to return if condition cc is true, code the return as:

[!cc] B around .return around:
Example

This example uses a symbolic register, tmp, and a machine-register, A5, as .return arguments:

.cproc ... .reg tmp ... .return tmp = legal symbolic name ... .return a5 = legal actual name
.trip
Specify Trip Count Values
Syntax

label .trip minimum value [,maximum value[, factor]]

Description

The .trip directive specifies the value of the trip count. The trip count indicates how many times a loop iterates. The .trip directive is valid within procedures only. Following are descriptions of the .trip directive parameters:

label The label represents the beginning of the loop. This is a required parameter.
minimum value The minimum number of times that the loop can iterate. This is a required parameter. The default is 1.
maximum value The maximum number of times that the loop can iterate. The maximum value is an optional parameter.
factor The factor used, along with minimum value and maximum value, to determine the number of times that the loop can iterate. In the following example, the loop executes some multiple of 8, between 8 and 48, times: loop: .trip 8, 48, 8

A factor of 2 states that your loop always executes an even number of times allowing the compiler to unroll once; this can result in a performance increase.

The factor is optional when the maximum value is specified.

If the assembly optimizer cannot ensure that the trip count is large enough to pipeline a loop for maximum performance, a pipelined version and an unpipelined version of the same loop are generated. This makes one of the loops a redundant loop. The pipelined or the unpipelined loop is executed based on a comparison between the trip count and the number of iterations of the loop that can execute in parallel. If the trip count is greater or equal to the number of parallel iterations, the pipelined loop is executed; otherwise, the unpipelined loop is executed. For more information about redundant loops, see Section 4.7.

You are not required to specify a .trip directive with every loop; however, you should use .trip if you know that a loop iterates some number of times. This generally means that redundant loops are not generated (unless the minimum value is really small) saving code size and execution time.

If you know that a loop always executes the same number of times whenever it is called, define maximum value (where maximum value equals minimum value) as well. The compiler may now be able to unroll your loop thereby increasing performance.

When you are compiling with the interrupt flexibility option (--interrupt_threshold=n), using a .trip maximum value allows the compiler to determine the maximum number of cycles that the loop can execute. Then, the compiler compares that value to the threshold value given by the --interrupt_threshold option. See Section 3.12 for more information.

Example

The .trip directive states that the loop will execute 16, 24, 32, 40 or 48 times when the w_vecsum routine is called.

w_vecsum: .cproc ptr_a, ptr_b, ptr_c, weight, cnt .reg ai, bi, prod, scaled_prod, ci .no_mdep loop: .trip 16, 48, 8 ldh *ptr_a++, ai ldh *ptr_b++, bi mpy weight, ai, prod shr prod, 15, scaled_prod add scaled_prod, bi, ci sth ci, *ptr_c++ [cnt] sub cnt, 1, cnt [cnt] b loop .endproc
.volatile
Declare Memory References as Volatile
Syntax

            .volatile memref1[, memref2, …]

Description

The .volatile directive allows you to designate memory references as volatile. Volatile loads and stores are not deleted. Volatile loads and stores are not reordered with respect to other volatile loads and stores.

If the .volatile directive references a memory location that may be modified during an interrupt, compile with the --interrupt_threshold=1 option to ensure all code referencing the volatile memory location can be interrupted.

Example

The st and ld memory references are designated as volatile.

.volatile st, ld STW W, *X{st} ; volatile store STW U, *V LDW *Y{ld}, Z ; volatile load