<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Sep 20, 2016 at 10:32 AM, Krzysztof Parzyszek via llvm-dev <span dir="ltr"><<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I have posted a patch that switches the API to one that supports this (yet non-existent functionality) earlier:<br>
<a href="https://reviews.llvm.org/D24631" rel="noreferrer" target="_blank">https://reviews.llvm.org/D2463<wbr>1</a><br>
<br>
The comments from that were incorporated into the following RFC.<br>
<br>
<br>
Motivation:<br>
<br>
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.<br>
<br>
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.<br>
<br>
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 <a href="https://reviews.llvm.org/D23561" rel="noreferrer" target="_blank">https://reviews.llvm.org/D2356<wbr>1</a> for more details.<br></blockquote><div><br></div><div>ARM SVE sounds like it will have similar issues: <a href="https://community.arm.com/groups/processors/blog/2016/08/22/technology-update-the-scalable-vector-extension-sve-for-the-armv8-a-architecture">https://community.arm.com/groups/processors/blog/2016/08/22/technology-update-the-scalable-vector-extension-sve-for-the-armv8-a-architecture</a></div><div><br></div><div>-- Sean Silva</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
<br>
General approach:<br>
<br>
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).<br>
<br>
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.<br>
<br>
3. Introduce TableGen support for specifying instruction selection patterns involving data types depending on the hardware mode.<br>
<br>
4. Require that the sub-/super-class relationships between register classes are the same across all hardware modes.<br>
<br>
<br>
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:<br>
<br>
def AddReg : Instruction {<br>
let OutOperandList = (outs GPR64:$Rd);<br>
let InOperandList = (ins GPR64:$Rs, GPR64:$Rt);<br>
let Pattern = [(set GPR64:$Rd, (add GPR64:$Rs, GPR64:$Rt))]>;<br>
}<br>
<br>
the pattern<br>
<br>
def: Pat<(add GPR128:$Rs, GPR128:$Rt), (AddReg $Rs, $Rt)>;<br>
<br>
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.<br>
<br>
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.<br>
<br>
1. Define a mode class. It will be recognized by TableGen as having a special meaning.<br>
<br>
class HwMode<list<Predicate> Ps> {<br>
// List of Predicate objects that determine whether this mode<br>
// applies. This is used for situation where the code generated by<br>
// TableGen needs to determine this, as opposed to TableGen itself,<br>
// for example in the isel pattern-matching code.<br>
list<Predicate> ModeDef = Ps;<br>
}<br>
<br>
>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.<br>
<br>
In the example of the AddReg instruction, we could define two modes:<br>
<br>
def Mode64: Mode<[...]>;<br>
def Mode128: Mode<[...]>;<br>
<br>
but so far there would not be much more that we could do.<br>
<br>
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:<br>
<br>
class HwModeSelect<list<HwMode> Ms> {<br>
list<HwMode> Modes; // List of unique hw modes.<br>
}<br>
<br>
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:<br>
<br>
class IntSelect<list<Mode> Ms, list<int> Is><br>
: HwModeSelect<Ms> {<br>
// Select an integer literal.<br>
list<int> Values = Is;<br>
}<br>
<br>
class ValueTypeSelect<list<Mode> Ms, list<ValueType> Ts><br>
: HwModeSelect<Ms> {<br>
// Select a value type.<br>
list<ValueType> Values = Ts;<br>
}<br>
<br>
class ValueTypeListSelect<list<Mode> Ms, list<list<ValueType>> Ls><br>
: HwModeSelect<Ms> {<br>
// Select a list of value types.<br>
list<list<ValueType>> Values = Ls;<br>
}<br>
<br>
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.<br>
<br>
class RegisterClass {<br>
...<br>
ValueTypeListSelect VarRegTypes; // The names of these members<br>
IntSelect VarRegSize; // could likely be improved...<br>
IntSelect VarSpillSize; //<br>
IntSelect VarSpillAlignment //<br>
}<br>
<br>
<br>
To fully implement the AddReg instruction, the target would then define the register class:<br>
<br>
class MyRegisterClass : RegisterClass<...> {<br>
let VarRegTypes = ValueTypeListSelect<[Mode64, Mode128],<br>
[[i64, v2i32, v4i16, v8i8], // Mode64<br>
[i128, v2i64, v4i32, v8i16, v16i8]]>; // Mode128<br>
let VarRegSize = IntSelect<[Mode64, Mode128], [64, 128]>;<br>
let VarSpillSize = IntSelect<[Mode64, Mode128], [64, 128]>;<br>
let VarSpillAlignment = IntSelect<[Mode64, Mode128], [64, 128]>;<br>
}<br>
<br>
def MyIntReg: MyRegisterClass { ... };<br>
<br>
And following that, the instruction:<br>
<br>
def AddReg: Instruction {<br>
let OutOperandList = (outs MyIntReg:$Rd);<br>
let InOperandList = (ins MyIntReg:$Rs, MyIntReg:$Rt);<br>
let AsmString = "add $Rd, $Rs, $Rt";<br>
let Pattern = [(set MyIntReg:$Rd, (add MyIntReg:$Rs,<br>
MyIntReg:$Rt))]>;<br>
}<span class="gmail-HOEnZb"><font color="#888888"><br>
<br>
<br>
<br>
<br>
-Krzysztof<br>
<br>
<br>
-- <br>
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation<br>
______________________________<wbr>_________________<br>
LLVM Developers mailing list<br>
<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/llvm-dev</a><br>
</font></span></blockquote></div><br></div></div>