在include/llvm/IR中定义了llvm IR的函数。
其中Intrinsics.td中描述了全部的指令,但是我基本上看不懂。大概是先定义各个类型函数的class,然后def 调用class进行函数的定义。
IntrinsicsX86.td定义了X86专有函数的定义。比如,let TargetPrefix = "x86" 是规定平台,def后面的是指令,GCCBuiltin的是指令名称,Intrinsic是函数的参数,第一个参数返回值,第二个参数,第三个是参数类型。
let TargetPrefix = "x86" in { // All intrinsics start with "llvm.x86.".
def int_x86_ssse3_phadd_w : GCCBuiltin<"__builtin_ia32_phaddw">,
Intrinsic<[llvm_x86mmx_ty], [llvm_x86mmx_ty,
llvm_x86mmx_ty], [IntrNoMem]>;
def int_x86_ssse3_phadd_w_128 : GCCBuiltin<"__builtin_ia32_phaddw128">,
Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty,
llvm_v8i16_ty], [IntrNoMem]>;
def int_x86_ssse3_phadd_d : GCCBuiltin<"__builtin_ia32_phaddd">,
Intrinsic<[llvm_x86mmx_ty], [llvm_x86mmx_ty,
llvm_x86mmx_ty], [IntrNoMem]>;
//还有很多,不复制了
}
我们在Intrinsic中增加两个指令:
let TargetPrefix = "x86" in {
def int_x86_max_qb: GCCBuiltin<"__builtin_x86_max_qb">,
Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [Commutative]>;
def int_x86_min_qb: GCCBuiltin<"__builtin_x86_min_qb">,
Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [Commutative]>;
}
然后给clang一个添加,在/tools/clang/include/clang/Basic/BuiltinsX86.def中增加我们添加的指令。这个文件就是对应x86的内置函数的。iii表示的是使用三个寄存器,并且都是整型的。
BUILTIN(__builtin_x86_max_qb, "iii", "")
BUILTIN(__builtin_x86_min_qb, "iii", "")
再来就是增加具体的编译过程,在/tools/clang/lib/CodeGen/CGBuiltin.cpp中增加解析。
在3792行,为不同的arch选择不同的函数,我们是x86的,所以选择EmitX86BuiltinExpr。在这个函数中,大概在9076行有一个siwtch,为不同的ID选择不同的返回值,我们参照其他指令为自己的指令增加解析。因为我们在Intrinsic中使用了GCCBuiltin(GCCBuiltin相当于是建立指令的一个模板),所以就不用在这里加了。
到这里,我们就完成了在Clang中增加指令的部分。然后我们还需要做的就是在x86后端中增加这条指令具体需要做的事情。
在lib/Target/X86/中增加这个指令的定义。
首先,增加X86的SDNode,在X86ISelLowering.h中找个位置增加上max_qb,这样你就有了X86ISD::max_qb。
然后在X86InstrInfo.td中增加伪指令。
我们先找个例子来看一下(截取了一段):
let mayLoad = 1, mayStore = 1, usesCustomInserter = 1,
SchedRW = [WriteRMW], Defs = [ESP] in {
let Uses = [ESP] in
def RDFLAGS32 : PseudoI<(outs GR32:$dst), (ins),
[(set GR32:$dst, (int_x86_flags_read_u32))]>,
Requires<[Not64BitMode]>;
}
let ... in 是一个句式,相当于指令运行时的条件(需要满足的状态)。mayLoad等前三个是X86的状态flag,SchedRW是指令的调度信息,Defs,Uses是需要的寄存器等。具体信息可以在include/llvm/Target/里查看
def是定义指令。例子中 RDFLAG32是指令名称。PseudoI是说指令时伪指令,还有I,RI等,都是不同类型的指令。outs ins是输入输出。写到这里发现有些这个例子中没有。重新写一下
def name :I<.(outs),(ins)," name 这里写的是汇编代码的输出样式",[set xxx, xxx,xxx]>,Sched<>,TB(指令类型),OpSize32(指令的size);
我们的指令可以这样写:后面一个max_qb是我们刚刚增加的X86ISD
def max_qb : PseudoI< (outs GR32:$dst), (ins GR32:$src1,GR32:$src2),
"max_qb\t {$dst, $src1,$src2}", [(set GR32:$dst, (max_qb GR32:$src1, GR32:$src2))]> ;
再在X86ISelLowering.cpp中增加一个case,大概是在26600行,有很多case,我们需要增加一个我们的case
case X86ISD::max_qb: return "X86ISD::max_qb";
然后还有一个伪指令具体含义没有定义,再找个合适的位置,增加一个case,写上具体要做的事情。这样就OK了。