当前位置: 首页 > news >正文

LLVM后端 td文件 tablegen 模式匹配 寄存器 指令集 calling convention

目录

一、寄存器

1.1 寄存器定义

1.2 寄存器分类

二、指令集

2.1 指令集定义

2.2 模式匹配

2.2.1 PatFrags与PatFrag

2.2.2 OutPatFrag

2.2.3 PatLeaf

2.2.4 ImmLeaf

2.2.5 IntImmLeaf和FPImmLeaf

2.2.6 Pat

2.2.7 ComplexPattern

2.3 指令合法化

2.3.1 Promote

2.3.2 Expand

2.3.3 Custom

2.3.4 Legal

2.4 模式匹配的底层实现(先记录,待扩展)

2.5 总结

三、calling convention


一、寄存器

1.1 寄存器定义

class X86Reg<string n, bits<16> Enc, list<Register> subregs = []> : Register<n> {let Namespace = "X86";let HWEncoding = Enc;let SubRegs = subregs;
}
def AL : X86Reg<"al", 0>;
def DL : X86Reg<"dl", 2>;
def CL : X86Reg<"cl", 1>;
def BL : X86Reg<"bl", 3>;

这是LLVM后端td文件中定义寄存器的例子,继承自Target.td文件中的Register父类,这里我们介绍一下父类Register中的两个结构,SubRegs和RegAltNameIndices,SubRegs指的是子寄存器,RegAltNameIndices表示寄存器的备用名称索引,这么解释可能不太好理解,我们看了例子就明白了。

// Target.td
class SubRegIndex<int size, int offset = 0> {
...
}// AArch64RegisterInfo.td
class AArch64Reg<bits<16> enc, string n, list<Register> subregs = [],list<string> altNames = []>: Register<n, altNames> {let HWEncoding = enc;let Namespace = "AArch64";let SubRegs = subregs;
}
def bsub : SubRegIndex<8>;
def dsub : SubRegIndex<64>;
def B0    : AArch64Reg<0,   "b0">, DwarfRegNum<[64]>;
let SubRegIndices = [bsub] in {
def H0    : AArch64Reg<0,   "h0", [B0]>, DwarfRegAlias<B0>;
...
}
...
let SubRegIndices = [dsub], RegAltNameIndices = [vreg, vlist1] in {
def Q0    : AArch64Reg<0,   "q0", [D0], ["v0", ""]>, DwarfRegAlias<B0>;
...
}

我们在文章AArch64 ARM64 寄存器介绍 简单介绍过AArch64架构的寄存器,我们看一下AArch64上的浮点寄存器的一个例子。我们先定义了8位的B系列寄存器,然后定义了H系列寄存器,且指定了subregs集合是对应的B系列寄存器,这个有什么效果呢?H寄存器用一个let SubRegIndices = [bsub]包了起来,意思就是H系列寄存器的SubRegIndices属性都为bsub,然后我们再看bsub的定义,是一个SubRegIndex类型的对象,SubRegIndex对象有两个参数第一个是size第二个是offset偏移(默认是0),因此H0与B0寄存器的关系就是,H0寄存器中从偏移0的位置开始8size的内容为B0寄存器,这个与我们之前介绍的B系列与H系列寄存器的关系也相符合。

DwarfRegAlias是gdb等调试器识别的寄存器编码,这里的意思是H0与B0的编码是别名关系,也就相当于H0与B0使用一样的编码,也就是B0定义中的DwarfRegNum<[64]>的64。名字中带有Dwarf的多数都是与调试有关的一些信息。

然后我们再看Q0寄存器,我们知道AArch64上Q系列是128位的寄存器,D系列是64位的寄存器,根据上边的分析很好理解定义了Q的前64位为对应的D系列寄存器,这里还使用了AArch64Reg的第四个参数altNames属性,这个是干什么的呢?根绝之前对于AArch64寄存器的介绍,其实Q系列和V系列的寄存器都是128位的,其实是相同的寄存器,就是有时候用的不同表示,这个就是备用名的意思。

在Aarch64架构中,q0v0寄存器实际上是同一个寄存器的不同表示方式。在Aarch64中,寄存器q0v0都代表了128位的寄存器,用于存储128位的向量数据。q0Quad Register的缩写,表示一个128位的寄存器,而v0则表示一个向量寄存器。因此,q0v0在Aarch64架构中是等效的,都指代同一个128位的向量寄存器,只是在不同的上下文中可能会使用不同的命名方式。

这里是GPT对于Q和V的解释,也可以进一步参考一下。AArch64上只有1个子寄存器,我们再看一个X86寄存器的例子。

let Namespace = "X86" in {def sub_8bit     : SubRegIndex<8>;def sub_8bit_hi  : SubRegIndex<8, 8>;def sub_8bit_hi_phony  : SubRegIndex<8, 8>;def sub_16bit    : SubRegIndex<16>;def sub_16bit_hi : SubRegIndex<16, 16>;def sub_32bit    : SubRegIndex<32>;...
}
// 8-bit registers, low registers
def AL : X86Reg<"al", 0>;
// 8-bit registers, high registers
def AH : X86Reg<"ah", 4>;
// 16-bit registers
let SubRegIndices = [sub_8bit, sub_8bit_hi], CoveredBySubRegs = 1 in {
def AX : X86Reg<"ax", 0, [AL,AH]>;
...
}
let isArtificial = 1 in {
...
// High word of the low 32 bits of the super-register:
def HAX   : X86Reg<"", -1>;
}
let SubRegIndices = [sub_16bit, sub_16bit_hi], CoveredBySubRegs = 1 in {
def EAX : X86Reg<"eax", 0, [AX, HAX]>, DwarfRegNum<[-2, 0, 0]>;
...
}
let SubRegIndices = [sub_32bit] in {
def RAX : X86Reg<"rax", 0, [EAX]>, DwarfRegNum<[0, -2, -2]>;...
}

X86_64 寄存器介绍 文章中有对X86寄存器的大体的介绍。根据上边的分析,我们很容易知道,AX寄存器的低8位是AL,高8位是AH,然后这里还有一个CoveredBySubRegs = 1的属性,这个属性指的是AX的子寄存器是否完全覆盖了AX,这里AX是16位寄存器,高低8位都有对应的子寄存器,因此完全覆盖了,所以CoveredBySubRegs为true,上面AArch64的例子中只使用了低位部分,因此没有完全覆盖,CoveredBySubRegs默认为false。

然后EAX的定义中低16位为AX,高16位为HAX,HAX定义中有一个isArtificial=1的属性,这个指的是这个寄存器是“人工”的,寄存器可以被指定为“人工”,这意味着它在关联架构中没有对应的物理寄存器。这对于创建与实际(非人工)寄存器的无法寻址部分对应的寄存器可能很有用。

