llvm学习笔记(4)

2.2.2.2. 一般操作数

寄存器以外的一般的操作数由Operand描述,它定义如下(v7.0597行的DecoderMethod替换为bit hasCompleteDecoder = 1):

593      class Operand<ValueType ty> : DAGOperand {

594        ValueType Type = ty;

595        string PrintMethod = "printOperand";

596        string EncoderMethod = "";

597        string DecoderMethod = "";

598        string OperandType = "OPERAND_UNKNOWN";

599        dag MIOperandInfo = (ops);

600     

601        // MCOperandPredicate - Optionally, a code fragment operating on

602        // const MCOperand &MCOp, and returning a bool, to indicate if

603        // the value of MCOp is valid for the specific subclass of Operand

604        code MCOperandPredicate;

605     

606        // ParserMatchClass - The "match class" that operands of this type fit

607        // in. Match classes are used to define the order in which instructions are

608        // match, to ensure that which instructions gets matched is deterministic.

609        //

610        // The target specific parser must be able to classify an parsed operand into

611        // a unique class, which does not partially overlap with any other classes. It

612        // can match a subset of some other class, in which case the AsmOperandClass

613        // should declare the other operand as one of its super classes.

614        AsmOperandClass ParserMatchClass = ImmAsmOperand;

615      }

成员MIOperandInfo则必须是操作符为ops的dag值,它用于指定多个子操作数,有子操作数的操作数称为复合操作数。对于复杂指令使用的操作数,子操作数是不可缺的。例如,X86里向SSE寄存器载入一个标量的TD定义(来自X86InstrFragmentsSIMD.td,v7.0多了SDNPWantParent属性):

374      def sse_load_f32: ComplexPattern<v4f32, 5, "SelectScalarSSELoad", [],

375                                        [SDNPHasChain, SDNPMayLoad, SDNPMemOperand,

376                                         SDNPWantRoot]>;

这个定义将用于其他复杂的,涉及内存值载入寄存器的指令操作数,比如指令VCVTSD2SI(来自X86InstrSSE.td,v7.0的定义略有不同,但本质没有区别):

1670   let Predicates = [UseAVX] in {

1671   defm VCVTSD2SI : sse12_cvt_sint<0x2D, VR128, GR32,

1672                     int_x86_sse2_cvtsd2si, sdmem, sse_load_f64, "cvtsd2si",

1673                     SSE_CVT_SD2SI>, XD, VEX, VEX_LIG;

1674   defm VCVTSD2SI64 : sse12_cvt_sint<0x2D, VR128, GR64,

1675                       int_x86_sse2_cvtsd2si64, sdmem, sse_load_f64, "cvtsd2si",

1676                       SSE_CVT_SD2SI>, XD, VEX, VEX_W, VEX_LIG;

1677   }

这一点在其基类sse12_cvt_sint中显示得很清楚:

1638   multiclass sse12_cvt_sint<bits<8> opc, RegisterClass SrcRC, RegisterClass DstRC,

1639                            Intrinsic Int, Operand memop, ComplexPattern mem_cpat,

1640                            string asm, OpndItins itins> {

1641     def rr : SI<opc, MRMSrcReg, (outs DstRC:$dst), (ins SrcRC:$src),

1642                 !strconcat(asm, "\t{$src, $dst|$dst, $src}"),

1643                 [(set DstRC:$dst, (Int SrcRC:$src))], itins.rr>,

1644              Sched<[itins.Sched]>;

1645     def rm : SI<opc, MRMSrcMem, (outs DstRC:$dst), (ins memop:$src),

1646                 !strconcat(asm, "\t{$src, $dst|$dst, $src}"),

1647                 [(set DstRC:$dst, (Int mem_cpat:$src))], itins.rm>,

1648              Sched<[itins.Sched.Folded]>;

1649   }

ComplexPattern描述的模式要求以C++形式模式匹配代码,它定义在TargetSelectionDAG.td。

1088   class ComplexPattern<ValueType ty, int numops, string fn,

1089                        list<SDNode> roots = [], list<SDNodeProperty> props = []> {

1090     ValueType Ty = ty;

1091     int NumOperands = numops;

1092     string SelectFunc = fn;

1093     list<SDNode> RootNodes = roots;

1094     list<SDNodeProperty> Properties = props;

1095   }

如果RootNodes不是空,它就指定了SelectFunc所要匹配的节点。而如果RootNodes是空,所要匹配的节点就由Properties指定。下面是X86的两个例子:

701      def addr: ComplexPattern<iPTR, 5, "SelectAddr", [], [SDNPWantParent]>;

702      def lea32addr : ComplexPattern<i32, 5, "SelectLEAAddr",

703                                     [add, sub, mul, X86mul_imm, shl, or, frameindex],

704                                     []>;

