目录

一、虚拟机篇 - 指令集存储数据结构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;
}

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

  1. Lua源码分析 - 虚拟机篇 - 语义解析之Opcode执行(18)

    目录 一.虚拟机篇 - 指令执行状态机luaV_execute 二.虚拟机篇 - 状态机的具体实现原理 一.虚拟机篇 - 指令执行状态机luaV_execute 在<Lua源码分析 - 主流程篇 ...

  2. Lua源码分析 - 基础篇 - Lua源码的结构和架构图(01)

    目录 一.Lua语言简介 二.Lua架构图 三.Lua源码结构 很久很久没有写博客了,一直忙于工作和项目,最近依然想静下来阅读一些好的源码.自从读完了Nginx和Memcache的源码后,对服务器端的 ...

  3. Spring 源码分析衍生篇十 :Last-Modified 缓存机制

    文章目录 一.前言 二.Last-Modify 三.实现方案 1. 实现 org.springframework.web.servlet.mvc.LastModified接口 1.1. 简单演示 1. ...

  4. asp.net mvc源码分析-Action篇 Action的执行

    接着上篇 asp.net mvc源码分析-Action篇 DefaultModelBinder 我们已经获取的了Action的参数,有前面的内容我们知道Action的调用时在ControllerAct ...

  5. asp.net mvc源码分析-Action篇 DefaultModelBinder

    接着上篇 asp.net mvc源码分析-Controller篇 ValueProvider 现在我们来看看ModelBindingContext这个对象. ModelBindingContext b ...

  6. asp.net mvc源码分析-Controllerl篇 ControllerDescriptor

    在上篇asp.net mvc源码分析-Controllerl篇 TempData数据存储 我们讲到了ActionInvoker.InvokeAction(ControllerContext, acti ...

  7. 鸿蒙系统源代码解析,鸿蒙内核源码分析(系统调用篇) | 图解系统调用全貌

    本篇说清楚系统调用 读本篇之前建议先读鸿蒙内核源码分析(总目录)工作模式篇. 本篇通过一张图和七段代码详细说明系统调用的整个过程,代码一捅到底,直到汇编层再也捅不下去. 先看图,这里的模式可以理解为空 ...

  8. 鸿蒙内核代码 行,鸿蒙内核源码分析(CPU篇) | 内核是如何描述CPU的 ? | 祝新的一年牛气冲天 ! | v36.01...

    本篇说清楚CPU 读本篇之前建议先读鸿蒙内核源码分析(总目录)进程/线程篇.指令是稳定的,但指令序列是变化的,只有这样计算机才能够实现用计算来解决一切问题这个目标.计算是稳定的,但计算的数据是多变的, ...

  9. 鸿蒙内核 cpu兼容,鸿蒙内核源码分析(CPU篇) | 整个内核就是一个死循环 | 祝新的一年牛气冲天 ! | v32.04...

    本篇说清楚CPU 读本篇之前建议先读鸿蒙内核源码分析(总目录)进程/线程篇. 指令是稳定的,但指令序列是变化的,只有这样计算机才能够实现用计算来解决一切问题这个目标.计算是稳定的,但计算的数据是多变的 ...

最新文章

  1. python动态时钟代码_Python基础 用Python实现时钟
  2. Android SlidingMenu以及ActionBarSherlock的基础使用教程
  3. python数学公式代码导入_在Matplotlib图中插入LaTex公式实例
  4. 地图上如何量方位角_楼承板厂家揭秘:压型钢板采购的7个坑之第2个坑_压型钢板如何在镀锌量上偷工减料?...
  5. flutter 禁止冒泡_【Flutter】Switch开关组件
  6. ubuntu-文件管理、编辑
  7. 京东一面:高并发下,如何保证分布式唯一全局 ID 生成?
  8. JavaScript权威指南学习笔记(一)
  9. 最完美的matlab绘图教程集合
  10. Twaver-HTML5基础学习(12)连线(Link)
  11. SpringCloud-SpringCloud Bus服务总线的介绍(Day9)
  12. Java ----excel操作(poi)
  13. 2、SpringBoot接口Http协议开发实战8节课(7-8)
  14. P2-前端基础-网页中的实体(转义字符)
  15. html、css 实现一个漂亮的表格
  16. PPT 如何取消幻灯片自动播放
  17. 通往奥格瑞玛的道路(二分+迪杰斯特拉堆优化)
  18. Android实习周记:第一周,井底之蛙上岸
  19. 菜鸟要飞分享的视频教程
  20. 自媒体如何利用文章和搬运视频伪原创来赚钱

热门文章

  1. 莫拉克电梯服务器说明书_默纳克电梯功能参数表
  2. 使用蓝桥杯单片机实现ADC采集和实时时钟界面转换与报警功能
  3. 什么是Web应用防火墙
  4. (一)libvirt库简介
  5. 20194311姜晨昊Exp2-后门原理与实践
  6. linux计划任务之crontab
  7. 【接口测试 】Day1-接口测试基础(附项目实战)
  8. 计算机网络与通信之局域网
  9. Python Qt GUI设计:菜单栏、工具栏和状态栏的使用方法(拓展篇—2)
  10. jgl表示什么(JGL什么意思)