简单认识LuaJIT SSA IR

一、IR代码书写结构理解

LuaJIT资料挺少,可以一手学习的基本上只有官方文档,我也是按照自己的理解,对官网上的部分内容做了记录。下面代码分别是Byte codeSSA IR,以及目标机器对应的MCode,此时的目标机器架构是LoongArch

loongson@loongson-pc:~/workspace$ luajit-2.1.0-beta3 -jdump
LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2021 Mike Pall. https://luajit.org/
JIT: ON MIPS64R2 fold cse dce fwd dse narrow loop abc sink fuse
> local x = 1.2 for i=1,1e3 do x = x * -3 end
---- TRACE 1 start stdin:1 //
0006  MULVN    0   0   1  ; -3
0007  FORL     1 => 0006
---- TRACE 1 IR
0001    int SLOAD  #3    I
0002 >  num SLOAD  #2    T
0003  + num MUL    0002  -3  
0004  + int ADD    0001  +1  
0005 >  int LE     0004  +1000
0006 ------ LOOP ------------
0007  + num MUL    0003  -3  
0008  + int ADD    0004  +1  
0009 >  int LE     0008  +1000
0010    int PHI    0004  0008
0011    num PHI    0003  0007
---- TRACE 1 mcode 80
555558bbff80  lui     ra, 49160
555558bbff84  dsll32  ra, ra, 0
555558bbff88  dmtc1   ra, f0
555558bbff8c  lw      r23, 8(r16)
555558bbff90  ld      ra, 0(r16)
555558bbff94  dsra32  ra, ra, 15
555558bbff98  ldc1    f1, 0(r16)
555558bbff9c  sltiu   ra, ra, -14
555558bbffa0  beqz    ra, 0x555558bbffd4    ->exit
555558bbffa4  li      ra, 0
555558bbffa8  mul.d   f31, f1, f0
555558bbffac  addiu   r23, r23, 1
555558bbffb0  slti    ra, r23, 1001
555558bbffb4  beqz    ra, 0x555558bbffd4    ->exit
555558bbffb8  li      ra, 1
->LOOP:
555558bbffbc  mul.d   f31, f31, f0
555558bbffc0  addiu   r23, r23, 1
555558bbffc4  slti    ra, r23, 1001
555558bbffc8  bnez    ra, 0x555558bbffbc    ->LOOP
555558bbffcc  li      ra, 3
---- TRACE 1 stop -> loop

针对上面SSA IR作如下解释,其中“文档”指的是官方文档

  • 第1列:IR指令编号,可以作为SSA ref,也就是说,若有指令想要获取当前指令产生的值(如PHI指令),就可以直接ref当前指令的编号到其opt。

  • 第2列:指令标志:
    ">" (IRT_GUARD = 0x80指令标志)有该标志的指令可以理解为是一个分叉指令,可能会是trace路径的出口。该标志一般在关系运算指令前面(见文档Guarded Assertions)。
    "+" (IRT_ISPHI = 0x40指令标志)有该标志的指令可能会是PHI指令的左或右操作数。PHI指令在循环trace的末尾,有两个操作数,分别引用同一变量在循环期间不同时间段的值,左操作数是对初始值的引用,右操作数是对每次循环迭代后该值的引用

  • 第3列:IR类型:int类型指的是32 bit signed integernum类型指的是double(见文档IR Types)。

  • 第4列:IR操作码,主要有以下分类(见文档各种操作码)

    Constants
    Guarded Assertions
    Bit Ops(位操作)
    Arithmetic Ops(算数操作)
    Memory References(内存引用,返回一个指针,供Loads and Stores操作使用)
    Loads and Stores
    Allocations
    Barriers
    Type Conversions(类型转换)
    Calls(函数调用操作码)
    Miscellaneous Ops(其他操作码)
    
  • 第5/6列:IR操作数(SSA ref或字面量)
    '#'+数字,表示该数字是一个stack slot number,可以理解成为对指定栈内存的引用,被用于SLOAD指令的做操作数。
    #0,函数栈帧。
    #1,is the first slot in the first frame (register 0 in the bytecode)。
    '[+-]'+字面量,表示字面量的正负值。
    '[0x%d+]'NULL是内存地址。
    ’”……“是字符串。
    '@' prefixes indicate slots (what is this?).
    Other possible values: "bias" (number 2^52+2^51 ?), "userdata:%p",
    "userdata:%p" (table)–when do these occur?.

二、IR在luaJit源码中的定义

luaJit对于IR结构的定义主要在 lj_ir.hlj_ircall.h 这两个个文件,IR emitter主要在 lj_ir.c 文件。以下是lj_ir.h文件,IR基本指令的定义:

1)IR instructions(操作码)

151 /* IR opcodes (max. 256). */
152 typedef enum {
    
    
153 #define IRENUM(name, m, m1, m2) IR_##name,
154 IRDEF(IRENUM)              
155 #undef IRENUM              
156   IR__MAX
157 } IROp;

