Difference between revisions of "NaplesPUInstrInfo.td"
Line 1: | Line 1: | ||
+ | [[Category:Tablegen Files]] | ||
NuPlusInstrInfo.td and [[NuPlusInstrFormats.td]] describe the nu+ instructions and the patterns to transform LLVM IR into machine code. The NuPlusInstrInfo.td contains the instructions definition and the patterns necessary for the translation from the LLVM IR to the nu+ machine code. | NuPlusInstrInfo.td and [[NuPlusInstrFormats.td]] describe the nu+ instructions and the patterns to transform LLVM IR into machine code. The NuPlusInstrInfo.td contains the instructions definition and the patterns necessary for the translation from the LLVM IR to the nu+ machine code. | ||
Revision as of 14:33, 16 October 2017
NuPlusInstrInfo.td and NuPlusInstrFormats.td describe the nu+ instructions and the patterns to transform LLVM IR into machine code. The NuPlusInstrInfo.td contains the instructions definition and the patterns necessary for the translation from the LLVM IR to the nu+ machine code.
The files "compiler/include/llvm/Target/Target.td" and "compiler/include/llvm/Target/TargetSelectionDAG.td" contain the Tablegen classes used for the description.
Instructions definition
The instructions are the definition of the classes contained in the NuPlusInstrFormats.td file. The definition is done by selecting the appropriate class or multiclass and specifying the required arguments such as the opcode, the asm radix and the pattern to match. Usually the classes define require only to specify a particular node, since the pattern is already defined.
Lets consider the definition of the integer ADD instructions. According to the nu+ ISA, the ADD instruction has some variations depending on the operands' nature; e.g. they can be both stored in two 32-bit scalar registers or one of them is a 9-bit immediate. However, all these variations are encapsulated in the FArithInt_TwoOp multiclass. Thus, the ADD instruction is instantiated specifying the asm radix add, the operand add and the opcode 4.
defm ADD : FArithInt_TwoOp<"add", add, 4>;
Pattern matching
During the instruction selection phase, there are some cases in which the instructions do not match with any specified pattern in the instruction definition, or the resulting legalized DAG does not contain the desired machine nodes. There are several ways to overcome this situation, some of which requires the modification of the NuPlusTargetLowering class. However the easiest way is to define a tablegen anonymous pattern, by instantiating the class Pat.
class Pat<dag pattern, dag result> : Pattern<pattern, [result]>;
Keep in mind that this solution affects how the input DAG is transformed into a legalized DAG, so the only type of dependency is the data dependecy among the operations expressed as nodes.
In the NuPlusInstrInfo.td file there is an entire section dedicated to these patterns. They are to detect intrinsics, match different operations to the same machine-dependent operation. Lets consider some examples.
This first example shows how the Pat class can be used to achieve complex pattern transformation. The purpose of the pattern is to emit the necessary nodes to load a 64-bit immediate in nu+. The goal is to break the immediate value in two 32-bit parts and load them in the sub-registers of a 64-bit register in a little-endian fashion. The pattern to match is (i64 imm:$val); i64 is the operation of 64-bit load, while imm:$val defines that the operand val is an immediate imm. This pattern is translated in the DAG reported in the second part of the definition. The order is from the inner node the the outer.
def : Pat<(i64 imm:$val),
(INSERT_SUBREG (INSERT_SUBREG (i64 (IMPLICIT_DEF)),
(LoadI32 (i32 (HI32I $val))),
sub_odd),
(LoadI32 (i32 (LO32I $val))),
sub_even)>;
The following example shows other uses of the Pat class, easier to understand than the previous one. The zextloadi1 nodes are defined in NuPlusInstrFormats.td and match loads followed by a zero extension.
def : Pat<(i32 (zextloadi1_mem ADDRri:$addr)), (L32BU_Mainmem ADDRri:$addr)>;
def : Pat<(i64 (zextloadi1_mem ADDRri:$addr)), (L64BU_Mainmem ADDRri:$addr)>;
def : Pat<(i32 (zextloadi1_scratch ADDRri:$addr)), (L32BU_Scratchpad ADDRri:$addr)>;
def : Pat<(i64 (zextloadi1_scratch ADDRri:$addr)), (L64BU_Scratchpad ADDRri:$addr)>;
Pseudo-instructions
In LLVM, a pseudo-instruction is defined by setting the value isPseudo to 1. There are also other flags such as isCodeGenOnly and isAsmParserOnly, which purpose is to help with codegen modeling. In particular:
- If isPseudo = 1, the instruction is considered a pseudo-instruction with no encoding informations and should be expanded, at the latest, during lowering to MCInst.
- If isCodeGenOnly = 1, the instruction does have encoding information and can go through to the CodeEmitter unchanged, but duplicates a canonical instruction definition's encoding and should be ignored when constructing the assembler match tables.
- If isAsmParserOnly = 1, the instruction is a pseudo-instruction for use by the assembler parser.
In the nu+ back-end the majority of pseudo-instructions have also the flag usesCustomInserter setted to 1. In this way LLVM expects that there is a custom function that performs the lowering of that Machineinstruction in a MCInst.
As an example, to load a 32-bit integer immediate, the nu+ back-end uses a custom inserter function defined in NuPlusTargetLowering. However to do so, we must first define a pseudo-instruction with usesCustomInserter = 1. In this case the instruction is called LoadI32.
let usesCustomInserter = 1 in {
def LoadI32 : Pseudo<
(outs GPR32:$dst),
(ins GPR32:$val),
[(set i32:$dst, (i32 imm:$val))]>;
...
}
With (outs GPR32:$dst), we say that the instruction has one output operand called dst which is a GPR32RegisterClass register. With (ins GPR32:$val), we say that the instruction has one input operand called val which is a GPR32RegisterClass register. The pattern [(set i32:$dst, (i32 imm:$val))] states that the immediate input val must be copied (set) in the output val.