DwarfRegNum定义了debug信息,用于gcc、gdb或者一个调试信息输入来标识一个寄存器。对于寄存器EAX,DwarfRegNum携带三个值的数组表示三种不同的模式:第一个元素用来表示X86-64,第二个用来在X86-32上的异常处理(EH),第三个是一般用途。-1是个表示gcc数字没有定义,-2表示该模式下寄存器号无效。

1.2 寄存器分类

// RegisterClass - Now that all of the registers are defined, and aliases
// between registers are defined, specify which registers belong to which
// register classes.  This also defines the default allocation order of
// registers by register allocators.
//
class RegisterClass<string namespace, list<ValueType> regTypes, int alignment,dag regList, RegAltNameIndex idx = NoRegAltName>: DAGOperand {
...
}
  • 参数一:namespace的名字,一般指的是架构名
  • 参数二:第二个参数是在include/llvm/CodeGen/ValueTypes.td中定义的ValueType寄存器类型值的列表。定义的值包括整数类型(例如i16、i32和i1)、浮点类型(f32、f64)和向量类型(例如v8i16表示8 x i16向量)。RegisterClass中的所有寄存器必须具有相同的ValueType,但某些寄存器可能以不同的配置存储矢量数据。例如,一个能处理128位矢量的寄存器可能能够处理16个8位整数元素、8个16位整数、4个32位整数等。
  • 参数三:指定了将寄存器存储或加载到内存时所需的对齐方式。
  • 参数四:最后一个参数regList指定了属于这个类的寄存器。如果未指定替代的分配顺序方法,则regList还定义了寄存器分配器使用的分配顺序。除了简单地列出寄存器(如(add R0, R1, ...)),还可以使用更高级的集合运算符。
def FPR64 : RegisterClass<"AArch64", [f64, i64, v2f32, v1f64, v8i8, v4i16, v2i32,v1i64, v4f16, v4bf16],64, (sequence "D%u", 0, 31)>;
// For tail calls, we can't use callee-saved registers, as they are restored
// to the saved value before the tail call, which would clobber a call address.
// This is for indirect tail calls to store the address of the destination.
def tcGPR64 : RegisterClass<"AArch64", [i64], 64, (sub GPR64common, X19, X20, X21,X22, X23, X24, X25, X26,X27, X28, FP, LR)>;

例如AArch64中的FPR64和tcGPR64这两个类,FPR64是一个64位浮点类型的寄存器类,sequence "D%u", 0, 31表示了D0-D31这32个寄存器,然后支持多种类型,我们能看到这些类型都是64位的各种整形、浮点及向量类型,因此寄存器可以支持不同的类型。

tcGPR64是个用于尾调用的寄存器类,这里做了一个集合的运算,将GPR64common寄存器类中的寄存器减去后边列出来的那些寄存器(callee saved寄存器)就是tcGPR64里边的寄存器。集合运算的话也可以做多次,例如我们sub之后可以再add,这些都是允许的操作。

二、指令集

2.1 指令集定义

  • Target.td — 定义了指令、操作数、InstrInfo等基本类的地方。
  • TargetSelectionDAG.td — 用于SelectionDAG指令选择生成器,包含SDTC*类(选择DAG类型约束)、SelectionDAG节点的定义(如imm、cond、bb、add、fadd、sub)以及模式支持(Pattern、Pat、PatFrag、PatLeaf、ComplexPattern)。
  • XXXInstrFormats.td — 用于定义特定目标指令的模式。
  • XXXInstrInfo.td — 指定了特定目标的指令模板、条件码和指令集的指令。对于架构修改,可能会使用不同的文件名。例如,对于带有SSE指令的奔腾处理器,该文件名为X86InstrSSE.td,对于带有MMX的奔腾处理器,该文件名为X86InstrMMX.td。

上述是指令集定义涉及到的几个文件,Target.td和TargetSelectionDAG.td是指令集相关接口的父类所在的文件,我们的工作是继承所需的功能然后实现我们要定制化的指令。

class Instruction {string Namespace = "";dag OutOperandList;    // A dag containing the MI def operand list.dag InOperandList;     // A dag containing the MI use operand list.string AsmString = ""; // The .s format to print the instruction with.list<dag> Pattern;     // Set to the DAG pattern for this instruction.list<Register> Uses = [];list<Register> Defs = [];list<Predicate> Predicates = [];  // predicates turned into isel match code... remainder not shown for space ...
}

这是Instruction父类的定义,架构手册中的单条指令通常被建模为多个目标指令,取决于其操作数。例如,手册可能描述一个加法指令,它接受寄存器或立即数操作数。LLVM目标可以使用两个指令ADDri和ADDrr来模拟这种情况。

// Class specifying the SSE execution domain, used by the SSEDomainFix pass.
// Keep in sync with tables in X86InstrInfo.cpp.
class Domain<bits<2> val> {bits<2> Value = val;
}
def GenericDomain   : Domain<0>;
def SSEPackedSingle : Domain<1>;
def SSEPackedDouble : Domain<2>;
def SSEPackedInt    : Domain<3>;
class X86Inst<bits<8> opcod, Format f, ImmType i, dag outs, dag ins,string AsmStr, Domain d = GenericDomain>: Instruction {let Namespace = "X86";bits<8> Opcode = opcod;Format Form = f;
...
}
class I<bits<8> o, Format f, dag outs, dag ins, string asm,list<dag> pattern, Domain d = GenericDomain>: X86Inst<o, f, NoImm, outs, ins, asm, d> {let Pattern = pattern;let CodeSize = 3;
}
def MOV8rr  : I<0x88, MRMDestReg, (outs GR8 :$dst), (ins GR8 :$src),"mov{b}\t{$src, $dst|$dst, $src}", []>;
def MOV16rr : I<0x89, MRMDestReg, (outs GR16:$dst), (ins GR16:$src),"mov{w}\t{$src, $dst|$dst, $src}", []>, OpSize16;
def MOV32rr : I<0x89, MRMDestReg, (outs GR32:$dst), (ins GR32:$src),"mov{l}\t{$src, $dst|$dst, $src}", []>, OpSize32;
def MOV64rr : RI<0x89, MRMDestReg, (outs GR64:$dst), (ins GR64:$src),"mov{q}\t{$src, $dst|$dst, $src}", []>;

第一个参数表示了操作数,第二个参数表示了指令类型,第三个参数表示了指令输出,第四个参数表示了指令输入,第五个参数表示了匹配模式,第五个参数是X86定一的表示是否是SSE指令集,默认不是。

