Reputation: 377
I'm trying to create a PPU interpreter through embedded opcode arrays to execute a handler to interpret the instruction. An instruction can be one of several forms (I-Form, B-Form, D-Form, X-Form, etc.) but I have a small issue with X-Form for primary opcode 31. I'm using a template class InterpretArray which auto-assigns handlers in an array. This class can also be used as a sub-array of handlers which can be invoked from an handler. Compiler VS2013 gives me that fatal error:
1>main.cpp(196): fatal error C1202: recursive type or function dependency context too complex
template < size_t xo_rc >
struct Interpreter < X_Form_31_XORc < xo_rc > >
It sounds like compiler does not like X_Form_31_XORc, any idea why? how can I avoid it?
Here the source that can be compiled fine if X_FORM is undefined:
#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
}
EDIT: I added B_Form which is similar to X_Form but not using alias and it works (but building is quite slow).
Upvotes: 0
Views: 808
Reputation:
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;
The instantiation of InterpretArray < X_Form_31_XORc, 0, 0x7FF >
triggers the instantiation of its base class OpcodeArrayRange < Interpreter, X_Form_31_XORc, 0, 0x7FF >
.
The instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 0, 0x7FF >
triggers the instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 1, 0x7FF >
, because of how you defined your initialize
method.
The instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 1, 0x7FF >
triggers the instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 2, 0x7FF >
, because of how you defined your initialize
method.
The instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 2, 0x7FF >
triggers the instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 3, 0x7FF >
, because of how you defined your initialize
method.
etc.
This recursive instantiation of templates is subject to limits. You've exceeded your compiler's limit (as well as GCC's and clang's). You probably simply shouldn't be referring to other template instantiations in your initialize
method.
Upvotes: 2