我正在尝试通过嵌入式操作码数组创建PPU解释器,以执行处理程序来解释指令。指令可以是几种形式之一(I形式,B形式,D形式,X形式等),但是对于主要操作码31的X形式我有一个小问题。我正在使用模板类InterpretArray可以在数组中自动分配处理程序。此类也可以用作处理程序的子数组,可以从处理程序中调用该子数组。 VS2013编译器给我致命错误:


  1> main.cpp(196):致命错误C1202:递归类型或函数依赖项上下文过于复杂


template < size_t xo_rc >
struct Interpreter < X_Form_31_XORc < xo_rc > >


听起来编译器不喜欢X_Form_31_XORc,为什么?我该如何避免呢?

如果未定义X_FORM,则可以在此处编译源代码:

#include <iostream>

#define B_FORM
//#define X_FORM

using namespace std;

// Dummy stuff

typedef unsigned int u32;
typedef signed   int s32;

union Instruction
{
#define FIELD(from, to, type) struct{ u32:(32-to-1); type:(to-from+1); u32:from; }
    u32 instruction;

    // Opcode fields
    FIELD(0, 5, u32 opcode);  // Primary opcode
    FIELD(26, 31, u32 op4);     // Extended opcode of 6-bits (up to 0x3F)
    FIELD(21, 31, u32 op4_);    // Extended opcode of 11-bits (up to 0x7FF)
    FIELD(21, 30, u32 op19);    // Extended opcode of 10-bits (up to 0x3FF)
    FIELD(27, 29, u32 op30);    // Extended opcode of 3-bits (up to 0x7)
    FIELD(21, 30, u32 op31);    // Extended opcode of 10-bits (up to 0x3FF)
    FIELD(30, 31, u32 op58);    // Extended opcode of 2-bits (up to 0x3)
    FIELD(26, 30, u32 op59);    // Extended opcode of 5-bits (up to 0x1F)
    FIELD(30, 31, u32 op62);    // Extended opcode of 2-bits (up to 0x3)
    FIELD(26, 30, u32 op63);    // Extended opcode of 5-bits (up to 0x1F)
    FIELD(21, 30, u32 op63_);   // Extended opcode of 10-bits (up to 0x3FF)

    // Instruction fields
    FIELD(30, 30, u32 aa);      // Bit/Flags: Absolute address bit
    FIELD(31, 31, u32 lk);      // Bit/Flags: Link bit: Update the link register (LR)
    FIELD(21, 21, u32 oe);      // Bit/Flags: OE bit: Enable enable setting OV and SO in the XER
    FIELD(31, 31, u32 rc);      // Bit/Flags: Record bit: Update the condition register (CR)
    FIELD(6, 6, u32 l6);      // Bit/Flags: ?
    FIELD(10, 10, u32 l10);     // Bit/Flags: ?
    FIELD(11, 11, u32 l11);     // Bit/Flags: ?
    FIELD(9, 10, u32 l9_10);   // Bit/Flags: ?
    FIELD(6, 10, u32 bo);      // Branching: Options for the branch conditional instructions
    FIELD(11, 15, u32 bi);      // Branching: CR bit to trigger branch conditional instructions
    FIELD(16, 29, s32 bd);      // Branching: Immediate 14-bit signed integer for branch displacement
    FIELD(19, 20, u32 bh);      // ?
    FIELD(11, 13, u32 bfa);     // ?
    FIELD(6, 8, u32 crfd);    // CR fields: Destination CR or FPSCR field
    FIELD(11, 13, u32 crfs);    // CR fields: Source CR or FPSCR field
    FIELD(6, 10, u32 crbd);    // CR fields: Destination bit in the CR or FPSCR
    FIELD(11, 15, u32 crba);    // CR fields: Source bit in the CR
    FIELD(16, 20, u32 crbb);    // CR fields: Source bit in the CR
    FIELD(12, 19, u32 crm);     // Identify the CR fields that are to be updated by the mtcrf instruction
    FIELD(16, 31, s32 d);       // Immediate 16-bit signed integer
    FIELD(16, 27, u32 dq);      // ?
    FIELD(16, 29, s32 ds);      // ?
    FIELD(7, 14, u32 fm);      // ?
    FIELD(6, 10, u32 frd);     // FPR: Destination
    FIELD(6, 10, u32 frs);     // FPR: Source
    FIELD(11, 15, u32 fra);     // FPR: Source
    FIELD(16, 20, u32 frb);     // FPR: Source
    FIELD(21, 25, u32 frc);     // FPR: Source
    FIELD(16, 19, u32 imm);     // Immediate for to place in FPSCR
    FIELD(6, 29, s32 li);      // Branching:
    FIELD(6, 29, s32 ll);      // Branching:
    FIELD(21, 25, u32 mb);      // First '1' bit of a 64-bit mask in rotate instructions
    FIELD(26, 26, u32 mb_);     // First '1' bit of a 64-bit mask in rotate instructions: Split field
    FIELD(26, 30, u32 me);      // Last '1' bit of a 64-bit mask in rotate instructions
    FIELD(21, 25, u32 me_);     // Last '1' bit of a 64-bit mask in rotate instructions: Split field
    FIELD(26, 26, u32 me__);    // Last '1' bit of a 64-bit mask in rotate instructions: Split field
    FIELD(16, 20, u32 nb);      // Number of bytes to move in an immediate string load or store
    FIELD(6, 10, u32 rd);      // GPR: Destination
    FIELD(6, 10, u32 rs);      // GPR: Source
    FIELD(11, 15, u32 ra);      // GPR: Source
    FIELD(16, 20, u32 rb);      // GPR: Source
    FIELD(16, 20, u32 sh);      // Shift amount
    FIELD(30, 30, u32 sh_);     // Shift amount: Split field
    FIELD(11, 20, u32 spr);     // Special-purpose register
    FIELD(9, 10, u32 strm);    // ?
    FIELD(20, 26, u32 lev);     // ?
    FIELD(16, 31, s32 simm);    // Immediate 16-bit signed integer
    FIELD(16, 31, u32 uimm);    // Immediate 16-bit unsigned integer
    FIELD(9, 10, u32 th);      // Data stream variant of the dcbt instruction
    FIELD(6, 10, u32 to);      // Trap conditions
    FIELD(6, 10, u32 vd);      // Vector/SIMD: Destination vector register
    FIELD(6, 10, u32 vs);      // Vector/SIMD: Source vector register
    FIELD(11, 15, u32 va);      // Vector/SIMD: Source vector register
    FIELD(16, 20, u32 vb);      // Vector/SIMD: Source vector register
    FIELD(21, 25, u32 vc);      // Vector/SIMD: Source vector register
    FIELD(22, 25, u32 vshb);    // Vector/SIMD: Specifies a shift amount in bytes
    FIELD(11, 15, s32 vsimm);   // Vector/SIMD: Immediate 5-bit signed integer
    FIELD(11, 15, u32 vuimm);   // Vector/SIMD: Immediate 5-bit unsigned integer
#undef FIELD
};

