6.lua指令与虚拟机

1. lua函数

1.1 函数定义

typedef int (*lua_CFunction) (lua_State *L);

typedef struct CClosure {
  ClosureHeader;
  lua_CFunction f;
  TValue upvalue[1];  /* list of upvalues */
} CClosure;

typedef struct Proto { //语法解析的输出
  CommonHeader;
  lu_byte numparams;  /* number of fixed (named) parameters */
  lu_byte is_vararg;
  lu_byte maxstacksize;  /* number of registers needed by this function */
  int sizeupvalues;  /* size of 'upvalues' */
  int sizek;  			//数组 成员k的大小
  int sizecode;       	//数组 成员code的大小
  int sizelineinfo;   	//数组 成员lineinfo的大小
  int sizep;          	//数组 成员p的大小
  int sizelocvars;    	//数组 成员locvars的大小
  int sizeabslineinfo;  /* size of 'abslineinfo' */
  int linedefined;  /* debug information  */
  int lastlinedefined;  /* debug information  */
  TValue *k;  			//常量
  Instruction *code;    //编译成的字节码
  struct Proto **p;     //子函数
  Upvaldesc *upvalues;  /* upvalue information */
  ls_byte *lineinfo;    //行号信息 于code字节码的下标一一对应 (debug information)
  AbsLineInfo *abslineinfo;  /* idem */
  LocVar *locvars;      //局部变量数组 (debug information)
  TString  *source;  /* used for debug information */
  GCObject *gclist;
} Proto;

typedef struct LClosure {
  ClosureHeader;
  struct Proto *p;	 //lua解析完的结构,和c函数对应
  UpVal *upvals[1];  /* list of upvalues */
} LClosure;


typedef union Closure {
  CClosure c;		 //c函数
  LClosure l;		 //lua函数
} Closure;


typedef struct CallInfo {
  StkId func;                         //栈基地址
  StkId	top;                          //栈顶
  struct CallInfo *previous, *next;   //双向链表指针
  union {
    struct {  /* only for Lua functions */
      const Instruction *savedpc;
      volatile l_signalT trap;
      int nextraargs;  /* # of extra arguments in vararg functions */
    } l;
    struct {  /* only for C functions */
      lua_KFunction k;  /* continuation in case of yields */
      ptrdiff_t old_errfunc;
      lua_KContext ctx;  /* context info. in case of yields */
    } c;
  } u;
  union {
    int funcidx;  /* called-function index */
    int nyield;  /* number of values yielded */
    int nres;  /* number of values returned */
    struct {  /* info about transferred values (for call/return hooks) */
      unsigned short ftransfer;  /* offset of first value transferred */
      unsigned short ntransfer;  /* number of values transferred */
    } transferinfo;
  } u2;
  short nresults;                   //返回值个数
  unsigned short callstatus;        //函数调用的状态
} CallInfo;

1.2 函数调用栈

CallInfo 的初始化,用Closure初始化CallInfo

CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
  lua_CFunction f;
 retry:
  switch (ttypetag(s2v(func))) {
    case LUA_VCCL:  /* C closure */
      f = clCvalue(s2v(func))->f;
      goto Cfunc;
    case LUA_VLCF:  /* light C function */
      f = fvalue(s2v(func));
     Cfunc: {
      int n;  /* number of returns */
      CallInfo *ci;
      checkstackGCp(L, LUA_MINSTACK, func);  /* ensure minimum stack size */
      L->ci = ci = next_ci(L);                 //如果cl->next为空,会申请内存并且加入双向链表
      ci->nresults = nresults;                 //返回值数量
      ci->callstatus = CIST_C;
      ci->top = L->top + LUA_MINSTACK;         //栈顶
      ci->func = func;                         //栈基地址
      lua_assert(ci->top <= L->stack_last);
      if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
        int narg = cast_int(L->top - func) - 1;
        luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
      }
      lua_unlock(L);
      n = (*f)(L);                             //调用lua c函数  返回的是返回值的个数
      lua_lock(L);
      api_checknelems(L, n);
      luaD_poscall(L, ci, n);                  //返回值移动
      return NULL;
    }
    case LUA_VLCL: {                           //lua 函数
      CallInfo *ci;
      Proto *p = clLvalue(s2v(func))->p;       //函数信息结构体 (指令、常量、子函数等)
      int narg = cast_int(L->top - func) - 1;  //传入的参数个数 (-1是因为包含自身)
      int nfixparams = p->numparams;           //函数的参数个数
      int fsize = p->maxstacksize;             //函数需要的栈大小
      checkstackGCp(L, fsize, func);
      L->ci = ci = next_ci(L);                 //如果cl->next为空,会申请内存并且加入双向链表
      ci->nresults = nresults;                 //返回值个数
      ci->u.l.savedpc = p->code;               //pc指向指令的起始位置
      ci->top = func + 1 + fsize;              //栈顶
      ci->func = func;                         //栈底
      L->ci = ci;
      for (; narg < nfixparams; narg++)        //如果传入的参数少于函数的参数,没穿入的参数设置为nil
        setnilvalue(s2v(L->top++));  /* complete missing arguments */
      lua_assert(ci->top <= L->stack_last);
      return ci;
    }
    default: {  /* not a function */
      checkstackGCp(L, 1, func);  /* space for metamethod */
      luaD_tryfuncTM(L, func);  /* try to get '__call' metamethod */
      goto retry;  /* try again with metamethod */
    }
  }
}

由这个函数可以看到 L->ci 保存了整个函数调用的栈。这个结构是一个双向链表,next向下调用,调用完previous返回。
函数调用的时候没有申请一个全新的栈给新的函数,使用的还是状态机中的L->stack栈。
每次函数调用的时候会以func为栈底(Closure函数对象)在ci->func保存,ci->top是栈顶。

调用栈
在这里插入图片描述

在这里插入图片描述

1.3 函数返回值

//nres是函数实际的返回值个数,wanted是函数解析完后预订的返回值个数
static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
  StkId firstresult;
  int i;
  switch (wanted) {  /* handle typical cases separately */
    case 0:             //没有返回值
      L->top = res;
      return;
    case 1:             //一个返回值
      if (nres == 0)    //如果实际没有返回,返回nil
        setnilvalue(s2v(res));
      else              //将返回值拷贝
        setobjs2s(L, res, L->top - nres);  //拷贝
      L->top = res + 1; //重新设置栈(返回值的上方)
      return;
    case LUA_MULTRET:
      wanted = nres;  /* we want all results */
      break;
    default:  /* two/more results and/or to-be-closed variables */
      if (hastocloseCfunc(wanted)) {  /* to-be-closed variables? */
        ptrdiff_t savedres = savestack(L, res);
        L->ci->callstatus |= CIST_CLSRET;  /* in case of yields */
        L->ci->u2.nres = nres;
        luaF_close(L, res, CLOSEKTOP, 1);
        L->ci->callstatus &= ~CIST_CLSRET;
        if (L->hookmask)  /* if needed, call hook after '__close's */
          rethook(L, L->ci, nres);
        res = restorestack(L, savedres);  /* close and hook can move stack */
        wanted = decodeNresults(wanted);
        if (wanted == LUA_MULTRET)
          wanted = nres;  /* we want all results */
      }
      break;
  }
  /* generic case */
  firstresult = L->top - nres;  //返回值起始的栈位置
  if (nres > wanted)            //如果实际返回值个数大于预定义的个数
    nres = wanted;  /* don't need them */
  for (i = 0; i < nres; i++)    //整体栈拷贝
    setobjs2s(L, res + i, firstresult + i);
  for (; i < wanted; i++)       //如果实际返回值个数小于预定义的个数,多余的设置为nil
    setnilvalue(s2v(res + i));
  L->top = res + wanted;        //重新设置栈(返回值的上方)
}

