Migrating APEX Header¶
Preface¶
ARChitect2 tool generates a special header file when APEX instructions are specified/selected in ARChitect. Here is an example:
/* **** DO NOT EDIT - this file is generated by ARChitect2 ****
*
* Description: Header file declaring the compiler extensions for apex components
*/
#ifndef _apexextensions_H_
#define _apexextensions_H_
#define APEX_EXT_CORE_REGS_EXT_CORE_REGS_PRESENT 1
// User extension aux register auxreg0
#define AR_AUXREG0 0xfffff800
#pragma Aux_register(0xfffff800, name=>"auxreg0")
// User extension aux register auxreg1
#define AR_AUXREG1 0xfffff801
#pragma Aux_register(0xfffff801, name=>"auxreg1")
// User extension core register r32
#define CR_R32 32
#pragma Core_register(32, name=>"r32")
// User extension core register r33
#define CR_R33 33
#pragma Core_register(33, name=>"r33")
// User extension core register r34
#define CR_R34 34
#pragma Core_register(34, name=>"r34")
// User extension core register r35
#define CR_R35 35
#pragma Core_register(35, name=>"r35")
// User extension instruction insn1
extern long insn1(long,long);
#pragma intrinsic(insn1,opcode=>7,sub_opcode=>5, effects=>"reg=32:is_read:is_written; reg=33:is_read:is_written; reg=34:is_read:is_written; reg=35:is_read:is_written; auxreg=0xfffff800:is_read:is_written; auxreg=0xfffff801:is_read:is_written")
// User extension instruction insn2
extern long insn2(long);
#pragma intrinsic(insn2,opcode=>7,sub_opcode=>1, effects=>"reg=32:is_read:is_written; reg=33:is_read:is_written; reg=34:is_read:is_written; reg=35:is_read:is_written; auxreg=0xfffff800:is_read:is_written; auxreg=0xfffff801:is_read:is_written")
#endif
Auxiliary Registers¶
// User extension aux register auxreg0
#define AR_AUXREG0 0xfffff800
#pragma Aux_register(0xfffff800, name=>"auxreg0")
MetaWare compiler accepts the definition of various extension features via pragmas. However, this is not the case for GNU. In this case, we need to use inline assembly to make the toolchain aware of the added functionality. Thus, to handle auxiliary register definition at C-level, we may need to define this:
Instantiate the register:
Extension Core Registers¶
Usually, GNU recognizes all the extension core registers as r32-r57
. Thus,
it is not necessary to define a core register which has the same name as in GNU.
Extension Instructions¶
// User extension instruction insn1
extern long insn1(long,long);
#pragma intrinsic(insn1,opcode=>7,sub_opcode=>5, effects=>"reg=32:is_read:is_written; reg=33:is_read:is_written; reg=34:is_read:is_written; reg=35:is_read:is_written; auxreg=0xfffff800:is_read:is_written; auxreg=0xfffff801:is_read:is_written")
MetaWare format uses the function insn1
declaration to select the instruction syntax, while
pragma
defines MOP, SOP and sides effects of the instruction. Thus, for our
example, insn1
must use SYNTAX_3OP
:
#define intrinsic_3OP(NAME, MOP, SOP) \
asm (".extInstruction " NAME "," #MOP "," \
#SOP ",SUFFIX_NONE, SYNTAX_3OP\n\t")
intrinsic_3OP ("insn1", 7, 5);
__extension__ static __inline int32_t __attribute__ ((__always_inline__))
insn1 (int32_t __a, int32_t __b)
{
int32_t __dst;
__asm__ ("insn1 %0, %1, %2\n\t"
: "=r" (__dst)
: "r" (__a), "rCal" (__b)
: "r32", "r33", "r34", "r35");
return __dst;
}
A list of clobber registers may be ignored as the compiler does not handle r32-r56
registers.
However, to be safe, it is good to mention them. As for auxiliary register, one can ignore or
just add memory
to the clobber list as AUX registers is memory.
Final Header File¶
#ifndef _apexextensions_H_
#define _apexextensions_H_
#include <stdint.h>
#define Aux_register(ADDR, NAME) \
asm (".extAuxRegister " NAME ", " #ADDR ", r|w")
#define intrinsic_3OP(NAME, MOP, SOP) \
asm (".extInstruction " NAME "," #MOP "," \
#SOP ",SUFFIX_NONE, SYNTAX_3OP\n\t")
#define intrinsic_2OP(NAME, MOP, SOP) \
asm (".extInstruction " NAME "," #MOP "," \
#SOP ",SUFFIX_NONE, SYNTAX_2OP\n\t")
Aux_register (0xfffff800, "auxreg0");
Aux_register (0xfffff801, "auxreg1");
intrinsic_3OP ("insn1", 7, 5);
intrinsic_2OP ("insn2", 7, 1);
__extension__ static __inline int32_t __attribute__ ((__always_inline__))
insn1 (int32_t __a, int32_t __b)
{
int32_t __dst;
__asm__ ("insn1 %0, %1, %2\n\t"
: "=r" (__dst)
: "r" (__a), "rCal" (__b)
: "r32", "r33", "r34", "r35", "memory");
return __dst;
}
__extension__ static __inline int32_t __attribute__ ((__always_inline__))
insn2 (int32_t __a)
{
int32_t __dst;
__asm__ ("insn2 %0, %1\n\t"
: "=r" (__dst)
: "rCal" (__a)
: "r32", "r33", "r34", "r35", "memory");
return __dst;
}
#endif