Using JLI Instructions with GNU

The ARCv2 ISA provides the JLI instruction, which is two-byte instructions that can be used to reduce code size for an application. To make use of it, we provide two new function attributes jli_always and jli_fixed which will force the compiler to call the indicated function using a jli_s instruction. The compiler also generates the entries in the JLI table for the case when we use jli_always attribute. In the case of jli_fixed the compiler assumes a fixed position of the function into JLI table. Thus, the user needs to provide an assembly file with the JLI table for the final link. This is useful when we want to have a table in ROM and a second table in the RAM memory.

The jli instruction usage can be also forced without the need to annotate the source code via -mjli-always command.

Optimizing Code using JLI Calls on Functions

The usual way of using jli calls is to use the attribute _jli_always_ with a function. For example:

int func (int i) __attribute__((jli_always));

int func (int i)
{
  return i*i;
}

int main ()
{
  printf ("func returned = %d \n", func (100));
  return 0;
}

which leads to:

main:
        push_s blink
        st.a fp,[sp,-4] ;28
        mov_s fp,sp     ;4
        mov_s r0,100    ;3
        jli_s @__jli.func
        mov_s r2,r0     ;4
        mov_s r1,r2     ;4
        mov_s r0,@.LC0  ;14
        bl @printf;1
        mov_s r2,0      ;3
        mov_s r0,r2     ;4
        ld.ab fp,[sp,4] ;25
        pop_s blink
        j_s [blink]

As we can see the call to func is done via the jli_s instruction, while the other calls are done using regular bl instruction. If we want all calls to non-local functions to be done using jli instructions we can use -mjli-always compiler option. However, we need to be careful in using this option as the JLI table can hold only 1024 entries. The compiler cannot efficiently check the number of entries as it only has a limited view over the whole application. In this case the GNU tool takes care of generating the JLI table, patching the jli_s instruction with the correct entry number corresponding to the called function, and the initialization of the jli_base auxiliary register.

A special way to use the jli instruction is for ROM patching. Because with the jli instruction function calls are made indirectly through the JLI table, the JLI table entries can be changed to invoke alternative functions without affecting the executable code. Thus, in this case the location of each function called via jli instruction must be fixed and known at compile time. To achieve this, we have introduced a new jli_fixed function attribute which accept a numerical parameter to specify the function call entry in the JLI table. This attribute is GNU specific.

Let us consider the following example:

int func (int i) __attribute__((jli_fixed(2)));

int func (int i)
{
  return i*i;
}

int main ()
{
  printf ("func returned = %d \n", func (100));
  return 0;
}

which leads to:

main:
        push_s blink
        st.a fp,[sp,-4] ;28
        mov_s fp,sp     ;4
        mov_s r0,100    ;3
        jli_s 2 ; @func
        mov_s r2,r0     ;4
        mov_s r1,r2     ;4
        mov_s r0,@.LC0  ;14
        bl @printf;1
        mov_s r2,0      ;3
        mov_s r0,r2     ;4
        ld.ab fp,[sp,4] ;25
        pop_s blink
        j_s [blink]

As we can see now, the operand of jli instruction is already resolved and points to entry 2 in the JLI table. In this case, the compiler doesn’t generate the JLI table, as it needs to be provided by the user. A JLI table can be something like this:

        .section .jlitab
        .align  4
JLI_table:
__jli.entry0:   b       entry0  ; 0
__jli.entry1:   b       entry1  ; 1
__jli.func:     b       func    ; 2

The initialization of the jli_base is again done by the crt0. However, in the case of RAM/ROM patching, one may want to overwrite the initial value with a new value based on the location of a patched JLI table. N.B. the RAM/ROM patching approach may require special startup and/or linker scripts which are not provided.

Discussion about MWDT/GNU Compatibility

In general the GNU jli implementation is compatible with MWDT implementation, except for the code that invokes the MetaWare runtime initialization code that sets the JLI_BASE register to address the JLI table. GNU additionally introduces the jli_fixed attribute to closely mimic the MWDT jli_call_fixed pragma.