struct PPUThread {}; // register context but we do not need it here

// Opcode part

// auto-initialize by recursively assigning all handlers in an opcode array
template< template< typename > class Handler,
          template< size_t   > class Indexer,
          size_t                     start_index,
          size_t                     end_index >
struct OpcodeArrayRange
{
    template< typename Owner >
    static __forceinline void initialize(Owner & owner)
    {
        owner.array[start_index] = Handler< Indexer< start_index > >::handle;
        OpcodeArrayRange< Handler, Indexer, start_index + 1, end_index >::initialize(owner);
    }
};

// auto-initialize by assigning the last handler in an opcode array
template< template< typename > class Handler,
          template< size_t   > class Indexer,
          size_t                     start_index >
struct OpcodeArrayRange < Handler, Indexer, start_index, start_index >
{
    template< typename Owner >
    static __forceinline void initialize(Owner & owner)
    {
        owner.array[start_index] = Handler< Indexer< start_index > >::handle;
    }
};

template < size_t po >
struct OPCD // Primary opcode, used for Indexer in OpcodeArrayRange
{
};

#ifdef B_FORM

template < size_t po >
using B_Form = OPCD < po >;

template < size_t bo_bi >
struct B_Form_BOBI
{
};

#endif

#ifdef X_FORM

template < size_t po >
using X_Form = OPCD < po > ; // Primary opcode + Extended opcode + Record bit

template < size_t po, size_t xo, size_t rc >
struct X_Form_XO_Rc // Primary opcode + Extended opcode + Record bit
{
};

template < size_t po, size_t xo_rc >
struct X_Form_XORc // glue Extended opcode and Record bit into one field, used for Indexer in OpcodeArrayRange
{
};

template < size_t xo_rc >
using X_Form_31_XORc = X_Form_XORc < 31, xo_rc > ; // alias to X_Form_XORc with Primary opcode 31

#endif

// Interpreter part

template< typename T >
struct Interpreter
{
};

// generic interpreter array
template< template< size_t > class OpcodeFormat,
          size_t                   start_index,
          size_t                   end_index >
struct InterpretArray :
    OpcodeArrayRange < Interpreter, OpcodeFormat, start_index, end_index >
{
    InterpretArray()
    {
        initialize(*this);
    }

    __forceinline void operator()(size_t      index,
                                  Instruction code,
                                  PPUThread&  thread)
    {
        array[index](code, thread);
    }

    void(*array[1 + end_index - start_index])(Instruction, PPUThread&);
};

// implementation for interpreting opcodes

