• 《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);
      }
    }

    getjump

    static int getjump (FuncState *fs, int pc) {
      int offset = GETARG_sBx(fs->f->code[pc]);
      if (offset == NO_JUMP)  /* point to itself represents end of list */
        return NO_JUMP;  /* end of list */
      else
        return (pc+1)+offset;  /* turn offset into absolute position */
    }

    fixjump

    static void fixjump (FuncState *fs, int pc, int dest) {
      Instruction *jmp = &fs->f->code[pc];
      int offset = dest-(pc+1);
      lua_assert(dest != NO_JUMP);
      if (abs(offset) > MAXARG_sBx)
        luaX_syntaxerror(fs->ls, "control structure too long");
      SETARG_sBx(*jmp, offset);
    }
  • 相关阅读:
    一个神奇的类库,可以执行字符串表达式
    Common lang一些边界方法总结(好记性不如烂笔头,需要慢慢积累).一定要利用好现有的轮子,例如Apache common与Google Guava
    借助强大的IDEA开发ide高效实现equals,hashcode以及toString方法
    数字签名是什么?
    字符串组合
    空格替换
    字符串替换 (replace)
    为什么要区分稳定和非稳定排序
    算法稳定性
    华为2018校招技术岗笔试
  • 原文地址:https://www.cnblogs.com/yyqng/p/14855091.html
Copyright © 2020-2023  润新知