这里是X86架构上对于各个size的mov指令的定义,MOV8rr等是mov指令在Machine code中的表现形式,movb等是输出到汇编中的形式,我们能看到指令定义的第二行的汇编表示形式用|符号链接了两句,这不是要输出两句,这是定义了两种汇编输出形式,AT&T和Intel形式。

2.2 模式匹配

当前存在的其中模式匹配的类型:

2.2.1 PatFrags与PatFrag

//===----------------------------------------------------------------------===//
// Selection DAG Node Transformation Functions.
//
// This mechanism allows targets to manipulate nodes in the output DAG once a
// match has been formed.  This is typically used to manipulate immediate
// values.
//
class SDNodeXForm<SDNode opc, code xformFunction> {SDNode Opcode = opc;code XFormFunction = xformFunction;
}
def NOOP_SDNodeXForm : SDNodeXForm<imm, [{}]>;
// Selection DAG Pattern Operations
class SDPatternOperator {list<SDNodeProperty> Properties = [];
}
/// PatFrags - Represents a set of pattern fragments.  Each single fragment
/// can match something on the DAG, from a single node to multiple nested other
/// fragments.   The whole set of fragments matches if any of the single
/// fragments match.  This allows e.g. matching and "add with overflow" and
/// a regular "add" with the same fragment set.
///
class PatFrags<dag ops, list<dag> frags, code pred = [{}],SDNodeXForm xform = NOOP_SDNodeXForm> : SDPatternOperator {dag Operands = ops;list<dag> Fragments = frags;code PredicateCode = pred;code GISelPredicateCode = [{}];code ImmediateCode = [{}];SDNodeXForm OperandTransform = xform;
...
}
// PatFrag - A version of PatFrags matching only a single fragment.
class PatFrag<dag ops, dag frag, code pred = [{}],SDNodeXForm xform = NOOP_SDNodeXForm>: PatFrags<ops, [frag], pred, xform>;

PatFrags与PatFrag的区别是很好区分的,PatFrags是多模式匹配,多个匹配规则中只要有一个匹配上就算匹配成功,PatFrag是单模式匹配,只有一个匹配规则。他们的参数都完全一样的。第一个参数指的是指令的操作数,第二个参数指的是匹配规则,PatFrags的这个参数类型是个list,表示可以有多个匹配规则,第三个参数是code类型,指的是调用cpp代码里边生成的一些谓词条件,第四个参数主要是用于表示立即数的。比较重要的是前两个参数,也是较常用的两个参数。

def AArch64sabd     : PatFrags<(ops node:$lhs, node:$rhs),[(abds node:$lhs, node:$rhs),(int_aarch64_neon_sabd node:$lhs, node:$rhs)]>;
defm SABD     : SIMDThreeSameVectorBHS<0,0b01110,"sabd", AArch64sabd>;

这是AArch64中对于一个PatFrags使用的例子,SABD指令在AArch64架构中代表"Signed Absolute Difference",用于计算两个操作数的绝对差值,并保留符号。AArch64sabd匹配模式中有两条匹配规则,只要有一个匹配上了,就认为匹配成功,然后生成对应的sabd指令。

2.2.2 OutPatFrag

// OutPatFrag is a pattern fragment that is used as part of an output pattern
// (not an input pattern). These do not have predicates or transforms, but are
// used to avoid repeated subexpressions in output patterns.
class OutPatFrag<dag ops, dag frag>: PatFrag<ops, frag, [{}], NOOP_SDNodeXForm>;// PPCInstr64Bit.td
def i64not : OutPatFrag<(ops node:$in),(NOR8 $in, $in)>;
def        : Pat<(not i64:$in),(i64not $in)>;

OutPatFrag主要是用于输出匹配的,经常作为Pat的result项,如果not i64的形式匹配上了,那么在PPC架构下展开成NOR8指令。

2.2.3 PatLeaf

// PatLeaf's are pattern fragments that have no operands.  This is just a helper
// to define immediates and other common things concisely.
class PatLeaf<dag frag, code pred = [{}], SDNodeXForm xform = NOOP_SDNodeXForm>: PatFrag<(ops), frag, pred, xform>;// X86InstrInfo.td
def MOV8mi  : Ii8 <0xC6, MRM0m, (outs), (ins i8mem :$dst, i8imm :$src),"mov{b}\t{$src, $dst|$dst, $src}",[(store (i8 imm_su:$src), addr:$dst)]>;
def imm_su : PatLeaf<(imm), [{return !shouldAvoidImmediateInstFormsForSize(N);
}]>;

PatLeaf继承自PatFrag,操作数项为空,主要是通过frag和pred来进行匹配。例如,对于X86中的这个指令和匹配规则,MOV8mi的匹配规则是匹配一条store指令,然后store指令的src项要满足imm_su这条匹配规则。imm_su这条匹配的是一个不满足cpp内定义的shouldAvoidImmediateInstFormsForSize这个函数结果的立即数(shouldAvoidImmediateInstFormsForSize是X86ISelDAGToDAG.cpp内的函数),如果全部匹配成功就会生成MOV8mi指令。

2.2.4 ImmLeaf

// ImmLeaf is a pattern fragment with a constraint on the immediate.  The
// constraint is a function that is run on the immediate (always with the value
// sign extended out to an int64_t) as Imm.  For example:
//
//  def immSExt8 : ImmLeaf<i16, [{ return (char)Imm == Imm; }]>;
//
// this is a more convenient form to match 'imm' nodes in than PatLeaf and also
// is preferred over using PatLeaf because it allows the code generator to
// reason more about the constraint.
//
// If FastIsel should ignore all instructions that have an operand of this type,
// the FastIselShouldIgnore flag can be set.  This is an optimization to reduce
// the code size of the generated fast instruction selector.
class ImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm,SDNode ImmNode = imm>: PatFrag<(ops), (vt ImmNode), [{}], xform> {let ImmediateCode = pred;bit FastIselShouldIgnore = false;// Is the data type of the immediate an APInt?bit IsAPInt = false;// Is the data type of the immediate an APFloat?bit IsAPFloat = false;
}// X86InstrInfo.td
def i16immSExt8  : ImmLeaf<i16, [{ return isInt<8>(Imm); }]>;

ImmLeaf是专门用于立即数的匹配模式,它继承自PatFrag类,操作数为空,第一个参数是一个类型,第二个参数是code代码,也就是主要的匹配规则。例如X86这里的匹配规则就是判断一个i16类型的立即数是不是Int8范围内的数。

2.2.5 IntImmLeaf和FPImmLeaf