void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
  int wanted = ci->nresults;
  if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
    rethook(L, ci, nres);
  //返回值拷贝到函数栈初始的位置
  moveresults(L, ci->func, nres, wanted);
  /* function cannot be in any of these cases when returning */
  lua_assert(!(ci->callstatus &
        (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
  L->ci = ci->previous;  /* back to caller (after closing variables) */
}

2. 指令集合

2.1 指令格式

这段定义位于lopcodes.h头文件中

/*===========================================================================
  We assume that instructions are unsigned 32-bit integers.
  All instructions have an opcode in the first 7 bits.
  Instructions can have the following formats:

        3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
        1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 ==========================================

iABC          C(8)     |      B(8)     |k|     A(8)      |   Op(7)     |
iABx                Bx(17)               |     A(8)      |   Op(7)     |
iAsBx              sBx (signed)(17)      |     A(8)      |   Op(7)     |
iAx                           Ax(25)                     |   Op(7)     |
isJ                           sJ(25)                     |   Op(7)     |

  A signed argument is represented in excess K: the represented value is
  the written unsigned value minus K, where K is half the maximum for the
  corresponding unsigned argument.
===========================================================================*/

可以看到lua5.4.3有5种指令格式 iABC ,iABx,iAsBx,iAx,isJ

  • 可以看到指令是uint32类型
  • 前7个位代表的操作,目前这种指令格式代表可以有2^7次方个(128)指令
  • A,B,C是lua的三个索引,其中A一定是是A寄存器的索引用来获取栈中的元素。B,C可以是B,C寄存器的索引,也可以是常量的索引
  • 带x结尾的,如Bx,sBx,Ax,代表的是立即数Bx,Ax都是无符号数,s开头的代表有符号数(sBx)
  • isJ只有一条指令是这种格式就是OP_JMP指令,sj是一个有符号数
//指令各部分的大小
#define SIZE_C  8
#define SIZE_B  8
#define SIZE_Bx  (SIZE_C + SIZE_B + 1)
#define SIZE_A  8
#define SIZE_Ax  (SIZE_Bx + SIZE_A)
#define SIZE_sJ  (SIZE_Bx + SIZE_A)

#define SIZE_OP  7
//指令部分的起始位置
#define POS_OP  0
#define POS_A  (POS_OP + SIZE_OP)
#define POS_k  (POS_A + SIZE_A)
#define POS_B  (POS_k + 1)
#define POS_C  (POS_B + SIZE_B)
#define POS_Bx  POS_k
#define POS_Ax  POS_A
#define POS_sJ  POS_A
  • 上面是各个指令部分的大小定义的宏和指令各部分的起始位置宏
//创建一个全为1的掩码,n代表掩码个数,p是偏移的位置
#define MASK1(n,p)	((~((~(Instruction)0)<<(n)))<<(p))
//创建一个全为0的掩码
#define MASK0(n,p)	(~MASK1(n,p))

//指令格式判断
enum OpMode {iABC, iABx, iAsBx, iAx, isJ};
//检测指令的格式m是这个枚举值
#define getOpMode(m)	(cast(enum OpMode, luaP_opmodes[m] & 7))
#define checkopm(i,m)	(getOpMode(GET_OPCODE(i)) == m)

//opcode的设置和获取
#define GET_OPCODE(i)	(cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))  //与上一个前7位的掩码
#define SET_OPCODE(i,o)	((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \         //把opcode位 置为0,然后或上新的opcode
		((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
//这两个宏和上面两个类似,只是偏移和大小变成了变量
#define getarg(i,pos,size)	(cast_int(((i)>>(pos)) & MASK1(size,0)))
#define setarg(i,v,pos,size)	((i) = (((i)&MASK0(size,pos)) | \
                ((cast(Instruction, v)<<pos)&MASK1(size,pos))))

//获取和设置指令各个部分的宏
#define GETARG_A(i)	getarg(i, POS_A, SIZE_A)
#define SETARG_A(i,v)	setarg(i, v, POS_A, SIZE_A)

#define GETARG_B(i)	check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
#define GETARG_sB(i)	sC2int(GETARG_B(i))
#define SETARG_B(i,v)	setarg(i, v, POS_B, SIZE_B)

#define GETARG_C(i)	check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
#define GETARG_sC(i)	sC2int(GETARG_C(i))
#define SETARG_C(i,v)	setarg(i, v, POS_C, SIZE_C)

#define TESTARG_k(i)	check_exp(checkopm(i, iABC), (cast_int(((i) & (1u << POS_k)))))
#define GETARG_k(i)	check_exp(checkopm(i, iABC), getarg(i, POS_k, 1))
#define SETARG_k(i,v)	setarg(i, v, POS_k, 1)

#define GETARG_Bx(i)	check_exp(checkopm(i, iABx), getarg(i, POS_Bx, SIZE_Bx))
#define SETARG_Bx(i,v)	setarg(i, v, POS_Bx, SIZE_Bx)

#define GETARG_Ax(i)	check_exp(checkopm(i, iAx), getarg(i, POS_Ax, SIZE_Ax))
#define SETARG_Ax(i,v)	setarg(i, v, POS_Ax, SIZE_Ax)

#define GETARG_sBx(i)  \
	check_exp(checkopm(i, iAsBx), getarg(i, POS_Bx, SIZE_Bx) - OFFSET_sBx)
#define SETARG_sBx(i,b)	SETARG_Bx((i),cast_uint((b)+OFFSET_sBx))

#define GETARG_sJ(i)  \
	check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ)
#define SETARG_sJ(i,j) \
	setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)

