[llvm-dev] RFC: Implement variable-sized register classes
Krzysztof Parzyszek via llvm-dev
llvm-dev at lists.llvm.org
Tue Oct 4 11:50:56 PDT 2016
If there are no objections, I'd like to start working on this soon...
For the AMDGPU target this implies that RC->getSize will no longer be
available in the MC layer.
-Krzysztof
On 9/20/2016 12:32 PM, Krzysztof Parzyszek wrote:
> I have posted a patch that switches the API to one that supports this
> (yet non-existent functionality) earlier:
> https://reviews.llvm.org/D24631
>
> The comments from that were incorporated into the following RFC.
>
>
> Motivation:
>
> Certain targets feature "variable-sized" registers, i.e. a situation
> where the register size can be configured by a hardware switch. A
> common instruction set would then operate on these registers regardless
> of what size they have been configured to have. A specific example of
> that is the HVX coprocessor on Hexagon. HVX provides a set of vector
> registers, and can be configured in one of two modes: one in which
> vectors are 512 bits long, and one in vectors are 1024 bits in length.
> The size only determines the number of elements in the vector register,
> and so the semantics of each HVX instruction does not change: it
> performs a given operation on all vector elements. The encoding of the
> instruction does not change between modes, in fact, it is possible to
> have a binary that runs in both modes.
>
> Currently the register size (strictly speaking, "spill slot size") and
> related properties are fixed and immutable in a RegisterClass. In order
> to allow multiple possible register sizes, several RegisterClass objects
> may need to be defined, which then will require each instruction to be
> defined twice. This is what the HVX code does. Another approach may be
> to define several sets of physical registers corresponding to different
> sizes, and have a large RegisterClass which would be the union of all of
> them. This could avoid having to duplicate the instructions, but would
> lead to problems with getting the actual spill slot size or alignment.
>
> Since the number of targets allowing this kind of variability is growing
> (besides Hexagon, there is RISC-V, MIPS, and out of tree targets, such
> as CHERI), LLVM should allow convenient handling of this type of a
> situation. See comments in https://reviews.llvm.org/D23561 for more
> details.
>
>
> General approach:
>
> 1. Introduce a concept of a hardware "mode". This "mode" should be
> immutable, that is, it should be treated as a fixed property of the
> hardware throughout the execution of the program being compiled. This is
> different from, for example, floating point rounding mode, which can be
> changed at run-time. In LLVM, the mode would be determined by subtarget
> features (reflected in TargetSubtargetInfo).
>
> 2. Move the register/spill size and alignment information from
> MCRegisterClass, and into TargetRegisterInfo. This means that this data
> will no longer be available to the MC layer. Note that the
> size/alignment information will be provided by the TargetRegisterInfo
> object, and not by each individual TargetRegisterClass. A
> TargetRegisterInfo object would be created for a specific hardware mode,
> so that it would be able to provide the necessary information without
> having to consult TargetSubtargetInfo.
>
> 3. Introduce TableGen support for specifying instruction selection
> patterns involving data types depending on the hardware mode.
>
> 4. Require that the sub-/super-class relationships between register
> classes are the same across all hardware modes.
>
>
> The largest impact of this change would be on TableGen, since it needs
> to be aware of the fact that value types under consideration would
> depend on a hardware mode. For example, when having an add-registers
> instruction defined to work on 64-bit registers, providing an additional
> selection pattern for 128-bit registers would present difficulties:
>
> def AddReg : Instruction {
> let OutOperandList = (outs GPR64:$Rd);
> let InOperandList = (ins GPR64:$Rs, GPR64:$Rt);
> let Pattern = [(set GPR64:$Rd, (add GPR64:$Rs, GPR64:$Rt))]>;
> }
>
> the pattern
>
> def: Pat<(add GPR128:$Rs, GPR128:$Rt), (AddReg $Rs, $Rt)>;
>
> would result in a type interference error from TableGen. If the class
> GPR64 was amended to also allow the value type i128, TableGen would no
> longer complain, but may generate invalid instruction selection code.
>
> To solve this, TableGen would need to be aware of the association
> between value types and hardware modes. The rest of this proposal
> describes the programming interface to provide necessary information to
> TableGen.
>
> 1. Define a mode class. It will be recognized by TableGen as having a
> special meaning.
>
> class HwMode<list<Predicate> Ps> {
> // List of Predicate objects that determine whether this mode
> // applies. This is used for situation where the code generated by
> // TableGen needs to determine this, as opposed to TableGen itself,
> // for example in the isel pattern-matching code.
> list<Predicate> ModeDef = Ps;
> }
>
> From the point of view of the code generated by TableGen, HwMode is
> equivalent to a list of Predicate objects. The difference is in how
> TableGen itself treats it: TableGen will distinguish two objects of
> class HwMode if they have different names, regardless of what sets of
> predicates they contain. One way to think of it is that the name of the
> object would serve as a tag denoting the hardware mode.
>
> In the example of the AddReg instruction, we could define two modes:
>
> def Mode64: Mode<[...]>;
> def Mode128: Mode<[...]>;
>
> but so far there would not be much more that we could do.
>
> 2. To make a use of the mode information, provide a class to associate a
> HwMode object with a particular value. This will be done by having two
> lists: one with HwMode objects and another with the corresponding
> values. Since TableGen does not provide a way to define class templates
> (in the same sense as C++ does), the actual interface will be split in
> two parts. First is the "mode selection" base class:
>
> class HwModeSelect<list<HwMode> Ms> {
> list<HwMode> Modes; // List of unique hw modes.
> }
>
> This will be a "built-in" class for TableGen. It will be a base class,
> and treated as "abstract" since it only contains half of the
> information. Each derived class would then need to define a member
> "Values", which is a list of corresponding values, of the same length as
> the list of modes. The following definitions will be useful for
> defining register classes and selection patterns:
>
> class IntSelect<list<Mode> Ms, list<int> Is>
> : HwModeSelect<Ms> {
> // Select an integer literal.
> list<int> Values = Is;
> }
>
> class ValueTypeSelect<list<Mode> Ms, list<ValueType> Ts>
> : HwModeSelect<Ms> {
> // Select a value type.
> list<ValueType> Values = Ts;
> }
>
> class ValueTypeListSelect<list<Mode> Ms, list<list<ValueType>> Ls>
> : HwModeSelect<Ms> {
> // Select a list of value types.
> list<list<ValueType>> Values = Ls;
> }
>
> 3. The class RegisterClass would get new members to hold the
> configurable size/alignment information. If defined, they would take
> precedence over the existing members RegTypes/Size/Alignment.
>
> class RegisterClass {
> ...
> ValueTypeListSelect VarRegTypes; // The names of these members
> IntSelect VarRegSize; // could likely be improved...
> IntSelect VarSpillSize; //
> IntSelect VarSpillAlignment //
> }
>
>
> To fully implement the AddReg instruction, the target would then define
> the register class:
>
> class MyRegisterClass : RegisterClass<...> {
> let VarRegTypes = ValueTypeListSelect<[Mode64, Mode128],
> [[i64, v2i32, v4i16, v8i8], // Mode64
> [i128, v2i64, v4i32, v8i16, v16i8]]>; // Mode128
> let VarRegSize = IntSelect<[Mode64, Mode128], [64, 128]>;
> let VarSpillSize = IntSelect<[Mode64, Mode128], [64, 128]>;
> let VarSpillAlignment = IntSelect<[Mode64, Mode128], [64, 128]>;
> }
>
> def MyIntReg: MyRegisterClass { ... };
>
> And following that, the instruction:
>
> def AddReg: Instruction {
> let OutOperandList = (outs MyIntReg:$Rd);
> let InOperandList = (ins MyIntReg:$Rs, MyIntReg:$Rt);
> let AsmString = "add $Rd, $Rs, $Rt";
> let Pattern = [(set MyIntReg:$Rd, (add MyIntReg:$Rs,
> MyIntReg:$Rt))]>;
> }
>
>
>
>
> -Krzysztof
>
>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
More information about the llvm-dev
mailing list