// An ImmLeaf except that Imm is an APInt. This is useful when you need to
// zero-extend the immediate instead of sign-extend it.
//
// Note that FastISel does not currently understand IntImmLeaf and will not
// generate code for rules that make use of it. As such, it does not make sense
// to replace ImmLeaf with IntImmLeaf. However, replacing PatLeaf with an
// IntImmLeaf will allow GlobalISel to import the rule.
class IntImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm>: ImmLeaf<vt, pred, xform> {let IsAPInt = true;let FastIselShouldIgnore = true;
}// An ImmLeaf except that Imm is an APFloat.
//
// Note that FastISel does not currently understand FPImmLeaf and will not
// generate code for rules that make use of it.
class FPImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm>: ImmLeaf<vt, pred, xform, fpimm> {let IsAPFloat = true;let FastIselShouldIgnore = true;
}

IntImmLeaf和FPImmLeaf是继承自ImmLeaf类的,分别用整数立即数和浮点立即数的匹配模式,用法上与ImmLeaf相同。

2.2.6 Pat

class Pattern<dag patternToMatch, list<dag> resultInstrs> {dag             PatternToMatch  = patternToMatch;list<dag>       ResultInstrs    = resultInstrs;list<Predicate> Predicates      = [];  // See class Instruction in Target.td.int             AddedComplexity = 0;   // See class Instruction in Target.td.
}// Pat - A simple (but common) form of a pattern, which produces a simple result
// not needing a full list.
class Pat<dag pattern, dag result> : Pattern<pattern, [result]>;// X86InstrInfo.td
def : Pat<(i32 relocImm:$src), (MOV32ri relocImm:$src)>;

Pat使用起来很简单,一共就是两个操作数,第一个是要匹配的形式,第二个是要输出的结果。X86的这个例子就是如果匹配到满足relocImm规则的i32的立即数,那么就将其转化成对应的MOV32ri指令。

Pat匹配模式使用起来比较简单,但是千万不要低估他,很有用。它可以匹配多条指令复合的形式,来生成对应结果的一条指令,做一些架构上的指令优化。它还可以匹配一些当前架构不支持的指令,然后生成当前架构支持的对应功能的指令(可能是多条指令)。

2.2.7 ComplexPattern

// Complex patterns, e.g. X86 addressing mode, requires pattern matching code
// in C++. NumOperands is the number of operands returned by the select function;
// SelectFunc is the name of the function used to pattern match the max. pattern;
// RootNodes are the list of possible root nodes of the sub-dags to match.
// e.g. X86 addressing mode - def addr : ComplexPattern<4, "SelectAddr", [add]>;
//
class ComplexPattern<ValueType ty, int numops, string fn,list<SDNode> roots = [], list<SDNodeProperty> props = [],int complexity = -1> {ValueType Ty = ty;int NumOperands = numops;string SelectFunc = fn;list<SDNode> RootNodes = roots;list<SDNodeProperty> Properties = props;int Complexity = complexity;
}// X86InstrInfo.td
// Define X86-specific addressing mode.
def addr      : ComplexPattern<iPTR, 5, "selectAddr", [], [SDNPWantParent]>;
def lea32addr : ComplexPattern<i32, 5, "selectLEAAddr",[add, sub, mul, X86mul_imm, shl, or, xor, frameindex],[]>;// X86ISelDAGToDAG.cpp
bool selectAddr(SDNode *Parent, SDValue N, SDValue &Base,SDValue &Scale, SDValue &Index, SDValue &Disp,SDValue &Segment);
bool selectLEAAddr(SDValue N, SDValue &Base,SDValue &Scale, SDValue &Index, SDValue &Disp,SDValue &Segment);

我们上边介绍的各种匹配模式的功能总归是有限的,有时候可能不能满足我们的需求,这个时候我们可以考虑用ComplexPattern匹配模式。第一个参数是类型第二个参数是操作数的数目,第三个函数是匹配函数,第四个参数是根节点,第五个参数是SDNode的一些属性,第六个参数是复杂度。

addr的匹配规则是selectAddr这个函数,我们能看到操作数是5,但是函数的实际操作数是7,这是为什么呢?这是因为复杂模式匹配自带一个SDValue N这个操作数,然后addr又指定了需要父节点的这个属性,因此会多一个SDNode *Parent的参数,因此共有7个参数。下方的lea32addr就只有6个参数。

2.3 指令合法化

LLVM中如何检查目标架构原生支持的操作,并为没有原生支持的操作添加回调函数到XXXTargetLowering类的构造函数中,以便指令选择过程知道如何处理。TargetLowering类的回调方法(声明在llvm/Target/TargetLowering.h中)包括:

  • setOperationAction — 通用操作。
  • setLoadExtAction — 带扩展的加载。
  • setTruncStoreAction — 截断存储。
  • setIndexedLoadAction — 索引加载。
  • setIndexedStoreAction — 索引存储。
  • setConvertAction — 类型转换。
  • setCondCodeAction — 支持给定条件码。

这些回调用于确定操作是否适用于指定类型(或类型)。在所有情况下,第三个参数是LegalAction类型的枚举值:Promote、Expand、Custom或Legal。

2.3.1 Promote

对于没有对给定类型进行本地支持的操作,指定的类型可能会被提升为一个更大的支持的类型。例如,SPARC不支持对布尔值(i1类型)进行符号扩展加载,因此在SparcISelLowering.cpp中,下面的第三个参数,Promote,会在加载之前将i1类型的值更改为一个Sparc架构所支持的更大的类型。

setLoadExtAction(ISD::SEXTLOAD, MVT::i1, Promote);

2.3.2 Expand

对于没有本地支持的类型,一个值可能需要进一步分解,而不是提升。对于没有本地支持的操作,可能需要使用其他操作的组合来达到类似的效果。在SPARC中,浮点正弦和余弦三角函数操作通过扩展到其他操作来实现支持,这由setOperationAction的第三个参数Expand指示。

setOperationAction(ISD::FSIN, MVT::f32, Expand);
setOperationAction(ISD::FCOS, MVT::f32, Expand);

2.3.3 Custom

对于某些操作,简单的类型提升或操作扩展可能不足够。在某些情况下,必须实现特殊的内部函数。

例如,常量值可能需要特殊处理,或者某个操作可能需要在堆栈中溢出和恢复寄存器,并与寄存器分配器一起工作。

如下所示,在X86ISelLowering.cpp代码中,要将处理绝对值运算,首先应该调用setOperationAction,将Custom作为第三个参数:

// Integer absolute.if (Subtarget.canUseCMOV()) {setOperationAction(ISD::ABS            , MVT::i16  , Custom);setOperationAction(ISD::ABS            , MVT::i32  , Custom);if (Subtarget.is64Bit())setOperationAction(ISD::ABS          , MVT::i64  , Custom);}

在LowerOperation方法中,对于每个Custom操作,应添加一个case语句来指示调用哪个函数。在下面的代码中,ABS操作码将调用LowerABS方法:

case ISD::ABS:                return LowerABS(Op, Subtarget, DAG);

