problem with non-allocatable register classes

I am using some non-allocatable RegisterClasses to define lists of registers that are used for various non-allocation-related processing in the back end. For example, we have a post-allocation functional unit selection pass that is guided by the register assignment, which does things like ‘myRegClass.contains(Reg)’ to see if a register is in the set of registers accessible by a given unit.

These sets contain lists of both allocatable and non-allocatable registers (for example, GPRs plus PC and SP). So I defined them as non-allocatable, and defined separate classes for use in the allocator.

The problem is that tablegen does all kinds of clever analysis of the register classes, determining subsets and supersets and such, which are used to answer methods like getCommonSubClass(). In some cases it synthesizes new classes, which may or may not be allocatable depending on how the non-allocatable classes get mixed in. If a function like getCommonSubClass() returns a non-allocatable class, bad things happen (e.g. assertion failures from setRegClass()).

It seems to me that functions like getCommonSubClass() should never return non-allocatable classes. But there seems to be no provision in tablegen to keep that from happening. It seems likely that no one has encountered this because for most targets non-allocatable classes contain registers that don’t overlap much with the allocatable classes.

Does anyone have any guidance as far as fixing or working around this?


I’m not sure I would agree that getCommonSubClass should never return a non-allocatable class. You still want to be able to do the same kinds of queries for non-allocatable classes. Maybe it might make sense to assert that if the input is allocatable the result register class also is?

Could you describe in more detail what your register class hierarchy looks like? It sounds to me like you need to refactor your register classes are split up into allocatable and non-allocatable components. Are you adding the non-allocatable registers directly to the allocatable classes, or adding non-allocatable subclasses to the allocatable register superclass?


Thanks. I can’t really detail the classes since the machine is still under wraps, but I’ll try to illustrate with a toy example.

// these classes just define register files for use in non-allocation passes

let IsAllocatable = 0 in {

def Afile : RegisterClass<A0,A1,A2,A3>;

def Bfile : RegisterClass<B0,B1,B2,B3>;

def Cfile : RegisterClass<C0,C1,C2,C3>;


// these classes are used by the allocator

let isAllocatable = 1 in {

def GPRAll: RegisterClass<(add Afile, Bfile, Cfile)>;

def GPRAB: RegisterClass<(add Afile, Bfile)>;

def Special: RegisterClass<(add C0, C1)>;


This seems basic enough. The problem comes in when tablegen starts creating its own synthesized classes, for example ‘GPRAB_and_Special’ (that may not happen in this toy example, but it does happen in my case). It’s not clear how or why, but sometimes the synthesized classes are allocatable and sometimes they’re not. Even if I make all my classes allocatable, sometimes the synthesized classes are non-allocatable. The synthesized classes are included in the subclass bitvectors, ready to be selected by the ‘CommonClass’ family of functions.

Expanding on your suggestion: perhaps for a function like getCommonSubClass(A,B), if both A and B are allocatable, then the result must be allocatable. That would be easy enough.