Skip to content

JLI Instructions

Overview

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 in 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.

Using JLI Attributes

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;
}

Here is an assembler translation:

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_s then 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_s instruction is for ROM patching. Because with the jli_s 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.

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;
}

Here is an assembler translation:

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_s instruction is already resolved and points to entry 2 in the JLI table. In this case, the compiler does not 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.

Compatibility with MetaWare

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.