最后,实现LowerABS方法。

2.3.4 Legal

在SparcISelLowering.cpp中,LegalizeAction枚举值Legal表示操作具有本地支持的默认条件,因此很少使用。对于CTPOP(用于计算整数中设置的位数的操作),仅在SPARC v9中本地支持。以下代码为非v9 SPARC实现启用了Expand转换技术。

setOperationAction(ISD::CTPOP, MVT::i32, Expand);
...
if (TM.getSubtarget<SparcSubtarget>().isV9())setOperationAction(ISD::CTPOP, MVT::i32, Legal);

2.4 模式匹配的底层实现(先记录,待扩展)

LLVM模式匹配的代码主要在CodeGenDAGPatterns.cpp这个文件里。

LLVM模式匹配通过模式匹配树来实现的,具体实现逻辑待日后进一步补充,这里先记录下。

2.5 总结

以上就是对后端指令集和匹配模式等的简单介绍。td文件的功能总归是有限的,llvm希望将所有架构相关的内容都在td文件里实现,不过目前还没有彻底实现这个想法,因此对于一些复杂的需求的话我们需要在XXXInstrInfo.cpp文件里进行进一步实现。

三、calling convention

def CC_AArch64_AAPCS : CallingConv<[CCIfType<[iPTR], CCBitConvertToType<i64>>,CCIfType<[v2f32], CCBitConvertToType<v2i32>>,CCIfType<[v2f64, v4f32], CCBitConvertToType<v2i64>>,// Big endian vectors must be passed as if they were 1-element vectors so that// their lanes are in a consistent order.CCIfBigEndian<CCIfType<[v2i32, v2f32, v4i16, v4f16, v4bf16, v8i8],CCBitConvertToType<f64>>>,CCIfBigEndian<CCIfType<[v2i64, v2f64, v4i32, v4f32, v8i16, v8f16, v8bf16, v16i8],CCBitConvertToType<f128>>>,// In AAPCS, an SRet is passed in X8, not X0 like a normal pointer parameter.// However, on windows, in some circumstances, the SRet is passed in X0 or X1// instead.  The presence of the inreg attribute indicates that SRet is// passed in the alternative register (X0 or X1), not X8:// - X0 for non-instance methods.// - X1 for instance methods.// The "sret" attribute identifies indirect returns.// The "inreg" attribute identifies non-aggregate types.// The position of the "sret" attribute identifies instance/non-instance// methods.// "sret" on argument 0 means non-instance methods.// "sret" on argument 1 means instance methods.CCIfInReg<CCIfType<[i64],CCIfSRet<CCIfType<[i64], CCAssignToReg<[X0, X1]>>>>>,CCIfSRet<CCIfType<[i64], CCAssignToReg<[X8]>>>,// Put ByVal arguments directly on the stack. Minimum size and alignment of a// slot is 64-bit.CCIfByVal<CCPassByVal<8, 8>>,// The 'nest' parameter, if any, is passed in X18.// Darwin uses X18 as the platform register and hence 'nest' isn't currently// supported there.CCIfNest<CCAssignToReg<[X18]>>,// Pass SwiftSelf in a callee saved register.CCIfSwiftSelf<CCIfType<[i64], CCAssignToReg<[X20]>>>,// A SwiftError is passed in X21.CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[X21]>>>,// Pass SwiftAsync in an otherwise callee saved register so that it will be// preserved for normal function calls.CCIfSwiftAsync<CCIfType<[i64], CCAssignToReg<[X22]>>>,CCIfConsecutiveRegs<CCCustom<"CC_AArch64_Custom_Block">>,CCIfType<[nxv16i8, nxv8i16, nxv4i32, nxv2i64, nxv2f16, nxv4f16, nxv8f16,nxv2bf16, nxv4bf16, nxv8bf16, nxv2f32, nxv4f32, nxv2f64],CCAssignToReg<[Z0, Z1, Z2, Z3, Z4, Z5, Z6, Z7]>>,CCIfType<[nxv16i8, nxv8i16, nxv4i32, nxv2i64, nxv2f16, nxv4f16, nxv8f16,nxv2bf16, nxv4bf16, nxv8bf16, nxv2f32, nxv4f32, nxv2f64],CCPassIndirect<i64>>,CCIfType<[nxv1i1, nxv2i1, nxv4i1, nxv8i1, nxv16i1],CCAssignToReg<[P0, P1, P2, P3]>>,CCIfType<[nxv1i1, nxv2i1, nxv4i1, nxv8i1, nxv16i1],CCPassIndirect<i64>>,// Handle i1, i8, i16, i32, i64, f32, f64 and v2f64 by passing in registers,// up to eight each of GPR and FPR.CCIfType<[i1, i8, i16], CCPromoteToType<i32>>,CCIfType<[i32], CCAssignToReg<[W0, W1, W2, W3, W4, W5, W6, W7]>>,// i128 is split to two i64s, we can't fit half to register X7.CCIfType<[i64], CCIfSplit<CCAssignToRegWithShadow<[X0, X2, X4, X6],[X0, X1, X3, X5]>>>,// i128 is split to two i64s, and its stack alignment is 16 bytes.CCIfType<[i64], CCIfSplit<CCAssignToStackWithShadow<8, 16, [X7]>>>,CCIfType<[i64], CCAssignToReg<[X0, X1, X2, X3, X4, X5, X6, X7]>>,CCIfType<[f16], CCAssignToReg<[H0, H1, H2, H3, H4, H5, H6, H7]>>,CCIfType<[bf16], CCAssignToReg<[H0, H1, H2, H3, H4, H5, H6, H7]>>,CCIfType<[f32], CCAssignToReg<[S0, S1, S2, S3, S4, S5, S6, S7]>>,CCIfType<[f64], CCAssignToReg<[D0, D1, D2, D3, D4, D5, D6, D7]>>,CCIfType<[v1i64, v2i32, v4i16, v8i8, v1f64, v2f32, v4f16, v4bf16],CCAssignToReg<[D0, D1, D2, D3, D4, D5, D6, D7]>>,CCIfType<[f128, v2i64, v4i32, v8i16, v16i8, v4f32, v2f64, v8f16, v8bf16],CCAssignToReg<[Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7]>>,// If more than will fit in registers, pass them on the stack instead.CCIfType<[i1, i8, i16, f16, bf16], CCAssignToStack<8, 8>>,CCIfType<[i32, f32], CCAssignToStack<8, 8>>,CCIfType<[i64, f64, v1f64, v2f32, v1i64, v2i32, v4i16, v8i8, v4f16, v4bf16],CCAssignToStack<8, 8>>,CCIfType<[f128, v2i64, v4i32, v8i16, v16i8, v4f32, v2f64, v8f16, v8bf16],CCAssignToStack<16, 16>>
]>;

