mirror of
https://gitlab.com/embeddable-common-lisp/ecl.git
synced 2026-03-15 09:20:23 -07:00
The interpreter is rewritten using indirect threaded code
This commit is contained in:
parent
c02df756cc
commit
d9fc012432
3 changed files with 262 additions and 139 deletions
|
|
@ -247,6 +247,8 @@ ECL 0.9k:
|
|||
|
||||
- TYPEP now can be optimized if the type argument is a constant.
|
||||
|
||||
- ECL's bytecode interpreter now uses indirect threading.
|
||||
|
||||
* System design:
|
||||
|
||||
- We introduce a new kind of lisp objects, the stack frames. These are objects
|
||||
|
|
|
|||
|
|
@ -630,93 +630,100 @@ interpret_progv(cl_object bytecodes, cl_opcode *vector) {
|
|||
}
|
||||
|
||||
void *
|
||||
ecl_interpret(cl_object bytecodes, void *pc) {
|
||||
ecl_interpret(cl_object bytecodes, void *pc)
|
||||
{
|
||||
ECL_OFFSET_TABLE;
|
||||
cl_opcode *vector = pc;
|
||||
cl_object reg0 = VALUES(0), reg1;
|
||||
static int i = 0;
|
||||
i++;
|
||||
BEGIN:
|
||||
switch (GET_OPCODE(vector)) {
|
||||
BEGIN_SWITCH {
|
||||
CASE(OP_NOP); {
|
||||
VALUES(0) = reg0 = Cnil;
|
||||
NVALUES = 0;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_QUOTE
|
||||
Sets REG0 to an immediate value.
|
||||
*/
|
||||
case OP_QUOTE:
|
||||
CASE(OP_QUOTE); {
|
||||
reg0 = GET_DATA(vector, bytecodes);
|
||||
break;
|
||||
|
||||
NEXT;
|
||||
}
|
||||
/* OP_VAR n{arg}, var{symbol}
|
||||
Sets REG0 to the value of the n-th local.
|
||||
VAR is the name of the variable for readability purposes.
|
||||
*/
|
||||
case OP_VAR: {
|
||||
CASE(OP_VAR); {
|
||||
int lex_env_index = GET_OPARG(vector);
|
||||
reg0 = ecl_lex_env_get_var(lex_env_index);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_VARS var{symbol}
|
||||
Sets REG0 to the value of the symbol VAR.
|
||||
VAR should be either a special variable or a constant.
|
||||
*/
|
||||
case OP_VARS: {
|
||||
CASE(OP_VARS); {
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
reg0 = search_global(var_name);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_PUSH
|
||||
Pushes the object in VALUES(0).
|
||||
*/
|
||||
case OP_PUSH:
|
||||
CASE(OP_PUSH); {
|
||||
cl_stack_push(reg0);
|
||||
break;
|
||||
|
||||
NEXT;
|
||||
}
|
||||
/* OP_PUSHV n{arg}
|
||||
Pushes the value of the n-th local onto the stack.
|
||||
*/
|
||||
case OP_PUSHV: {
|
||||
CASE(OP_PUSHV); {
|
||||
int lex_env_index = GET_OPARG(vector);
|
||||
cl_stack_push(ecl_lex_env_get_var(lex_env_index));
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_PUSHVS var{symbol}
|
||||
Pushes the value of the symbol VAR onto the stack.
|
||||
VAR should be either a special variable or a constant.
|
||||
*/
|
||||
case OP_PUSHVS: {
|
||||
CASE(OP_PUSHVS); {
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
cl_stack_push(search_global(var_name));
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_PUSHQ value{object}
|
||||
Pushes "value" onto the stack.
|
||||
*/
|
||||
case OP_PUSHQ:
|
||||
CASE(OP_PUSHQ); {
|
||||
cl_stack_push(GET_DATA(vector, bytecodes));
|
||||
break;
|
||||
|
||||
NEXT;
|
||||
}
|
||||
/* OP_CALL n{arg}
|
||||
Calls the function in REG0 with N arguments which
|
||||
have been deposited in the stack. The output values
|
||||
are left in VALUES(...)
|
||||
*/
|
||||
case OP_CALL: {
|
||||
CASE(OP_CALL); {
|
||||
cl_fixnum n = GET_OPARG(vector);
|
||||
VALUES(0) = reg0 = interpret_funcall(n, reg0);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_CALLG n{arg}, name{arg}
|
||||
Calls the function NAME with N arguments which have been
|
||||
deposited in the stack. The output values are left in VALUES.
|
||||
*/
|
||||
case OP_CALLG: {
|
||||
CASE(OP_CALLG); {
|
||||
cl_fixnum n = GET_OPARG(vector);
|
||||
cl_object f = GET_DATA(vector, bytecodes);
|
||||
VALUES(0) = reg0 = interpret_funcall(n, f);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_FCALL n{arg}
|
||||
|
|
@ -724,24 +731,24 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
have been also deposited in the stack. The output values
|
||||
are left in VALUES(...)
|
||||
*/
|
||||
case OP_FCALL: {
|
||||
CASE(OP_FCALL); {
|
||||
cl_fixnum n = GET_OPARG(vector);
|
||||
cl_object fun = cl_env.stack_top[-n-1];
|
||||
VALUES(0) = reg0 = interpret_funcall(n, fun);
|
||||
cl_stack_pop();
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_MCALL
|
||||
Similar to FCALL, but gets the number of arguments from
|
||||
the stack (They all have been deposited by OP_PUSHVALUES)
|
||||
*/
|
||||
case OP_MCALL: {
|
||||
CASE(OP_MCALL); {
|
||||
cl_fixnum n = fix(cl_stack_pop());
|
||||
cl_object fun = cl_env.stack_top[-n-1];
|
||||
VALUES(0) = reg0 = interpret_funcall(n, fun);
|
||||
cl_stack_pop();
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_PCALL n{arg}
|
||||
|
|
@ -749,10 +756,10 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
have been deposited in the stack. The first output value
|
||||
is pushed on the stack.
|
||||
*/
|
||||
case OP_PCALL: {
|
||||
CASE(OP_PCALL); {
|
||||
cl_fixnum n = GET_OPARG(vector);
|
||||
cl_stack_push(interpret_funcall(n, reg0));
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_PCALLG n{arg}, name{arg}
|
||||
|
|
@ -760,11 +767,11 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
deposited in the stack. The first output value is pushed on
|
||||
the stack.
|
||||
*/
|
||||
case OP_PCALLG: {
|
||||
CASE(OP_PCALLG); {
|
||||
cl_fixnum n = GET_OPARG(vector);
|
||||
cl_object f = GET_DATA(vector, bytecodes);
|
||||
cl_stack_push(interpret_funcall(n, f));
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_PFCALL n{arg}
|
||||
|
|
@ -772,37 +779,38 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
have been also deposited in the stack. The first output value
|
||||
is pushed on the stack.
|
||||
*/
|
||||
case OP_PFCALL: {
|
||||
CASE(OP_PFCALL); {
|
||||
cl_fixnum n = GET_OPARG(vector);
|
||||
cl_object fun = cl_env.stack_top[-n-1];
|
||||
cl_object reg0 = interpret_funcall(n, fun);
|
||||
cl_env.stack_top[-1] = reg0;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_EXIT
|
||||
Marks the end of a high level construct (BLOCK, CATCH...)
|
||||
or a function.
|
||||
*/
|
||||
case OP_EXIT:
|
||||
CASE(OP_EXIT); {
|
||||
return (char *)vector;
|
||||
|
||||
case OP_FLET:
|
||||
}
|
||||
CASE(OP_FLET); {
|
||||
vector = interpret_flet(bytecodes, vector);
|
||||
break;
|
||||
case OP_LABELS:
|
||||
NEXT;
|
||||
}
|
||||
CASE(OP_LABELS); {
|
||||
vector = interpret_labels(bytecodes, vector);
|
||||
break;
|
||||
|
||||
NEXT;
|
||||
}
|
||||
/* OP_LFUNCTION n{arg}, function-name{symbol}
|
||||
Calls the local or global function with N arguments
|
||||
which have been deposited in the stack.
|
||||
*/
|
||||
case OP_LFUNCTION: {
|
||||
CASE(OP_LFUNCTION); {
|
||||
int lex_env_index = GET_OPARG(vector);
|
||||
cl_object fun_record = ecl_lex_env_get_record(lex_env_index);
|
||||
reg0 = CAR(fun_record);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_FUNCTION name{symbol}
|
||||
|
|
@ -810,19 +818,19 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
may be defined in the global environment or in the local
|
||||
environment. This last value takes precedence.
|
||||
*/
|
||||
case OP_FUNCTION:
|
||||
CASE(OP_FUNCTION);
|
||||
reg0 = ecl_fdefinition(GET_DATA(vector, bytecodes));
|
||||
break;
|
||||
NEXT;
|
||||
|
||||
/* OP_CLOSE name{symbol}
|
||||
Extracts the function associated to a symbol. The function
|
||||
may be defined in the global environment or in the local
|
||||
environment. This last value takes precedence.
|
||||
*/
|
||||
case OP_CLOSE: {
|
||||
CASE(OP_CLOSE); {
|
||||
cl_object function_object = GET_DATA(vector, bytecodes);
|
||||
reg0 = close_around(function_object, cl_env.lex_env);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_GO n{arg}
|
||||
OP_QUOTE tag-name{symbol}
|
||||
|
|
@ -830,33 +838,33 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
the lexical environment. TAG-NAME is kept for debugging
|
||||
purposes.
|
||||
*/
|
||||
case OP_GO: {
|
||||
CASE(OP_GO); {
|
||||
cl_object id = ecl_lex_env_get_tag(GET_OPARG(vector));
|
||||
cl_object tag_name = GET_DATA(vector, bytecodes);
|
||||
cl_go(id, tag_name);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_RETURN n{arg}
|
||||
Returns from the block whose record in the lexical environment
|
||||
occuppies the n-th position.
|
||||
*/
|
||||
case OP_RETURN: {
|
||||
CASE(OP_RETURN); {
|
||||
int lex_env_index = GET_OPARG(vector);
|
||||
cl_object block_record = ecl_lex_env_get_record(lex_env_index);
|
||||
cl_object id = CAR(block_record);
|
||||
cl_object block_name = CDR(block_record);
|
||||
cl_return_from(id, block_name);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_THROW
|
||||
Jumps to an enclosing CATCH form whose tag matches the one
|
||||
of the THROW. The tag is taken from the stack, while the
|
||||
output values are left in VALUES(...).
|
||||
*/
|
||||
case OP_THROW: {
|
||||
CASE(OP_THROW); {
|
||||
cl_object tag_name = cl_stack_pop();
|
||||
cl_throw(tag_name);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_JMP label{arg}
|
||||
OP_JNIL label{arg}
|
||||
|
|
@ -866,58 +874,59 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
Direct or conditional jumps. The conditional jumps are made
|
||||
comparing with the value of REG0.
|
||||
*/
|
||||
case OP_JMP: {
|
||||
CASE(OP_JMP); {
|
||||
cl_oparg jump = GET_OPARG(vector);
|
||||
vector += jump - OPARG_SIZE;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_JNIL: {
|
||||
CASE(OP_JNIL); {
|
||||
cl_oparg jump = GET_OPARG(vector);
|
||||
NVALUES = 1;
|
||||
if (Null(VALUES(0)))
|
||||
vector += jump - OPARG_SIZE;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_JT: {
|
||||
CASE(OP_JT); {
|
||||
cl_oparg jump = GET_OPARG(vector);
|
||||
NVALUES = 1;
|
||||
if (!Null(VALUES(0)))
|
||||
vector += jump - OPARG_SIZE;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_JEQL: {
|
||||
CASE(OP_JEQL); {
|
||||
cl_oparg value = GET_OPARG(vector);
|
||||
cl_oparg jump = GET_OPARG(vector);
|
||||
if (ecl_eql(reg0, bytecodes->bytecodes.data[value]))
|
||||
vector += jump - OPARG_SIZE;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_JNEQL: {
|
||||
CASE(OP_JNEQL); {
|
||||
cl_oparg value = GET_OPARG(vector);
|
||||
cl_oparg jump = GET_OPARG(vector);
|
||||
if (!ecl_eql(reg0, bytecodes->bytecodes.data[value]))
|
||||
vector += jump - OPARG_SIZE;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_NOT:
|
||||
CASE(OP_NOT); {
|
||||
reg0 = (reg0 == Cnil)? Ct : Cnil;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_UNBIND n{arg}
|
||||
Undo "n" local bindings.
|
||||
*/
|
||||
case OP_UNBIND: {
|
||||
CASE(OP_UNBIND); {
|
||||
cl_index n = GET_OPARG(vector);
|
||||
while (n--)
|
||||
cl_env.lex_env = CDR(cl_env.lex_env);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_UNBINDS n{arg}
|
||||
Undo "n" bindings of special variables.
|
||||
*/
|
||||
case OP_UNBINDS: {
|
||||
CASE(OP_UNBINDS); {
|
||||
cl_index n = GET_OPARG(vector);
|
||||
bds_unwind_n(n);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_BIND name{symbol}
|
||||
OP_PBIND name{symbol}
|
||||
|
|
@ -926,41 +935,41 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
Binds a lexical or special variable to the either the
|
||||
value of REG0 or the first value of the stack.
|
||||
*/
|
||||
case OP_BIND: {
|
||||
CASE(OP_BIND); {
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
bind_var(var_name, reg0);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_PBIND: {
|
||||
CASE(OP_PBIND); {
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
cl_object value = cl_stack_pop();
|
||||
bind_var(var_name, value);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_VBIND: {
|
||||
CASE(OP_VBIND); {
|
||||
cl_index n = GET_OPARG(vector);
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
cl_object value = (n < NVALUES) ? VALUES(n) : Cnil;
|
||||
bind_var(var_name, value);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_BINDS: {
|
||||
CASE(OP_BINDS); {
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
bds_bind(var_name, reg0);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_PBINDS: {
|
||||
CASE(OP_PBINDS); {
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
cl_object value = cl_stack_pop();
|
||||
bds_bind(var_name, value);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_VBINDS: {
|
||||
CASE(OP_VBINDS); {
|
||||
cl_index n = GET_OPARG(vector);
|
||||
cl_object var_name = GET_DATA(vector, bytecodes);
|
||||
cl_object value = (n < NVALUES) ? VALUES(n) : Cnil;
|
||||
bds_bind(var_name, value);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_SETQ n{arg}
|
||||
OP_PSETQ n{arg}
|
||||
|
|
@ -970,31 +979,31 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
to either the value in REG0 (OP_SETQ[S]) or to the
|
||||
first value on the stack (OP_PSETQ[S]).
|
||||
*/
|
||||
case OP_SETQ: {
|
||||
CASE(OP_SETQ); {
|
||||
int lex_env_index = GET_OPARG(vector);
|
||||
ecl_lex_env_set_var(lex_env_index, reg0);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_SETQS: {
|
||||
CASE(OP_SETQS); {
|
||||
cl_object var = GET_DATA(vector, bytecodes);
|
||||
/* INV: Not NIL, and of type t_symbol */
|
||||
if (var->symbol.stype & stp_constant)
|
||||
FEassignment_to_constant(var);
|
||||
ECL_SETQ(var, reg0);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_PSETQ: {
|
||||
CASE(OP_PSETQ); {
|
||||
int lex_env_index = GET_OPARG(vector);
|
||||
ecl_lex_env_set_var(lex_env_index, cl_stack_pop());
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_PSETQS: {
|
||||
CASE(OP_PSETQS); {
|
||||
cl_object var = GET_DATA(vector, bytecodes);
|
||||
/* INV: Not NIL, and of type t_symbol */
|
||||
if (var->symbol.stype & stp_constant)
|
||||
FEassignment_to_constant(var);
|
||||
ECL_SETQ(var, cl_stack_pop());
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
/* OP_BLOCK label{arg}
|
||||
|
|
@ -1005,10 +1014,11 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
Executes the enclosed code in a named block.
|
||||
LABEL points to the first instruction after OP_EXIT.
|
||||
*/
|
||||
case OP_BLOCK:
|
||||
CASE(OP_BLOCK); {
|
||||
reg0 = GET_DATA(vector, bytecodes);
|
||||
reg1 = new_frame_id();
|
||||
goto DO_BLOCK;
|
||||
}
|
||||
/* OP_CATCH label{arg}
|
||||
...
|
||||
OP_EXIT_FRAME
|
||||
|
|
@ -1017,9 +1027,10 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
Sets a catch point using the tag in VALUES(0). LABEL points to the
|
||||
first instruction after the end (OP_EXIT) of the block
|
||||
*/
|
||||
case OP_CATCH:
|
||||
CASE(OP_CATCH); {
|
||||
reg1 = reg0;
|
||||
goto DO_BLOCK;
|
||||
}
|
||||
/* OP_DO label
|
||||
... ; code executed within a NIL block
|
||||
OP_EXIT_FRAME
|
||||
|
|
@ -1027,9 +1038,10 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
|
||||
High level construct for the DO and BLOCK forms.
|
||||
*/
|
||||
case OP_DO:
|
||||
CASE(OP_DO); {
|
||||
reg0 = Cnil;
|
||||
reg1 = new_frame_id();
|
||||
}
|
||||
DO_BLOCK: {
|
||||
cl_opcode *exit;
|
||||
GET_LABEL(exit, vector);
|
||||
|
|
@ -1042,14 +1054,15 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
frs_pop();
|
||||
vector = (cl_opcode *)cl_stack_pop(); /* FIXME! */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_EXIT_FRAME:
|
||||
NEXT;
|
||||
}
|
||||
CASE(OP_EXIT_FRAME); {
|
||||
bds_unwind(cl_env.frs_top->frs_bds_top);
|
||||
cl_env.lex_env = cl_env.frs_top->frs_lex;
|
||||
frs_pop();
|
||||
cl_stack_pop();
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_TAGBODY n{arg}
|
||||
label1
|
||||
...
|
||||
|
|
@ -1062,7 +1075,7 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
|
||||
High level construct for the TAGBODY form.
|
||||
*/
|
||||
case OP_TAGBODY: {
|
||||
CASE(OP_TAGBODY); {
|
||||
cl_object id = new_frame_id();
|
||||
int n = GET_OPARG(vector);
|
||||
/* Here we save the location of the jump table */
|
||||
|
|
@ -1083,67 +1096,70 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
vector = table + *(cl_oparg *)table;
|
||||
cl_env.lex_env = cl_env.frs_top->frs_lex;
|
||||
}
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_EXIT_TAGBODY:
|
||||
CASE(OP_EXIT_TAGBODY); {
|
||||
cl_env.lex_env = CDR(cl_env.frs_top->frs_lex);
|
||||
frs_pop();
|
||||
cl_stack_pop();
|
||||
case OP_NIL:
|
||||
}
|
||||
CASE(OP_NIL); {
|
||||
reg0 = Cnil;
|
||||
break;
|
||||
case OP_PUSHNIL:
|
||||
NEXT;
|
||||
}
|
||||
CASE(OP_PUSHNIL); {
|
||||
cl_stack_push(Cnil);
|
||||
break;
|
||||
case OP_VALUEREG0:
|
||||
NEXT;
|
||||
}
|
||||
CASE(OP_VALUEREG0); {
|
||||
VALUES(0) = reg0;
|
||||
NVALUES = 1;
|
||||
break;
|
||||
case OP_NOP:
|
||||
VALUES(0) = reg0 = Cnil;
|
||||
NVALUES = 0;
|
||||
break;
|
||||
case OP_MSETQ:
|
||||
NEXT;
|
||||
}
|
||||
CASE(OP_MSETQ); {
|
||||
vector = interpret_msetq(bytecodes, vector);
|
||||
reg0 = VALUES(0);
|
||||
break;
|
||||
case OP_PROGV:
|
||||
NEXT;
|
||||
}
|
||||
CASE(OP_PROGV); {
|
||||
vector = interpret_progv(bytecodes, vector);
|
||||
reg0 = VALUES(0);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_PUSHVALUES
|
||||
Pushes the values output by the last form, plus the number
|
||||
of values.
|
||||
*/
|
||||
PUSH_VALUES:
|
||||
case OP_PUSHVALUES: {
|
||||
CASE(OP_PUSHVALUES); {
|
||||
cl_index i;
|
||||
for (i=0; i<NVALUES; i++)
|
||||
cl_stack_push(VALUES(i));
|
||||
cl_stack_push(MAKE_FIXNUM(NVALUES));
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_PUSHMOREVALUES
|
||||
Adds more values to the ones pushed by OP_PUSHVALUES.
|
||||
*/
|
||||
case OP_PUSHMOREVALUES: {
|
||||
CASE(OP_PUSHMOREVALUES); {
|
||||
cl_index i, n = fix(cl_stack_pop());
|
||||
for (i=0; i<NVALUES; i++)
|
||||
cl_stack_push(VALUES(i));
|
||||
cl_stack_push(MAKE_FIXNUM(n + NVALUES));
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_POP
|
||||
Pops a singe value pushed by a OP_PUSH* operator.
|
||||
*/
|
||||
case OP_POP:
|
||||
CASE(OP_POP); {
|
||||
VALUES(0) = reg0 = cl_stack_pop();
|
||||
NVALUES = 1;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_POPVALUES
|
||||
Pops all values pushed by a OP_PUSHVALUES operator.
|
||||
*/
|
||||
case OP_POPVALUES: {
|
||||
CASE(OP_POPVALUES); {
|
||||
int n = NVALUES = fix(cl_stack_pop());
|
||||
if (n == 0) {
|
||||
VALUES(0) = Cnil;
|
||||
|
|
@ -1151,24 +1167,24 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
VALUES(--n) = cl_stack_pop();
|
||||
} while (n);
|
||||
reg0 = VALUES(0);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_VALUES n{arg}
|
||||
Pop N values from the stack and store them in VALUES(...)
|
||||
*/
|
||||
case OP_VALUES: {
|
||||
CASE(OP_VALUES); {
|
||||
cl_fixnum n = GET_OPARG(vector);
|
||||
NVALUES = n;
|
||||
while (--n)
|
||||
VALUES(n) = cl_stack_pop();
|
||||
VALUES(0) = reg0 = cl_stack_pop();
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_NTHVAL
|
||||
Set VALUES(0) to the N-th value of the VALUES(...) list.
|
||||
The index N-th is extracted from the top of the stack.
|
||||
*/
|
||||
case OP_NTHVAL: {
|
||||
CASE(OP_NTHVAL); {
|
||||
cl_fixnum n = fix(cl_stack_pop());
|
||||
if (n < 0) {
|
||||
FEerror("Wrong index passed to NTH-VAL", 1, MAKE_FIXNUM(n));
|
||||
|
|
@ -1178,7 +1194,7 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
VALUES(0) = reg0 = VALUES(n);
|
||||
}
|
||||
NVALUES = 1;
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
/* OP_PROTECT label
|
||||
... ; code to be protected and whose value is output
|
||||
|
|
@ -1193,7 +1209,7 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
is always executed, even if a THROW, RETURN or GO happen within the
|
||||
first piece of code.
|
||||
*/
|
||||
case OP_PROTECT: {
|
||||
CASE(OP_PROTECT); {
|
||||
cl_opcode *exit;
|
||||
GET_LABEL(exit, vector);
|
||||
cl_stack_push((cl_object)exit);
|
||||
|
|
@ -1204,16 +1220,17 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
cl_stack_push(MAKE_FIXNUM(cl_env.nlj_fr - cl_env.frs_top));
|
||||
goto PUSH_VALUES;
|
||||
}
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_PROTECT_NORMAL:
|
||||
CASE(OP_PROTECT_NORMAL); {
|
||||
bds_unwind(cl_env.frs_top->frs_bds_top);
|
||||
cl_env.lex_env = cl_env.frs_top->frs_lex;
|
||||
frs_pop();
|
||||
cl_stack_pop();
|
||||
cl_stack_push(MAKE_FIXNUM(1));
|
||||
goto PUSH_VALUES;
|
||||
case OP_PROTECT_EXIT: {
|
||||
}
|
||||
CASE(OP_PROTECT_EXIT); {
|
||||
volatile cl_fixnum n = NVALUES = fix(cl_stack_pop());
|
||||
while (n--)
|
||||
VALUES(n) = cl_stack_pop();
|
||||
|
|
@ -1221,9 +1238,9 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
n = fix(cl_stack_pop());
|
||||
if (n <= 0)
|
||||
ecl_unwind(cl_env.frs_top + n);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_STEPIN: {
|
||||
CASE(OP_STEPIN); {
|
||||
cl_object form = GET_DATA(vector, bytecodes);
|
||||
cl_object a = SYM_VAL(@'si::*step-action*');
|
||||
cl_index n = cl_stack_push_values();
|
||||
|
|
@ -1244,9 +1261,9 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
* actually never happen. */
|
||||
}
|
||||
cl_stack_pop_values(n);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
case OP_STEPCALL: {
|
||||
CASE(OP_STEPCALL); {
|
||||
/* We are going to call a function. However, we would
|
||||
* like to step _in_ the function. STEPPER takes care of
|
||||
* that. */
|
||||
|
|
@ -1257,7 +1274,7 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
}
|
||||
reg0 = interpret_funcall(n, reg0);
|
||||
}
|
||||
case OP_STEPOUT: {
|
||||
CASE(OP_STEPOUT); {
|
||||
cl_object a = SYM_VAL(@'si::*step-action*');
|
||||
cl_index n = cl_stack_push_values();
|
||||
if (a == Ct) {
|
||||
|
|
@ -1274,14 +1291,9 @@ ecl_interpret(cl_object bytecodes, void *pc) {
|
|||
/* Not stepping, nothing to be done. */
|
||||
}
|
||||
cl_stack_pop_values(n);
|
||||
break;
|
||||
NEXT;
|
||||
}
|
||||
|
||||
default:
|
||||
FEerror("Internal error: Unknown code ~S",
|
||||
1, MAKE_FIXNUM(*(vector-1)));
|
||||
}
|
||||
goto BEGIN;
|
||||
}
|
||||
|
||||
@(defun si::interpreter_stack ()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
/* -*- mode: c; c-basic-offset: 8 -*- */
|
||||
/**********************************************************************
|
||||
***
|
||||
*** IMPORTANT: ANY CHANGE IN THIS FILE MUST BE MATCHED BY
|
||||
*** APPROPRIATE CHANGES IN THE INTERPRETER AND COMPILER
|
||||
*** IN PARTICULAR, IT MAY HURT THE THREADED INTERPRETER
|
||||
*** CODE.
|
||||
**********************************************************************/
|
||||
/*
|
||||
OP_BLOCK block-name{obj}
|
||||
...
|
||||
|
|
@ -203,3 +210,105 @@ typedef int16_t cl_oparg;
|
|||
#define GET_OPARG(v) (*((cl_oparg *)(v)++))
|
||||
#define GET_DATA(v,b) (b->bytecodes.data[GET_OPARG(v)])
|
||||
#define GET_LABEL(pc,v) {pc = (v) + READ_OPARG(v); v += OPARG_SIZE;}
|
||||
|
||||
/**********************************************************************
|
||||
* THREADED INTERPRETER CODE
|
||||
*
|
||||
* By using labels as values, we can build a variant of the
|
||||
* interpreter code that leads to better performance because (i) it
|
||||
* saves a range check on the opcode size and (ii) each opcode has a
|
||||
* dispatch instruction at the end, so that the processor may better
|
||||
* predict jumps.
|
||||
*/
|
||||
#if (defined(__GNUC__) && !defined(__STRICT_ANSI__))
|
||||
#define ECL_THREADED_INTERPRETER
|
||||
#endif
|
||||
|
||||
#ifdef ECL_THREADED_INTERPRETER
|
||||
#define BEGIN_SWITCH \
|
||||
NEXT;
|
||||
#define CASE(name) \
|
||||
LBL_##name:
|
||||
#define NEXT \
|
||||
goto *(&&LBL_OP_NOP + offsets[GET_OPCODE(vector)])
|
||||
#else
|
||||
#define BEGIN_SWITCH \
|
||||
switch (GET_OPCODE(vector))
|
||||
#define NEXT \
|
||||
goto BEGIN
|
||||
#define CASE(name) \
|
||||
case name:
|
||||
#endif
|
||||
|
||||
#if !defined(ECL_THREADED_INTERPRETER)
|
||||
#define ECL_OFFSET_TABLE
|
||||
#else
|
||||
#define ECL_OFFSET_TABLE \
|
||||
static const int offsets[] = {\
|
||||
&&LBL_OP_NOP - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_QUOTE - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_VAR - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_VARS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PUSH - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PUSHV - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PUSHVS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PUSHQ - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_CALLG - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_CALL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_FCALL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PCALLG - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PCALL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PFCALL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_MCALL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_EXIT - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_FLET - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_LABELS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_LFUNCTION - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_FUNCTION - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_CLOSE - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_GO - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_RETURN - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_THROW - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_JMP - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_JNIL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_JT - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_JEQL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_JNEQL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_UNBIND - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_UNBINDS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_BIND - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PBIND - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_VBIND - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_BINDS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PBINDS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_VBINDS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_SETQ - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_SETQS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PSETQ - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PSETQS - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_BLOCK - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_DO - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_CATCH - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_TAGBODY - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_EXIT_TAGBODY - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_EXIT_FRAME - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PROTECT - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PROTECT_NORMAL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PROTECT_EXIT - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_MSETQ - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PROGV - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PUSHVALUES - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_POP - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_POPVALUES - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PUSHMOREVALUES - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_VALUES - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_VALUEREG0 - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_NTHVAL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_NIL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_NOT - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_PUSHNIL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_STEPIN - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_STEPCALL - &&LBL_OP_NOP,\
|
||||
&&LBL_OP_STEPOUT - &&LBL_OP_NOP\
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue