[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:
https://community.arm.com/groups/processors/blog/2016/08/22/technology-update-the-scalable-vector-extension-sve-for-the-armv8-a-architecture
-- 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