这是AArch64CallingConvention.td文件内一条AArch64的calling convention的定义,我们简单来分析下。

/// CCIfType - If the current argument is one of the specified types, apply
/// Action A.
class CCIfType<list<ValueType> vts, CCAction A> : CCPredicateAction<A> {list<ValueType> VTs = vts;
}
/// CCBitConvertToType - If applied, this bitconverts the specified current
/// value to the specified type.
class CCBitConvertToType<ValueType destTy> : CCAction {ValueType DestTy = destTy;
}CCIfType<[iPTR], CCBitConvertToType<i64>>
CCIfType<[v2f32], CCBitConvertToType<v2i32>>
CCIfType<[v2f64, v4f32], CCBitConvertToType<v2i64>>

CCBitConvertToType就是当做目的类型来处理,这三条的意思是对于地址类型当做i64来处理,对于v2f32向量类型当做v2i32向量类型来处理,第三条同理。

/// CCAssignToReg - This action matches if there is a register in the specified
/// list that is still available.  If so, it assigns the value to the first
/// available register and succeeds.
class CCAssignToReg<list<Register> regList> : CCAction {list<Register> RegList = regList;
}
/// CCIfSRet - If this argument is marked with the 'sret' attribute, apply
/// the specified action.
class CCIfSRet<CCAction A> : CCIf<"ArgFlags.isSRet()", A> {}CCIfSRet<CCIfType<[i64], CCAssignToReg<[X8]>>>

这里的意思是,如果这是个sret的参数的话,又是个i64类型的话,如果X8寄存器目前可用的话我们就将其存到X8寄存器中,不可用的话一般会存到栈中。注意,这里不是严格的i64类型,因为前边我们对于指针类型的也是当做i64来处理的。

/// CCIfByVal - If the current argument has ByVal parameter attribute, apply
/// Action A.
class CCIfByVal<CCAction A> : CCIf<"ArgFlags.isByVal()", A> {
}
/// CCPassByVal - This action always matches: it assigns the value to a stack
/// slot to implement ByVal aggregate parameter passing. Size and alignment
/// specify the minimum size and alignment for the stack slot.
class CCPassByVal<int size, int align> : CCAction {int Size = size;int Align = align;
}// Put ByVal arguments directly on the stack. Minimum size and alignment of a
// slot is 64-bit.
CCIfByVal<CCPassByVal<8, 8>>

这里的意思是,对于一个ByVal的参数我们将其存到栈上。

/// CCCustom - Calls a custom arg handling function.
class CCCustom<string fn> : CCAction {string FuncName = fn;
}
/// CCIfConsecutiveRegs - If the current argument has InConsecutiveRegs
/// parameter attribute, apply Action A.
class CCIfConsecutiveRegs<CCAction A> : CCIf<"ArgFlags.isInConsecutiveRegs()", A> {
}CCIfConsecutiveRegs<CCCustom<"CC_AArch64_Custom_Block">>

这里的意思是如果当前的参数满足这个条件的话,那么我们会调用CC_AArch64_Custom_Block这个参数处理函数,CC_AArch64_Custom_Block这个函数在AArch64CallingConvention.cpp文件内。

/// CCPromoteToType - If applied, this promotes the specified current value to
/// the specified type.
class CCPromoteToType<ValueType destTy> : CCAction {ValueType DestTy = destTy;
}CCIfType<[i1, i8, i16], CCPromoteToType<i32>>,
CCIfType<[i32], CCAssignToReg<[W0, W1, W2, W3, W4, W5, W6, W7]>>,

这里的意思是将i1、i8、i16扩展成i32来处理,对于i32类型,将其按顺序存到可用的W0-W7寄存器中,W0-W7也就是AArch64中32位的整形参数寄存器。

CallingConvention.td文件中还有个比较常用的语法CCDelegateTo,我们也简单介绍下。

/// CCDelegateTo - This action invokes the specified sub-calling-convention.  It
/// is successful if the specified CC matches.
class CCDelegateTo<CallingConv cc> : CCAction {CallingConv CC = cc;
}def CC_AArch64_Win64_VarArg : CallingConv<[CCIfType<[f16, bf16], CCBitConvertToType<i16>>,CCIfType<[f32], CCBitConvertToType<i32>>,CCIfType<[f64], CCBitConvertToType<i64>>,CCDelegateTo<CC_AArch64_AAPCS>
]>;

对于这个calling convention,将f16和bf16当做i16来处理,将f32当做i32来处理,将f64当做i64来处理,做完这些之后我们再使用CC_AArch64_AAPCS这个calling convention。

def RetCC_X86_32 : CallingConv<[// If FastCC, use RetCC_X86_32_Fast.CCIfCC<"CallingConv::Fast", CCDelegateTo<RetCC_X86_32_Fast>>,CCIfCC<"CallingConv::Tail", CCDelegateTo<RetCC_X86_32_Fast>>,// CFGuard_Check never returns a value so does not need a RetCC.// If HiPE, use RetCC_X86_32_HiPE.CCIfCC<"CallingConv::HiPE", CCDelegateTo<RetCC_X86_32_HiPE>>,CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<RetCC_X86_32_VectorCall>>,CCIfCC<"CallingConv::X86_RegCall", CCDelegateTo<RetCC_X86_32_RegCall>>,// Otherwise, use RetCC_X86_32_C.CCDelegateTo<RetCC_X86_32_C>
]>;

这是X86CallingConvention.td文件内的,CCDelegateTo也可以这样使用。对于获取到的不同的calling convention我们使用对应的calling convention策略,都没匹配上的话,就使用最后这个策略,有点类似于switch case default。

相关文章:

LLVM后端 td文件 tablegen 模式匹配 寄存器 指令集 calling convention

目录 一、寄存器 1.1 寄存器定义 1.2 寄存器分类 二、指令集 2.1 指令集定义 2.2 模式匹配 2.2.1 PatFrags与PatFrag 2.2.2 OutPatFrag 2.2.3 PatLeaf 2.2.4 ImmLeaf 2.2.5 IntImmLeaf和FPImmLeaf 2.2.6 Pat 2.2.7 ComplexPattern 2.3 指令合法化 2.3.1 Promote…...

嵌入式交叉编译:frp

参考 LINUX FRP下载编译_linux编译frpc-CSDN博客 编译 make -f Makefile.cross-compiles 检查 $ make -f Makefile.cross-compiles Build darwin-amd64... Build darwin-amd64 done Build darwin-arm64... Build darwin-arm64 done Build freebsd-amd64... Build freebsd-…...

SpringBoot实现的大文件上传

