risc-v中文社区

 找回密码
 立即注册
查看: 1778|回复: 0

Lua源码分析 - 虚拟机篇 - 语义解析之Opcode生成(17)

[复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2022-6-20 16:00:49 | 显示全部楼层 |阅读模式
目录

一、虚拟机篇 - 指令集存储数据结构Proto

二、虚拟机篇 - Opcode的定义和宏函数

三、虚拟机篇 - 核心函数luaK_codeABC和luaK_codeABx

四、虚拟机篇 - 常量处理函数addk

上一篇我们讲解了语义的解析编译过程。我们基本知道了Lua的代码是一遍解析文件,一遍编译成字节码指令的。这一节,我们主要讲一下lcode.c文件,Opcode是如何生成的。

一、虚拟机篇 - 指令集存储数据结构Proto
上一篇,我们有讲到Proto是主要用来存储指令集的。

指令集存放:解析完毕的指令集,都会放置到Proto->code[n]上,code是一个数组形式存储。FuncState->pc指向下一个code的地址。Proto结构挂载在FuncState函数栈状态结构上。
常量存放:如果是常量,则先设置到Lua的栈上lua_State。同时也将值设置到Proto->k[n]上,所以k主要是用于存放常量的数组。
所以,看明白整体的指令集存放结构后,我们就可以通盘看一下lcode.c里面相关的核心函数了。



二、虚拟机篇 - Opcode的定义和宏函数
lcode.c文件中,主要对常用的操作符函数进行了封装。而真正最底层的Opcode的定义和函数主要在lopcodes.h文件中。

首先看一下字节码操作符,定义了一个枚举的类型,值从0开始。

typedef enum {
/*----------------------------------------------------------------------
name                args        description
------------------------------------------------------------------------*/
OP_MOVE,/*        A B        R(A) := R(B)                                        */
OP_LOADK,/*        A Bx        R(A) := Kst(Bx)                                        */
OP_LOADKX,/*        A         R(A) := Kst(extra arg)                                */
OP_LOADBOOL,/*        A B C        R(A) := (Bool)B; if (C) pc++                        */
OP_LOADNIL,/*        A B        R(A), R(A+1), ..., R(A+B) := nil                */
OP_GETUPVAL,/*        A B        R(A) := UpValue[B]                                */

OP_GETTABUP,/*        A B C        R(A) := UpValue[B][RK(C)]                        */
OP_GETTABLE,/*        A B C        R(A) := R(B)[RK(C)]                                */

..........

OP_VARARG,/*        A B        R(A), R(A+1), ..., R(A+B-2) = vararg                */

OP_EXTRAARG/*        Ax        extra (larger) argument for previous opcode        */
} OpCode;

//操作指令从0开始计算,所以总的值是 OP_EXTRAARG + 1
#define NUM_OPCODES        (cast(int, OP_EXTRAARG) + 1)
指令集主要存放在code[n]数组上,code的类型是Instruction,而Instruction在宏定义中是一个32位的unsigned int类型

lopcodes.h文件中,通过定义CREATE_ABC、CREATE_ABx、CREATE_Ax定义三个不同的指令集生成函数。

生成函数的主要逻辑就是位运算,在32位int类型中,切割成4个位置,放置操作指令。前面6位放置Opcode操作指令,8位放置操作指令A,9位放置操作指令B,9位放置操作指令C。

//位运算
#define CREATE_ABC(o,a,b,c)        ((cast(Instruction, o)<<POS_OP) \
                        | (cast(Instruction, a)<<POS_A) \
                        | (cast(Instruction, b)<<POS_B) \
                        | (cast(Instruction, c)<<POS_C))

#define CREATE_ABx(o,a,bc)        ((cast(Instruction, o)<<POS_OP) \
                        | (cast(Instruction, a)<<POS_A) \
                        | (cast(Instruction, bc)<<POS_Bx))

#define CREATE_Ax(o,a)                ((cast(Instruction, o)<<POS_OP) \
                        | (cast(Instruction, a)<<POS_Ax))

/*
** size and position of opcode arguments.
*/
#define SIZE_C                9 //指令C
#define SIZE_B                9 //指令B
#define SIZE_Bx                (SIZE_C + SIZE_B)
#define SIZE_A                8 //指令A
#define SIZE_Ax                (SIZE_C + SIZE_B + SIZE_A)

#define SIZE_OP                6 //操作符6位

#define POS_OP                0
#define POS_A                (POS_OP + SIZE_OP) //6
#define POS_C                (POS_A + SIZE_A) //14
#define POS_B                (POS_C + SIZE_C) //23
#define POS_Bx                POS_C //14
#define POS_Ax                POS_A
三、虚拟机篇 - 核心函数luaK_codeABC和luaK_codeABx
在lcode.c中,我们可以看到luaK_codeABC和luaK_codeABx函数是最基础的生成Opcode指令集的函数。

