/******************************************************************** ** Copyright (c) 2018-2020 Guan Wenliang ** This file is part of the Berry default interpreter. ** skiars@qq.com, https://github.com/Skiars/berry ** See Copyright Notice in the LICENSE file or at ** https://github.com/Skiars/berry/blob/master/LICENSE ********************************************************************/ #include "be_code.h" #include "be_decoder.h" #include "be_parser.h" #include "be_lexer.h" #include "be_vector.h" #include "be_list.h" #include "be_var.h" #include "be_exec.h" #include "be_vm.h" #define NOT_MASK (1 << 0) #define NOT_EXPR (1 << 1) #define FUNC_RET_FLAG (1 << 0) #define isset(v, mask) (((v) & (mask)) != 0) #define min(a, b) ((a) < (b) ? (a) : (b)) #define notexpr(e) isset((e)->not, NOT_EXPR) #define notmask(e) isset((e)->not, NOT_MASK) #define exp2anyreg(f, e) exp2reg(f, e, -1) /* -1 means allocate a new register if needed */ #define var2anyreg(f, e) var2reg(f, e, -1) /* -1 means allocate a new register if needed */ #define hasjump(e) ((e)->t != (e)->f || notexpr(e)) #define code_bool(f, r, b, j) codeABC(f, OP_LDBOOL, r, b, j) #define code_call(f, a, b) codeABC(f, OP_CALL, a, b, 0) #define code_getmbr(f, a, b, c) codeABC(f, OP_GETMBR, a, b, c) #define jumpboolop(e, b) ((b) != notmask(e) ? OP_JMPT : OP_JMPF) #if BE_USE_SCRIPT_COMPILER static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst); #if BE_DEBUG_RUNTIME_INFO static void codelineinfo(bfuncinfo *finfo) { bvector *vec = &finfo->linevec; int line = finfo->lexer->lastline; blineinfo *li = be_vector_end(vec); if (be_vector_isempty(vec) || li->linenumber != line) { be_vector_push(finfo->lexer->vm, vec, NULL); li = be_vector_end(vec); li->endpc = finfo->pc; li->linenumber = line; finfo->proto->lineinfo = be_vector_data(vec); finfo->proto->nlineinfo = be_vector_capacity(vec); } else { li->endpc = finfo->pc; } } #else #define codelineinfo(finfo) #endif /* Add new instruction in the code vector */ static int codeinst(bfuncinfo *finfo, binstruction ins) { /* put new instruction in code array */ be_vector_push_c(finfo->lexer->vm, &finfo->code, &ins); finfo->proto->code = be_vector_data(&finfo->code); finfo->proto->codesize = be_vector_capacity(&finfo->code); codelineinfo(finfo); return finfo->pc++; } static int codeABC(bfuncinfo *finfo, bopcode op, int a, int b, int c) { return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_RKB(b) | ISET_RKC(c)); } static int codeABx(bfuncinfo *finfo, bopcode op, int a, int bx) { return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_Bx(bx)); } /* Move value from register b to register a */ /* Check the previous instruction to compact both instruction as one if possible */ /* If b is a constant, add LDCONST or add MOVE otherwise */ static void code_move(bfuncinfo *finfo, int a, int b) { if (finfo->pc) { /* If not the first instruction of the function */ binstruction *i = be_vector_end(&finfo->code); /* get the last instruction */ bopcode op = IGET_OP(*i); if (op <= OP_LDNIL) { /* binop or unop */ /* remove redundant MOVE instruction */ int x = IGET_RA(*i), y = IGET_RKB(*i), z = IGET_RKC(*i); if (b == x && (a == y || (op < OP_NEG && a == z))) { *i = (*i & ~IRA_MASK) | ISET_RA(a); return; } } } if (isK(b)) { codeABx(finfo, OP_LDCONST, a, b & 0xFF); } else { codeABC(finfo, OP_MOVE, a, b, 0); } } /* Free register at top (checks that it´s a register) */ /* Warning: the register must be at top of stack */ static void free_expreg(bfuncinfo *finfo, bexpdesc *e) { /* release temporary register */ if (e && e->type == ETREG) { be_code_freeregs(finfo, 1); } } /* Privat. Allocate `count` new registers on the stack and uptade proto´s max nstack accordingly */ /* Note: deallocate is simpler and handled by a macro */ static void allocstack(bfuncinfo *finfo, int count) { int nstack = finfo->freereg + count; if (nstack > finfo->proto->nstack) { if (nstack >= 255) { be_lexerror(finfo->lexer, "register overflow (more than 255)"); } finfo->proto->nstack = (bbyte)nstack; } } /* Allocate `count` registers at top of stack, update stack accordingly */ int be_code_allocregs(bfuncinfo *finfo, int count) { int base = finfo->freereg; allocstack(finfo, count); finfo->freereg += (char)count; return base; } static void setjump(bfuncinfo *finfo, int pc, int dst) { binstruction *p = be_vector_at(&finfo->code, pc); int offset = dst - (pc + 1); /* instruction edit jump destination */ *p = (*p & ~IBx_MASK) | ISET_sBx(offset); } static int isjumpbool(bfuncinfo *finfo, int pc) { binstruction *p = be_vector_at(&finfo->code, pc); bopcode op = IGET_OP(*p); if (op == OP_JMPT || op == OP_JMPF) { return 1; } return 0; } static int get_jump(bfuncinfo *finfo, int pc) { binstruction *i = be_vector_at(&finfo->code, pc); int offset = IGET_sBx(*i); return offset == NO_JUMP ? NO_JUMP : pc + 1 + offset; } static void patchlistaux(bfuncinfo *finfo, int list, int vtarget, int dtarget) { while (list != NO_JUMP) { int next = get_jump(finfo, list); if (isjumpbool(finfo, list)) { setjump(finfo, list, dtarget); /* jump to default target */ } else { setjump(finfo, list, vtarget); } list = next; } } static int appendjump(bfuncinfo *finfo, bopcode op, bexpdesc *e) { int reg = e ? var2anyreg(finfo, e) : 0; if (isK(reg)) { reg = be_code_allocregs(finfo, 1); code_move(finfo, reg, e->v.idx); e->v.idx = reg; e->type = ETREG; } return codeABx(finfo, op, reg, NO_JUMP + IsBx_MAX); } int be_code_jump(bfuncinfo *finfo) { return appendjump(finfo, OP_JMP, NULL); } void be_code_jumpto(bfuncinfo *finfo, int dst) { be_code_patchlist(finfo, be_code_jump(finfo), dst); } void be_code_jumpbool(bfuncinfo *finfo, bexpdesc *e, int jture) { int pc = appendjump(finfo, jumpboolop(e, jture), e); be_code_conjump(finfo, jture ? &e->t : &e->f, pc); be_code_patchjump(finfo, jture ? e->f : e->t); free_expreg(finfo, e); jture ? (e->f = NO_JUMP) : (e->t = NO_JUMP); e->not = 0; } /* connect jump */ void be_code_conjump(bfuncinfo *finfo, int *list, int jmp) { if (jmp == NO_JUMP) { return; } if (*list == NO_JUMP) { *list = jmp; } else { int next, l = *list; while ((next = (get_jump(finfo, l))) != NO_JUMP) { l = next; } setjump(finfo, l, jmp); } } void be_code_patchlist(bfuncinfo *finfo, int list, int dst) { if (dst == finfo->pc) { be_code_patchjump(finfo, list); } else { patchlistaux(finfo, list, dst, dst); } } void be_code_patchjump(bfuncinfo *finfo, int jmp) { patchlistaux(finfo, jmp, finfo->pc, finfo->pc); } /* Allocate new constant for value k */ /* If k is NULL then push `nil` value */ static int newconst(bfuncinfo *finfo, bvalue *k) { int idx = be_vector_count(&finfo->kvec); be_vector_push_c(finfo->lexer->vm, &finfo->kvec, k); finfo->proto->ktab = be_vector_data(&finfo->kvec); finfo->proto->nconst = be_vector_capacity(&finfo->kvec); if (k == NULL) { var_setnil(&finfo->proto->ktab[idx]); } return idx; } /* Find constant by value and return constant number, or -1 if constant does not exist */ /* The search is linear and limited to BE_CONST_SEARCH_SIZE elements for performance reasons */ static int findconst(bfuncinfo *finfo, bexpdesc *e) { int i, count = be_vector_count(&finfo->kvec); /* if the constant table is too large, the lookup * operation will become very time consuming. * so only search the constant table for the * previous value. **/ count = count < BE_CONST_SEARCH_SIZE ? count : BE_CONST_SEARCH_SIZE; for (i = 0; i < count; ++i) { bvalue *k = be_vector_at(&finfo->kvec, i); switch (e->type) { case ETINT: if (var_isint(k) && k->v.i == e->v.i) { return i; } break; case ETREAL: if (var_isreal(k) && k->v.r == e->v.r) { return i; } break; case ETSTRING: if (var_isstr(k) && be_eqstr(k->v.p, e->v.s)) { return i; } break; default: break; } } return -1; } /* convert expdesc to constant and return kreg index (either constant kindex or register number) */ static int exp2const(bfuncinfo *finfo, bexpdesc *e) { int idx = findconst(finfo, e); /* does the constant already exist? */ if (idx == -1) { /* if not add it */ bvalue k; switch (e->type) { case ETINT: k.type = BE_INT; k.v.i = e->v.i; break; case ETREAL: k.type = BE_REAL; k.v.r = e->v.r; break; case ETSTRING: k.type = BE_STRING; k.v.s = e->v.s; break; default: /* all other values are filled later */ break; } idx = newconst(finfo, &k); /* create new constant */ } if (idx < 256) { /* if constant number fits in KB or KC */ e->type = ETCONST; /* new type is constant by index */ e->v.idx = setK(idx); } else { /* index value is too large */ e->type = ETREG; /* does not fit in compact mode, allocate an explicit register and emit LDCONTS */ e->v.idx = be_code_allocregs(finfo, 1); codeABx(finfo, OP_LDCONST, e->v.idx, idx); } return e->v.idx; } static void free_suffix(bfuncinfo *finfo, bexpdesc *e) { int idx = e->v.ss.idx; int nlocal = be_list_count(finfo->local); /* release suffix register */ if (!isK(idx) && idx >= nlocal) { be_code_freeregs(finfo, 1); } /* release object register */ if (e->v.ss.tt == ETREG && (int)e->v.ss.obj >= nlocal && (e->v.ss.obj + 1 >= finfo->freereg)) { be_code_freeregs(finfo, 1); } } static int suffix_destreg(bfuncinfo *finfo, bexpdesc *e1, int dst, bbool no_reg_reuse) { int cand_dst = dst; /* candidate for new dst */ int nlocal = be_list_count(finfo->local); int reg1 = (e1->v.ss.tt == ETREG) ? e1->v.ss.obj : -1; /* check if obj is ETREG or -1 */ int reg2 = (!isK(e1->v.ss.idx) && e1->v.ss.idx >= nlocal) ? e1->v.ss.idx : -1; /* check if idx is ETREG or -1 */ if (no_reg_reuse) { /* if no_reg_reuse flag, then don't reuse any register, this is useful for compound assignments */ reg1 = reg2 = -1; } if (reg1 >= 0 && reg2 >= 0) { /* both are ETREG, we keep the lowest and discard the other */ if (reg1 != reg2) { cand_dst = min(reg1, reg2); be_code_freeregs(finfo, 1); /* and free the other one */ } else { cand_dst = reg1; /* both ETREG are equal, we return its value */ } } else if (reg1 >= 0) { cand_dst = reg1; } else if (reg2 >= 0) { cand_dst = reg2; } else { // dst unchanged } if (dst >= finfo->freereg) { dst = cand_dst; /* if dst was allocating a new register, use the more precise candidate */ } return dst; } static int code_suffix(bfuncinfo *finfo, bopcode op, bexpdesc *e, int dst, bbool no_reg_reuse) { dst = suffix_destreg(finfo, e, dst, no_reg_reuse); if (dst > finfo->freereg) { dst = finfo->freereg; } codeABC(finfo, op, dst, e->v.ss.obj, e->v.ss.idx); return dst; } /* idx: the proto index in proto_table * dst: the destination register **/ static void code_closure(bfuncinfo *finfo, int idx, int dst) { codeABx(finfo, OP_CLOSURE, dst, idx); /* load closure to register */ } /* Given an integer, check if we should create a constant */ /* True for values 0..3 and if there is room for kindex */ /* This optimization makes code more compact for commonly used ints */ static bbool constint(bfuncinfo *finfo, bint i) { /* cache common numbers */ if ((i < IsBx_MIN || i > IsBx_MAX) || (i >= 0 && i <= 3 && be_vector_count(&finfo->kvec) < 256)) { return btrue; } return bfalse; } /* Compute variable from an expdesc */ /* Return constant index, or existing register or fallback to dst */ /* At exit, If dst is `freereg`, the register is allocated */ static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst) { bbool no_reg_reuse = (dst >= 0); /* if dst reg is explicitly specified, do not optimize register allocation */ if (dst < 0) { /* if unspecified, allocate a new register if needed */ dst = finfo->freereg; } be_assert(e != NULL); switch (e->type) { case ETINT: if (constint(finfo, e->v.i)) { return exp2const(finfo, e); } else { codeABx(finfo, OP_LDINT, dst, var_toidx(e) + IsBx_MAX); } break; case ETBOOL: code_bool(finfo, dst, e->v.i != 0, 0); break; case ETNIL: codeABx(finfo, OP_LDNIL, dst, 0); break; case ETREAL: case ETSTRING: return exp2const(finfo, e); case ETPROTO: code_closure(finfo, e->v.idx, dst); break; case ETGLOBAL: codeABx(finfo, OP_GETGBL, dst, e->v.idx); break; case ETNGLOBAL: codeABC(finfo, OP_GETNGBL, dst, e->v.ss.idx, 0); break; case ETUPVAL: codeABx(finfo, OP_GETUPV, dst, e->v.idx); break; case ETMEMBER: dst = code_suffix(finfo, OP_GETMBR, e, dst, no_reg_reuse); break; case ETINDEX: dst = code_suffix(finfo, OP_GETIDX, e, dst, no_reg_reuse); break; case ETLOCAL: case ETREG: case ETCONST: return e->v.idx; default: return dst; /* error */ } if (dst == finfo->freereg) { /* use a new register */ be_code_allocregs(finfo, 1); } e->type = ETREG; e->v.idx = dst; return dst; } static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst) { int reg = var2reg(finfo, e, dst); if (hasjump(e)) { /* if conditional expression */ int pcf = NO_JUMP; /* position of an eventual LOAD false */ int pct = NO_JUMP; /* position of an eventual LOAD true */ int jpt = appendjump(finfo, jumpboolop(e, 1), e); reg = e->v.idx; be_code_conjump(finfo, &e->t, jpt); pcf = code_bool(finfo, reg, 0, 1); pct = code_bool(finfo, reg, 1, 0); patchlistaux(finfo, e->f, finfo->pc, pcf); patchlistaux(finfo, e->t, finfo->pc, pct); e->t = NO_JUMP; e->f = NO_JUMP; e->not = 0; } return reg; } /* Select dest registers from both expressions */ /* If one of them is already a register, keep it */ /* If e1 or e2 are registers, we keep the lowest and free the highest (that must be at top) */ /* If none is a register, allocate a new one */ /* Returns the destination register, guaranteed to be ETREG */ static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, int dst) { if (dst < 0) { dst = finfo->freereg; } int cand_dst = dst; int con1 = e1->type == ETREG, con2 = e2->type == ETREG; if (con1 && con2) { cand_dst = min(e1->v.idx, e2->v.idx); be_code_freeregs(finfo, 1); } else if (con1) { cand_dst = e1->v.idx; } else if (con2) { cand_dst = e2->v.idx; } else { if (dst >= finfo->freereg) { cand_dst = be_code_allocregs(finfo, 1); return cand_dst; } } if (dst >= finfo->freereg) { return cand_dst; } else { return dst; } } /* compute binary expression and update e1 as result */ /* On exit, e1 is guaranteed to be ETREG, which may have been allocated */ static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2, int dst) { int src1 = exp2reg(finfo, e1, dst); /* potentially force the target for src1 reg */ int src2 = exp2anyreg(finfo, e2); dst = codedestreg(finfo, e1, e2, dst); codeABC(finfo, op, dst, src1, src2); e1->type = ETREG; e1->v.idx = dst; /* update register as output */ } void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e) { switch (op) { case OptAnd: be_code_jumpbool(finfo, e, bfalse); break; case OptOr: be_code_jumpbool(finfo, e, btrue); break; default: exp2anyreg(finfo, e); break; } } /* Apply binary operator `op` to e1 and e2, result in e1 */ void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst) { switch (op) { case OptAnd: var2anyreg(finfo, e2); be_code_conjump(finfo, &e2->f, e1->f); *e1 = *e2; break; case OptOr: var2anyreg(finfo, e2); be_code_conjump(finfo, &e2->t, e1->t); *e1 = *e2; break; case OptAdd: case OptSub: case OptMul: case OptDiv: case OptMod: case OptLT: case OptLE: case OptEQ: case OptNE: case OptGT: case OptGE: case OptConnect: case OptBitAnd: case OptBitOr: case OptBitXor: case OptShiftL: case OptShiftR: binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2, dst); break; default: break; } } /* Apply unary operator and return register number */ /* If input is register, change in place or allocate new register */ static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e) { int src = exp2anyreg(finfo, e); int dst = e->type == ETREG ? src : be_code_allocregs(finfo, 1); if (!(op == OP_MOVE && src == dst)) { /* skip if MOVE from same src / dst */ codeABC(finfo, op, dst, src, 0); } e->type = ETREG; e->v.idx = dst; } /* Apply not to conditional expression */ /* If literal compute the value */ /* Or invert t/f subexpressions */ static void code_not(bfuncinfo *finfo, bexpdesc *e) { switch (e->type) { case ETINT: e->v.i = e->v.i == 0; break; case ETREAL: e->v.i = e->v.r == cast(breal, 0); break; case ETNIL: e->v.i = 1; break; case ETBOOL: e->v.i = !e->v.i; break; case ETSTRING: e->v.i = 0; break; default: { unaryexp(finfo, OP_MOVE, e); int temp = e->t; e->t = e->f; e->f = temp; e->not = NOT_EXPR | (e->not ^ NOT_MASK); return; } } e->type = ETBOOL; } /* Negative value of literal or emit NEG opcode */ static int code_neg(bfuncinfo *finfo, bexpdesc *e) { switch (e->type) { case ETINT: e->v.i = -e->v.i; break; case ETREAL: e->v.r = -e->v.r; break; case ETNIL: case ETBOOL: case ETSTRING: return 1; /* error */ default: unaryexp(finfo, OP_NEG, e); } return 0; } /* Bit flip of literal or emit FLIP opcode */ static int code_flip(bfuncinfo *finfo, bexpdesc *e) { switch (e->type) { case ETINT: e->v.i = ~e->v.i; break; case ETREAL: case ETNIL: case ETBOOL: case ETSTRING: return 2; /* error */ default: unaryexp(finfo, OP_FLIP, e); } return 0; } /* Apply unary operator: not, neg or bitflip */ int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e) { switch (op) { case OptNot: code_not(finfo, e); break; case OptFlip: /* do nothing */ return code_flip(finfo, e); case OptSub: return code_neg(finfo, e); default: break; } return 0; } static void setbgblvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src) { if (isK(src)) { /* move const to register */ code_move(finfo, finfo->freereg, src); src = finfo->freereg; } codeABC(finfo, op, src, e1->v.idx, 0); } static void setsupvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src) { if (isK(src)) { /* move const to register */ code_move(finfo, finfo->freereg, src); src = finfo->freereg; } codeABx(finfo, op, src, e1->v.idx); } static void setsfxvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src) { int obj = e1->v.ss.obj; free_suffix(finfo, e1); if (isK(obj)) { /* move const to register */ code_move(finfo, finfo->freereg, obj); obj = finfo->freereg; } codeABC(finfo, op, obj, e1->v.ss.idx, src); } /* Assign expr e2 to e1 */ /* e1 must be in a register and have a valid idx */ /* return 1 if assignment was possible, 0 if type is not compatible */ int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2) { int src = exp2reg(finfo, e2, e1->type == ETLOCAL ? e1->v.idx : -1); /* Convert e2 to kreg */ /* If e1 is a local variable, use the register */ if (e1->type != ETLOCAL || e1->v.idx != src) { free_expreg(finfo, e2); /* free source (checks only ETREG) */ /* TODO e2 is at top */ } switch (e1->type) { case ETLOCAL: /* It can't be ETREG. */ if (e1->v.idx != src) { code_move(finfo, e1->v.idx, src); /* do explicit move only if needed */ } break; case ETGLOBAL: /* store to grobal R(A) -> G(Bx) by global index */ setsupvar(finfo, OP_SETGBL, e1, src); break; case ETNGLOBAL: /* store to global R(A) -> G(Bx) by name */ setbgblvar(finfo, OP_SETNGBL, e1, src); break; case ETUPVAL: setsupvar(finfo, OP_SETUPV, e1, src); break; case ETMEMBER: /* store to member R(A).RK(B) <- RK(C) */ setsfxvar(finfo, OP_SETMBR, e1, src); break; case ETINDEX: /* store to member R(A)[RK(B)] <- RK(C) */ setsfxvar(finfo, OP_SETIDX, e1, src); break; default: return 1; } return 0; } /* Get the expdesc as a register */ /* if already in a register, use the existing register */ /* if local or const, allocate a new register and copy value */ int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e) { int dst = finfo->freereg; int src = exp2anyreg(finfo, e); /* get variable register index */ if (e->type != ETREG) { /* move local and const to new register */ code_move(finfo, dst, src); be_code_allocregs(finfo, 1); } else { dst = src; } return dst; } int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e) { int dst = finfo->freereg; be_assert(e->type == ETMEMBER); dst = code_suffix(finfo, OP_GETMET, e, dst, bfalse); /* method [object] args */ be_code_allocregs(finfo, dst == finfo->freereg ? 2 : 1); return dst; } /* Generate a CALL instruction at base register with argc consecutive values */ /* i.e. arg1 is base+1... */ /* Important: argc registers are freed upon call, which are supposed to be registers above base */ void be_code_call(bfuncinfo *finfo, int base, int argc) { codeABC(finfo, OP_CALL, base, argc, 0); be_code_freeregs(finfo, argc); } int be_code_proto(bfuncinfo *finfo, bproto *proto) { int idx = be_vector_count(&finfo->pvec); /* append proto to current function proto table */ be_vector_push_c(finfo->lexer->vm, &finfo->pvec, &proto); finfo->proto->ptab = be_vector_data(&finfo->pvec); finfo->proto->nproto = be_vector_capacity(&finfo->pvec); return idx; } void be_code_closure(bfuncinfo *finfo, bexpdesc *e, int idx) { int reg = (e->type == ETGLOBAL || e->type == ETNGLOBAL) ? finfo->freereg: e->v.idx; code_closure(finfo, idx, reg); if (e->type == ETGLOBAL) { /* store to global R(A) -> G(Bx) */ codeABx(finfo, OP_SETGBL, reg, e->v.idx); } else if (e->type == ETNGLOBAL) { /* store R(A) -> GLOBAL[RK(B)] */ codeABC(finfo, OP_SETNGBL, reg, e->v.idx, 0); } } void be_code_close(bfuncinfo *finfo, int isret) { bblockinfo *binfo = finfo->binfo; if (isret) { /* in 'return' */ while (binfo && !binfo->hasupval) { binfo = binfo->prev; } if (binfo) { codeABC(finfo, OP_CLOSE, 0, 0, 0); } } else if (binfo->prev) { /* leave block */ if (binfo->hasupval) { codeABC(finfo, OP_CLOSE, binfo->nactlocals, 0, 0); } } } static void leave_function(bfuncinfo *finfo) { int try_depth = 0; /* count of exception catch blocks */ bblockinfo *binfo = finfo->binfo; for (; binfo; binfo = binfo->prev) { if (binfo->type & BLOCK_EXCEPT) { ++try_depth; /* leave the exception catch block */ } } if (try_depth) { /* exception catch blocks that needs to leave */ be_code_exblk(finfo, try_depth); } } void be_code_ret(bfuncinfo *finfo, bexpdesc *e) { if (finfo->binfo->prev == NULL) { if (finfo->flags & FUNC_RET_FLAG) { return; } finfo->flags |= FUNC_RET_FLAG; } if (e) { int reg = exp2anyreg(finfo, e); be_code_close(finfo, 1); leave_function(finfo); codeABC(finfo, OP_RET, e->type != ETVOID, reg, 0); free_expreg(finfo, e); } else { be_code_close(finfo, 1); codeABC(finfo, OP_RET, 0, 0, 0); } } /* Package a suffix object from `c` with key `k` */ /* Both expdesc are materialized in kregs */ static void package_suffix(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k) { int key = exp2anyreg(finfo, k); c->v.ss.obj = exp2anyreg(finfo, c); c->v.ss.tt = c->type; c->v.ss.idx = key; } int be_code_nglobal(bfuncinfo *finfo, bexpdesc *k) { return exp2anyreg(finfo, k); } /* Package a MEMBER suffix object from `c` with key `k` */ void be_code_member(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k) { package_suffix(finfo, c, k); c->type = ETMEMBER; } /* Package a INDEX suffix object from `c` with key `k` */ void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k) { package_suffix(finfo, c, k); c->type = ETINDEX; } void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c) { int src; bvalue var; var_setclass(&var, c); /* new var of CLASS type */ src = newconst(finfo, &var); /* allocate a new constant and return kreg */ if (dst->type == ETLOCAL) { /* if target is a local variable, just assign */ codeABx(finfo, OP_LDCONST, dst->v.idx, src); } else if (dst->type == ETGLOBAL) { /* otherwise set as global with same name as class name */ codeABx(finfo, OP_LDCONST, finfo->freereg, src); codeABx(finfo, OP_SETGBL, finfo->freereg, dst->v.idx); } else if (dst->type == ETNGLOBAL) { codeABx(finfo, OP_LDCONST, finfo->freereg, src); codeABC(finfo, OP_SETNGBL, finfo->freereg, dst->v.idx, 0); } codeABx(finfo, OP_CLASS, 0, src); /* emit CLASS opcode to register class */ } void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s) { int self = exp2anyreg(finfo, c); int super = exp2anyreg(finfo, s); codeABC(finfo, OP_SETSUPER, self, super, 0); free_expreg(finfo, c); free_expreg(finfo, s); } /* Emit IMPORT opcode for import module */ /* `m` is module name, is copied to register if not already */ /* `v` is destination where the imported module is stored */ /* If destination is a local variable, it is the destination of the IMPORT opcode */ /* otherwise the value is copied to a temporary register and stored to the destination */ /* TODO is this optilization useful, isn´t it done anyways by be_code_move optim? */ void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v) { int dst, src = exp2anyreg(finfo, m); if (v->type == ETLOCAL) { dst = v->v.idx; codeABC(finfo, OP_IMPORT, dst, src, 0); } else { dst = be_code_allocregs(finfo, 1); codeABC(finfo, OP_IMPORT, dst, src, 0); m->type = ETREG; m->v.idx = dst; be_code_setvar(finfo, v, m); } } int be_code_exblk(bfuncinfo *finfo, int depth) { if (depth == 0) { return appendjump(finfo, OP_EXBLK, NULL); } codeABx(finfo, OP_EXBLK, 1, depth); return 0; } void be_code_catch(bfuncinfo *finfo, int base, int ecnt, int vcnt, int *jmp) { codeABC(finfo, OP_CATCH, base, ecnt, vcnt); if (jmp) { *jmp = NO_JUMP; /* reset jump point */ be_code_conjump(finfo, jmp, be_code_jump(finfo)); } } /* Emit RAISE opcode */ /* e1 is the exception code */ /* e2 is the exception description */ /* both are materialized to a temp register (if not null) */ void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2) { if (e1) { int src1 = exp2anyreg(finfo, e1); int src2 = e2 ? exp2anyreg(finfo, e2) : 0; codeABC(finfo, OP_RAISE, e2 != NULL, src1, src2); } else { codeABC(finfo, OP_RAISE, 2, 0, 0); /* rethrow the current exception with parameters already on top of stack */ } /* release the register occupied by the expression */ free_expreg(finfo, e1); free_expreg(finfo, e2); } #endif