//指令创建宏
#define CREATE_ABCk(o,a,b,c,k)	((cast(Instruction, o)<<POS_OP) \
			| (cast(Instruction, a)<<POS_A) \
			| (cast(Instruction, b)<<POS_B) \
			| (cast(Instruction, c)<<POS_C) \
			| (cast(Instruction, k)<<POS_k))

#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))

#define CREATE_sJ(o,j,k)	((cast(Instruction, o) << POS_OP) \
			| (cast(Instruction, j) << POS_sJ) \
			| (cast(Instruction, k) << POS_k))
  • 上面是指令获取设置相关的宏

2.2 指令表

typedef enum {
  /*----------------------------------------------------------------------
    name		args	description
  ------------------------------------------------------------------------*/
  OP_MOVE,/*	A B	R[A] := R[B]					*/
  OP_LOADI,/*	A sBx	R[A] := sBx					*/
  OP_LOADF,/*	A sBx	R[A] := (lua_Number)sBx				*/
  OP_LOADK,/*	A Bx	R[A] := K[Bx]					*/
  OP_LOADKX,/*	A	R[A] := K[extra arg]				*/
  OP_LOADFALSE,/*	A	R[A] := false					*/
  OP_LFALSESKIP,/*A	R[A] := false; pc++				*/
  OP_LOADTRUE,/*	A	R[A] := true					*/
  OP_LOADNIL,/*	A B	R[A], R[A+1], ..., R[A+B] := nil		*/
  OP_GETUPVAL,/*	A B	R[A] := UpValue[B]				*/
  OP_SETUPVAL,/*	A B	UpValue[B] := R[A]				*/
  
  OP_GETTABUP,/*	A B C	R[A] := UpValue[B][K[C]:string]			*/
  OP_GETTABLE,/*	A B C	R[A] := R[B][R[C]]				*/
  OP_GETI,/*	A B C	R[A] := R[B][C]					*/
  OP_GETFIELD,/*	A B C	R[A] := R[B][K[C]:string]			*/
  
  OP_SETTABUP,/*	A B C	UpValue[A][K[B]:string] := RK(C)		*/
  OP_SETTABLE,/*	A B C	R[A][R[B]] := RK(C)				*/
  OP_SETI,/*	A B C	R[A][B] := RK(C)				*/
  OP_SETFIELD,/*	A B C	R[A][K[B]:string] := RK(C)			*/
  
  OP_NEWTABLE,/*	A B C k	R[A] := {}					*/
  
  OP_SELF,/*	A B C	R[A+1] := R[B]; R[A] := R[B][RK(C):string]	*/
  
  OP_ADDI,/*	A B sC	R[A] := R[B] + sC				*/
  
  OP_ADDK,/*	A B C	R[A] := R[B] + K[C]:number			*/
  OP_SUBK,/*	A B C	R[A] := R[B] - K[C]:number			*/
  OP_MULK,/*	A B C	R[A] := R[B] * K[C]:number			*/
  OP_MODK,/*	A B C	R[A] := R[B] % K[C]:number			*/
  OP_POWK,/*	A B C	R[A] := R[B] ^ K[C]:number			*/
  OP_DIVK,/*	A B C	R[A] := R[B] / K[C]:number			*/
  OP_IDIVK,/*	A B C	R[A] := R[B] // K[C]:number			*/
  
  OP_BANDK,/*	A B C	R[A] := R[B] & K[C]:integer			*/
  OP_BORK,/*	A B C	R[A] := R[B] | K[C]:integer			*/
  OP_BXORK,/*	A B C	R[A] := R[B] ~ K[C]:integer			*/
  
  OP_SHRI,/*	A B sC	R[A] := R[B] >> sC				*/
  OP_SHLI,/*	A B sC	R[A] := sC << R[B]				*/
  
  OP_ADD,/*	A B C	R[A] := R[B] + R[C]				*/
  OP_SUB,/*	A B C	R[A] := R[B] - R[C]				*/
  OP_MUL,/*	A B C	R[A] := R[B] * R[C]				*/
  OP_MOD,/*	A B C	R[A] := R[B] % R[C]				*/
  OP_POW,/*	A B C	R[A] := R[B] ^ R[C]				*/
  OP_DIV,/*	A B C	R[A] := R[B] / R[C]				*/
  OP_IDIV,/*	A B C	R[A] := R[B] // R[C]				*/
  
  OP_BAND,/*	A B C	R[A] := R[B] & R[C]				*/
  OP_BOR,/*	A B C	R[A] := R[B] | R[C]				*/
  OP_BXOR,/*	A B C	R[A] := R[B] ~ R[C]				*/
  OP_SHL,/*	A B C	R[ALO_>)?] := R[B] << R[C]				*/
  OP_SHR,/*	A B C	R[A] := R[B] >> R[C]				*/
  
  OP_MMBIN,/*	A B C	call C metamethod over R[A] and R[B]		*/
  OP_MMBINI,/*	A sB C k	call C metamethod over R[A] and sB	*/
  OP_MMBINK,/*	A B C k		call C metamethod over R[A] and K[B]	*/
  
  OP_UNM,/*	A B	R[A] := -R[B]					*/
  OP_BNOT,/*	A B	R[A] := ~R[B]					*/
  OP_NOT,/*	A B	R[A] := not R[B]				*/
  OP_LEN,/*	A B	R[A] := #R[B] (length operator)			*/
  
  OP_CONCAT,/*	A B	R[A] := R[A].. ... ..R[A + B - 1]		*/
  
  OP_CLOSE,/*	A	close all upvalues >= R[A]			*/
  OP_TBC,/*	A	mark variable A "to be closed"			*/
  OP_JMP,/*	sJ	pc += sJ					*/
  OP_EQ,/*	A B k	if ((R[A] == R[B]) ~= k) then pc++		*/
  OP_LT,/*	A B k	if ((R[A] <  R[B]) ~= k) then pc++		*/
  OP_LE,/*	A B k	if ((R[A] <= R[B]) ~= k) then pc++		*/
  
  OP_EQK,/*	A B k	if ((R[A] == K[B]) ~= k) then pc++		*/
  OP_EQI,/*	A sB k	if ((R[A] == sB) ~= k) then pc++		*/
  OP_LTI,/*	A sB k	if ((R[A] < sB) ~= k) then pc++			*/
  OP_LEI,/*	A sB k	if ((R[A] <= sB) ~= k) then pc++		*/
  OP_GTI,/*	A sB k	if ((R[A] > sB) ~= k) then pc++			*/
  OP_GEI,/*	A sB k	if ((R[A] >= sB) ~= k) then pc++		*/
  
  OP_TEST,/*	A k	if (not R[A] == k) then pc++			*/
  OP_TESTSET,/*	A B k	if (not R[B] == k) then pc++ else R[A] := R[B]	*/
  
  OP_CALL,/*	A B C	R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */
  OP_TAILCALL,/*	A B C k	return R[A](R[A+1], ... ,R[A+B-1])		*/
  
  OP_RETURN,/*	A B C k	return R[A], ... ,R[A+B-2]	(see note)	*/
  OP_RETURN0,/*		return						*/
  OP_RETURN1,/*	A	return R[A]					*/
  
  OP_FORLOOP,/*	A Bx	update counters; if loop continues then pc-=Bx; */
  OP_FORPREP,/*	A Bx	<check values and prepare counters>;
                          if not to run then pc+=Bx+1;			*/
  
  OP_TFORPREP,/*	A Bx	create upvalue for R[A + 3]; pc+=Bx		*/
  OP_TFORCALL,/*	A C	R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]);	*/
  OP_TFORLOOP,/*	A Bx	if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx }	*/
  
  OP_SETLIST,/*	A B C k	R[A][C+i] := R[A+i], 1 <= i <= B		*/
  
  OP_CLOSURE,/*	A Bx	R[A] := closure(KPROTO[Bx])			*/
  
  OP_VARARG,/*	A C	R[A], R[A+1], ..., R[A+C-2] = vararg		*/
  
  OP_VARARGPREP,/*A	(adjust vararg parameters)			*/
  
  OP_EXTRAARG/*	Ax	extra (larger) argument for previous opcode	*/
  } OpCode;

上面的枚举是指令表也是opcode的取值,这段代码的注释是最有意义的它代表着指令的运行规则,这个注释需要结合虚拟机的代码才能看懂,暂时不做解释

3. 虚拟机

在这里插入图片描述

3.1 寄存器、栈基址、常量

#define RA(i)	(base+GETARG_A(i))
#define RB(i)	(base+GETARG_B(i))
#define vRB(i)	s2v(RB(i))
#define KB(i)	(k+GETARG_B(i))
#define RC(i)	(base+GETARG_C(i))
#define vRC(i)	s2v(RC(i))
#define KC(i)	(k+GETARG_C(i))
#define RKC(i)	((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i)))

void luaV_execute (lua_State *L, CallInfo *ci) {
  LClosure *cl;
  TValue *k;
  StkId base;
  const Instruction *pc;
  int trap;
...
 returning:  /* trap already set */
  cl = clLvalue(s2v(ci->func));
  k = cl->p->k;                   //常量集合
  pc = ci->u.l.savedpc;           //pc寄存器 (指令集)
...
  base = ci->func + 1;            //栈基址
...
  • 寄存器ra、rb、rc访问有RA、RB、RC的宏,这些寄存器本质就是lua栈加偏移。也就是说lua是通过这些寄存器去访问lua栈元素的
  • 常量集合有KB、KC这些去访问。

3.2 指令运行

指令执行的逻辑

for(){
  switch(opcode(pc)) {
    case opcode1:
      break;
    case opcode2:
      break;
    ...
  }
  pc++;
}

3.2.1 动态goto

lua5.4.3采用的是动态goto去实现for-switch的逻辑

#include <stdio.h>     
#include <stdlib.h>

int main(int argc,char *argv[])
{
    void *addr = &&end;   //获取end位置的地址
    goto *addr;           //goto解引用跳转

    printf("aaaaaaa\n");

end:
    printf("bbbbbbb\n");
    printf("aaaaaaa\n");
    printf("aaaaaaa\n");

    return 0;
}

这是c的语法可以使用双取值去得到标签的地址,可以用指针去存储这个地址。

如何使用goto实现for-switch的逻辑

#include <stdio.h>                         
#include <string.h>

enum {
    ADD,
    SUB,
    MUL,
    DIV,
    END 
};

int main(int argc,char *argv[])
{
    static const void *const disptab[] = { 
        &&L_ADD,
        &&L_SUB,
        &&L_MUL,
        &&L_DIV,
        &&L_END
    };  

    int code[] = {ADD,ADD,MUL,MUL,END};
    int i = 0;
    int sum = 0;
    goto *disptab[code[i++]];
L_ADD:
    sum += 2;
    goto *disptab[code[i++]];
L_SUB:
    sum -= 2;
    goto *disptab[code[i++]];
L_MUL:
    sum *= 2;
    goto *disptab[code[i++]];
L_DIV:
    sum /= 2;
    goto *disptab[code[i++]];
L_END:
    printf("sum = %d\n",sum);

    return 0;
}                        
  • 先在goto之前获取所有lable的地址,组成一个地址表
  • 定义一个code指令数组,指令操作的值和lable数组的值一致,剩下的直接goto code数组就行了

lua5.4.3 goto的使用

void luaV_execute (lua_State *L, CallInfo *ci) {
  LClosure *cl;
  TValue *k;
  StkId base;
  const Instruction *pc;
  int trap;
#if LUA_USE_JUMPTABLE
#include "ljumptab.h"
#endif
...

可以看到虚拟机开始前include了"ljumptab.h"头文件

#undef vmdispatch
#undef vmcase
#undef vmbreak
//动态goto
#define vmdispatch(x)     goto *disptab[x];

#define vmcase(l)     L_##l:

#define vmbreak		vmfetch(); vmdispatch(GET_OPCODE(i));

//获取标签的所有地址
static const void *const disptab[NUM_OPCODES] = {

#if 0
** you can update the following list with this command:
**
**  sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p'  lopcodes.h
**
#endif

&&L_OP_MOVE,
&&L_OP_LOADI,
&&L_OP_LOADF,
&&L_OP_LOADK,
&&L_OP_LOADKX,

这里获取了整个lable的地址保存在了disptab数组中。

#define vmfetch()	{ \
  if (l_unlikely(trap)) {  /* stack reallocation or hooks? */ \
    trap = luaG_traceexec(L, pc);  /* handle hooks */ \
    updatebase(ci);  /* correct stack */ \
  } \
  i = *(pc++); \
  ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \
}

for (;;) {                      //指令循环
    Instruction i;                //当前的指令
    StkId ra;                     //'A'寄存器  (RA、RB、RC 访问栈资源)
    vmfetch();                    //i ra 赋值
// low-level line tracing for debugging Lua
// printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
    lua_assert(base == ci->func + 1);
    lua_assert(base <= L->top && L->top < L->stack_last);
    /* invalidate top for instructions not expecting it */
    lua_assert(isIT(i) || (cast_void(L->top = base), 1));
    vmdispatch (GET_OPCODE(i)) {
      vmcase(OP_MOVE) {           //R[A] := R[B]	
        setobjs2s(L, ra, RB(i));
        vmbreak;
      }
      vmcase(OP_LOADI) {          //R[A] := sBx   (sBX 是有符号的立即数)
        lua_Integer b = GETARG_sBx(i);
        setivalue(s2v(ra), b);
        vmbreak;
      }
      vmcase(OP_LOADF) {          //R[A] := (lua_Number)sBx  (to double)
        int b = GETARG_sBx(i);
        setfltvalue(s2v(ra), cast_num(b));
        vmbreak;
      }
...
  • vmcase是一个拼接label的宏
  • vmfetch是指令循环,走向下一个指令
  • vmdispatch是goto语句跳转到指定的lable
  • 当一个指令执行完会跑vmbreak,vmbreak是vmfetch、vmdispatch两步,指向下一个指令并且跳转到指定的指令
  • 这段代码是完全等价for-switch的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值