前言 大文件分片上传和断点续传是为了解决在网络传输过程中可能遇到的问题&#xff0c;以提高文件传输的效率和稳定性。 首先&#xff0c;大文件分片上传是将大文件分割成较小的片段进行上传。这样做的好处是可以减少单个文件的传输时间&#xff0c;因为较小的文件片段更容易快…...

【Python高级编程】用 Matplotlib 绘制迷人的图表

用 Matplotlib 绘制迷人的图表 引言 Matplotlib 是 Python 中广泛使用的绘图库&#xff0c;用于创建各种图表和可视化。本文将逐步指导您使用 Matplotlib 绘制基本图表&#xff0c;包括折线图、条形图和散点图。 安装 Matplotlib 使用 pip 安装 Matplotlib&#xff1a; pi…...

【UML用户指南】-19-对基本行为建模-用例图

目录 1、组成结构 2、表示法 3、一般用法 3.1、对主题的语境建模 3.2、对主题的需求建模 4、常用建模技术 4.1、对系统的语境建模 4.1.1、设计过程 4.2、对系统的需求建模 4.2.1、设计过程&#xff1a; 5、正向工程 UML 中的用例图是对系统的动态方面建模的 5 种图之…...

mysql密码过期的修改(Your password has expired. ..)

参考文章&#xff1a;mysql密码过期的修改方法&#xff08;your password has expired&#xff09;_我是知青-RuoYi 若依 (csdn.net) 问题&#xff1a;Your password has expired. To log inyou must change it using a clientthat supports expired passwords. 解决方式&…...

vivado SLR

描述 超级逻辑区&#xff08;SLR&#xff09;是包含在堆叠硅中的单个FPGA芯片 互连&#xff08;SSI&#xff09;设备。堆叠式硅互连&#xff08;SSI&#xff09;技术使用无源硅 具有微凸块和硅通孔&#xff08;TSV&#xff09;的内插器&#xff0c;用于组合多个FPGA管芯 切片&a…...

【CSS】深入了解圆角属性border-radius

border-radius 是 CSS 中的一个非常有用的属性&#xff0c;它允许你创建具有圆角边框的元素。这个属性可以应用于一个元素的四个角&#xff0c;或者分别应用于每个角。下面我们将深入了解 border-radius 的使用方法和一些高级技巧。 基本用法 你可以通过为 border-radius 指定…...

LabVIEW与C#的区别及重新开发自动测试程序的可行性分析

LabVIEW和C#是两种广泛使用的编程语言&#xff0c;各自有不同的应用领域和特点。本文将详细比较LabVIEW与C#在自动测试程序开发中的区别&#xff0c;并分析将已完成的LabVIEW自动测试程序重新用C#开发的合理性。本文帮助评估这种转换的必要性和潜在影响。 LabVIEW与C#的区别 开…...

人工智能—美国加利福尼亚州房价预测实战

引言 在当今快速发展的房地产市场中&#xff0c;房价预测已成为一个至关重要的领域。它不仅关系到投资者的决策&#xff0c;也直接影响到普通购房者的生活质量。特别是在美国加利福尼亚州&#xff0c;这个以其高房价和房地产市场的波动性而闻名的地方&#xff0c;准确的房价预…...

python pandas处理股票量化数据:笔记2

有一个同学用我的推荐链接注册了tushare社区帐号https://tushare.pro/register?reg671815&#xff0c;现在有了170分积分。目前使用数据的频率受限制。不过可以在调试期间通过python控制台获取数据&#xff0c;将数据保存在本地以后使用不用高频率访问tushare数据接口&#xf…...

enum库

Python enum 模块教程 enum 是 Python 3.4 引入的一个模块&#xff0c;用于定义枚举类型。枚举类型是一种特殊的数据类型&#xff0c;由一组命名的值组成&#xff0c;这些值称为枚举成员。使用 enum 可以提高代码的可读性和可维护性&#xff0c;特别是在处理一组相关的常量值时…...

【CT】LeetCode手撕—141. 环形链表

目录 题目1- 思路2- 实现⭐141. 环形链表——题解思路 3- ACM实现 题目 原题连接&#xff1a;141. 环形链表 1- 思路 模式识别 模式1&#xff1a;判断链表的环 ——> 快慢指针 思路 快指针 ——> 走两步慢指针 ——> 走一步判断环&#xff1a;若快慢相遇则有环&a…...

python,自定义token生成

1、使用的包PyJWT来实现token生成 安装&#xff1a;pip install PyJWT2.8.0 2、使用例子&#xff1a; import jwt import time pip install pyJWT2.8.0 SECRET_KEY %^ES*E&Ryurehuie9*7^%$#$EDFGHUYTRE#$%^&%$##$RTYGHIK DEFAULT_EXP 7 * 24 * 60def create_token(…...

小米SU7遇冷,下一代全新车型被官方意外曝光

不知道大伙儿有没有发现&#xff0c;最近小米 SU7 热度好像突然之间就淡了不少&#xff1f; 作为小米首款车型&#xff0c;SU7 自上市以来一直承载着新能源轿车领域流量标杆这样一个存在。 发售 24 小时订单量破 8 万&#xff0c;2 个月后累计交付破 2 万台。 看得出来限制它…...

JavaScript 函数与事件

1. JavaScript自定义函数 语法&#xff1a; function 函数名&#xff08;参数列表&#xff09;{ 方法体; } 在函数被调用时&#xff0c;一个 arguments 对象就会被创建&#xff0c;它只能使用在函数体中&#xff0c;以数组的形式来管理函数的实际…...

Qt 焦点系统关键点总结

1.1 焦点窗口 指的是当前时刻拥有键盘输入的窗口。 Qt提供了如下接口&#xff0c;用于设置窗口是否是”可获取焦点“窗口&#xff1a; void QWidget::setFocusPolicy(Qt::FocusPolicy policy); Qt::FocusPolicy Qt::TabFocus 与焦点链相关&#xff0c;详解见下一…...

SpringBoot+Maven项目的配置构建

文章目录 1、application.properties2、pom.xml 1、application.properties 也可使用yml yaml #静态资源 spring.mvc.static-path-pattern/images/** #上传文件大小设置 spring.http.multipart.max-file-size10MB spring.http.multipart.max-request-size10MBspring.mvc.path…...

c#调用c++dll方法

添加dll文件到debug目录&#xff0c;c#生成的exe的相同目录 就可以直接使用了&#xff0c;放在构造函数里面测试...

ACM算法学习路线、清单

入门 模拟、暴力、贪心、高精度、排序 图论 搜索 BFS、DFS、IDDFS、IDA*、A*、双向BFS、记忆化 最短路 SPFA、bellman-fort(队列优化)、Dijkstra(堆优化)、Johnson、Floyd、差分约束、第k短路 树 树的重心和直径、dfs序、树链刨分与动态树、LCA、Prufer编码及Cayley定理…...