template< size_t po >
struct Interpreter < OPCD < po > >
{
    static void handle(Instruction /*code*/, PPUThread& /*thread*/)
    {
        std::cout
            << "OPCD #"
            << po
            << std::endl;
    }
};

#ifdef B_FORM

template < size_t bo_bi >
struct Interpreter < B_Form_BOBI < bo_bi > >
{
    static void handle(Instruction code, PPUThread& thread)
    {
        std::cout
            << "OPCD #31, BO #"
            << (bo_bi >> 5)
            << ", BI #"
            << (bo_bi & 31)
            << ", AA #"
            << (code.aa)
            << ", LK #"
            << (code.lk)
            << std::endl;
    }
};

static InterpretArray < B_Form_BOBI, 0, 0x3FF > interpret_B_Form_BOBI;

template< >
struct Interpreter < B_Form < 16 > >
{
    static void handle(Instruction code, PPUThread& thread)
    {
        interpret_B_Form_BOBI((code.instruction >> 16) & 0x3FF, code, thread);
    }
};

#endif

#ifdef X_FORM

template < size_t xo_rc >
struct Interpreter < X_Form_31_XORc < xo_rc > >
{
    static void handle(Instruction /*code*/, PPUThread& /*thread*/)
    {
        std::cout
            << "OPCD #31, XO #"
            << (xo_rc >> 1)
            << ", Rc #"
            << (xo_rc & 1)
            << std::endl;
    }
};

// specific interpreter array for instructions selected by their extended code
// and Record bit when primary opcode is 31
static InterpretArray < X_Form_31_XORc, 0, 0x7FF > interpret_X_Form_31;

template< >
struct Interpreter < X_Form < 31 > > // note that X_Form is an alias to OPCD
{
    static void handle(Instruction code, PPUThread& thread)
    {
        interpret_X_Form_31((code.instruction & 0x7FF), code, thread);
    }
};

#endif

// specific interpreter array for instructions
// selected by primary opcode
static InterpretArray < OPCD, 0, 0x3F > interpret;

int main()
{
    Instruction insn;
    PPUThread   thread;

    {
        insn.opcode = 2;
        interpret(insn.opcode, insn, thread);
    }
#ifdef B_FORM
    {
        insn.opcode = 16;
        insn.bo = 2;
        insn.bi = 3;
        insn.aa = 1;
        insn.lk = 1;
        interpret(insn.opcode, insn, thread);
    }
#endif
#ifdef X_FORM
    {
        insn.opcode = 31;
        insn.op31 = 2;
        insn.rc = 0;
        interpret(insn.opcode, insn, thread);
    }
    {
        insn.opcode = 31;
        insn.op31 = 2;
        insn.rc = 1;
        interpret(insn.opcode, insn, thread);
    }
#endif
}


编辑:我添加了B_Form,它类似于X_Form,但不使用别名并且可以工作(但构建速度很慢)。

最佳答案

template< template< typename > class Handler,
          template< size_t   > class Indexer,
          size_t                     start_index,
          size_t                     end_index >
struct OpcodeArrayRange
{
    template< typename Owner >
    static __forceinline void initialize(Owner & owner)
    {
        owner.array[start_index] = Handler< Indexer< start_index > >::handle;
        OpcodeArrayRange< Handler, Indexer, start_index + 1, end_index >::initialize(owner);
    }
};

  
  ...

template< template< size_t > class OpcodeFormat,
          size_t                   start_index,
          size_t                   end_index >
struct InterpretArray :
    OpcodeArrayRange < Interpreter, OpcodeFormat, start_index, end_index >

  
  ...

static InterpretArray < X_Form_31_XORc, 0, 0x7FF > interpret_X_Form_31;



InterpretArray < X_Form_31_XORc, 0, 0x7FF >的实例化触发其基类OpcodeArrayRange < Interpreter, X_Form_31_XORc, 0, 0x7FF >的实例化。

OpcodeArrayRange < Interpreter, X_Form_31_XORc, 0, 0x7FF >的实例化会触发OpcodeArrayRange < Interpreter, X_Form_31_XORc, 1, 0x7FF >的实例化,这是因为您如何定义initialize方法。

OpcodeArrayRange < Interpreter, X_Form_31_XORc, 1, 0x7FF >的实例化会触发OpcodeArrayRange < Interpreter, X_Form_31_XORc, 2, 0x7FF >的实例化,这是因为您如何定义initialize方法。

OpcodeArrayRange < Interpreter, X_Form_31_XORc, 2, 0x7FF >的实例化会触发OpcodeArrayRange < Interpreter, X_Form_31_XORc, 3, 0x7FF >的实例化,这是因为您如何定义initialize方法。

等等

模板的此递归实例受到限制。您已经超出了编译器的限制(以及GCC和clang的限制)。您可能根本不应该在initialize方法中引用其他模板实例。

09-13 00:20