IROp是对IR操作码的枚举定义,需要注意的是每个操作码的枚举顺序很重要(顺序决定了其值),如IR_EQ的值为8IR_NE的值为9,这样则可以通过表达式 (int)IR_EQ^1) == (int)IR_NE 在两者之间相互转换,类似的表达式还有如下:

162 LJ_STATIC_ASSERT(((int)IR_EQ^1) == (int)IR_NE);
163 LJ_STATIC_ASSERT(((int)IR_LT^1) == (int)IR_GE);
164 LJ_STATIC_ASSERT(((int)IR_LE^1) == (int)IR_GT);
165 LJ_STATIC_ASSERT(((int)IR_LT^3) == (int)IR_GT);
166 LJ_STATIC_ASSERT(((int)IR_LT^4) == (int)IR_ULT);
167 
168 /* Delta between xLOAD and xSTORE. */
169 #define IRDELTA_L2S     ((int)IR_ASTORE - (int)IR_ALOAD)
170 
171 LJ_STATIC_ASSERT((int)IR_HLOAD + IRDELTA_L2S == (int)IR_HSTORE);
172 LJ_STATIC_ASSERT((int)IR_ULOAD + IRDELTA_L2S == (int)IR_USTORE);
173 LJ_STATIC_ASSERT((int)IR_FLOAD + IRDELTA_L2S == (int)IR_FSTORE);
174 LJ_STATIC_ASSERT((int)IR_XLOAD + IRDELTA_L2S == (int)IR_XSTORE);

2)Named IR literals(IR使用到的一些name字面量,比如某个字面值代表那个函数等)

184 typedef enum {
    
                 
185 #define FPMENUM(name)       IRFPM_##name,
186 IRFPMDEF(FPMENUM)          
187 #undef FPMENUM             
188   IRFPM__MAX
189 } IRFPMathOp; 

IRFPMathOp是对 FPMATH操作码 操作数的定义,FPMATH操作码操作码被用来一元浮点运算,其中的浮点运算对应关系如下:

OP Description
FPM_FLOOR floor(ref)
FPM_CEIL ceil(ref)
FPM_TRUNC trunc(ref)
FPM_SQRT sqrt(ref)
FPM_EXP exp(ref)
FPM_EXP2 exp2(ref)
FPM_LOG log(ref)
FPM_LOG2 log2(ref)
FPM_LOG10 log10(ref)
FPM_SIN sin(ref)
FPM_COS cos(ref)
FPM_TAN tan(ref)

IRFieldID是对 FLOADFREF操作码 的fields的定义:

213 typedef enum {
    
    
214 #define FLENUM(name, ofs)   IRFL_##name,
215 IRFLDEF(FLENUM)
216 #undef FLENUM
217   IRFL__MAX
218 } IRFieldID;

下面这些定义分别对应的是表格中操作码的#flags,如下:

220 /* SLOAD mode bits, stored in op2. */
221 #define IRSLOAD_PARENT      0x01    /* Coalesce with parent trace. */
222 #define IRSLOAD_FRAME       0x02    /* Load 32 bits of ftsz. */
223 #define IRSLOAD_TYPECHECK   0x04    /* Needs type check. */
224 #define IRSLOAD_CONVERT     0x08    /* Number to integer conversion. */
225 #define IRSLOAD_READONLY    0x10    /* Read-only, omit slot store. */
226 #define IRSLOAD_INHERIT     0x20    /* Inherited by exits/side traces. */
227 
228 /* XLOAD mode, stored in op2. */
229 #define IRXLOAD_READONLY    1   /* Load from read-only data. */
230 #define IRXLOAD_VOLATILE    2   /* Load from volatile data. */
231 #define IRXLOAD_UNALIGNED   4   /* Unaligned load. */

237 /* CONV mode, stored in op2. */
238 #define IRCONV_SRCMASK      0x001f  /* Source IRType. */
239 #define IRCONV_DSTMASK      0x03e0  /* Dest. IRType (also in ir->t). */
240 #define IRCONV_DSH      5
241 #define IRCONV_NUM_INT      ((IRT_NUM<<IRCONV_DSH)|IRT_INT)
242 #define IRCONV_INT_NUM      ((IRT_INT<<IRCONV_DSH)|IRT_NUM)
243 #define IRCONV_SEXT     0x0800  /* Sign-extend integer to integer. */
244 #define IRCONV_MODEMASK     0x0fff
245 #define IRCONV_CONVMASK     0xf000
246 #define IRCONV_CSH      12
247 /* Number to integer conversion mode. Ordered by strength of the checks. */
248 #define IRCONV_TOBIT  (0<<IRCONV_CSH)   /* None. Cache only: TOBIT conv. */
249 #define IRCONV_ANY    (1<<IRCONV_CSH)   /* Any FP number is ok. */
250 #define IRCONV_INDEX  (2<<IRCONV_CSH)   /* Check + special backprop rules. */
251 #define IRCONV_CHECK  (3<<IRCONV_CSH)   /* Number checked for integerness. */
OP Left Right Description
XLOAD xref #flags Extended load
SLOAD #slot #flags Stack slot load
CONV src #flags Generic type conversion