两个函数都调用CREATE_AB*宏函数,实现Opcode生成。并通过luaK_code函数将指令集存放到Proto->code[n]数组上。

/*
** Format and emit an 'iABC' instruction. (Assertions check consistency
** of parameters versus opcode.)
** 生成指令集
*/
int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {
  lua_assert(getOpMode(o) == iABC);
  lua_assert(getBMode(o) != OpArgN || b == 0);
  lua_assert(getCMode(o) != OpArgN || c == 0);
  lua_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C);
  return luaK_code(fs, CREATE_ABC(o, a, b, c));
}


/*
** Format and emit an 'iABx' instruction.
** 生成指令集
*/
int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
  lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
  lua_assert(getCMode(o) == OpArgN);
  lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx);
  return luaK_code(fs, CREATE_ABx(o, a, bc));
}

/*
** Emit instruction 'i', checking for array sizes and saving also its
** line information. Return 'i' position.
** Opcode存放在Proto结构上
** 其中f->code数组用于存放code
** fs->pc主要是计数器,标记code的个数及数组下标
*/
static int luaK_code (FuncState *fs, Instruction i) {
  Proto *f = fs->f;
  dischargejpc(fs);  /* 'pc' will change */
  /* put new instruction in code array */
  luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
                  MAX_INT, "opcodes");
  f->code[fs->pc] = i;
  /* save corresponding line information */
  luaM_growvector(fs->ls->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
                  MAX_INT, "opcodes");
  f->lineinfo[fs->pc] = fs->ls->lastline;
  return fs->pc++;
}
从luaK_nil函数中,我们也能看到,最终调用了luaK_codeAB*基础函数,实现指令集Opcode的生成

void luaK_nil (FuncState *fs, int from, int n) {
  Instruction *previous;
  int l = from + n - 1;  /* last register to set nil */
  if (fs->pc > fs->lasttarget) {  /* no jumps to current position? */
    previous = &fs->f->code[fs->pc-1];
    if (GET_OPCODE(*previous) == OP_LOADNIL) {  /* previous is LOADNIL? */
      int pfrom = GETARG_A(*previous);  /* get previous range */
      int pl = pfrom + GETARG_B(*previous);
      if ((pfrom <= from && from <= pl + 1) ||
          (from <= pfrom && pfrom <= l + 1)) {  /* can connect both? */
        if (pfrom < from) from = pfrom;  /* from = min(from, pfrom) */
        if (pl > l) l = pl;  /* l = max(l, pl) */
        SETARG_A(*previous, from);
        SETARG_B(*previous, l - from);
        return;
      }
    }  /* else go through */
  }
  luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0);  /* else no optimization */
}
四、虚拟机篇 - 常量处理函数addk
前面我们讲过,常量会放置到Proto->k[n]上,同时也会放置到Lua的栈上。常量的处理主要通过addk实现的。

我们以字符串处理为例,可以看一下代码的具体实现。

/*
** Add a string to list of constants and return its index.
*/
int luaK_stringK (FuncState *fs, TString *s) {
  TValue o;
  setsvalue(fs->ls->L, &o, s);
  return addk(fs, &o, &o);  /* use string itself as key */
}

static int addk (FuncState *fs, TValue *key, TValue *v) {
  lua_State *L = fs->ls->L;
  Proto *f = fs->f;
  TValue *idx = luaH_set(L, fs->ls->h, key);  /* index scanner table */
  int k, oldsize;
  if (ttisinteger(idx)) {  /* is there an index there? */
    k = cast_int(ivalue(idx));
    /* correct value? (warning: must distinguish floats from integers!) */
    if (k < fs->nk && ttype(&f->k[k]) == ttype(v) &&
                      luaV_rawequalobj(&f->k[k], v))
      return k;  /* reuse index */
  }
  /* constant not found; create a new entry */
  oldsize = f->sizek;
  k = fs->nk;
  /* numerical value does not need GC barrier;
     table has no metatable, so it does not need to invalidate cache */
  setivalue(idx, k);
  luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
  while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
  setobj(L, &f->k[k], v);
  fs->nk++;
  luaC_barrier(L, f, v);
  return k;
}

————————————————
版权声明:本文为CSDN博主「老码农zhuli」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/initphp/article/details/105274652

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



Archiver|手机版|小黑屋|risc-v中文社区

GMT+8, 2024-4-19 16:52 , Processed in 0.020438 second(s), 17 queries .

risc-v中文社区论坛 官方网站

Copyright © 2018-2021, risc-v open source

快速回复 返回顶部 返回列表