Migrating ARChitect APEX generated header to GNU

The ARChitect2 tool generates a special header file when APEX instructions are specified/selected in ARChitect. Such an example can be:

/* **** 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

MWDT specific header analysis

Let us take each element of the autogenerated header and analyse it.

The Auxiliary registers

// User extension aux register auxreg0
#define AR_AUXREG0 0xfffff800
#pragma Aux_register(0xfffff800, name=>"auxreg0")

The MWDT 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:

#define Aux_register(ADDR, NAME)                \
  asm (".extAuxRegister " NAME ", " #ADDR ", r|w")

Having this instantiation:

Aux_register (0xfffff800, "auxreg0");

The extension core registers

// User extension core register r32
#define CR_R32 32
#pragma Core_register(32, name=>"r32")

Normally, GNU recognize all the extension core registers as r32-r57. Thus, it is not needed to define a core register which has the same name as in GNU.

User 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")

MWDT format is using the function _insn1_ declaration to select the instruction syntax, while the pragma is defining the MOP, SOP and sides effects of the instruction. Thus, for our example, _insn1_ is using SYNTAX_3OP leading to:

#define intrinsic_3OP(NAME, MOP, SOP)           \
  asm (".extInstruction " NAME "," #MOP ","             \
  #SOP ",SUFFIX_NONE, SYNTAX_3OP\n\t")

intrinsic_3OP ("insn1", 7, 5);

for instruction declaration, and:

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

to be used in C.

The list of clobber registers can be ignored as the compiler does not handle the r32 to r56 register. However, to be safe, it is good to inform it about them. As for auxiliary register, one can ignore or just add “memory” to the clobber list as AUX registers are memory.

GNU header

#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