sqoop的安装配置

1. 上传并解压安装包 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C ../server/ 重命名&#xff1a;mv sqoop-1.4.7.bin__hadoop-2.6.0 sqoop 2. 配置环境变量 sudo vim /etc/profile # 配置sqoop的环境变量 export SQOOP_HOME/export/server/sqoop export PATH$PATH…...

代码随想录算法训练营第六十四天 | 图论理论基础、深搜理论基础、广搜理论基础、98. 所有可达路径

图论理论基础 我写在了个人语雀笔记中 https://www.yuque.com/yuqueyonghu8mml9e/bmbl71/ex473q4y0ebs0l3r?singleDoc# 深搜理论基础 https://www.yuque.com/yuqueyonghu8mml9e/bmbl71/zamfikz08c2haptn?singleDoc# 98. 所有可达路径 题目链接&#xff1a;98. 所有可达…...

【教师资格证考试综合素质——法律专项】教师法笔记以及练习题

《中华人民共和国教师法》 一&#xff0e;首次颁布&#xff1a;第一部《中华人民共和国教师法》于1993年10月31日由第八届全国人民代表大会常务委员会第四次会议通过&#xff0c;1994年1月1日起执行。 二&#xff0e;历次修改&#xff1a;2009年8月27日第十一届全国人民代表…...

图卷积网络(Graph Convolutional Network, GCN)

图卷积网络&#xff08;Graph Convolutional Network, GCN&#xff09;是一种用于处理图结构数据的深度学习模型。GCN编码器的核心思想是通过邻接节点的信息聚合来更新节点表示。 图的表示 一个图 G通常表示为 G(V,E)&#xff0c;其中&#xff1a; V 是节点集合&#xff0c;…...

【diffusers 极速入门(一)】pipeline 实际调用的是什么? __call__ 方法!

在使用 diffusers 库进行图像生成时&#xff0c;你可能会发现管道&#xff08;pipeline&#xff09;对象可以像函数一样被调用。这背后的魔法是什么呢&#xff1f;答案是&#xff1a;__call__ 方法&#xff01;本文将通过简单的案例代码&#xff0c;带你快速了解 diffusers 管道…...

【DPDK学习路径】二、DPDK简介

DPDK(Data Plane Development Kit)是一个框架&#xff0c;用于快速报文处理。 在linux内核提供的报文处理模型中&#xff0c;接收报文的处理路径为&#xff1a;首先由网卡硬件接收&#xff0c;产生硬中断&#xff0c;触发网卡驱动程序注册的中断函数处理&#xff0c;之后产生软…...

python基础 002 - 2 常用数据类型

python的常用数据类型 int , 整型 1,2,3float ,小数&#xff0c;浮点类型1.2bool , boolean 布尔&#xff0c;真假。判断命题。True Flasestr &#xff0c;字符串 list , 列表 a []tuple, 元组 a ()dict , dictionary, 字典 a {}set , 集合 a {} 1 查看数据类型 typ…...

爆赞!GitHub首本Python开发实战背记手册,标星果然百万名不虚传

Python (发音:[ paiθ(ə) n; (US) paiθɔn ] n. 蟒蛇&#xff0c;巨蛇 )&#xff0c;是一种面向对象的解释性的计算机程序设计语言&#xff0c;也是一种功能强大而完善的通用型语言&#xff0c;已经具有十多年的发展历史&#xff0c;成熟且稳定。Python 具有脚本语言中最丰富…...

Spring源码-xxxAware实现类和BeanPostProcessor接口调用过程

xxxAware实现类作用 以ApplicationContextAware接口为例 ApplicationContextAware的作用是可以方便获取Spring容器ApplicationContext&#xff0c;从而可以获取容器内的Bean package org.springframework.context;import org.springframework.beans.BeansException; import or…...

Uni-app x

uni-app x&#xff0c;是下一代 uni-app&#xff0c;是一个跨平台应用开发引擎。 uni-app x 是一个庞大的工程&#xff0c;它包括uts语言、uvue渲染引擎、uni的组件和API、以及扩展机制。 uts是一门类ts的、跨平台的、新语言。uts在iOS端编译为swift、在Android端编译为kotli…...

网站开发策划方案/丁的老头seo博客

Hadoop运行模式 &#xff08;1&#xff09;Hadoop官方网站&#xff1a;Apache Hadoop &#xff08;2&#xff09;Hadoop运行模式包括&#xff1a;本地模式、伪分布式模式以及完全分布式模式。 a.本地模式&#xff1a;单机运行&#xff0c;只是用来演示一下官方案例。生产环境…...

哪个网站能在线做司考题目/晋城今日头条新闻

任天堂的Switch主机已经上市将近两年&#xff0c;迟迟没有发布中文语言支持&#xff0c;虽然界面内容不复杂&#xff0c;但不少任然期望能看到中文界面。今日(2019年1月17日)任天堂台湾及任天堂香港官方网站发布公告称&#xff0c;Nintendo Switch即将更新的新系统版本&#xf…...

营销型网站策划 建设的考试题/惠州大亚湾经济技术开发区

本文转载自&#xff1a;http://blog.csdn.net/yushanddddfenghailin/article/details/17268473 列表框 列表框&#xff08;ListBox&#xff09;是Windows应用程序中重要的输入手段&#xff0c;其中包括多个选项用户可以从其中选择一个或者多个&#xff0c;程序根据用户的选择做…...

阿里云做外贸网站/seo搜索引擎优化包邮

我们可以通过正则来验证年月日的格式是否正确&#xff0c;可以定义通用的方法来验证所有年月日的格式&#xff0c;调用如下方法即可。 /*** 年月日格式验证* param string $value* return boolean*/ function vlidate_date($date) {$pattern /^(((1[6-9]|[2-9]\d)(\d{2})-((0?…...

小程序嵌套wordpress/百度竞价点击软件

程序员必备简捷开发辅助工具总结 写在前面&#xff1a; 工欲善其事必先利其器&#xff0c;拥有简捷的开发辅助工具能大大提高我们程序猿的开发效率。Melo刚到学校就给大家总结了一些常用的辅助开发的工具&#xff0c;希望大家能喜欢&#xff0c;闲话不多说&#xff0c;马上开…...

不用代码做网站的工具/淘宝摄影培训推荐

NFS&#xff1a;Net File System&#xff0c;网络文件系统&#xff08;NFS&#xff09;是文件系统之上的一个网络抽象&#xff0c;来允许远程客户端以与本地文件系统类似的方式&#xff0c;来通过网络进行访问&#xff0c;它允许在多个用户之间共享公共文件系统&#xff0c;并提…...