《lua设计与实现》第6章 指令的解析与执行--6.6 关系逻辑类指令

生成跳转指令时并不确定最后将跳转到哪里去(因为往往还未解析到目标代码段)。因此这里会用到编译原理中一个叫“回填”的技术。相关的指令:

typedef enum {
/*----------------------------------------------------------------------
name        args    description
------------------------------------------------------------------------*/
// ......
OP_LOADBOOL,/*    A B C    R(A) := (Bool)B; if (C) pc++            */
// ......
OP_JMP,     /*    sBx    pc+=sBx                    */

OP_EQ,      /*    A B C    if ((RK(B) == RK(C)) ~= A) then pc++        */
OP_LT,      /*    A B C    if ((RK(B) <  RK(C)) ~= A) then pc++          */
OP_LE,      /*    A B C    if ((RK(B) <= RK(C)) ~= A) then pc++          */

OP_TEST,    /*    A C    if not (R(A) <=> C) then pc++            */ 
OP_TESTSET, /*    A B C    if (R(B) <=> C) then R(A) := R(B) else pc++    */ 
// ......
} OpCode;

所有的逻辑跳转类指令无非是这样的形式:

if (cond)
  goto label1;
label2:
  func2();
  goto label_end;
label1:
  func1();
label_end:
  func _end();

同 一个表达式只有两个跳转链表,一般称为truelist和 falselist 。 在具体的实现中,就是expdesc 结构体中的成员 t和f

if (cond)
  //生成一个跳转语句,此时 label1 位置未知,因此生成跳转语句的跳转点加入 cond 的 truelist
label2:
  func2();
  //生成一个跳转语句,此时 label_end 位置未知,因此生成跳转语句的跳转点加入 cond 的 falselist
label1:
  func1();
label_end:
  func _end();

 

回填技术涉及如下两个操作:

  1. 将当前生成的未知其目的地址的跳转语句加入到某个空悬链表中

  2. 以某个位置的数据,回填上面生成的空悬链表的悬空地址

OP_JMP指令中,sBx:跳转目的地址的偏移量。

A,B,C都是OP_JMP指令,并跳转到同一个目的地址。

A的跳转地址:B的偏移量
B的跳转地址:C的偏移量
C的跳转地址:NO_JUMP(-1)

typedef enum {
// ......
OP_JMP,     /*    sBx    pc+=sBx                    */
// ......
} OpCode;

 

将一个新的跳转位置加入空悬跳转链表的操作在函数 luaK concat 中:

// l1 : 空悬链表的第一个指令位置
// l2 : 待加入该链表的指令位置
void luaK_concat (FuncState *fs, int *l1, int l2) {
  if (l2 == NO_JUMP) return; // l2 存储的指令不是一个跳转指令
  else if (*l1 == NO_JUMP)   // 跳转链表为空,没有空悬的跳转指令在该链表中
    *l1 = l2;
  else {
    int list = *l1;
    int next;
    while ((next = getjump(fs, list)) != NO_JUMP)  /* find last element */
      list = next;
    //将最后一个元素的跳转位置设置为 12
    fixjump(fs, list, l2);
  }
}

 

上一篇:doris 删除


下一篇:SpringCloud Gateway 路由转发性能优化