还有少许其他特殊操作码的定义,可以自行查找对应。

3)IR operands,IR 操作数

以下是对IR操作数的定义,其中IRMode为操作数的kind,分为了四种:

260 /* IR operand mode (2 bit). */
261 typedef enum {
    
    
262   IRMref,       /* IR reference. */
263   IRMlit,       /* 16 bit unsigned literal. */
264   IRMcst,       /* Constant literal: i, gcr or ptr. */
265   IRMnone       /* Unused operand. */
266 } IRMode;
267 #define IRM___      IRMnone

4)IR instruction types,IR 指令类型

每个IR指令的都有一个输出结果的类型,IRType则是对其的定义(8 bit)

314 /* IR result type and flags (8 bit). */
315 typedef enum {
    
    
316 #define IRTENUM(name, size) IRT_##name,
317 IRTDEF(IRTENUM)
318 #undef IRTENUM
319   IRT__MAX,
320 
321   /* Native pointer type and the corresponding integer type. */
322   IRT_PTR = LJ_64 ? IRT_P64 : IRT_P32,
323   IRT_PGC = LJ_GC64 ? IRT_P64 : IRT_P32,
324   IRT_IGC = LJ_GC64 ? IRT_I64 : IRT_INT,
325   IRT_INTP = LJ_64 ? IRT_I64 : IRT_INT,
326   IRT_UINTP = LJ_64 ? IRT_U64 : IRT_U32,
327 
328   /* Additional flags. */
329   IRT_MARK = 0x20,  /* Marker for misc. purposes. */
330   IRT_ISPHI = 0x40, /* Instruction is left or right PHI operand. */
331   IRT_GUARD = 0x80, /* Instruction is a guard. */
332 
333   /* Masks. */
334   IRT_TYPE = 0x1f,
335   IRT_T = 0xff
336 } IRType;

这里还定义了两个函数,itypeTValuelj_obj.h

  • itype2irt :将itype转成IRType
  • irt_toitype_IRType转成itype

5)IR references

Tagged IR references都有一个IRType,也就是下面的irt,这样方便快速IR type checks。irt is 8 bitflags is 8 bitref is 16 bit

446 /* Fixed references. */
447 enum {
    
    
448   REF_BIAS =    0x8000,
449   REF_TRUE =    REF_BIAS-3,
450   REF_FALSE =   REF_BIAS-2,
451   REF_NIL = REF_BIAS-1, /* \--- Constants grow downwards. */
452   REF_BASE =    REF_BIAS,   /* /--- IR grows upwards. */
453   REF_FIRST =   REF_BIAS+1,
454   REF_DROP =    0xffff
455 };
456 

471 /* Tagged IR references (32 bit).
472 **
473 ** +-------+-------+---------------+
474 ** |  irt  | flags |      ref      |
475 ** +-------+-------+---------------+
476 */

6)IR format

524 /* IR instruction format (64 bit).
525 **
526 **    16      16     8   8   8   8
527 ** +-------+-------+---+---+---+---+
528 ** |  op1  |  op2  | t | o | r | s |
529 ** +-------+-------+---+---+---+---+
530 ** |  op12/i/gco32 |   ot  | prev  | (alternative fields in union)
531 ** +-------+-------+---+---+---+---+
532 ** |  TValue/gco64                 | (2nd IR slot for 64 bit constants)
533 ** +---------------+-------+-------+
534 **        32           16      16
535 **
536 ** prev is only valid prior to register allocation and then reused for r + s.
537 */

539 typedef union IRIns {
    
    
540   struct {
    
    
541     LJ_ENDIAN_LOHI(
542       IRRef1 op1;   /* IR operand 1. */
543     , IRRef1 op2;   /* IR operand 2. */
544     )
545     IROpT ot;       /* IR opcode and type (overlaps t and o). */
546     IRRef1 prev;    /* Previous ins in same chain (overlaps r and s). */
547   };
548   struct {
    
    
549     IRRef2 op12;    /* IR operand 1 and 2 (overlaps op1 and op2). */
550     LJ_ENDIAN_LOHI(
551       IRType1 t;    /* IR type. */
552     , IROp1 o;      /* IR opcode. */
553     )
554     LJ_ENDIAN_LOHI(
555       uint8_t r;    /* Register allocation (overlaps prev). */
556     , uint8_t s;    /* Spill slot allocation (overlaps prev). */
557     )
558   };
559   int32_t i;        /* 32 bit signed integer literal (overlaps op12). */
560   GCRef gcr;        /* GCobj constant (overlaps op12 or entire slot). */
561   MRef ptr;     /* Pointer constant (overlaps op12 or entire slot). */
562   TValue tv;        /* TValue constant (overlaps entire slot). */
563 } IRIns;

猜你喜欢

转载自blog.csdn.net/qq_42570601/article/details/120842753