[llvm-dev] RFC: Implement variable-sized register classes

Sean Silva via llvm-dev llvm-dev at lists.llvm.org
Fri Sep 23 13:01:47 PDT 2016

On Tue, Sep 20, 2016 at 10:32 AM, Krzysztof Parzyszek via llvm-dev <
llvm-dev at lists.llvm.org> 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.

ARM SVE sounds like it will have similar issues:

-- Sean Silva

> 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
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20160923/b02e29b3/attachment.html>

More information about the llvm-dev mailing list