Addr没有RootNodes,属性SDNPWantParent指示SelectAddr对包含它的dag值进行匹配。而lea32addr的RootNodes则指示SelectLEAAddr对add、sub、X86mul_imm、frameindex、shl、or及mul类型的节点进行匹配。

同样,sse_load_f32也是对包含它的dag值使用SelectScalarSSELoad进行匹配。它期望操作数的类型是v4f32,对应的Operand对应是(v7.0MIOperandInfo最后的参数是SEGMENT_REG):

381      def ssmem: Operand<v4f32> {

382        let PrintMethod = "printf32mem";

383        let MIOperandInfo = (ops ptr_rc, i8imm, ptr_rc_nosp, i32imm, i8imm);

384        let ParserMatchClass = X86Mem32AsmOperand;

385        let OperandType = "OPERAND_MEMORY";

386      }

383行的MIOperandInfo指出了ssmem实际上要用到5个操作数。而LLVM对此提供了374行所指出的函数SelectScalarSSELoad,它的原型是(v7.0因为增加了SDNPWantParent属性,函数声明里在第二个参数的位置插入了:SDNode *Parent):

bool X86DAGToDAGISel::SelectScalarSSELoad(SDNode *Root,

                                          SDValue N, SDValue &Base,

                                          SDValue &Scale, SDValue &Index,

                                          SDValue &Disp, SDValue &Segment,

                                          SDValue &PatternNodeWithChain);

SelectScalarSSELoad只被sse_load_f32及sse_load_f64这两个ComplexPattern调用(在TableGen生成的文件X86GenDAGISel.inc中,同样在v7.0中,在第二个参数位置插入一个Parent实参):

    Result.resize(NextRes+6);

    return SelectScalarSSELoad(Root, N, Result[NextRes+0].first, Result[NextRes+1].first, Result[NextRes+2].first, Result[NextRes+3].first, Result[NextRes+4].first, Result[NextRes+5].first);

从中可以知道ptr_rc保存基址,第一个i8imm保存Scale,ptr_rc_nosp保存索引,i32imm保存偏移,第二个i8imm保存段寄存器。

这些子操作数也同样可以被援引,但需要具名,如下例(来自SystemZOperands.td):

464      def brtarget16tls : PCRelTLSOperand<OtherVT, PCRelTLS16> {

465        let MIOperandInfo = (ops brtarget16:$func, tlssym:$sym);

466        let EncoderMethod = "getPC16DBLTLSEncoding";

467        let DecoderMethod = "decodePC16DBLOperand";

468      }

通过$brtarget16tls.func来援引brtarget16tls中brtarget16类型子操作数中的$func子操作数。

2.2.3. 约束条件

LLVM的文档并未说明上述Instruction声明中各个域之间存在的约束条件。实际上,不是所有的域都是自由的,以一个例子来说明(v7.0X86指令定义不再直接从InstrItinClass派生,改为通过WriteRes派生定义来描述。因此下例不再使用316行的IIC_MMX_MOVQ_RR作为基类):

310     def MMX_MOVQ2DQrr : MMXS2SIi8<0xD6, MRMSrcReg, (outs VR128:$dst),

311                                   (ins VR64:$src), "movq2dq\t{$src, $dst|$dst, $src}",

312                                   [(set VR128:$dst,

313                                     (v2i64

314                                       (scalar_to_vector

315                                         (i64 (bitconvert (x86mmx VR64:$src))))))],

316                                   IIC_MMX_MOVQ_RR>;

这是一个Instruction的深度派生声明,刚才看到的dag值的例子就摘自这里(313~315行)。它将赋给Instruction声明333行的Pattern成员,而310行的(outs VR128:$dst)则将赋给327行的成员OutOperandList,311行的(ins VR64:$src)给328行的InOperandList。这三个成员间存在这样的约束:OutOperandList必须以outs作为操作符,InOperandList则必须以ins作为操作符,而且两者中所有的操作数必须具名。另外,Pattern中出现的符号名(即这个例子中的$src,$dst)必须来自OutOperandList以及InOperandList,而且分别作为输入、输出操作数。

另外,Instruction成员Constraints是一个字符串,用于描述对输入、输出操作数的约束(定义的435行)。目前有两种约束形式。其一是@earlyclobber $reg,表示寄存器reg的内容很早就被破坏,另一个是绑定约束,其形式是$src = $dst。显然,这些名字都必须是输入、输出操作数的名字。

另外,311行的字符串"movq2dq\t{$src, $dst|$dst, $src}"会赋给329行的AsmString,是这个Instruction声明所对应的汇编语句。在指令生成遍(阶段),将以这个语句为模板,生成汇编代码。

Instruction声明430行的Itinerary与431行的SchedRW都是用于描述指令在目标机器上执行的方方面面,它们是指令调度的依据。它们的定义依赖于具体的目标机器。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值