10566 lines
299 KiB
C
10566 lines
299 KiB
C
/*
|
|
MzScheme
|
|
Copyright (c) 2004-2008 PLT Scheme Inc.
|
|
Copyright (c) 1995-2001 Matthew Flatt
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301 USA.
|
|
|
|
libscheme
|
|
Copyright (c) 1994 Brent Benson
|
|
All rights reserved.
|
|
*/
|
|
|
|
/* This file contains
|
|
|
|
* the main eval-apply loop, in scheme_do_eval()
|
|
|
|
* the main compile loop, int scheme_compile_expand_expr()
|
|
|
|
* compilation and bytecode [un]marshaling for
|
|
- applications
|
|
- sequences (along with code in syntax.c)
|
|
- branches (along with code in syntax.c)
|
|
- with-continuation-mark
|
|
[These are here instead of syntax.c because they are
|
|
tightly integrated into the evaluation loop.]
|
|
|
|
* C and Scheme stack management routines
|
|
|
|
Evaluation:
|
|
|
|
The bytecode evaluator uses the C stack for continuations, and a
|
|
separate Scheme stack for activation-frame variables and collecting
|
|
application arguments. Closures are flat, so mutable variables are
|
|
boxed. A third stack is used for continuation marks, only as
|
|
needed.
|
|
|
|
Tail calls are, for the most part, gotos within scheme_do_eval(). A
|
|
C function called by the main evaluation loop can perform a
|
|
trampoling tail call via scheme_tail_apply. The trampoline must
|
|
return to its caller without allocating any memory, because an
|
|
allocation optimization in the tail-call code assumes no GCs will
|
|
occur between the time that a tail call is issued and the time when
|
|
it's handled.
|
|
|
|
Multiple values are returned as a special SCHEME_MULTIPLE_VALUES
|
|
token that indicates actual values are stored in the current
|
|
thread's record.
|
|
|
|
The apply half of the eval-apply loop branches on all possible
|
|
application types. All primitive functions (including cons) are
|
|
implemented by C functions outside the loop. Continuation
|
|
applications are handled directly in scheme_do_eval(). That leaves
|
|
calls to closures, which are also performed within scheme_do_eval()
|
|
(so that most tail calls avoid the trampoline), and native code,
|
|
which is analogous to a primitive.
|
|
|
|
The eval half of the loop detects a limited set of core syntactic
|
|
forms, such as application and letrecs. Otherwise, it dispatches to
|
|
external functions to implement elaborate syntactic forms, such as
|
|
begin0 and case-lambda expressions.
|
|
|
|
When collecting the arguments for an application, scheme_do_eval()
|
|
avoids recursive C calls to evaluate arguments by recogzining
|
|
easily-evaluated expressions, such as constrants and variable
|
|
lookups. This can be viewed as a kind of half-way A-normalization.
|
|
|
|
Bytecodes are not linear. They're actually trees of expression
|
|
nodes.
|
|
|
|
Top-level variables (global or module) are referenced through the
|
|
Scheme stack, so that the variables can be "re-linked" each time a
|
|
module is instantiated. Syntax constants are similarly accessed
|
|
through the Scheme stack. The global variables and syntax objects
|
|
are sometimes called the "prefix", and scheme_push_prefix()
|
|
initializes the prefix portion of the stack.
|
|
|
|
Compilation:
|
|
|
|
Compilation works in four passes.
|
|
|
|
The first pass, called "compile", performs most of the work and
|
|
tracks variable usage (including whether a variable is mutated or
|
|
not).
|
|
|
|
The second pass, called "optimize", performs constant propagation,
|
|
constant folding, and function inlining; this pass mutates records
|
|
produced by the first pass.
|
|
|
|
The third pass, called "resolve", finishes compilation by computing
|
|
variable offsets and indirections (often mutating the records
|
|
produced by the first pass). It is also responsible for closure
|
|
conversion (i.e., converting closure content to arguments) and
|
|
lifting (of procedures that close over nothing or only globals).
|
|
Beware that the resulting bytecode object is a graph, not a tree,
|
|
due to sharing (potentially cyclic) of closures that are "empty"
|
|
but actually refer to other "empty" closures.
|
|
|
|
The fourth pass, "sfs", performs another liveness analysis on stack
|
|
slows and inserts operations to clear stack slots as necessary to
|
|
make execution safe for space. In particular, dead slots need to be
|
|
cleared before a non-tail call into arbitrary Scheme code.
|
|
|
|
Just-in-time compilation:
|
|
|
|
If the JIT is enabled, then `eval' processes a compiled expression
|
|
one more time (functionally): `lambda' and `case-lambda' forms are
|
|
converted to native-code generators, instead of bytecode variants.
|
|
|
|
*/
|
|
|
|
#include "schpriv.h"
|
|
#include "schrunst.h"
|
|
#include "schexpobs.h"
|
|
|
|
#ifdef USE_STACKAVAIL
|
|
#include <malloc.h>
|
|
#endif
|
|
#ifdef UNIX_FIND_STACK_BOUNDS
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#endif
|
|
#ifdef BEOS_FIND_STACK_BOUNDS
|
|
# include <be/kernel/OS.h>
|
|
#endif
|
|
#ifdef OSKIT_FIXED_STACK_BOUNDS
|
|
# include <oskit/machine/base_stack.h>
|
|
#endif
|
|
#include "schmach.h"
|
|
#ifdef MACOS_STACK_LIMIT
|
|
#include <Memory.h>
|
|
#endif
|
|
|
|
#define EMBEDDED_DEFINES_START_ANYWHERE 0
|
|
|
|
/* globals */
|
|
Scheme_Object *scheme_eval_waiting;
|
|
Scheme_Object *scheme_multiple_values;
|
|
int scheme_continuation_application_count;
|
|
|
|
volatile int scheme_fuel_counter;
|
|
|
|
int scheme_startup_use_jit = 1;
|
|
void scheme_set_startup_use_jit(int v) { scheme_startup_use_jit = v; }
|
|
|
|
static Scheme_Object *app_symbol;
|
|
static Scheme_Object *datum_symbol;
|
|
static Scheme_Object *top_symbol;
|
|
static Scheme_Object *top_level_symbol;
|
|
|
|
static Scheme_Object *app_expander;
|
|
static Scheme_Object *datum_expander;
|
|
static Scheme_Object *top_expander;
|
|
|
|
static Scheme_Object *stop_expander;
|
|
|
|
static Scheme_Object *quick_stx;
|
|
static int taking_shortcut;
|
|
|
|
Scheme_Object *scheme_stack_dump_key;
|
|
|
|
/* locals */
|
|
static Scheme_Object *eval(int argc, Scheme_Object *argv[]);
|
|
static Scheme_Object *compile(int argc, Scheme_Object *argv[]);
|
|
static Scheme_Object *compiled_p(int argc, Scheme_Object *argv[]);
|
|
static Scheme_Object *expand(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *local_expand(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *local_expand_expr(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *local_expand_catch_lifts(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *local_transformer_expand(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *local_transformer_expand_catch_lifts(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *local_eval(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *expand_once(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *expand_to_top_form(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *enable_break(int, Scheme_Object *[]);
|
|
static Scheme_Object *current_eval(int argc, Scheme_Object *[]);
|
|
static Scheme_Object *current_compile(int argc, Scheme_Object *[]);
|
|
|
|
static Scheme_Object *eval_stx(int argc, Scheme_Object *argv[]);
|
|
static Scheme_Object *compile_stx(int argc, Scheme_Object *argv[]);
|
|
static Scheme_Object *expand_stx(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *expand_stx_once(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *expand_stx_to_top_form(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *top_introduce_stx(int argc, Scheme_Object **argv);
|
|
|
|
static Scheme_Object *allow_set_undefined(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *compile_module_constants(int argc, Scheme_Object **argv);
|
|
static Scheme_Object *use_jit(int argc, Scheme_Object **argv);
|
|
|
|
static Scheme_Object *app_syntax(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Compile_Info *rec, int drec);
|
|
static Scheme_Object *app_expand(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec);
|
|
static Scheme_Object *datum_syntax(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Compile_Info *rec, int drec);
|
|
static Scheme_Object *datum_expand(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec);
|
|
static Scheme_Object *top_syntax(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Compile_Info *rec, int drec);
|
|
static Scheme_Object *top_expand(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec);
|
|
|
|
static Scheme_Object *write_application(Scheme_Object *obj);
|
|
static Scheme_Object *read_application(Scheme_Object *obj);
|
|
static Scheme_Object *write_sequence(Scheme_Object *obj);
|
|
static Scheme_Object *read_sequence(Scheme_Object *obj);
|
|
static Scheme_Object *read_sequence_save_first(Scheme_Object *obj);
|
|
static Scheme_Object *write_branch(Scheme_Object *obj);
|
|
static Scheme_Object *read_branch(Scheme_Object *obj);
|
|
static Scheme_Object *write_with_cont_mark(Scheme_Object *obj);
|
|
static Scheme_Object *read_with_cont_mark(Scheme_Object *obj);
|
|
static Scheme_Object *write_syntax(Scheme_Object *obj);
|
|
static Scheme_Object *read_syntax(Scheme_Object *obj);
|
|
static Scheme_Object *write_quote_syntax(Scheme_Object *obj);
|
|
static Scheme_Object *read_quote_syntax(Scheme_Object *obj);
|
|
|
|
static Scheme_Object *define_values_symbol, *letrec_values_symbol, *lambda_symbol;
|
|
static Scheme_Object *unknown_symbol, *void_link_symbol, *quote_symbol;
|
|
static Scheme_Object *letrec_syntaxes_symbol, *begin_symbol;
|
|
static Scheme_Object *let_values_symbol;
|
|
|
|
static Scheme_Object *internal_define_symbol;
|
|
static Scheme_Object *module_symbol;
|
|
static Scheme_Object *module_begin_symbol;
|
|
static Scheme_Object *expression_symbol;
|
|
|
|
static Scheme_Object *protected_symbol;
|
|
|
|
static Scheme_Object *zero_rands_ptr; /* &zero_rands_ptr is dummy rands pointer */
|
|
|
|
int scheme_overflow_count;
|
|
|
|
static Scheme_Object *scheme_compile_expand_expr(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Expand_Info *rec, int drec,
|
|
int app_position);
|
|
|
|
static Scheme_Object *_eval_compiled_multi_with_prompt(Scheme_Object *obj, Scheme_Env *env);
|
|
|
|
#define cons(x,y) scheme_make_pair(x,y)
|
|
|
|
typedef void (*DW_PrePost_Proc)(void *);
|
|
|
|
#ifdef USE_STACK_BOUNDARY_VAR
|
|
unsigned long scheme_stack_boundary;
|
|
#endif
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
static void register_traversers(void);
|
|
#endif
|
|
|
|
/* Lookahead types for evaluating application arguments. */
|
|
/* 4 cases + else => magic number for some compilers doing a switch? */
|
|
enum {
|
|
SCHEME_EVAL_CONSTANT = 0,
|
|
SCHEME_EVAL_GLOBAL,
|
|
SCHEME_EVAL_LOCAL,
|
|
SCHEME_EVAL_LOCAL_UNBOX,
|
|
SCHEME_EVAL_GENERAL
|
|
};
|
|
|
|
#define icons scheme_make_pair
|
|
|
|
/*========================================================================*/
|
|
/* initialization */
|
|
/*========================================================================*/
|
|
|
|
void
|
|
scheme_init_eval (Scheme_Env *env)
|
|
{
|
|
#ifdef MZ_PRECISE_GC
|
|
register_traversers();
|
|
#endif
|
|
|
|
#ifdef MZ_EVAL_WAITING_CONSTANT
|
|
scheme_eval_waiting = MZ_EVAL_WAITING_CONSTANT;
|
|
#else
|
|
REGISTER_SO(scheme_eval_waiting);
|
|
scheme_eval_waiting = scheme_alloc_eternal_object();
|
|
scheme_eval_waiting->type = scheme_eval_waiting_type;
|
|
#endif
|
|
|
|
#ifdef MZ_EVAL_WAITING_CONSTANT
|
|
scheme_multiple_values = MZ_MULTIPLE_VALUES_CONSTANT;
|
|
#else
|
|
REGISTER_SO(scheme_multiple_values);
|
|
scheme_multiple_values = scheme_alloc_eternal_object();
|
|
scheme_multiple_values->type = scheme_multiple_values_type;
|
|
#endif
|
|
|
|
REGISTER_SO(define_values_symbol);
|
|
REGISTER_SO(letrec_values_symbol);
|
|
REGISTER_SO(lambda_symbol);
|
|
REGISTER_SO(unknown_symbol);
|
|
REGISTER_SO(void_link_symbol);
|
|
REGISTER_SO(quote_symbol);
|
|
REGISTER_SO(letrec_syntaxes_symbol);
|
|
REGISTER_SO(begin_symbol);
|
|
REGISTER_SO(let_values_symbol);
|
|
|
|
define_values_symbol = scheme_intern_symbol("define-values");
|
|
letrec_values_symbol = scheme_intern_symbol("letrec-values");
|
|
let_values_symbol = scheme_intern_symbol("let-values");
|
|
lambda_symbol = scheme_intern_symbol("lambda");
|
|
unknown_symbol = scheme_intern_symbol("unknown");
|
|
void_link_symbol = scheme_intern_symbol("-v");
|
|
quote_symbol = scheme_intern_symbol("quote");
|
|
letrec_syntaxes_symbol = scheme_intern_symbol("letrec-syntaxes+values");
|
|
begin_symbol = scheme_intern_symbol("begin");
|
|
|
|
REGISTER_SO(module_symbol);
|
|
REGISTER_SO(module_begin_symbol);
|
|
REGISTER_SO(internal_define_symbol);
|
|
REGISTER_SO(expression_symbol);
|
|
REGISTER_SO(top_level_symbol);
|
|
|
|
module_symbol = scheme_intern_symbol("module");
|
|
module_begin_symbol = scheme_intern_symbol("module-begin");
|
|
internal_define_symbol = scheme_intern_symbol("internal-define");
|
|
expression_symbol = scheme_intern_symbol("expression");
|
|
top_level_symbol = scheme_intern_symbol("top-level");
|
|
|
|
REGISTER_SO(protected_symbol);
|
|
protected_symbol = scheme_intern_symbol("protected");
|
|
|
|
REGISTER_SO(scheme_stack_dump_key);
|
|
scheme_stack_dump_key = scheme_make_symbol("stk"); /* uninterned! */
|
|
|
|
scheme_install_type_writer(scheme_application_type, write_application);
|
|
scheme_install_type_reader(scheme_application_type, read_application);
|
|
scheme_install_type_writer(scheme_application2_type, write_application);
|
|
scheme_install_type_reader(scheme_application2_type, read_application);
|
|
scheme_install_type_writer(scheme_application3_type, write_application);
|
|
scheme_install_type_reader(scheme_application3_type, read_application);
|
|
scheme_install_type_writer(scheme_sequence_type, write_sequence);
|
|
scheme_install_type_reader(scheme_sequence_type, read_sequence);
|
|
scheme_install_type_writer(scheme_branch_type, write_branch);
|
|
scheme_install_type_reader(scheme_branch_type, read_branch);
|
|
scheme_install_type_writer(scheme_with_cont_mark_type, write_with_cont_mark);
|
|
scheme_install_type_reader(scheme_with_cont_mark_type, read_with_cont_mark);
|
|
scheme_install_type_writer(scheme_quote_syntax_type, write_quote_syntax);
|
|
scheme_install_type_reader(scheme_quote_syntax_type, read_quote_syntax);
|
|
scheme_install_type_writer(scheme_syntax_type, write_syntax);
|
|
scheme_install_type_reader(scheme_syntax_type, read_syntax);
|
|
|
|
scheme_install_type_writer(scheme_begin0_sequence_type, write_sequence);
|
|
scheme_install_type_reader(scheme_begin0_sequence_type, read_sequence_save_first);
|
|
|
|
scheme_add_global_constant("eval",
|
|
scheme_make_prim_w_arity2(eval,
|
|
"eval",
|
|
1, 2,
|
|
0, -1),
|
|
env);
|
|
scheme_add_global_constant("eval-syntax",
|
|
scheme_make_prim_w_arity2(eval_stx,
|
|
"eval-syntax",
|
|
1, 2,
|
|
0, -1),
|
|
env);
|
|
scheme_add_global_constant("compile",
|
|
scheme_make_prim_w_arity(compile,
|
|
"compile",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("compile-syntax",
|
|
scheme_make_prim_w_arity(compile_stx,
|
|
"compile-syntax",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("compiled-expression?",
|
|
scheme_make_prim_w_arity(compiled_p,
|
|
"compiled-expression?",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("expand",
|
|
scheme_make_prim_w_arity(expand,
|
|
"expand",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("expand-syntax",
|
|
scheme_make_prim_w_arity(expand_stx,
|
|
"expand-syntax",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("local-expand",
|
|
scheme_make_prim_w_arity(local_expand,
|
|
"local-expand",
|
|
3, 4),
|
|
env);
|
|
scheme_add_global_constant("syntax-local-expand-expression",
|
|
scheme_make_prim_w_arity(local_expand_expr,
|
|
"syntax-local-expand-expression",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("syntax-local-bind-syntaxes",
|
|
scheme_make_prim_w_arity(local_eval,
|
|
"syntax-local-bind-syntaxes",
|
|
3, 3),
|
|
env);
|
|
scheme_add_global_constant("local-expand/capture-lifts",
|
|
scheme_make_prim_w_arity(local_expand_catch_lifts,
|
|
"local-expand/capture-lifts",
|
|
3, 5),
|
|
env);
|
|
scheme_add_global_constant("local-transformer-expand",
|
|
scheme_make_prim_w_arity(local_transformer_expand,
|
|
"local-transformer-expand",
|
|
3, 4),
|
|
env);
|
|
scheme_add_global_constant("local-transformer-expand/capture-lifts",
|
|
scheme_make_prim_w_arity(local_transformer_expand_catch_lifts,
|
|
"local-transformer-expand/capture-lifts",
|
|
3, 5),
|
|
env);
|
|
scheme_add_global_constant("expand-once",
|
|
scheme_make_prim_w_arity(expand_once,
|
|
"expand-once",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("expand-syntax-once",
|
|
scheme_make_prim_w_arity(expand_stx_once,
|
|
"expand-syntax-once",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("expand-to-top-form",
|
|
scheme_make_prim_w_arity(expand_to_top_form,
|
|
"expand-to-top-form",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("expand-syntax-to-top-form",
|
|
scheme_make_prim_w_arity(expand_stx_to_top_form,
|
|
"expand-syntax-to-top-form",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("namespace-syntax-introduce",
|
|
scheme_make_prim_w_arity(top_introduce_stx,
|
|
"namespace-syntax-introduce",
|
|
1, 1),
|
|
env);
|
|
scheme_add_global_constant("break-enabled",
|
|
scheme_make_prim_w_arity(enable_break,
|
|
"break-enabled",
|
|
0, 1),
|
|
env);
|
|
scheme_add_global_constant("current-eval",
|
|
scheme_register_parameter(current_eval,
|
|
"current-eval",
|
|
MZCONFIG_EVAL_HANDLER),
|
|
env);
|
|
scheme_add_global_constant("current-compile",
|
|
scheme_register_parameter(current_compile,
|
|
"current-compile",
|
|
MZCONFIG_COMPILE_HANDLER),
|
|
env);
|
|
|
|
scheme_add_global_constant("compile-allow-set!-undefined",
|
|
scheme_register_parameter(allow_set_undefined,
|
|
"compile-allow-set!-undefined",
|
|
MZCONFIG_ALLOW_SET_UNDEFINED),
|
|
env);
|
|
scheme_add_global_constant("compile-enforce-module-constants",
|
|
scheme_register_parameter(compile_module_constants,
|
|
"compile-enforce-module-constants",
|
|
MZCONFIG_COMPILE_MODULE_CONSTS),
|
|
env);
|
|
|
|
scheme_add_global_constant("eval-jit-enabled",
|
|
scheme_register_parameter(use_jit,
|
|
"eval-jit-enabled",
|
|
MZCONFIG_USE_JIT),
|
|
env);
|
|
|
|
REGISTER_SO(app_symbol);
|
|
REGISTER_SO(datum_symbol);
|
|
REGISTER_SO(top_symbol);
|
|
|
|
app_symbol = scheme_intern_symbol("#%app");
|
|
datum_symbol = scheme_intern_symbol("#%datum");
|
|
top_symbol = scheme_intern_symbol("#%top");
|
|
|
|
REGISTER_SO(app_expander);
|
|
REGISTER_SO(datum_expander);
|
|
REGISTER_SO(top_expander);
|
|
|
|
app_expander = scheme_make_compiled_syntax(app_syntax,
|
|
app_expand);
|
|
scheme_add_global_keyword("#%app",
|
|
app_expander,
|
|
env);
|
|
|
|
datum_expander = scheme_make_compiled_syntax(datum_syntax,
|
|
datum_expand);
|
|
scheme_add_global_keyword("#%datum",
|
|
datum_expander,
|
|
env);
|
|
|
|
top_expander = scheme_make_compiled_syntax(top_syntax,
|
|
top_expand);
|
|
scheme_add_global_keyword("#%top",
|
|
top_expander,
|
|
env);
|
|
|
|
REGISTER_SO(quick_stx);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* C stack and Scheme stack handling */
|
|
/*========================================================================*/
|
|
|
|
# define DO_CHECK_FOR_BREAK(p, e) \
|
|
if (DECREMENT_FUEL(scheme_fuel_counter, 1) <= 0) { \
|
|
e scheme_thread_block(0); \
|
|
(p)->ran_some = 1; \
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_handle_stack_overflow(Scheme_Object *(*k)(void))
|
|
{
|
|
/* "Stack overflow" means running out of C-stack space. The other
|
|
end of this handler (i.e., the target for the longjmp) is
|
|
scheme_top_level_do in fun.c */
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Overflow *overflow;
|
|
Scheme_Overflow_Jmp *jmp;
|
|
|
|
scheme_about_to_move_C_stack();
|
|
|
|
scheme_overflow_k = k;
|
|
scheme_overflow_count++;
|
|
|
|
overflow = MALLOC_ONE_RT(Scheme_Overflow);
|
|
#ifdef MZTAG_REQUIRED
|
|
overflow->type = scheme_rt_overflow;
|
|
#endif
|
|
overflow->prev = scheme_current_thread->overflow;
|
|
overflow->stack_start = p->stack_start;
|
|
p->overflow = overflow;
|
|
|
|
jmp = MALLOC_ONE_RT(Scheme_Overflow_Jmp);
|
|
#ifdef MZTAG_REQUIRED
|
|
jmp->type = scheme_rt_overflow_jmp;
|
|
#endif
|
|
overflow->jmp = jmp;
|
|
|
|
scheme_init_jmpup_buf(&overflow->jmp->cont);
|
|
scheme_zero_unneeded_rands(scheme_current_thread); /* for GC */
|
|
if (scheme_setjmpup(&overflow->jmp->cont, overflow->jmp, ADJUST_STACK_START(p->stack_start))) {
|
|
p = scheme_current_thread;
|
|
overflow = p->overflow;
|
|
p->overflow = overflow->prev;
|
|
p->error_buf = overflow->jmp->savebuf;
|
|
if (!overflow->jmp->captured) /* reset if not captured in a continuation */
|
|
scheme_reset_jmpup_buf(&overflow->jmp->cont);
|
|
if (!scheme_overflow_reply) {
|
|
/* No reply value means we should continue some escape. */
|
|
if (p->cjs.jumping_to_continuation
|
|
&& p->cjs.is_escape) {
|
|
/* Jump directly to prompt: */
|
|
Scheme_Prompt *prompt = (Scheme_Prompt *)p->cjs.jumping_to_continuation;
|
|
scheme_longjmp(*prompt->prompt_buf, 1);
|
|
} else if (p->cjs.jumping_to_continuation
|
|
&& SCHEME_CONTP(p->cjs.jumping_to_continuation)) {
|
|
Scheme_Cont *c = (Scheme_Cont *)p->cjs.jumping_to_continuation;
|
|
p->cjs.jumping_to_continuation = NULL;
|
|
scheme_longjmpup(&c->buf);
|
|
} else {
|
|
/* Continue normal escape: */
|
|
scheme_longjmp(scheme_error_buf, 1);
|
|
}
|
|
} else {
|
|
Scheme_Object *reply = scheme_overflow_reply;
|
|
scheme_overflow_reply = NULL;
|
|
return reply;
|
|
}
|
|
} else {
|
|
p->stack_start = scheme_overflow_stack_start;
|
|
scheme_longjmpup(&scheme_overflow_jmp->cont);
|
|
}
|
|
return NULL; /* never gets here */
|
|
}
|
|
|
|
void scheme_init_stack_check()
|
|
/* Finds the C stack limit --- platform-specific. */
|
|
{
|
|
int *v, stack_grows_up;
|
|
unsigned long deeper;
|
|
#ifdef UNIX_FIND_STACK_BOUNDS
|
|
struct rlimit rl;
|
|
#endif
|
|
|
|
deeper = scheme_get_deeper_address();
|
|
stack_grows_up = (deeper > (unsigned long)&v);
|
|
|
|
#ifdef STACK_GROWS_UP
|
|
if (!stack_grows_up) {
|
|
if (scheme_console_printf)
|
|
scheme_console_printf("Stack grows DOWN, not UP.\n");
|
|
else
|
|
printf("Stack grows DOWN, not UP.\n");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
#ifdef STACK_GROWS_DOWN
|
|
if (stack_grows_up) {
|
|
if (scheme_console_printf)
|
|
scheme_console_printf("Stack grows UP, not DOWN.\n");
|
|
else
|
|
printf("Stack grows UP, not DOWN.\n");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_STACK_BOUNDARY_VAR
|
|
if (!scheme_stack_boundary) {
|
|
# ifdef ASSUME_FIXED_STACK_SIZE
|
|
scheme_stack_boundary = scheme_get_stack_base();
|
|
if (stack_grows_up)
|
|
scheme_stack_boundary += (FIXED_STACK_SIZE - STACK_SAFETY_MARGIN);
|
|
else
|
|
scheme_stack_boundary += (STACK_SAFETY_MARGIN - FIXED_STACK_SIZE);
|
|
# endif
|
|
|
|
# ifdef WINDOWS_FIND_STACK_BOUNDS
|
|
scheme_stack_boundary = scheme_get_stack_base();
|
|
scheme_stack_boundary += (STACK_SAFETY_MARGIN - 0x100000);
|
|
# endif
|
|
|
|
# ifdef MACOS_FIND_STACK_BOUNDS
|
|
scheme_stack_boundary = (unsigned long)&v + STACK_SAFETY_MARGIN - StackSpace();
|
|
# endif
|
|
|
|
# ifdef PALMOS_FIND_STACK_BOUNDS
|
|
{
|
|
Ptr s, e;
|
|
SysGetStackInfo(Ptr &s, &e);
|
|
scheme_stack_boundary = (unsigned long)e + STACK_SAFETY_MARGIN;
|
|
}
|
|
# endif
|
|
|
|
# ifdef BEOS_FIND_STACK_BOUNDS
|
|
{
|
|
thread_info info;
|
|
get_thread_info(find_thread(NULL), &info);
|
|
scheme_stack_boundary = (unsigned long)info.stack_base + STACK_SAFETY_MARGIN;
|
|
}
|
|
# endif
|
|
|
|
# ifdef OSKIT_FIXED_STACK_BOUNDS
|
|
scheme_stack_boundary = (unsigned long)base_stack_start + STACK_SAFETY_MARGIN;
|
|
# endif
|
|
|
|
# ifdef UNIX_FIND_STACK_BOUNDS
|
|
getrlimit(RLIMIT_STACK, &rl);
|
|
|
|
{
|
|
unsigned long bnd, lim;
|
|
bnd = (unsigned long)scheme_get_stack_base();
|
|
|
|
lim = (unsigned long)rl.rlim_cur;
|
|
# ifdef UNIX_STACK_MAXIMUM
|
|
if (lim > UNIX_STACK_MAXIMUM)
|
|
lim = UNIX_STACK_MAXIMUM;
|
|
# endif
|
|
|
|
if (stack_grows_up)
|
|
bnd += (lim - STACK_SAFETY_MARGIN);
|
|
else
|
|
bnd += (STACK_SAFETY_MARGIN - lim);
|
|
|
|
scheme_stack_boundary = bnd;
|
|
}
|
|
# endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
int scheme_check_runstack(long size)
|
|
/* Checks whether the Scheme stack has `size' room left */
|
|
{
|
|
return ((MZ_RUNSTACK - MZ_RUNSTACK_START) >= (size + SCHEME_TAIL_COPY_THRESHOLD));
|
|
}
|
|
|
|
void *scheme_enlarge_runstack(long size, void *(*k)())
|
|
/* Adds a Scheme stack segment, of at least `size' bytes */
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Saved_Stack *saved;
|
|
void *v;
|
|
int cont_count;
|
|
volatile int escape;
|
|
mz_jmp_buf newbuf, * volatile savebuf;
|
|
|
|
saved = MALLOC_ONE_RT(Scheme_Saved_Stack);
|
|
|
|
#ifdef MZTAG_REQUIRED
|
|
saved->type = scheme_rt_saved_stack;
|
|
#endif
|
|
saved->prev = p->runstack_saved;
|
|
saved->runstack_start = MZ_RUNSTACK_START;
|
|
saved->runstack_offset = (MZ_RUNSTACK - MZ_RUNSTACK_START);
|
|
saved->runstack_size = p->runstack_size;
|
|
|
|
size += SCHEME_TAIL_COPY_THRESHOLD;
|
|
|
|
if (size) {
|
|
/* If we keep growing the stack, then probably it
|
|
needs to be much larger, so at least double the
|
|
stack size, to a point: */
|
|
long min_size;
|
|
min_size = 2 * (p->runstack_size);
|
|
if (min_size > 128000)
|
|
min_size = 128000;
|
|
if (size < min_size)
|
|
size = min_size;
|
|
} else {
|
|
/* This is for a prompt. Re-use the current size,
|
|
up to a point: */
|
|
size = p->runstack_size;
|
|
if (size > 1000)
|
|
size = 1000;
|
|
}
|
|
|
|
if (p->spare_runstack && (size <= p->spare_runstack_size)) {
|
|
size = p->spare_runstack_size;
|
|
MZ_RUNSTACK_START = p->spare_runstack;
|
|
p->spare_runstack = NULL;
|
|
} else {
|
|
MZ_RUNSTACK_START = scheme_alloc_runstack(size);
|
|
}
|
|
p->runstack_size = size;
|
|
MZ_RUNSTACK = MZ_RUNSTACK_START + size;
|
|
p->runstack_saved = saved;
|
|
|
|
cont_count = scheme_cont_capture_count;
|
|
|
|
savebuf = p->error_buf;
|
|
p->error_buf = &newbuf;
|
|
if (scheme_setjmp(newbuf)) {
|
|
v = NULL;
|
|
escape = 1;
|
|
p = scheme_current_thread; /* might have changed! */
|
|
} else {
|
|
v = k();
|
|
escape = 0;
|
|
p = scheme_current_thread; /* might have changed! */
|
|
|
|
if (cont_count == scheme_cont_capture_count) {
|
|
if (!p->spare_runstack || (p->runstack_size > p->spare_runstack_size)) {
|
|
p->spare_runstack = MZ_RUNSTACK_START;
|
|
p->spare_runstack_size = p->runstack_size;
|
|
}
|
|
}
|
|
}
|
|
|
|
p->error_buf = savebuf;
|
|
|
|
saved = p->runstack_saved;
|
|
|
|
p->runstack_saved = saved->prev;
|
|
MZ_RUNSTACK_START = saved->runstack_start;
|
|
MZ_RUNSTACK = MZ_RUNSTACK_START + saved->runstack_offset;
|
|
p->runstack_size = saved->runstack_size;
|
|
|
|
if (escape) {
|
|
scheme_longjmp(*p->error_buf, 1);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* compiling applications, sequences, and branches */
|
|
/*========================================================================*/
|
|
|
|
static int is_current_inspector_call(Scheme_Object *a)
|
|
{
|
|
if (SAME_TYPE(SCHEME_TYPE(a), scheme_application_type)) {
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)a;
|
|
if (!app->num_args
|
|
&& SAME_OBJ(app->args[0], scheme_current_inspector_proc))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int is_proc_spec_proc(Scheme_Object *p)
|
|
{
|
|
Scheme_Type vtype;
|
|
|
|
if (SCHEME_PROCP(p)) {
|
|
p = scheme_get_or_check_arity(p, -1);
|
|
if (SCHEME_INTP(p)) {
|
|
return (SCHEME_INT_VAL(p) >= 1);
|
|
} else if (SCHEME_STRUCTP(p)
|
|
&& scheme_is_struct_instance(scheme_arity_at_least, p)) {
|
|
p = ((Scheme_Structure *)p)->slots[0];
|
|
if (SCHEME_INTP(p))
|
|
return (SCHEME_INT_VAL(p) >= 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
vtype = SCHEME_TYPE(p);
|
|
|
|
if (vtype == scheme_unclosed_procedure_type) {
|
|
if (((Scheme_Closure_Data *)p)->num_params >= 1)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int scheme_omittable_expr(Scheme_Object *o, int vals, int fuel, int resolved)
|
|
/* Checks whether the bytecode `o' returns `vals' values with no
|
|
side-effects and without pushing and using continuation marks.
|
|
-1 for vals means that any return count is ok.
|
|
Also used with fully resolved expression by `module' to check
|
|
for "functional" bodies. */
|
|
{
|
|
Scheme_Type vtype;
|
|
|
|
/* FIXME: can overflow the stack */
|
|
|
|
try_again:
|
|
|
|
vtype = SCHEME_TYPE(o);
|
|
|
|
if ((vtype > _scheme_compiled_values_types_)
|
|
|| ((vtype == scheme_local_type)
|
|
&& !(SCHEME_LOCAL_FLAGS(o) & SCHEME_LOCAL_CLEAR_ON_READ))
|
|
|| ((vtype == scheme_local_unbox_type)
|
|
&& !(SCHEME_LOCAL_FLAGS(o) & SCHEME_LOCAL_CLEAR_ON_READ))
|
|
|| (vtype == scheme_unclosed_procedure_type)
|
|
|| (vtype == scheme_compiled_unclosed_procedure_type)
|
|
|| (vtype == scheme_case_lambda_sequence_type)
|
|
|| (vtype == scheme_quote_syntax_type)
|
|
|| (vtype == scheme_compiled_quote_syntax_type))
|
|
return ((vals == 1) || (vals < 0));
|
|
|
|
if (vtype == scheme_toplevel_type) {
|
|
if (resolved && ((vals == 1) || (vals < 0))) {
|
|
if (SCHEME_TOPLEVEL_FLAGS(o)
|
|
& (SCHEME_TOPLEVEL_CONST | SCHEME_TOPLEVEL_READY))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((vtype == scheme_syntax_type)
|
|
&& (SCHEME_PINT_VAL(o) == CASE_LAMBDA_EXPD))
|
|
return 1;
|
|
|
|
if ((vtype == scheme_compiled_quote_syntax_type)) {
|
|
return ((vals == 1) || (vals < 0));
|
|
}
|
|
|
|
if ((vtype == scheme_branch_type)) {
|
|
Scheme_Branch_Rec *b;
|
|
b = (Scheme_Branch_Rec *)o;
|
|
return (scheme_omittable_expr(b->test, 1, fuel - 1, resolved)
|
|
&& scheme_omittable_expr(b->tbranch, vals, fuel - 1, resolved)
|
|
&& scheme_omittable_expr(b->fbranch, vals, fuel - 1, resolved));
|
|
}
|
|
|
|
#if 0
|
|
/* We can't do this because a set! to a lexical is turned into
|
|
a let_value_type! */
|
|
if ((vtype == scheme_let_value_type)) {
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)o;
|
|
return (scheme_omittable_expr(lv->value, lv->count, fuel - 1, resolved)
|
|
&& scheme_omittable_expr(lv->body, vals, fuel - 1, resolved));
|
|
}
|
|
#endif
|
|
|
|
if ((vtype == scheme_let_one_type)) {
|
|
Scheme_Let_One *lo = (Scheme_Let_One *)o;
|
|
return (scheme_omittable_expr(lo->value, 1, fuel - 1, resolved)
|
|
&& scheme_omittable_expr(lo->body, vals, fuel - 1, resolved));
|
|
}
|
|
|
|
if ((vtype == scheme_let_void_type)) {
|
|
Scheme_Let_Void *lv = (Scheme_Let_Void *)o;
|
|
/* recognize (letrec ([x <omittable>]) ...): */
|
|
if (SAME_TYPE(SCHEME_TYPE(lv->body), scheme_let_value_type)) {
|
|
Scheme_Let_Value *lv2 = (Scheme_Let_Value *)lv->body;
|
|
if ((lv2->count == 1)
|
|
&& (lv2->position == 0)
|
|
&& scheme_omittable_expr(lv2->value, 1, fuel - 1, resolved))
|
|
o = lv2->body;
|
|
else
|
|
o = lv->body;
|
|
} else
|
|
o = lv->body;
|
|
goto try_again;
|
|
}
|
|
|
|
if ((vtype == scheme_letrec_type)) {
|
|
o = ((Scheme_Letrec *)o)->body;
|
|
goto try_again;
|
|
}
|
|
|
|
if ((vtype == scheme_application_type)) {
|
|
/* Look for multiple values, or for `make-struct-type'.
|
|
(The latter is especially useful to Honu.) */
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)o;
|
|
if (((vals == 5) || (vals < 0))
|
|
&& (app->num_args >= 4) && (app->num_args <= 10)
|
|
&& SAME_OBJ(scheme_make_struct_type_proc, app->args[0])) {
|
|
/* Look for (make-struct-type sym #f non-neg-int non-neg-int [omitable null]) */
|
|
if (SCHEME_SYMBOLP(app->args[1])
|
|
&& SCHEME_FALSEP(app->args[2])
|
|
&& SCHEME_INTP(app->args[3])
|
|
&& (SCHEME_INT_VAL(app->args[3]) >= 0)
|
|
&& SCHEME_INTP(app->args[4])
|
|
&& (SCHEME_INT_VAL(app->args[4]) >= 0)
|
|
&& ((app->num_args < 5)
|
|
|| scheme_omittable_expr(app->args[5], 1, fuel - 1, resolved))
|
|
&& ((app->num_args < 6)
|
|
|| SCHEME_NULLP(app->args[6]))
|
|
&& ((app->num_args < 7)
|
|
|| SCHEME_FALSEP(app->args[7])
|
|
|| is_current_inspector_call(app->args[7]))
|
|
&& ((app->num_args < 8)
|
|
|| SCHEME_FALSEP(app->args[8])
|
|
|| is_proc_spec_proc(app->args[8]))
|
|
&& ((app->num_args < 9)
|
|
|| SCHEME_NULLP(app->args[9]))) {
|
|
return 1;
|
|
}
|
|
}
|
|
if ((app->num_args == vals) || (vals < 0)) {
|
|
if (SAME_OBJ(scheme_values_func, app->args[0])) {
|
|
int i;
|
|
for (i = app->num_args; i--; ) {
|
|
if (!scheme_omittable_expr(app->args[i + 1], 1, fuel - 1, resolved))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
if ((vals == 1) || (vals < 0)) {
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if ((vtype == scheme_application2_type)) {
|
|
if ((vals == 1) || (vals < 0)) {
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)o;
|
|
if (SAME_OBJ(scheme_values_func, app->rator)) {
|
|
if (scheme_omittable_expr(app->rand, 1, fuel - 1, resolved))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ((vtype == scheme_application3_type)) {
|
|
if ((vals == 2) || (vals < 0)) {
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)o;
|
|
if (SAME_OBJ(scheme_values_func, app->rator)) {
|
|
if (scheme_omittable_expr(app->rand1, 1, fuel - 1, resolved)
|
|
&& scheme_omittable_expr(app->rand2, 1, fuel - 1, resolved))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int scheme_is_compiled_procedure(Scheme_Object *o, int can_be_closed, int can_be_liftable)
|
|
{
|
|
if (SAME_TYPE(SCHEME_TYPE(o), scheme_compiled_unclosed_procedure_type)) {
|
|
if (!can_be_closed || !can_be_liftable) {
|
|
Scheme_Closure_Data *data;
|
|
data = (Scheme_Closure_Data *)o;
|
|
/* Because == 0 is like a constant */
|
|
if (!can_be_closed && !data->closure_size)
|
|
return 0;
|
|
/* Because procs that reference only globals are lifted: */
|
|
if (!can_be_liftable && (data->closure_size == 1) && scheme_closure_has_top_level(data))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
int scheme_get_eval_type(Scheme_Object *obj)
|
|
/* Categories for short-cutting recursive calls to the evaluator */
|
|
{
|
|
Scheme_Type type;
|
|
|
|
type = SCHEME_TYPE(obj);
|
|
|
|
if (type > _scheme_values_types_)
|
|
return SCHEME_EVAL_CONSTANT;
|
|
else if (SAME_TYPE(type, scheme_local_type))
|
|
return SCHEME_EVAL_LOCAL;
|
|
else if (SAME_TYPE(type, scheme_local_unbox_type))
|
|
return SCHEME_EVAL_LOCAL_UNBOX;
|
|
else if (SAME_TYPE(type, scheme_toplevel_type))
|
|
return SCHEME_EVAL_GLOBAL;
|
|
else
|
|
return SCHEME_EVAL_GENERAL;
|
|
}
|
|
|
|
static Scheme_Object *try_apply(Scheme_Object *f, Scheme_Object *args)
|
|
/* Apply `f' to `args' and ignore failues --- used for constant
|
|
folding attempts */
|
|
{
|
|
Scheme_Object * volatile result;
|
|
mz_jmp_buf *savebuf, newbuf;
|
|
|
|
scheme_current_thread->skip_error = 5;
|
|
savebuf = scheme_current_thread->error_buf;
|
|
scheme_current_thread->error_buf = &newbuf;
|
|
|
|
if (scheme_setjmp(newbuf))
|
|
result = NULL;
|
|
else
|
|
result = _scheme_apply_to_list(f, args);
|
|
|
|
scheme_current_thread->error_buf = savebuf;
|
|
scheme_current_thread->skip_error = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
static int foldable_body(Scheme_Object *f)
|
|
{
|
|
Scheme_Closure_Data *d;
|
|
|
|
d = SCHEME_COMPILED_CLOS_CODE(f);
|
|
|
|
scheme_delay_load_closure(d);
|
|
|
|
return (SCHEME_TYPE(d->code) > _scheme_values_types_);
|
|
}
|
|
|
|
static Scheme_Object *make_application(Scheme_Object *v)
|
|
{
|
|
Scheme_Object *o;
|
|
int i, nv;
|
|
volatile int n;
|
|
|
|
o = v;
|
|
n = 0;
|
|
nv = 0;
|
|
while (!SCHEME_NULLP(o)) {
|
|
Scheme_Type type;
|
|
|
|
n++;
|
|
type = SCHEME_TYPE(SCHEME_CAR(o));
|
|
if (type < _scheme_compiled_values_types_)
|
|
nv = 1;
|
|
o = SCHEME_CDR(o);
|
|
}
|
|
|
|
if (!nv) {
|
|
/* They're all values. Applying folding prim or closure? */
|
|
Scheme_Object *f;
|
|
|
|
f = SCHEME_CAR(v);
|
|
|
|
if ((SCHEME_PRIMP(f) && ((((Scheme_Primitive_Proc *)f)->pp.flags & SCHEME_PRIM_OPT_MASK)
|
|
== SCHEME_PRIM_OPT_FOLDING))
|
|
|| (SCHEME_CLSD_PRIMP(f)
|
|
&& ((((Scheme_Closed_Primitive_Proc *)f)->pp.flags & SCHEME_PRIM_OPT_MASK)
|
|
== SCHEME_PRIM_OPT_FOLDING))
|
|
|| (SAME_TYPE(SCHEME_TYPE(f), scheme_closure_type)
|
|
&& (foldable_body(f)))) {
|
|
f = try_apply(f, SCHEME_CDR(v));
|
|
|
|
if (f)
|
|
return f;
|
|
}
|
|
}
|
|
|
|
if (n == 2) {
|
|
Scheme_App2_Rec *app;
|
|
|
|
app = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
|
|
app->iso.so.type = scheme_application2_type;
|
|
|
|
app->rator = SCHEME_CAR(v);
|
|
v = SCHEME_CDR(v);
|
|
app->rand = SCHEME_CAR(v);
|
|
|
|
return (Scheme_Object *)app;
|
|
} else if (n == 3) {
|
|
Scheme_App3_Rec *app;
|
|
|
|
app = MALLOC_ONE_TAGGED(Scheme_App3_Rec);
|
|
app->iso.so.type = scheme_application3_type;
|
|
|
|
app->rator = SCHEME_CAR(v);
|
|
v = SCHEME_CDR(v);
|
|
app->rand1 = SCHEME_CAR(v);
|
|
v = SCHEME_CDR(v);
|
|
app->rand2 = SCHEME_CAR(v);
|
|
|
|
return (Scheme_Object *)app;
|
|
} else {
|
|
Scheme_App_Rec *app;
|
|
|
|
app = scheme_malloc_application(n);
|
|
|
|
for (i = 0; i < n; i++, v = SCHEME_CDR(v)) {
|
|
app->args[i] = SCHEME_CAR(v);
|
|
}
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
}
|
|
|
|
Scheme_App_Rec *scheme_malloc_application(int n)
|
|
{
|
|
Scheme_App_Rec *app;
|
|
int size;
|
|
|
|
size = (sizeof(Scheme_App_Rec)
|
|
+ ((n - 1) * sizeof(Scheme_Object *))
|
|
+ n * sizeof(char));
|
|
app = (Scheme_App_Rec *)scheme_malloc_tagged(size);
|
|
|
|
app->so.type = scheme_application_type;
|
|
|
|
app->num_args = n - 1;
|
|
|
|
return app;
|
|
}
|
|
|
|
void scheme_finish_application(Scheme_App_Rec *app)
|
|
{
|
|
int i, devals, n;
|
|
|
|
n = app->num_args + 1;
|
|
|
|
devals = sizeof(Scheme_App_Rec) + (app->num_args * sizeof(Scheme_Object *));
|
|
|
|
for (i = 0; i < n; i++) {
|
|
char etype;
|
|
etype = scheme_get_eval_type(app->args[i]);
|
|
((char *)app XFORM_OK_PLUS devals)[i] = etype;
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *check_converted_rator(Scheme_Object *rator, Resolve_Info *info, Scheme_Object **new_rator,
|
|
int orig_arg_cnt, int *_rdelta)
|
|
{
|
|
Scheme_Object *lifted;
|
|
int flags;
|
|
|
|
if (!SAME_TYPE(SCHEME_TYPE(rator), scheme_local_type))
|
|
return NULL;
|
|
|
|
(void)scheme_resolve_info_lookup(info, SCHEME_LOCAL_POS(rator), &flags, &lifted, orig_arg_cnt + 1);
|
|
|
|
if (lifted && SCHEME_RPAIRP(lifted)) {
|
|
Scheme_Object *vec, *arity;
|
|
|
|
*new_rator = SCHEME_CAR(lifted);
|
|
vec = SCHEME_CDR(lifted);
|
|
*_rdelta = 0;
|
|
|
|
if (SCHEME_VEC_SIZE(vec) > 1) {
|
|
/* Check that actual argument count matches expected. If
|
|
it doesn't, we need to generate explicit code to report
|
|
the error, so that the conversion's arity change isn't
|
|
visible. */
|
|
arity = SCHEME_VEC_ELS(vec)[0];
|
|
if (SCHEME_INTP(arity)) {
|
|
if (orig_arg_cnt == SCHEME_INT_VAL(arity))
|
|
arity = NULL;
|
|
} else {
|
|
arity = SCHEME_BOX_VAL(arity);
|
|
if (orig_arg_cnt >= SCHEME_INT_VAL(arity))
|
|
arity = NULL;
|
|
else {
|
|
Scheme_App2_Rec *app;
|
|
app = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
|
|
app->iso.so.type = scheme_application2_type;
|
|
app->rator = scheme_make_arity_at_least;
|
|
app->rand = arity;
|
|
arity = (Scheme_Object *)app;
|
|
*_rdelta = 1; /* so app gets resolved */
|
|
}
|
|
}
|
|
/* If arity is non-NULL, there's a mismatch. */
|
|
if (arity) {
|
|
/* Generate a call to `raise-arity-error' instead of
|
|
the current *new_rator: */
|
|
Scheme_Object *old_rator = *new_rator;
|
|
if (SAME_TYPE(SCHEME_TYPE(old_rator), scheme_toplevel_type)) {
|
|
/* More coordinate trouble. old_rator was computed for an
|
|
application with a potentially different number of arguments. */
|
|
int delta;
|
|
delta = 3 - SCHEME_VEC_SIZE(vec);
|
|
if (delta)
|
|
old_rator = scheme_shift_toplevel(old_rator, delta);
|
|
}
|
|
vec = scheme_make_vector(3, NULL);
|
|
SCHEME_VEC_ELS(vec)[0] = scheme_make_integer(0);
|
|
SCHEME_VEC_ELS(vec)[1] = old_rator;
|
|
SCHEME_VEC_ELS(vec)[2] = arity;
|
|
*new_rator = scheme_raise_arity_error_proc;
|
|
}
|
|
}
|
|
|
|
return vec;
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *resolve_application(Scheme_Object *o, Resolve_Info *orig_info, int already_resolved_arg_count)
|
|
{
|
|
Resolve_Info *info;
|
|
Scheme_App_Rec *app;
|
|
int i, n, devals;
|
|
|
|
app = (Scheme_App_Rec *)o;
|
|
|
|
n = app->num_args + 1;
|
|
|
|
if (!already_resolved_arg_count) {
|
|
/* Check whether this is an application of a converted closure: */
|
|
Scheme_Object *additions = NULL, *rator;
|
|
int rdelta;
|
|
additions = check_converted_rator(app->args[0], orig_info, &rator, n - 1, &rdelta);
|
|
if (additions) {
|
|
/* Expand application with m arguments */
|
|
Scheme_App_Rec *app2;
|
|
Scheme_Object *loc;
|
|
int m;
|
|
m = SCHEME_VEC_SIZE(additions) - 1;
|
|
app2 = scheme_malloc_application(n + m);
|
|
for (i = 0; i < m; i++) {
|
|
loc = SCHEME_VEC_ELS(additions)[i+1];
|
|
if (SCHEME_BOXP(loc))
|
|
loc = SCHEME_BOX_VAL(loc);
|
|
app2->args[i + 1] = loc;
|
|
}
|
|
for (i = 1; i < n; i++) {
|
|
app2->args[i + m] = app->args[i];
|
|
}
|
|
app2->args[0] = rator;
|
|
n += m;
|
|
app = app2;
|
|
already_resolved_arg_count = m + 1 + rdelta;
|
|
}
|
|
}
|
|
|
|
devals = sizeof(Scheme_App_Rec) + ((n - 1) * sizeof(Scheme_Object *));
|
|
|
|
info = scheme_resolve_info_extend(orig_info, n - 1, 0, 0);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
Scheme_Object *le;
|
|
if (already_resolved_arg_count) {
|
|
already_resolved_arg_count--;
|
|
} else {
|
|
le = scheme_resolve_expr(app->args[i], info);
|
|
app->args[i] = le;
|
|
}
|
|
}
|
|
|
|
info->max_let_depth += (n - 1);
|
|
if (orig_info->max_let_depth < info->max_let_depth)
|
|
orig_info->max_let_depth = info->max_let_depth;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
char et;
|
|
et = scheme_get_eval_type(app->args[i]);
|
|
((char *)app XFORM_OK_PLUS devals)[i] = et;
|
|
}
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
static Scheme_Object *resolve_application3(Scheme_Object *o, Resolve_Info *orig_info, int already_resolved_arg_count);
|
|
|
|
static void set_app2_eval_type(Scheme_App2_Rec *app)
|
|
{
|
|
short et;
|
|
|
|
et = scheme_get_eval_type(app->rand);
|
|
et = et << 3;
|
|
et += scheme_get_eval_type(app->rator);
|
|
|
|
SCHEME_APPN_FLAGS(app) = et;
|
|
}
|
|
|
|
static Scheme_Object *resolve_application2(Scheme_Object *o, Resolve_Info *orig_info, int already_resolved_arg_count)
|
|
{
|
|
Resolve_Info *info;
|
|
Scheme_App2_Rec *app;
|
|
Scheme_Object *le;
|
|
|
|
app = (Scheme_App2_Rec *)o;
|
|
|
|
if (!already_resolved_arg_count) {
|
|
/* Check whether this is an application of a converted closure: */
|
|
Scheme_Object *additions = NULL, *rator;
|
|
int rdelta;
|
|
additions = check_converted_rator(app->rator, orig_info, &rator, 1, &rdelta);
|
|
if (additions) {
|
|
int m;
|
|
m = SCHEME_VEC_SIZE(additions) - 1;
|
|
if (!m) {
|
|
app->rator = rator;
|
|
already_resolved_arg_count = 1 + rdelta;
|
|
} else if (m > 1) {
|
|
/* Expand application with m arguments */
|
|
Scheme_App_Rec *app2;
|
|
Scheme_Object *loc;
|
|
int i;
|
|
app2 = scheme_malloc_application(2 + m);
|
|
for (i = 0; i < m; i++) {
|
|
loc = SCHEME_VEC_ELS(additions)[i+1];
|
|
if (SCHEME_BOXP(loc))
|
|
loc = SCHEME_BOX_VAL(loc);
|
|
app2->args[i + 1] = loc;
|
|
}
|
|
app2->args[0] = rator;
|
|
app2->args[m+1] = app->rand;
|
|
return resolve_application((Scheme_Object *)app2, orig_info, m + 1 + rdelta);
|
|
} else {
|
|
Scheme_App3_Rec *app2;
|
|
Scheme_Object *loc;
|
|
app2 = MALLOC_ONE_TAGGED(Scheme_App3_Rec);
|
|
app2->iso.so.type = scheme_application3_type;
|
|
app2->rator = rator;
|
|
loc = SCHEME_VEC_ELS(additions)[1];
|
|
if (SCHEME_BOXP(loc))
|
|
loc = SCHEME_BOX_VAL(loc);
|
|
app2->rand1 = loc;
|
|
app2->rand2 = app->rand;
|
|
return resolve_application3((Scheme_Object *)app2, orig_info, 2 + rdelta);
|
|
}
|
|
}
|
|
}
|
|
|
|
info = scheme_resolve_info_extend(orig_info, 1, 0, 0);
|
|
|
|
if (!already_resolved_arg_count) {
|
|
le = scheme_resolve_expr(app->rator, info);
|
|
app->rator = le;
|
|
} else
|
|
already_resolved_arg_count--;
|
|
|
|
if (!already_resolved_arg_count) {
|
|
le = scheme_resolve_expr(app->rand, info);
|
|
app->rand = le;
|
|
} else
|
|
already_resolved_arg_count--;
|
|
|
|
info->max_let_depth += 1;
|
|
if (orig_info->max_let_depth < info->max_let_depth)
|
|
orig_info->max_let_depth = info->max_let_depth;
|
|
|
|
set_app2_eval_type(app);
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
static int eq_testable_constant(Scheme_Object *v)
|
|
{
|
|
if (SCHEME_SYMBOLP(v)
|
|
|| SCHEME_FALSEP(v)
|
|
|| SAME_OBJ(v, scheme_true)
|
|
|| SCHEME_VOIDP(v))
|
|
return 1;
|
|
|
|
if (SCHEME_CHARP(v) && (SCHEME_CHAR_VAL(v) < 256))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void set_app3_eval_type(Scheme_App3_Rec *app)
|
|
{
|
|
short et;
|
|
|
|
et = scheme_get_eval_type(app->rand2);
|
|
et = et << 3;
|
|
et += scheme_get_eval_type(app->rand1);
|
|
et = et << 3;
|
|
et += scheme_get_eval_type(app->rator);
|
|
|
|
SCHEME_APPN_FLAGS(app) = et;
|
|
}
|
|
|
|
static Scheme_Object *resolve_application3(Scheme_Object *o, Resolve_Info *orig_info, int already_resolved_arg_count)
|
|
{
|
|
Resolve_Info *info;
|
|
Scheme_App3_Rec *app;
|
|
Scheme_Object *le;
|
|
|
|
app = (Scheme_App3_Rec *)o;
|
|
|
|
if (!already_resolved_arg_count) {
|
|
/* Check whether this is an application of a converted closure: */
|
|
Scheme_Object *additions = NULL, *rator;
|
|
int rdelta;
|
|
additions = check_converted_rator(app->rator, orig_info, &rator, 2, &rdelta);
|
|
if (additions) {
|
|
int m, i;
|
|
m = SCHEME_VEC_SIZE(additions) - 1;
|
|
if (m) {
|
|
/* Expand application with m arguments */
|
|
Scheme_App_Rec *app2;
|
|
Scheme_Object *loc;
|
|
app2 = scheme_malloc_application(3 + m);
|
|
for (i = 0; i < m; i++) {
|
|
loc = SCHEME_VEC_ELS(additions)[i+1];
|
|
if (SCHEME_BOXP(loc))
|
|
loc = SCHEME_BOX_VAL(loc);
|
|
app2->args[i + 1] = loc;
|
|
}
|
|
app2->args[0] = rator;
|
|
app2->args[m+1] = app->rand1;
|
|
app2->args[m+2] = app->rand2;
|
|
return resolve_application((Scheme_Object *)app2, orig_info, m + 1 + rdelta);
|
|
} else {
|
|
app->rator = rator;
|
|
already_resolved_arg_count = 1 + rdelta;
|
|
}
|
|
}
|
|
}
|
|
|
|
info = scheme_resolve_info_extend(orig_info, 2, 0, 0);
|
|
|
|
if (already_resolved_arg_count) {
|
|
already_resolved_arg_count--;
|
|
} else {
|
|
le = scheme_resolve_expr(app->rator, info);
|
|
app->rator = le;
|
|
}
|
|
|
|
if (already_resolved_arg_count) {
|
|
already_resolved_arg_count--;
|
|
} else {
|
|
le = scheme_resolve_expr(app->rand1, info);
|
|
app->rand1 = le;
|
|
}
|
|
|
|
if (already_resolved_arg_count) {
|
|
already_resolved_arg_count--;
|
|
} else {
|
|
le = scheme_resolve_expr(app->rand2, info);
|
|
app->rand2 = le;
|
|
}
|
|
|
|
/* Optimize `equal?' or `eqv?' test on certain types
|
|
to `eq?'. This is especially helpful for the JIT. */
|
|
if ((SAME_OBJ(app->rator, scheme_equal_prim)
|
|
|| SAME_OBJ(app->rator, scheme_eqv_prim))
|
|
&& (eq_testable_constant(app->rand1)
|
|
|| eq_testable_constant(app->rand2))) {
|
|
app->rator = scheme_eq_prim;
|
|
}
|
|
|
|
set_app3_eval_type(app);
|
|
|
|
info->max_let_depth += 2;
|
|
if (orig_info->max_let_depth < info->max_let_depth)
|
|
orig_info->max_let_depth = info->max_let_depth;
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_make_branch(Scheme_Object *test, Scheme_Object *thenp,
|
|
Scheme_Object *elsep)
|
|
{
|
|
Scheme_Branch_Rec *b;
|
|
|
|
if (SCHEME_TYPE(test) > _scheme_compiled_values_types_) {
|
|
if (SCHEME_FALSEP(test))
|
|
return elsep;
|
|
else
|
|
return thenp;
|
|
}
|
|
|
|
b = MALLOC_ONE_TAGGED(Scheme_Branch_Rec);
|
|
b->so.type = scheme_branch_type;
|
|
|
|
b->test = test;
|
|
b->tbranch = thenp;
|
|
b->fbranch = elsep;
|
|
|
|
return (Scheme_Object *)b;
|
|
}
|
|
|
|
static Scheme_Object *resolve_branch(Scheme_Object *o, Resolve_Info *info)
|
|
{
|
|
Scheme_Branch_Rec *b;
|
|
Scheme_Object *t, *tb, *fb;
|
|
|
|
b = (Scheme_Branch_Rec *)o;
|
|
|
|
t = scheme_resolve_expr(b->test, info);
|
|
tb = scheme_resolve_expr(b->tbranch, info);
|
|
fb = scheme_resolve_expr(b->fbranch, info);
|
|
|
|
b->test = t;
|
|
b->tbranch = tb;
|
|
b->fbranch = fb;
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *resolve_wcm(Scheme_Object *o, Resolve_Info *info)
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)o;
|
|
Scheme_Object *k, *v, *b;
|
|
|
|
k = scheme_resolve_expr(wcm->key, info);
|
|
v = scheme_resolve_expr(wcm->val, info);
|
|
b = scheme_resolve_expr(wcm->body, info);
|
|
wcm->key = k;
|
|
wcm->val = v;
|
|
wcm->body = b;
|
|
|
|
return (Scheme_Object *)wcm;
|
|
}
|
|
|
|
static Scheme_Sequence *malloc_sequence(int count)
|
|
{
|
|
return (Scheme_Sequence *)scheme_malloc_tagged(sizeof(Scheme_Sequence)
|
|
+ (count - 1)
|
|
* sizeof(Scheme_Object *));
|
|
}
|
|
|
|
Scheme_Object *scheme_make_sequence_compilation(Scheme_Object *seq, int opt)
|
|
{
|
|
/* We have to be defensive in processing `seq'; it might be bad due
|
|
to a bad .zo */
|
|
Scheme_Object *list, *v, *good;
|
|
Scheme_Sequence *o;
|
|
int count, i, k, total, last, first, setgood, addconst;
|
|
Scheme_Type type;
|
|
|
|
type = scheme_sequence_type;
|
|
|
|
list = seq;
|
|
count = i = 0;
|
|
good = NULL;
|
|
total = 0;
|
|
first = 1;
|
|
setgood = 1;
|
|
while (SCHEME_PAIRP(list)) {
|
|
v = SCHEME_CAR(list);
|
|
list = SCHEME_CDR(list);
|
|
last = SCHEME_NULLP(list);
|
|
|
|
if (((opt > 0) || !first) && SAME_TYPE(SCHEME_TYPE(v), type)) {
|
|
/* "Inline" nested begins */
|
|
count += ((Scheme_Sequence *)v)->count;
|
|
total++;
|
|
} else if (opt
|
|
&& (((opt > 0) && !last) || ((opt < 0) && !first))
|
|
&& scheme_omittable_expr(v, -1, -1, 0)) {
|
|
/* A value that is not the result. We'll drop it. */
|
|
total++;
|
|
} else {
|
|
if (setgood)
|
|
good = v;
|
|
count++;
|
|
total++;
|
|
}
|
|
i++;
|
|
if (first) {
|
|
if (opt < 0)
|
|
setgood = 0;
|
|
first = 0;
|
|
}
|
|
}
|
|
|
|
if (!SCHEME_NULLP(list))
|
|
return NULL; /* bad .zo */
|
|
|
|
if (!count)
|
|
return scheme_compiled_void();
|
|
|
|
if (count == 1) {
|
|
if ((opt < 0) && !scheme_omittable_expr(SCHEME_CAR(seq), 1, -1, 0)) {
|
|
/* We can't optimize (begin0 expr cont) to expr because
|
|
exp is not in tail position in the original (so we'd mess
|
|
up continuation marks). */
|
|
addconst = 1;
|
|
} else
|
|
return good;
|
|
} else
|
|
addconst = 0;
|
|
|
|
o = malloc_sequence(count + addconst);
|
|
|
|
o->so.type = ((opt < 0) ? scheme_begin0_sequence_type : scheme_sequence_type);
|
|
o->count = count + addconst;
|
|
|
|
--total;
|
|
for (i = k = 0; i < count; k++) {
|
|
v = SCHEME_CAR(seq);
|
|
seq = SCHEME_CDR(seq);
|
|
|
|
if (((opt > 0) || k) && SAME_TYPE(SCHEME_TYPE(v), type)) {
|
|
int c, j;
|
|
Scheme_Object **a;
|
|
|
|
c = ((Scheme_Sequence *)v)->count;
|
|
a = ((Scheme_Sequence *)v)->array; /* <-- mismaligned for precise GC */
|
|
for (j = 0; j < c; j++) {
|
|
o->array[i++] = a[j];
|
|
}
|
|
} else if (opt
|
|
&& (((opt > 0) && (k < total))
|
|
|| ((opt < 0) && k))
|
|
&& scheme_omittable_expr(v, -1, -1, 0)) {
|
|
/* Value not the result. Do nothing. */
|
|
} else
|
|
o->array[i++] = v;
|
|
}
|
|
|
|
if (addconst)
|
|
o->array[i] = scheme_make_integer(0);
|
|
|
|
return (Scheme_Object *)o;
|
|
}
|
|
|
|
static Scheme_Object *look_for_letv_change(Scheme_Sequence *s)
|
|
{
|
|
int i;
|
|
|
|
/* Change (begin e1 ... (set!-for-let [x 10] (void)) e2 ...)
|
|
to (begin e1 ... (set!-for-let [x 10] e2 ...)), which
|
|
avoids an unneeded recursive call in the evaluator */
|
|
|
|
for (i = 0; i < s->count - 1; i++) {
|
|
Scheme_Object *v;
|
|
v = s->array[i];
|
|
if (SAME_TYPE(SCHEME_TYPE(v), scheme_let_value_type)) {
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)v;
|
|
if (scheme_omittable_expr(lv->body, 1, -1, 0)) {
|
|
int esize = s->count - (i + 1);
|
|
int nsize = i + 1;
|
|
Scheme_Object *nv, *ev;
|
|
|
|
if (nsize > 1) {
|
|
Scheme_Sequence *naya;
|
|
|
|
naya = malloc_sequence(nsize);
|
|
naya->so.type = scheme_sequence_type;
|
|
naya->count = nsize;
|
|
nv = (Scheme_Object *)naya;
|
|
|
|
for (i = 0; i < nsize; i++) {
|
|
naya->array[i] = s->array[i];
|
|
}
|
|
} else
|
|
nv = (Scheme_Object *)lv;
|
|
|
|
if (esize > 1) {
|
|
Scheme_Sequence *e;
|
|
e = malloc_sequence(esize);
|
|
e->so.type = scheme_sequence_type;
|
|
e->count = esize;
|
|
|
|
for (i = 0; i < esize; i++) {
|
|
e->array[i] = s->array[i + nsize];
|
|
}
|
|
|
|
ev = (Scheme_Object *)look_for_letv_change(e);
|
|
} else
|
|
ev = s->array[nsize];
|
|
|
|
lv->body = ev;
|
|
|
|
return nv;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (Scheme_Object *)s;
|
|
}
|
|
|
|
static Scheme_Object *resolve_sequence(Scheme_Object *o, Resolve_Info *info)
|
|
{
|
|
Scheme_Sequence *s = (Scheme_Sequence *)o;
|
|
int i;
|
|
|
|
for (i = s->count; i--; ) {
|
|
Scheme_Object *le;
|
|
le = scheme_resolve_expr(s->array[i], info);
|
|
s->array[i] = le;
|
|
}
|
|
|
|
return look_for_letv_change(s);
|
|
}
|
|
|
|
Scheme_Object *scheme_make_syntax_resolved(int idx, Scheme_Object *data)
|
|
{
|
|
Scheme_Object *v;
|
|
|
|
v = scheme_alloc_object();
|
|
v->type = scheme_syntax_type;
|
|
SCHEME_PINT_VAL(v) = idx;
|
|
SCHEME_IPTR_VAL(v) = (void *)data;
|
|
|
|
return v;
|
|
}
|
|
|
|
Scheme_Object *scheme_make_syntax_compiled(int idx, Scheme_Object *data)
|
|
{
|
|
Scheme_Object *v;
|
|
|
|
v = scheme_alloc_object();
|
|
v->type = scheme_compiled_syntax_type;
|
|
SCHEME_PINT_VAL(v) = idx;
|
|
SCHEME_IPTR_VAL(v) = (void *)data;
|
|
|
|
return v;
|
|
}
|
|
|
|
static Scheme_Object *link_module_variable(Scheme_Object *modidx,
|
|
Scheme_Object *varname,
|
|
Scheme_Object *insp,
|
|
int pos, int mod_phase,
|
|
Scheme_Env *env)
|
|
{
|
|
Scheme_Object *modname;
|
|
Scheme_Env *menv;
|
|
|
|
/* If it's a name id, resolve the name. */
|
|
modname = scheme_module_resolve(modidx, 1);
|
|
|
|
if (env->module && SAME_OBJ(env->module->modname, modname)
|
|
&& (env->mod_phase == mod_phase))
|
|
menv = env;
|
|
else {
|
|
menv = scheme_module_access(modname, env, mod_phase);
|
|
|
|
if (!menv && env->phase) {
|
|
/* The failure might be due a laziness in required-syntax
|
|
execution. Force all laziness at the prior level
|
|
and try again. */
|
|
scheme_module_force_lazy(env, 1);
|
|
menv = scheme_module_access(modname, env, mod_phase);
|
|
}
|
|
|
|
if (!menv) {
|
|
scheme_wrong_syntax("link", NULL, varname,
|
|
"namespace mismatch; reference (phase %d) to a module"
|
|
" %D that is not available (phase %d); reference"
|
|
" appears in module: %D",
|
|
env->phase,
|
|
modname,
|
|
mod_phase,
|
|
env->module ? env->module->modname : scheme_false);
|
|
return NULL;
|
|
}
|
|
|
|
if (!SAME_OBJ(menv, env)) {
|
|
varname = scheme_check_accessible_in_module(menv, insp, NULL, varname, NULL, NULL, insp, pos, 0, NULL);
|
|
}
|
|
}
|
|
|
|
return (Scheme_Object *)scheme_global_bucket(varname, menv);
|
|
}
|
|
|
|
static Scheme_Object *link_toplevel(Scheme_Object *expr, Scheme_Env *env,
|
|
Scheme_Object *src_modidx,
|
|
Scheme_Object *dest_modidx)
|
|
{
|
|
if (SAME_TYPE(SCHEME_TYPE(expr), scheme_variable_type)) {
|
|
Scheme_Bucket_With_Home *b = (Scheme_Bucket_With_Home *)expr;
|
|
|
|
if (!env || !b->home->module)
|
|
return (Scheme_Object *)b;
|
|
else
|
|
return link_module_variable(b->home->module->modname,
|
|
(Scheme_Object *)b->bucket.bucket.key,
|
|
b->home->module->insp,
|
|
-1, b->home->mod_phase,
|
|
env);
|
|
} else {
|
|
Module_Variable *mv = (Module_Variable *)expr;
|
|
|
|
return link_module_variable(scheme_modidx_shift(mv->modidx,
|
|
src_modidx,
|
|
dest_modidx),
|
|
mv->sym, mv->insp,
|
|
mv->pos, mv->mod_phase,
|
|
env);
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *resolve_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *expr = (Scheme_Object *)p->ku.k.p1;
|
|
Resolve_Info *info = (Resolve_Info *)p->ku.k.p2;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
|
|
return scheme_resolve_expr(expr, info);
|
|
}
|
|
|
|
Scheme_Object *scheme_resolve_expr(Scheme_Object *expr, Resolve_Info *info)
|
|
{
|
|
Scheme_Type type = SCHEME_TYPE(expr);
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
# include "mzstkchk.h"
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
|
|
p->ku.k.p1 = (void *)expr;
|
|
p->ku.k.p2 = (void *)info;
|
|
|
|
return scheme_handle_stack_overflow(resolve_k);
|
|
}
|
|
#endif
|
|
|
|
switch (type) {
|
|
case scheme_local_type:
|
|
{
|
|
int pos, flags;
|
|
Scheme_Object *lifted;
|
|
|
|
pos = scheme_resolve_info_lookup(info, SCHEME_LOCAL_POS(expr), &flags, &lifted, 0);
|
|
if (lifted) {
|
|
/* Lexical reference replaced with top-level reference for a lifted value: */
|
|
return lifted;
|
|
} else {
|
|
return scheme_make_local((flags & SCHEME_INFO_BOXED)
|
|
? scheme_local_unbox_type
|
|
: scheme_local_type,
|
|
pos,
|
|
0);
|
|
}
|
|
}
|
|
case scheme_compiled_syntax_type:
|
|
{
|
|
Scheme_Syntax_Resolver f;
|
|
|
|
f = scheme_syntax_resolvers[SCHEME_PINT_VAL(expr)];
|
|
return f((Scheme_Object *)SCHEME_IPTR_VAL(expr), info);
|
|
}
|
|
case scheme_application_type:
|
|
return resolve_application(expr, info, 0);
|
|
case scheme_application2_type:
|
|
return resolve_application2(expr, info, 0);
|
|
case scheme_application3_type:
|
|
return resolve_application3(expr, info, 0);
|
|
case scheme_sequence_type:
|
|
return resolve_sequence(expr, info);
|
|
case scheme_branch_type:
|
|
return resolve_branch(expr, info);
|
|
case scheme_with_cont_mark_type:
|
|
return resolve_wcm(expr, info);
|
|
case scheme_compiled_unclosed_procedure_type:
|
|
return scheme_resolve_closure_compilation(expr, info, 1, 0, 0, NULL);
|
|
case scheme_compiled_let_void_type:
|
|
return scheme_resolve_lets(expr, info);
|
|
case scheme_compiled_toplevel_type:
|
|
return scheme_resolve_toplevel(info, expr);
|
|
case scheme_compiled_quote_syntax_type:
|
|
{
|
|
Scheme_Quote_Syntax *qs;
|
|
int i, c, p;
|
|
|
|
i = SCHEME_LOCAL_POS(expr);
|
|
i = scheme_resolve_quote_syntax_offset(i, info);
|
|
c = scheme_resolve_toplevel_pos(info);
|
|
p = scheme_resolve_quote_syntax_pos(info);
|
|
|
|
qs = MALLOC_ONE_TAGGED(Scheme_Quote_Syntax);
|
|
qs->so.type = scheme_quote_syntax_type;
|
|
qs->depth = c;
|
|
qs->position = i;
|
|
qs->midpoint = p;
|
|
|
|
return (Scheme_Object *)qs;
|
|
}
|
|
case scheme_variable_type:
|
|
case scheme_module_variable_type:
|
|
scheme_signal_error("got top-level in wrong place");
|
|
return 0;
|
|
default:
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
Scheme_Object *scheme_resolve_list(Scheme_Object *expr, Resolve_Info *info)
|
|
{
|
|
Scheme_Object *first = scheme_null, *last = NULL;
|
|
|
|
while (SCHEME_PAIRP(expr)) {
|
|
Scheme_Object *pr;
|
|
|
|
pr = scheme_make_pair(scheme_resolve_expr(SCHEME_CAR(expr), info),
|
|
scheme_null);
|
|
|
|
if (last)
|
|
SCHEME_CDR(last) = pr;
|
|
else
|
|
first = pr;
|
|
last = pr;
|
|
|
|
expr = SCHEME_CDR(expr);
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* uncompile */
|
|
/*========================================================================*/
|
|
|
|
#if 0
|
|
|
|
/* For debugging, currently incomplete: */
|
|
|
|
static Scheme_Object *uncompile(int argc, Scheme_Object *argv[]);
|
|
Scheme_Object *scheme_uncompile_expr(Scheme_Object *expr, Resolve_Prefix *prefix);
|
|
|
|
static Scheme_Object *uncompile_k()
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *expr = (Scheme_Object *)p->ku.k.p1;
|
|
Resolve_Prefix *prefix = (Resolve_Prefix *)p->ku.k.p2;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
|
|
return scheme_uncompile_expr(expr, prefix);
|
|
}
|
|
|
|
Scheme_Object *scheme_uncompile_expr(Scheme_Object *expr, Resolve_Prefix *prefix)
|
|
{
|
|
char buf[32];
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
# include "mzstkchk.h"
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
|
|
p->ku.k.p1 = (void *)expr;
|
|
p->ku.k.p2 = (void *)prefix;
|
|
|
|
return scheme_handle_stack_overflow(uncompile_k);
|
|
}
|
|
#endif
|
|
|
|
switch (SCHEME_TYPE(expr)) {
|
|
case scheme_toplevel_type:
|
|
{
|
|
expr = prefix->toplevels[SCHEME_TOPLEVEL_POS(expr)];
|
|
if (SAME_TYPE(SCHEME_TYPE(expr), scheme_variable_type)) {
|
|
return cons(scheme_intern_symbol("#%top"),
|
|
(Scheme_Object *)((Scheme_Bucket *)expr)->key);
|
|
} else {
|
|
Module_Variable *mv = (Module_Variable *)expr;
|
|
|
|
return cons(scheme_intern_symbol("#%top"),
|
|
cons(mv->modidx, mv->sym));
|
|
}
|
|
}
|
|
case scheme_local_type:
|
|
{
|
|
sprintf(buf, "@%d", SCHEME_LOCAL_POS(expr));
|
|
return scheme_intern_symbol(buf);
|
|
}
|
|
case scheme_local_unbox_type:
|
|
{
|
|
sprintf(buf, "@!%d", SCHEME_LOCAL_POS(expr));
|
|
return scheme_intern_symbol(buf);
|
|
}
|
|
case scheme_compiled_syntax_type:
|
|
{
|
|
return scheme_void;
|
|
}
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
|
|
int i;
|
|
expr = scheme_null;
|
|
for (i = app->num_args + 1; i--; ) {
|
|
expr = cons(scheme_uncompile_expr(app->args[i], prefix),
|
|
expr);
|
|
}
|
|
return expr;
|
|
}
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
|
|
return cons(scheme_uncompile_expr(app->rator, prefix),
|
|
cons(scheme_uncompile_expr(app->rand, prefix),
|
|
scheme_null));
|
|
}
|
|
case scheme_application3_type:
|
|
{
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
|
|
return cons(scheme_uncompile_expr(app->rator, prefix),
|
|
cons(scheme_uncompile_expr(app->rand1, prefix),
|
|
cons(scheme_uncompile_expr(app->rand2, prefix),
|
|
scheme_null)));
|
|
}
|
|
case scheme_sequence_type:
|
|
case scheme_branch_type:
|
|
case scheme_with_cont_mark_type:
|
|
return scheme_void;
|
|
case scheme_let_value_type:
|
|
{
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)expr;
|
|
sprintf(buf, "@%d", lv->position);
|
|
return cons(scheme_intern_symbol("let!"),
|
|
cons(scheme_make_integer(lv->count),
|
|
cons(scheme_intern_symbol(buf),
|
|
cons(scheme_uncompile_expr(lv->value, prefix),
|
|
cons(scheme_uncompile_expr(lv->body, prefix),
|
|
scheme_null)))));
|
|
}
|
|
case scheme_let_void_type:
|
|
{
|
|
Scheme_Let_Void *lv = (Scheme_Let_Void *)expr;
|
|
return cons(scheme_intern_symbol("let-undefined"),
|
|
cons(scheme_make_integer(lv->count),
|
|
cons(scheme_uncompile_expr(lv->body, prefix),
|
|
scheme_null)));
|
|
}
|
|
case scheme_letrec_type:
|
|
{
|
|
Scheme_Letrec *lr = (Scheme_Letrec *)expr;
|
|
int i;
|
|
|
|
expr = scheme_null;
|
|
for (i = lr->count; i--; ) {
|
|
sprintf(buf, "@%d", i);
|
|
expr = cons(cons(scheme_intern_symbol(buf),
|
|
cons(scheme_uncompile_expr(lr->procs[i], prefix),
|
|
scheme_null)),
|
|
expr);
|
|
}
|
|
|
|
return cons(scheme_intern_symbol("letrec!"),
|
|
cons(expr,
|
|
cons(scheme_uncompile_expr(lr->body, prefix),
|
|
scheme_null)));
|
|
}
|
|
case scheme_let_one_type:
|
|
{
|
|
Scheme_Let_One *lo = (Scheme_Let_One *)expr;
|
|
return cons(scheme_intern_symbol("let"),
|
|
cons(scheme_uncompile_expr(lo->value, prefix),
|
|
cons(scheme_uncompile_expr(lo->body, prefix),
|
|
scheme_null)));
|
|
}
|
|
case scheme_unclosed_procedure_type:
|
|
{
|
|
Scheme_Closure_Data *data = (Scheme_Closure_Data *)expr;
|
|
Scheme_Object *vec;
|
|
int i;
|
|
vec = scheme_make_vector(data->closure_size, NULL);
|
|
for (i = data->closure_size; i--; ) {
|
|
SCHEME_VEC_ELS(vec)[i] = scheme_make_integer(data->closure_map[i]);
|
|
}
|
|
return cons(scheme_intern_symbol((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST) ? "lambda*" : "lambda"),
|
|
cons(data->name ? data->name : scheme_false,
|
|
cons(scheme_make_integer(data->num_params),
|
|
cons(vec,
|
|
cons(scheme_uncompile_expr(data->code, prefix),
|
|
scheme_null)))));
|
|
}
|
|
default:
|
|
if (SCHEME_CLOSUREP(expr)) {
|
|
return scheme_uncompile_expr((Scheme_Object *)SCHEME_COMPILED_CLOS_CODE(expr), prefix);
|
|
}
|
|
return cons(scheme_intern_symbol("quote"), cons(expr, scheme_null));
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *
|
|
uncompile(int argc, Scheme_Object *argv[])
|
|
{
|
|
Scheme_Compilation_Top *t;
|
|
|
|
if (!SAME_TYPE(SCHEME_TYPE(argv[0]), scheme_compilation_top_type))
|
|
scheme_wrong_type("compiled->datum", "compiled code", 0, argc, argv);
|
|
|
|
t = (Scheme_Compilation_Top *)argv[0];
|
|
|
|
return scheme_uncompile_expr(t->code, t->prefix);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*========================================================================*/
|
|
/* optimize */
|
|
/*========================================================================*/
|
|
|
|
static Scheme_Object *try_optimize_fold(Scheme_Object *f, Scheme_Object *o, Optimize_Info *info)
|
|
{
|
|
if ((SCHEME_PRIMP(f)
|
|
&& ((((Scheme_Primitive_Proc *)f)->pp.flags & SCHEME_PRIM_OPT_MASK)
|
|
== SCHEME_PRIM_OPT_FOLDING))
|
|
|| (SCHEME_CLSD_PRIMP(f)
|
|
&& ((((Scheme_Closed_Primitive_Proc *)f)->pp.flags & SCHEME_PRIM_OPT_MASK)
|
|
== SCHEME_PRIM_OPT_FOLDING))) {
|
|
Scheme_Object *args;
|
|
|
|
switch (SCHEME_TYPE(o)) {
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)o;
|
|
int i;
|
|
|
|
args = scheme_null;
|
|
for (i = app->num_args; i--; ) {
|
|
args = scheme_make_pair(app->args[i + 1], args);
|
|
}
|
|
}
|
|
break;
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)o;
|
|
args = scheme_make_pair(app->rand, scheme_null);
|
|
}
|
|
break;
|
|
case scheme_application3_type:
|
|
default:
|
|
{
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)o;
|
|
args = scheme_make_pair(app->rand1,
|
|
scheme_make_pair(app->rand2,
|
|
scheme_null));
|
|
}
|
|
break;
|
|
}
|
|
|
|
return try_apply(f, args);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *apply_inlined(Scheme_Object *p, Scheme_Closure_Data *data, Optimize_Info *info,
|
|
int argc, Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3)
|
|
{
|
|
Scheme_Let_Header *lh;
|
|
Scheme_Compiled_Let_Value *lv, *prev = NULL;
|
|
int i;
|
|
int *flags, flag;
|
|
|
|
if (!argc) {
|
|
info = scheme_optimize_info_add_frame(info, 0, 0, 0);
|
|
info->inline_fuel >>= 1;
|
|
p = scheme_optimize_expr(p, info);
|
|
info->next->single_result = info->single_result;
|
|
info->next->preserves_marks = info->preserves_marks;
|
|
scheme_optimize_info_done(info);
|
|
return p;
|
|
}
|
|
|
|
lh = MALLOC_ONE_TAGGED(Scheme_Let_Header);
|
|
lh->iso.so.type = scheme_compiled_let_void_type;
|
|
lh->count = argc;
|
|
lh->num_clauses = argc;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
lv = MALLOC_ONE_TAGGED(Scheme_Compiled_Let_Value);
|
|
lv->so.type = scheme_compiled_let_value_type;
|
|
lv->count = 1;
|
|
lv->position = i;
|
|
|
|
if (app)
|
|
lv->value = app->args[i + 1];
|
|
else if (app3)
|
|
lv->value = (i ? app3->rand2 : app3->rand1);
|
|
else if (app2)
|
|
lv->value = app2->rand;
|
|
|
|
flag = scheme_closure_argument_flags(data, i);
|
|
flags = (int *)scheme_malloc_atomic(sizeof(int));
|
|
flags[0] = flag;
|
|
lv->flags = flags;
|
|
|
|
if (prev)
|
|
prev->body = (Scheme_Object *)lv;
|
|
else
|
|
lh->body = (Scheme_Object *)lv;
|
|
prev = lv;
|
|
}
|
|
|
|
if (prev)
|
|
prev->body = p;
|
|
else
|
|
lh->body = p;
|
|
|
|
return scheme_optimize_lets((Scheme_Object *)lh, info, 1);
|
|
}
|
|
|
|
#if 0
|
|
# define LOG_INLINE(x) x
|
|
#else
|
|
# define LOG_INLINE(x) /*empty*/
|
|
#endif
|
|
|
|
Scheme_Object *optimize_for_inline(Optimize_Info *info, Scheme_Object *le, int argc,
|
|
Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3,
|
|
int *_flags)
|
|
/* If not app, app2, or app3, just return a known procedure, if any */
|
|
{
|
|
int offset = 0;
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(le), scheme_local_type)) {
|
|
/* Check for inlining: */
|
|
le = scheme_optimize_info_lookup(info, SCHEME_LOCAL_POS(le), &offset);
|
|
if (!le)
|
|
return NULL;
|
|
}
|
|
|
|
while (SAME_TYPE(SCHEME_TYPE(le), scheme_compiled_toplevel_type)) {
|
|
if (info->top_level_consts) {
|
|
int pos;
|
|
pos = SCHEME_TOPLEVEL_POS(le);
|
|
le = scheme_hash_get(info->top_level_consts, scheme_make_integer(pos));
|
|
if (!le)
|
|
return NULL;
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
if (le && SAME_TYPE(SCHEME_TYPE(le), scheme_compiled_unclosed_procedure_type)) {
|
|
Scheme_Closure_Data *data = (Scheme_Closure_Data *)le;
|
|
int sz;
|
|
|
|
if (!app && !app2 && !app3) {
|
|
return le;
|
|
}
|
|
|
|
*_flags = SCHEME_CLOSURE_DATA_FLAGS(data);
|
|
|
|
if (data->num_params == argc) {
|
|
sz = scheme_closure_body_size(data, 1);
|
|
if ((sz >= 0) && (sz <= (info->inline_fuel * (argc + 2)))) {
|
|
le = scheme_optimize_clone(0, data->code, info, offset, argc);
|
|
if (le) {
|
|
LOG_INLINE(fprintf(stderr, "Inline %s\n", data->name ? scheme_write_to_string(data->name, NULL) : "???"));
|
|
return apply_inlined(le, data, info, argc, app, app2, app3);
|
|
} else {
|
|
LOG_INLINE(fprintf(stderr, "No inline %s\n", data->name ? scheme_write_to_string(data->name, NULL) : "???"));
|
|
}
|
|
} else {
|
|
LOG_INLINE(fprintf(stderr, "No fuel %s %d*%d/%d\n", data->name ? scheme_write_to_string(data->name, NULL) : "???",
|
|
sz, info->inline_fuel * (argc + 2),
|
|
info->inline_fuel));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (le && SCHEME_PRIMP(le)) {
|
|
int opt;
|
|
opt = ((Scheme_Prim_Proc_Header *)le)->flags & SCHEME_PRIM_OPT_MASK;
|
|
if (opt >= SCHEME_PRIM_OPT_NONCM)
|
|
*_flags = (CLOS_PRESERVES_MARKS | CLOS_SINGLE_RESULT);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void reset_rator(Scheme_Object *app, Scheme_Object *a)
|
|
{
|
|
switch (SCHEME_TYPE(app)) {
|
|
case scheme_application_type:
|
|
((Scheme_App_Rec *)app)->args[0] = a;
|
|
break;
|
|
case scheme_application2_type:
|
|
((Scheme_App2_Rec *)app)->rator = a;
|
|
break;
|
|
case scheme_application3_type:
|
|
((Scheme_App3_Rec *)app)->rator = a;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *check_app_let_rator(Scheme_Object *app, Scheme_Object *rator, Optimize_Info *info, int argc)
|
|
{
|
|
if (SAME_TYPE(SCHEME_TYPE(rator), scheme_compiled_let_void_type)) {
|
|
Scheme_Let_Header *head = (Scheme_Let_Header *)rator;
|
|
|
|
if ((head->count == 1) && (head->num_clauses == 1)) {
|
|
Scheme_Object *body;
|
|
Scheme_Compiled_Let_Value *clv;
|
|
|
|
clv = (Scheme_Compiled_Let_Value *)head->body;
|
|
body = clv->body;
|
|
if (SAME_TYPE(SCHEME_TYPE(body), scheme_local_type)
|
|
&& (SCHEME_LOCAL_POS(body) == 0)
|
|
&& scheme_is_compiled_procedure(clv->value, 1, 1)) {
|
|
|
|
reset_rator(app, scheme_false);
|
|
app = scheme_optimize_shift(app, 1, 0);
|
|
reset_rator(app, scheme_make_local(scheme_local_type, 0, 0));
|
|
|
|
clv->body = app;
|
|
|
|
if (clv->flags[0] & SCHEME_WAS_APPLIED_EXCEPT_ONCE) {
|
|
clv->flags[0] -= SCHEME_WAS_APPLIED_EXCEPT_ONCE;
|
|
clv->flags[0] |= SCHEME_WAS_ONLY_APPLIED;
|
|
}
|
|
|
|
return scheme_optimize_expr(rator, info);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *optimize_application(Scheme_Object *o, Optimize_Info *info)
|
|
{
|
|
Scheme_Object *le;
|
|
Scheme_App_Rec *app;
|
|
int i, n, all_vals = 1, rator_flags = 0;
|
|
|
|
app = (Scheme_App_Rec *)o;
|
|
|
|
le = check_app_let_rator(o, app->args[0], info, app->num_args);
|
|
if (le) return le;
|
|
|
|
n = app->num_args + 1;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (!i) {
|
|
le = optimize_for_inline(info, app->args[i], n - 1, app, NULL, NULL, &rator_flags);
|
|
if (le)
|
|
return le;
|
|
}
|
|
|
|
le = scheme_optimize_expr(app->args[i], info);
|
|
app->args[i] = le;
|
|
|
|
if (i && (SCHEME_TYPE(le) < _scheme_compiled_values_types_))
|
|
all_vals = 0;
|
|
}
|
|
|
|
if (all_vals) {
|
|
le = try_optimize_fold(app->args[0], (Scheme_Object *)app, info);
|
|
if (le)
|
|
return le;
|
|
}
|
|
|
|
info->size += 1;
|
|
|
|
info->preserves_marks = !!(rator_flags & CLOS_PRESERVES_MARKS);
|
|
info->single_result = !!(rator_flags & CLOS_SINGLE_RESULT);
|
|
if (rator_flags & CLOS_RESULT_TENTATIVE) {
|
|
info->preserves_marks = -info->preserves_marks;
|
|
info->single_result = -info->single_result;
|
|
}
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
static Scheme_Object *optimize_application2(Scheme_Object *o, Optimize_Info *info)
|
|
{
|
|
Scheme_App2_Rec *app;
|
|
Scheme_Object *le;
|
|
int rator_flags = 0;
|
|
|
|
app = (Scheme_App2_Rec *)o;
|
|
|
|
le = check_app_let_rator(o, app->rator, info, 1);
|
|
if (le) return le;
|
|
|
|
le = optimize_for_inline(info, app->rator, 1, NULL, app, NULL, &rator_flags);
|
|
if (le)
|
|
return le;
|
|
|
|
le = scheme_optimize_expr(app->rator, info);
|
|
app->rator = le;
|
|
|
|
le = scheme_optimize_expr(app->rand, info);
|
|
app->rand = le;
|
|
if (SCHEME_TYPE(le) > _scheme_compiled_values_types_) {
|
|
le = try_optimize_fold(app->rator, (Scheme_Object *)app, info);
|
|
if (le)
|
|
return le;
|
|
}
|
|
|
|
if (SAME_OBJ(scheme_procedure_p_proc, app->rator)) {
|
|
if (SAME_TYPE(scheme_compiled_unclosed_procedure_type, SCHEME_TYPE(app->rand))) {
|
|
info->preserves_marks = 1;
|
|
info->single_result = 1;
|
|
return scheme_true;
|
|
}
|
|
if (SAME_TYPE(SCHEME_TYPE(app->rand), scheme_local_type)) {
|
|
int offset;
|
|
Scheme_Object *expr;
|
|
expr = scheme_optimize_reverse(info, SCHEME_LOCAL_POS(app->rand), 0);
|
|
if (scheme_optimize_info_lookup(info, SCHEME_LOCAL_POS(expr), &offset)) {
|
|
info->preserves_marks = 1;
|
|
info->single_result = 1;
|
|
return scheme_true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SAME_OBJ(scheme_values_func, app->rator)
|
|
&& scheme_omittable_expr(app->rand, 1, -1, 0)) {
|
|
info->preserves_marks = 1;
|
|
info->single_result = 1;
|
|
return app->rand;
|
|
}
|
|
|
|
info->preserves_marks = !!(rator_flags & CLOS_PRESERVES_MARKS);
|
|
info->single_result = !!(rator_flags & CLOS_SINGLE_RESULT);
|
|
if (rator_flags & CLOS_RESULT_TENTATIVE) {
|
|
info->preserves_marks = -info->preserves_marks;
|
|
info->single_result = -info->single_result;
|
|
}
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
static Scheme_Object *optimize_application3(Scheme_Object *o, Optimize_Info *info)
|
|
{
|
|
Scheme_App3_Rec *app;
|
|
Scheme_Object *le;
|
|
int all_vals = 1;
|
|
int rator_flags = 0;
|
|
|
|
app = (Scheme_App3_Rec *)o;
|
|
|
|
le = check_app_let_rator(o, app->rator, info, 2);
|
|
if (le) return le;
|
|
|
|
le = optimize_for_inline(info, app->rator, 2, NULL, NULL, app, &rator_flags);
|
|
if (le)
|
|
return le;
|
|
|
|
le = scheme_optimize_expr(app->rator, info);
|
|
app->rator = le;
|
|
|
|
/* 1st arg */
|
|
|
|
le = scheme_optimize_expr(app->rand1, info);
|
|
app->rand1 = le;
|
|
|
|
if (SCHEME_TYPE(le) < _scheme_compiled_values_types_)
|
|
all_vals = 0;
|
|
|
|
/* 2nd arg */
|
|
|
|
le = scheme_optimize_expr(app->rand2, info);
|
|
app->rand2 = le;
|
|
|
|
if (SCHEME_TYPE(le) < _scheme_compiled_values_types_)
|
|
all_vals = 0;
|
|
|
|
/* Fold or continue */
|
|
|
|
if (all_vals) {
|
|
le = try_optimize_fold(app->rator, (Scheme_Object *)app, info);
|
|
if (le)
|
|
return le;
|
|
}
|
|
|
|
info->size += 1;
|
|
|
|
/* Check for (call-with-values (lambda () M) N): */
|
|
if (SAME_OBJ(app->rator, scheme_call_with_values_proc)) {
|
|
if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_compiled_unclosed_procedure_type)) {
|
|
Scheme_Closure_Data *data = (Scheme_Closure_Data *)app->rand1;
|
|
|
|
if (!data->num_params) {
|
|
/* Convert to apply-values form: */
|
|
return scheme_optimize_apply_values(app->rand2, data->code, info,
|
|
((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_SINGLE_RESULT)
|
|
? ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_RESULT_TENTATIVE)
|
|
? -1
|
|
: 1)
|
|
: 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
info->preserves_marks = !!(rator_flags & CLOS_PRESERVES_MARKS);
|
|
info->single_result = !!(rator_flags & CLOS_SINGLE_RESULT);
|
|
if (rator_flags & CLOS_RESULT_TENTATIVE) {
|
|
info->preserves_marks = -info->preserves_marks;
|
|
info->single_result = -info->single_result;
|
|
}
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
Scheme_Object *scheme_optimize_apply_values(Scheme_Object *f, Scheme_Object *e,
|
|
Optimize_Info *info,
|
|
int e_single_result)
|
|
/* f and e are already optimized */
|
|
{
|
|
Scheme_Object *f_is_proc = NULL;
|
|
|
|
info->preserves_marks = 0;
|
|
info->single_result = 0;
|
|
|
|
{
|
|
Scheme_Object *rev;
|
|
if (SAME_TYPE(SCHEME_TYPE(f), scheme_local_type)) {
|
|
rev = scheme_optimize_reverse(info, SCHEME_LOCAL_POS(f), 1);
|
|
} else
|
|
rev = f;
|
|
|
|
if (rev) {
|
|
int rator2_flags;
|
|
Scheme_Object *o_f;
|
|
o_f = optimize_for_inline(info, rev, 1, NULL, NULL, NULL, &rator2_flags);
|
|
if (o_f) {
|
|
f_is_proc = rev;
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(o_f), scheme_compiled_unclosed_procedure_type)) {
|
|
Scheme_Closure_Data *data2 = (Scheme_Closure_Data *)o_f;
|
|
int flags = SCHEME_CLOSURE_DATA_FLAGS(data2);
|
|
info->preserves_marks = !!(flags & CLOS_PRESERVES_MARKS);
|
|
info->single_result = !!(flags & CLOS_SINGLE_RESULT);
|
|
if (flags & CLOS_RESULT_TENTATIVE) {
|
|
info->preserves_marks = -info->preserves_marks;
|
|
info->single_result = -info->single_result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!f_is_proc && SCHEME_PROCP(f)) {
|
|
f_is_proc = f;
|
|
}
|
|
}
|
|
|
|
if (f_is_proc && (e_single_result > 0)) {
|
|
/* Just make it an application (N M): */
|
|
Scheme_App2_Rec *app2;
|
|
Scheme_Object *cloned, *f_cloned;
|
|
|
|
app2 = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
|
|
app2->iso.so.type = scheme_application2_type;
|
|
|
|
/* We'd like to try to inline here. The problem is that
|
|
e (the argument) has been optimized already,
|
|
which means it's in the wrong coordinate system.
|
|
If we can shift-clone it, then it will be back in the right
|
|
coordinates. */
|
|
|
|
cloned = scheme_optimize_clone(1, e, info, 0, 0);
|
|
if (cloned) {
|
|
if (SAME_TYPE(SCHEME_TYPE(f_is_proc), scheme_compiled_unclosed_procedure_type))
|
|
f_cloned = scheme_optimize_clone(1, f_is_proc, info, 0, 0);
|
|
else {
|
|
/* Otherwise, no clone is needed; in the case of a lexical
|
|
variable, we already reversed it. */
|
|
f_cloned = f_is_proc;
|
|
}
|
|
|
|
if (f_cloned) {
|
|
app2->rator = f_cloned;
|
|
app2->rand = cloned;
|
|
info->inline_fuel >>= 1; /* because we've already optimized the rand */
|
|
return optimize_application2((Scheme_Object *)app2, info);
|
|
}
|
|
}
|
|
|
|
app2->rator = f;
|
|
app2->rand = e;
|
|
return (Scheme_Object *)app2;
|
|
}
|
|
|
|
return scheme_make_syntax_compiled(APPVALS_EXPD, cons(f, e));
|
|
}
|
|
|
|
static Scheme_Object *optimize_sequence(Scheme_Object *o, Optimize_Info *info)
|
|
{
|
|
Scheme_Sequence *s = (Scheme_Sequence *)o;
|
|
Scheme_Object *le;
|
|
int i;
|
|
int drop = 0, preserves_marks = 0, single_result = 0;
|
|
|
|
for (i = s->count; i--; ) {
|
|
le = scheme_optimize_expr(s->array[i], info);
|
|
if (i == s->count - 1) {
|
|
single_result = info->single_result;
|
|
preserves_marks = info->preserves_marks;
|
|
}
|
|
|
|
/* Inlining and constant propagation can expose
|
|
omittable expressions. */
|
|
if ((i + 1 != s->count)
|
|
&& scheme_omittable_expr(le, -1, -1, 0)) {
|
|
drop++;
|
|
s->array[i] = NULL;
|
|
} else {
|
|
s->array[i] = le;
|
|
}
|
|
}
|
|
|
|
info->preserves_marks = preserves_marks;
|
|
info->single_result = single_result;
|
|
|
|
if (drop + 1 == s->count) {
|
|
return s->array[drop];
|
|
} else if (drop) {
|
|
Scheme_Sequence *s2;
|
|
int j = 0;
|
|
|
|
s2 = malloc_sequence(s->count - drop);
|
|
s2->so.type = scheme_sequence_type;
|
|
s2->count = s->count - drop;
|
|
|
|
for (i = 0; i < s->count; i++) {
|
|
if (s->array[i]) {
|
|
s2->array[j++] = s->array[i];
|
|
}
|
|
}
|
|
|
|
s = s2;
|
|
}
|
|
|
|
info->size += 1;
|
|
|
|
return (Scheme_Object *)s;
|
|
}
|
|
|
|
int scheme_compiled_duplicate_ok(Scheme_Object *fb)
|
|
{
|
|
return (SCHEME_VOIDP(fb)
|
|
|| SAME_OBJ(fb, scheme_true)
|
|
|| SCHEME_FALSEP(fb)
|
|
|| SCHEME_SYMBOLP(fb)
|
|
|| SCHEME_KEYWORDP(fb)
|
|
|| SCHEME_EOFP(fb)
|
|
|| SCHEME_INTP(fb)
|
|
|| SCHEME_NULLP(fb)
|
|
|| (SCHEME_CHARP(fb) && (SCHEME_CHAR_VAL(fb) < 256))
|
|
|| SAME_TYPE(SCHEME_TYPE(fb), scheme_local_type)
|
|
/* Values that are hashed by the printer to avoid
|
|
duplication: */
|
|
|| SCHEME_CHAR_STRINGP(fb)
|
|
|| SCHEME_BYTE_STRINGP(fb)
|
|
|| SAME_TYPE(SCHEME_TYPE(fb), scheme_regexp_type)
|
|
|| SCHEME_NUMBERP(fb)
|
|
|| SCHEME_PRIMP(fb));
|
|
}
|
|
|
|
static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info)
|
|
{
|
|
Scheme_Branch_Rec *b;
|
|
Scheme_Object *t, *tb, *fb;
|
|
int preserves_marks = 1, single_result = 1;
|
|
|
|
b = (Scheme_Branch_Rec *)o;
|
|
|
|
t = b->test;
|
|
tb = b->tbranch;
|
|
fb = b->fbranch;
|
|
|
|
/* Try optimize: (if (not x) y z) => (if x z y) */
|
|
while (1) {
|
|
if (SAME_TYPE(SCHEME_TYPE(t), scheme_application2_type)) {
|
|
Scheme_App2_Rec *app;
|
|
|
|
app = (Scheme_App2_Rec *)t;
|
|
if (SAME_PTR(scheme_not_prim, app->rator)) {
|
|
t = tb;
|
|
tb = fb;
|
|
fb = t;
|
|
t = app->rand;
|
|
} else
|
|
break;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(t), scheme_compiled_let_void_type)) {
|
|
/* Maybe convert: (let ([x M]) (if x x N)) => (if M #t N) */
|
|
t = scheme_optimize_lets_for_test(t, info);
|
|
} else
|
|
t = scheme_optimize_expr(t, info);
|
|
|
|
if (SCHEME_TYPE(t) > _scheme_compiled_values_types_) {
|
|
if (SCHEME_FALSEP(t))
|
|
return scheme_optimize_expr(fb, info);
|
|
else
|
|
return scheme_optimize_expr(tb, info);
|
|
} else if (SAME_TYPE(SCHEME_TYPE(t), scheme_compiled_quote_syntax_type)
|
|
|| SAME_TYPE(SCHEME_TYPE(t), scheme_compiled_unclosed_procedure_type))
|
|
return scheme_optimize_expr(tb, info);
|
|
|
|
tb = scheme_optimize_expr(tb, info);
|
|
|
|
if (!info->preserves_marks)
|
|
preserves_marks = 0;
|
|
else if (info->preserves_marks < 0)
|
|
preserves_marks = -1;
|
|
if (!info->single_result)
|
|
single_result = 0;
|
|
else if (info->single_result < 0)
|
|
single_result = -1;
|
|
|
|
fb = scheme_optimize_expr(fb, info);
|
|
|
|
if (!info->preserves_marks)
|
|
preserves_marks = 0;
|
|
else if (preserves_marks && (info->preserves_marks < 0))
|
|
preserves_marks = -1;
|
|
if (!info->single_result)
|
|
single_result = 0;
|
|
else if (single_result && (info->single_result < 0))
|
|
single_result = -1;
|
|
|
|
info->preserves_marks = preserves_marks;
|
|
info->single_result = single_result;
|
|
|
|
/* Try optimize: (if x x #f) => x */
|
|
if (SAME_TYPE(SCHEME_TYPE(t), scheme_local_type)
|
|
&& SAME_TYPE(SCHEME_TYPE(tb), scheme_local_type)
|
|
&& (SCHEME_LOCAL_POS(t) == SCHEME_LOCAL_POS(tb))
|
|
&& SCHEME_FALSEP(fb)) {
|
|
return t;
|
|
}
|
|
|
|
/* Convert: (if (if M N #f) M2 K) => (if M (if N M2 K) K)
|
|
for simple constants K. This is useful to expose simple
|
|
tests to the JIT. */
|
|
if (SAME_TYPE(SCHEME_TYPE(t), scheme_branch_type)
|
|
&& scheme_compiled_duplicate_ok(fb)) {
|
|
Scheme_Branch_Rec *b2 = (Scheme_Branch_Rec *)t;
|
|
if (SCHEME_FALSEP(b2->fbranch)) {
|
|
Scheme_Branch_Rec *b3;
|
|
b3 = MALLOC_ONE_TAGGED(Scheme_Branch_Rec);
|
|
b3->so.type = scheme_branch_type;
|
|
b3->test = b2->tbranch;
|
|
b3->tbranch = tb;
|
|
b3->fbranch = fb;
|
|
t = b2->test;
|
|
tb = (Scheme_Object *)b3;
|
|
}
|
|
}
|
|
|
|
b->test = t;
|
|
b->tbranch = tb;
|
|
b->fbranch = fb;
|
|
|
|
info->size += 1;
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *optimize_wcm(Scheme_Object *o, Optimize_Info *info)
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)o;
|
|
Scheme_Object *k, *v, *b;
|
|
|
|
k = scheme_optimize_expr(wcm->key, info);
|
|
|
|
v = scheme_optimize_expr(wcm->val, info);
|
|
|
|
b = scheme_optimize_expr(wcm->body, info);
|
|
|
|
/* info->single_result is already set */
|
|
info->preserves_marks = 0;
|
|
|
|
wcm->key = k;
|
|
wcm->val = v;
|
|
wcm->body = b;
|
|
|
|
info->size += 1;
|
|
|
|
return (Scheme_Object *)wcm;
|
|
}
|
|
|
|
static Scheme_Object *optimize_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *expr = (Scheme_Object *)p->ku.k.p1;
|
|
Optimize_Info *info = (Optimize_Info *)p->ku.k.p2;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
|
|
return scheme_optimize_expr(expr, info);
|
|
}
|
|
|
|
Scheme_Object *scheme_optimize_expr(Scheme_Object *expr, Optimize_Info *info)
|
|
{
|
|
Scheme_Type type = SCHEME_TYPE(expr);
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
# include "mzstkchk.h"
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
|
|
p->ku.k.p1 = (void *)expr;
|
|
p->ku.k.p2 = (void *)info;
|
|
|
|
return scheme_handle_stack_overflow(optimize_k);
|
|
}
|
|
#endif
|
|
|
|
info->preserves_marks = 1;
|
|
info->single_result = 1;
|
|
|
|
switch (type) {
|
|
case scheme_local_type:
|
|
{
|
|
Scheme_Object *val;
|
|
int pos, delta;
|
|
|
|
info->size += 1;
|
|
|
|
pos = SCHEME_LOCAL_POS(expr);
|
|
|
|
val = scheme_optimize_info_lookup(info, pos, NULL);
|
|
if (val) {
|
|
if (SAME_TYPE(SCHEME_TYPE(val), scheme_compiled_toplevel_type))
|
|
return scheme_optimize_expr(val, info);
|
|
return val;
|
|
}
|
|
|
|
delta = scheme_optimize_info_get_shift(info, pos);
|
|
if (delta)
|
|
expr = scheme_make_local(scheme_local_type, pos + delta, 0);
|
|
|
|
return expr;
|
|
}
|
|
case scheme_compiled_syntax_type:
|
|
{
|
|
Scheme_Syntax_Optimizer f;
|
|
|
|
f = scheme_syntax_optimizers[SCHEME_PINT_VAL(expr)];
|
|
return f((Scheme_Object *)SCHEME_IPTR_VAL(expr), info);
|
|
}
|
|
case scheme_application_type:
|
|
return optimize_application(expr, info);
|
|
case scheme_application2_type:
|
|
return optimize_application2(expr, info);
|
|
case scheme_application3_type:
|
|
return optimize_application3(expr, info);
|
|
case scheme_sequence_type:
|
|
return optimize_sequence(expr, info);
|
|
case scheme_branch_type:
|
|
return optimize_branch(expr, info);
|
|
case scheme_with_cont_mark_type:
|
|
return optimize_wcm(expr, info);
|
|
case scheme_compiled_unclosed_procedure_type:
|
|
return scheme_optimize_closure_compilation(expr, info);
|
|
case scheme_compiled_let_void_type:
|
|
return scheme_optimize_lets(expr, info, 0);
|
|
case scheme_compiled_toplevel_type:
|
|
if (info->top_level_consts) {
|
|
int pos;
|
|
Scheme_Object *c;
|
|
|
|
while (1) {
|
|
pos = SCHEME_TOPLEVEL_POS(expr);
|
|
c = scheme_hash_get(info->top_level_consts, scheme_make_integer(pos));
|
|
if (c && SAME_TYPE(SCHEME_TYPE(c), scheme_compiled_toplevel_type))
|
|
expr = c;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (c) {
|
|
if (scheme_compiled_duplicate_ok(c))
|
|
return c;
|
|
|
|
/* We can't inline, but mark the top level as a constant,
|
|
so we can direct-jump and avoid null checks in JITed code: */
|
|
expr = scheme_toplevel_to_flagged_toplevel(expr, SCHEME_TOPLEVEL_CONST);
|
|
} else {
|
|
/* false is mapped to a table of non-constant ready values: */
|
|
c = scheme_hash_get(info->top_level_consts, scheme_false);
|
|
if (c) {
|
|
c = scheme_hash_get((Scheme_Hash_Table *)c, scheme_make_integer(pos));
|
|
|
|
if (c) {
|
|
/* We can't inline, but mark the top level as ready,
|
|
so we can avoid null checks in JITed code: */
|
|
expr = scheme_toplevel_to_flagged_toplevel(expr, SCHEME_TOPLEVEL_READY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
scheme_optimize_info_used_top(info);
|
|
return expr;
|
|
case scheme_compiled_quote_syntax_type:
|
|
scheme_optimize_info_used_top(info);
|
|
return expr;
|
|
case scheme_variable_type:
|
|
case scheme_module_variable_type:
|
|
scheme_signal_error("got top-level in wrong place");
|
|
return 0;
|
|
default:
|
|
info->size += 1;
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
Scheme_Object *scheme_optimize_clone(int dup_ok, Scheme_Object *expr, Optimize_Info *info, int delta, int closure_depth)
|
|
/* Past closure_depth, need to reverse optimize to unoptimzed with respect to info;
|
|
delta is the amount to skip in info to get to the frame that bound the code.
|
|
If dup_ok is 1, then the old copy will be dropped, so it's ok to "duplicate"
|
|
any constant. */
|
|
{
|
|
int t;
|
|
|
|
t = SCHEME_TYPE(expr);
|
|
|
|
switch(t) {
|
|
case scheme_local_type:
|
|
{
|
|
int pos = SCHEME_LOCAL_POS(expr);
|
|
if (pos >= closure_depth) {
|
|
expr = scheme_optimize_reverse(info, pos + delta - closure_depth, 0);
|
|
if (closure_depth)
|
|
expr = scheme_make_local(scheme_local_type, SCHEME_LOCAL_POS(expr) + closure_depth, 0);
|
|
}
|
|
return expr;
|
|
}
|
|
case scheme_compiled_syntax_type:
|
|
{
|
|
Scheme_Syntax_Cloner f;
|
|
|
|
f = scheme_syntax_cloners[SCHEME_PINT_VAL(expr)];
|
|
if (!f) return NULL;
|
|
return f(dup_ok, (Scheme_Object *)SCHEME_IPTR_VAL(expr), info, delta, closure_depth);
|
|
}
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr, *app2;
|
|
|
|
app2 = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
|
|
app2->iso.so.type = scheme_application2_type;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, app->rator, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
app2->rator = expr;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, app->rand, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
app2->rand = expr;
|
|
|
|
return (Scheme_Object *)app2;
|
|
}
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)expr, *app2;
|
|
int i;
|
|
|
|
app2 = scheme_malloc_application(app->num_args + 1);
|
|
|
|
for (i = app->num_args + 1; i--; ) {
|
|
expr = scheme_optimize_clone(dup_ok, app->args[i], info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
app2->args[i] = expr;
|
|
}
|
|
|
|
return (Scheme_Object *)app2;
|
|
}
|
|
case scheme_application3_type:
|
|
{
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr, *app2;
|
|
|
|
app2 = MALLOC_ONE_TAGGED(Scheme_App3_Rec);
|
|
app2->iso.so.type = scheme_application3_type;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, app->rator, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
app2->rator = expr;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, app->rand1, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
app2->rand1 = expr;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, app->rand2, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
app2->rand2 = expr;
|
|
|
|
return (Scheme_Object *)app2;
|
|
}
|
|
case scheme_compiled_let_void_type:
|
|
{
|
|
Scheme_Let_Header *head = (Scheme_Let_Header *)expr, *head2;
|
|
Scheme_Object *body;
|
|
Scheme_Compiled_Let_Value *lv, *lv2, *prev = NULL;
|
|
int i, *flags, sz;
|
|
|
|
head2 = MALLOC_ONE_TAGGED(Scheme_Let_Header);
|
|
head2->iso.so.type = scheme_compiled_let_void_type;
|
|
head2->count = head->count;
|
|
head2->num_clauses = head->num_clauses;
|
|
SCHEME_LET_FLAGS(head2) = SCHEME_LET_FLAGS(head);
|
|
|
|
/* Build let-value change: */
|
|
body = head->body;
|
|
for (i = head->num_clauses; i--; ) {
|
|
lv = (Scheme_Compiled_Let_Value *)body;
|
|
|
|
sz = sizeof(int) * lv->count;
|
|
flags = (int *)scheme_malloc_atomic(sz);
|
|
memcpy(flags, lv->flags, sz);
|
|
|
|
lv2 = MALLOC_ONE_TAGGED(Scheme_Compiled_Let_Value);
|
|
lv2->so.type = scheme_compiled_let_value_type;
|
|
lv2->count = lv->count;
|
|
lv2->position = lv->position;
|
|
lv2->flags = flags;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, lv->value, info, delta, closure_depth + head->count);
|
|
if (!expr) return NULL;
|
|
lv2->value = expr;
|
|
|
|
if (prev)
|
|
prev->body = (Scheme_Object *)lv2;
|
|
else
|
|
head2->body = (Scheme_Object *)lv2;
|
|
prev = lv2;
|
|
|
|
body = lv->body;
|
|
}
|
|
if (prev)
|
|
prev->body = body;
|
|
else
|
|
head2->body = body;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, body, info, delta, closure_depth + head->count);
|
|
if (!expr) return NULL;
|
|
|
|
if (prev)
|
|
prev->body = expr;
|
|
else
|
|
head2->body = expr;
|
|
|
|
return (Scheme_Object *)head2;
|
|
}
|
|
case scheme_sequence_type:
|
|
case scheme_begin0_sequence_type:
|
|
{
|
|
Scheme_Sequence *seq = (Scheme_Sequence *)expr, *seq2;
|
|
int i;
|
|
|
|
seq2 = malloc_sequence(seq->count);
|
|
seq2->so.type = seq->so.type;
|
|
seq2->count = seq->count;
|
|
|
|
for (i = seq->count; i--; ) {
|
|
expr = scheme_optimize_clone(dup_ok, seq->array[i], info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
seq2->array[i] = expr;
|
|
}
|
|
|
|
return (Scheme_Object *)seq2;
|
|
}
|
|
case scheme_branch_type:
|
|
{
|
|
Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr, *b2;
|
|
|
|
b2 = MALLOC_ONE_TAGGED(Scheme_Branch_Rec);
|
|
b2->so.type = scheme_branch_type;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, b->test, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
b2->test = expr;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, b->tbranch, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
b2->tbranch = expr;
|
|
|
|
expr = scheme_optimize_clone(dup_ok, b->fbranch, info, delta, closure_depth);
|
|
if (!expr) return NULL;
|
|
b2->fbranch = expr;
|
|
|
|
return (Scheme_Object *)b2;
|
|
}
|
|
case scheme_compiled_unclosed_procedure_type:
|
|
return scheme_clone_closure_compilation(dup_ok, expr, info, delta, closure_depth);
|
|
case scheme_compiled_toplevel_type:
|
|
case scheme_compiled_quote_syntax_type:
|
|
return expr;
|
|
default:
|
|
if (t > _scheme_compiled_values_types_) {
|
|
if (dup_ok || scheme_compiled_duplicate_ok(expr))
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Scheme_Object *scheme_optimize_shift(Scheme_Object *expr, int delta, int after_depth)
|
|
/* Shift lexical addresses deeper by delta if already deeper than after_depth;
|
|
can mutate. */
|
|
{
|
|
int t;
|
|
|
|
/* FIXME: need stack check */
|
|
|
|
t = SCHEME_TYPE(expr);
|
|
|
|
switch(t) {
|
|
case scheme_local_type:
|
|
case scheme_local_unbox_type:
|
|
{
|
|
int pos = SCHEME_LOCAL_POS(expr);
|
|
if (pos >= after_depth) {
|
|
expr = scheme_make_local(t, SCHEME_LOCAL_POS(expr) + delta, 0);
|
|
}
|
|
return expr;
|
|
}
|
|
case scheme_compiled_syntax_type:
|
|
{
|
|
Scheme_Syntax_Shifter f;
|
|
|
|
f = scheme_syntax_shifters[SCHEME_PINT_VAL(expr)];
|
|
|
|
if (!f) {
|
|
scheme_signal_error("scheme_optimize_shift: no shift available for %d", SCHEME_PINT_VAL(expr));
|
|
return NULL;
|
|
}
|
|
return f((Scheme_Object *)SCHEME_IPTR_VAL(expr), delta, after_depth);
|
|
}
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
|
|
int i;
|
|
|
|
for (i = app->num_args + 1; i--; ) {
|
|
expr = scheme_optimize_shift(app->args[i], delta, after_depth);
|
|
app->args[i] = expr;
|
|
}
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
|
|
|
|
expr = scheme_optimize_shift(app->rator, delta, after_depth);
|
|
app->rator = expr;
|
|
|
|
expr = scheme_optimize_shift(app->rand, delta, after_depth);
|
|
app->rand = expr;
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
case scheme_application3_type:
|
|
{
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
|
|
|
|
expr = scheme_optimize_shift(app->rator, delta, after_depth);
|
|
app->rator = expr;
|
|
|
|
expr = scheme_optimize_shift(app->rand1, delta, after_depth);
|
|
app->rand1 = expr;
|
|
|
|
expr = scheme_optimize_shift(app->rand2, delta, after_depth);
|
|
app->rand2 = expr;
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
case scheme_compiled_let_void_type:
|
|
{
|
|
Scheme_Let_Header *head = (Scheme_Let_Header *)expr;
|
|
Scheme_Object *body;
|
|
Scheme_Compiled_Let_Value *lv = NULL;
|
|
int i;
|
|
|
|
/* Build let-value change: */
|
|
body = head->body;
|
|
for (i = head->num_clauses; i--; ) {
|
|
lv = (Scheme_Compiled_Let_Value *)body;
|
|
|
|
expr = scheme_optimize_shift(lv->value, delta, after_depth + head->count);
|
|
lv->value = expr;
|
|
|
|
body = lv->body;
|
|
}
|
|
expr = scheme_optimize_shift(body, delta, after_depth + head->count);
|
|
|
|
if (head->num_clauses)
|
|
lv->body = expr;
|
|
else
|
|
head->body = expr;
|
|
|
|
return (Scheme_Object *)head;
|
|
}
|
|
case scheme_sequence_type:
|
|
case scheme_begin0_sequence_type:
|
|
{
|
|
Scheme_Sequence *seq = (Scheme_Sequence *)expr;
|
|
int i;
|
|
|
|
for (i = seq->count; i--; ) {
|
|
expr = scheme_optimize_shift(seq->array[i], delta, after_depth);
|
|
seq->array[i] = expr;
|
|
}
|
|
|
|
return (Scheme_Object *)seq;
|
|
}
|
|
case scheme_branch_type:
|
|
{
|
|
Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr;
|
|
|
|
expr = scheme_optimize_shift(b->test, delta, after_depth);
|
|
b->test = expr;
|
|
|
|
expr = scheme_optimize_shift(b->tbranch, delta, after_depth);
|
|
b->tbranch = expr;
|
|
|
|
expr = scheme_optimize_shift(b->fbranch, delta, after_depth);
|
|
b->fbranch = expr;
|
|
|
|
return (Scheme_Object *)b;
|
|
}
|
|
case scheme_with_cont_mark_type:
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)expr;
|
|
|
|
expr = scheme_optimize_shift(wcm->key, delta, after_depth);
|
|
wcm->key = expr;
|
|
|
|
expr = scheme_optimize_shift(wcm->val, delta, after_depth);
|
|
wcm->val = expr;
|
|
|
|
expr = scheme_optimize_shift(wcm->body, delta, after_depth);
|
|
wcm->body = expr;
|
|
|
|
return (Scheme_Object *)wcm;
|
|
}
|
|
case scheme_compiled_unclosed_procedure_type:
|
|
return scheme_shift_closure_compilation(expr, delta, after_depth);
|
|
case scheme_compiled_toplevel_type:
|
|
case scheme_compiled_quote_syntax_type:
|
|
return expr;
|
|
default:
|
|
return expr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* sfs */
|
|
/*========================================================================*/
|
|
|
|
/* For debugging and measuring the worst-case cost of sfs clears: */
|
|
#define MAX_SFS_CLEARING 0
|
|
|
|
#define SFS_LOG(x) /* nothing */
|
|
|
|
Scheme_Object *scheme_sfs(Scheme_Object *o, SFS_Info *info, int max_let_depth)
|
|
{
|
|
int init, i;
|
|
|
|
SFS_LOG(printf("sfs %d\n", SCHEME_TYPE(o)));
|
|
|
|
if (!info) {
|
|
info = scheme_new_sfs_info(max_let_depth);
|
|
}
|
|
|
|
info->pass = 0;
|
|
info->ip = 1;
|
|
info->saved = scheme_null;
|
|
info->min_touch = -1;
|
|
info->max_touch = -1;
|
|
info->tail_pos = 1;
|
|
init = info->stackpos;
|
|
o = scheme_sfs_expr(o, info, -1);
|
|
|
|
if (info->seqn)
|
|
scheme_signal_error("ended in the middle of an expression?");
|
|
|
|
# if MAX_SFS_CLEARING
|
|
info->max_nontail = info->ip;
|
|
# endif
|
|
|
|
for (i = info->depth; i-- > init; ) {
|
|
info->max_calls[i] = info->max_nontail;
|
|
}
|
|
|
|
{
|
|
Scheme_Object *v;
|
|
v = scheme_reverse(info->saved);
|
|
info->saved = v;
|
|
}
|
|
|
|
info->pass = 1;
|
|
info->seqn = 0;
|
|
info->ip = 1;
|
|
info->tail_pos = 1;
|
|
info->stackpos = init;
|
|
o = scheme_sfs_expr(o, info, -1);
|
|
|
|
return o;
|
|
}
|
|
|
|
SFS_Info *scheme_new_sfs_info(int depth)
|
|
{
|
|
SFS_Info *info;
|
|
int *max_used, *max_calls;
|
|
|
|
info = MALLOC_ONE_RT(SFS_Info);
|
|
SET_REQUIRED_TAG(info->type = scheme_rt_sfs_info);
|
|
|
|
info->depth = depth;
|
|
info->stackpos = depth;
|
|
info->tlpos = depth;
|
|
|
|
max_used = (int *)scheme_malloc_atomic(sizeof(int) * depth);
|
|
max_calls = (int *)scheme_malloc_atomic(sizeof(int) * depth);
|
|
|
|
memset(max_used, 0, sizeof(int) * depth);
|
|
memset(max_calls, 0, sizeof(int) * depth);
|
|
|
|
info->max_used = max_used;
|
|
info->max_calls = max_calls;
|
|
|
|
return info;
|
|
}
|
|
|
|
static void scheme_sfs_save(SFS_Info *info, Scheme_Object *v)
|
|
{
|
|
if (info->pass)
|
|
scheme_signal_error("internal error: wrong pass to save info");
|
|
v = scheme_make_pair(v, info->saved);
|
|
info->saved = v;
|
|
}
|
|
|
|
static Scheme_Object *scheme_sfs_next_saved(SFS_Info *info)
|
|
{
|
|
Scheme_Object *v;
|
|
|
|
if (!info->pass)
|
|
scheme_signal_error("internal error: wrong pass to get saved info");
|
|
if (!SCHEME_PAIRP(info->saved))
|
|
scheme_signal_error("internal error: no saved info");
|
|
|
|
v = SCHEME_CAR(info->saved);
|
|
info->saved = SCHEME_CDR(info->saved);
|
|
return v;
|
|
}
|
|
|
|
void scheme_sfs_start_sequence(SFS_Info *info, int cnt, int last_is_tail)
|
|
{
|
|
info->seqn += (cnt - (last_is_tail ? 1 : 0));
|
|
}
|
|
|
|
void scheme_sfs_push(SFS_Info *info, int cnt, int track)
|
|
{
|
|
info->stackpos -= cnt;
|
|
|
|
if (info->stackpos < 0)
|
|
scheme_signal_error("internal error: pushed too deep");
|
|
|
|
if (track) {
|
|
while (cnt--) {
|
|
scheme_sfs_used(info, cnt);
|
|
}
|
|
}
|
|
}
|
|
|
|
void scheme_sfs_used(SFS_Info *info, int pos)
|
|
{
|
|
if (info->pass)
|
|
return;
|
|
|
|
pos += info->stackpos;
|
|
|
|
if ((pos < 0) || (pos >= info->depth)) {
|
|
scheme_signal_error("internal error: stack use out of bounds");
|
|
}
|
|
if (pos == info->tlpos)
|
|
scheme_signal_error("internal error: misuse of toplevel pointer");
|
|
|
|
SFS_LOG(printf("touch %d %d\n", pos, info->ip));
|
|
|
|
if ((info->min_touch == -1)
|
|
|| (pos < info->min_touch))
|
|
info->min_touch = pos;
|
|
if (pos > info->max_touch)
|
|
info->max_touch = pos;
|
|
|
|
info->max_used[pos] = info->ip;
|
|
}
|
|
|
|
Scheme_Object *scheme_sfs_add_clears(Scheme_Object *expr, Scheme_Object *clears, int pre)
|
|
{
|
|
int len, i;
|
|
Scheme_Object *loc;
|
|
Scheme_Sequence *s;
|
|
|
|
if (SCHEME_NULLP(clears))
|
|
return expr;
|
|
|
|
len = scheme_list_length(clears);
|
|
|
|
s = malloc_sequence(len + 1);
|
|
s->so.type = (pre ? scheme_sequence_type : scheme_begin0_sequence_type);
|
|
s->count = len + 1;
|
|
s->array[pre ? len : 0] = expr;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
loc = scheme_make_local(scheme_local_type,
|
|
SCHEME_INT_VAL(SCHEME_CAR(clears)),
|
|
SCHEME_LOCAL_CLEAR_ON_READ);
|
|
s->array[i + (pre ? 0 : 1)] = loc;
|
|
clears = SCHEME_CDR(clears);
|
|
}
|
|
|
|
if (pre)
|
|
return (Scheme_Object *)s;
|
|
else
|
|
return scheme_make_syntax_resolved(BEGIN0_EXPD, (Scheme_Object *)s);
|
|
}
|
|
|
|
static void sfs_note_app(SFS_Info *info, Scheme_Object *rator)
|
|
{
|
|
if (!info->pass) {
|
|
if (!info->tail_pos) {
|
|
if (SCHEME_PRIMP(rator)) {
|
|
int opt;
|
|
opt = ((Scheme_Prim_Proc_Header *)rator)->flags & SCHEME_PRIM_OPT_MASK;
|
|
if (opt >= SCHEME_PRIM_OPT_IMMEDIATE)
|
|
/* Don't need to clear stack before an immediate/folding call */
|
|
return;
|
|
}
|
|
info->max_nontail = info->ip;
|
|
} else {
|
|
if (!MAX_SFS_CLEARING && (info->selfpos >= 0)) {
|
|
if (SAME_TYPE(SCHEME_TYPE(rator), scheme_local_type)) {
|
|
if ((SCHEME_LOCAL_POS(rator) + info->stackpos) == info->selfpos) {
|
|
/* No point in clearing out any of the closure before the
|
|
tail call. */
|
|
int i;
|
|
for (i = info->selflen; i--; ) {
|
|
if ((info->selfstart + i) != info->tlpos)
|
|
scheme_sfs_used(info, (info->selfstart - info->stackpos) + i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *sfs_application(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_Object *orig, *naya = NULL;
|
|
Scheme_App_Rec *app;
|
|
int i, n;
|
|
|
|
app = (Scheme_App_Rec *)o;
|
|
n = app->num_args + 1;
|
|
|
|
scheme_sfs_start_sequence(info, n, 0);
|
|
scheme_sfs_push(info, n-1, 0);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
orig = app->args[i];
|
|
naya = scheme_sfs_expr(orig, info, -1);
|
|
app->args[i] = naya;
|
|
}
|
|
|
|
sfs_note_app(info, app->args[0]);
|
|
|
|
scheme_finish_application(app);
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_application2(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_App2_Rec *app;
|
|
Scheme_Object *nrator, *nrand;
|
|
|
|
app = (Scheme_App2_Rec *)o;
|
|
|
|
scheme_sfs_start_sequence(info, 2, 0);
|
|
scheme_sfs_push(info, 1, 0);
|
|
|
|
nrator = scheme_sfs_expr(app->rator, info, -1);
|
|
nrand = scheme_sfs_expr(app->rand, info, -1);
|
|
app->rator = nrator;
|
|
app->rand = nrand;
|
|
|
|
sfs_note_app(info, app->rator);
|
|
|
|
set_app2_eval_type(app);
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_application3(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_App3_Rec *app;
|
|
Scheme_Object *nrator, *nrand1, *nrand2;
|
|
|
|
app = (Scheme_App3_Rec *)o;
|
|
|
|
scheme_sfs_start_sequence(info, 3, 0);
|
|
scheme_sfs_push(info, 2, 0);
|
|
|
|
nrator = scheme_sfs_expr(app->rator, info, -1);
|
|
nrand1 = scheme_sfs_expr(app->rand1, info, -1);
|
|
nrand2 = scheme_sfs_expr(app->rand2, info, -1);
|
|
|
|
app->rator = nrator;
|
|
app->rand1 = nrand1;
|
|
app->rand2 = nrand2;
|
|
|
|
sfs_note_app(info, app->rator);
|
|
|
|
set_app3_eval_type(app);
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_sequence(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_Object *orig, *naya;
|
|
Scheme_Sequence *seq;
|
|
int i, n;
|
|
|
|
seq = (Scheme_Sequence *)o;
|
|
n = seq->count;
|
|
|
|
scheme_sfs_start_sequence(info, n, 1);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
orig = seq->array[i];
|
|
naya = scheme_sfs_expr(orig, info, -1);
|
|
seq->array[i] = naya;
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
#define SFS_BRANCH_W 4
|
|
|
|
static Scheme_Object *sfs_one_branch(SFS_Info *info, int ip,
|
|
Scheme_Object *vec, int delta,
|
|
Scheme_Object *tbranch)
|
|
{
|
|
int t_min_t, t_max_t, t_cnt, n, stackpos, i, save_nt, b_end, nt;
|
|
Scheme_Object *t_vec, *o;
|
|
Scheme_Object *clears = scheme_null;
|
|
|
|
info->min_touch = -1;
|
|
info->max_touch = -1;
|
|
save_nt = info->max_nontail;
|
|
|
|
SFS_LOG(printf("%d %d %s %d\n", info->pass, ip, (delta ? "else" : "then"), ip));
|
|
|
|
if (info->pass) {
|
|
/* Re-install max_used entries that refer to the branch */
|
|
o = SCHEME_VEC_ELS(vec)[delta * SFS_BRANCH_W];
|
|
t_min_t = SCHEME_INT_VAL(o);
|
|
o = SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 2];
|
|
nt = SCHEME_INT_VAL(o);
|
|
if (nt > info->max_nontail)
|
|
info->max_nontail = nt;
|
|
if (t_min_t > -1) {
|
|
t_vec = SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 1];
|
|
t_cnt = SCHEME_VEC_SIZE(t_vec);
|
|
for (i = 0; i < t_cnt; i++) {
|
|
o = SCHEME_VEC_ELS(t_vec)[i];
|
|
if (SCHEME_INTP(o)) {
|
|
n = SCHEME_INT_VAL(o);
|
|
SFS_LOG(printf(" @%d %d\n", i + t_min_t, n));
|
|
if (info->max_used[i + t_min_t] < n) {
|
|
SFS_LOG(printf(" |%d %d %d\n", i + t_min_t, n, info->max_nontail));
|
|
info->max_used[i + t_min_t] = n;
|
|
info->max_calls[i + t_min_t] = info->max_nontail;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* If the other branch has last use for something not used in this
|
|
branch, and if there's a non-tail call in this branch
|
|
of later, then we'll have to start with explicit clears.
|
|
Note that it doesn't matter whether the other branch actually
|
|
clears them (i.e., the relevant non-tail call might be only
|
|
in this branch). */
|
|
o = SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 3];
|
|
b_end = SCHEME_INT_VAL(o);
|
|
SFS_LOG(printf(" %d %d %d %d\n", nt, ip, b_end, save_nt));
|
|
if (((nt > (ip + 1)) && (nt < b_end)) /* => non-tail call in branch */
|
|
|| ((ip + 1) < save_nt)) { /* => non-tail call after branches */
|
|
SFS_LOG(printf(" other\n"));
|
|
o = SCHEME_VEC_ELS(vec)[(1 - delta) * SFS_BRANCH_W];
|
|
t_min_t = SCHEME_INT_VAL(o);
|
|
if (t_min_t > -1) {
|
|
int at_ip, pos;
|
|
t_vec = SCHEME_VEC_ELS(vec)[((1 - delta) * SFS_BRANCH_W) + 1];
|
|
t_cnt = SCHEME_VEC_SIZE(t_vec);
|
|
o = SCHEME_VEC_ELS(vec)[((1 - delta) * SFS_BRANCH_W) + 2];
|
|
nt = SCHEME_INT_VAL(o);
|
|
o = SCHEME_VEC_ELS(vec)[((1 - delta) * SFS_BRANCH_W) + 3];
|
|
b_end = SCHEME_INT_VAL(o);
|
|
for (i = 0; i < t_cnt; i++) {
|
|
o = SCHEME_VEC_ELS(t_vec)[i];
|
|
if (SCHEME_INTP(o)) {
|
|
n = SCHEME_INT_VAL(o);
|
|
pos = i + t_min_t;
|
|
at_ip = info->max_used[pos];
|
|
SFS_LOG(printf(" ?%d %d %d\n", pos, n, at_ip));
|
|
/* is last use in other branch? */
|
|
if (((!delta && (at_ip == ip))
|
|
|| (delta && (at_ip == n)))) {
|
|
/* Yes, so add clear */
|
|
SFS_LOG(printf(" !%d %d %d\n", pos, n, at_ip));
|
|
pos -= info->stackpos;
|
|
clears = scheme_make_pair(scheme_make_integer(pos),
|
|
clears);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stackpos = info->stackpos;
|
|
|
|
tbranch = scheme_sfs_expr(tbranch, info, -1);
|
|
|
|
if (info->pass)
|
|
info->max_nontail = save_nt;
|
|
# if MAX_SFS_CLEARING
|
|
else
|
|
info->max_nontail = info->ip;
|
|
# endif
|
|
|
|
tbranch = scheme_sfs_add_clears(tbranch, clears, 1);
|
|
|
|
if (!info->pass) {
|
|
t_min_t = info->min_touch;
|
|
t_max_t = info->max_touch;
|
|
if (t_min_t < stackpos)
|
|
t_min_t = stackpos;
|
|
if (t_max_t < stackpos)
|
|
t_max_t = -1;
|
|
SFS_LOG(printf("%d %s %d [%d,%d] /%d\n", info->pass, (delta ? "else" : "then"), ip,
|
|
t_min_t, t_max_t, stackpos));
|
|
if (t_max_t < 0) {
|
|
t_min_t = -1;
|
|
t_vec = scheme_false;
|
|
} else {
|
|
t_cnt = t_max_t - t_min_t + 1;
|
|
t_vec = scheme_make_vector(t_cnt, NULL);
|
|
for (i = 0; i < t_cnt; i++) {
|
|
n = info->max_used[i + t_min_t];
|
|
SFS_LOG(printf("%d %s %d %d -> %d/%d\n", info->pass, (delta ? "else" : "then"), ip,
|
|
i + t_min_t, n, info->max_calls[i+ t_min_t]));
|
|
if (n > ip) {
|
|
SCHEME_VEC_ELS(t_vec)[i] = scheme_make_integer(n);
|
|
info->max_used[i + t_min_t] = ip;
|
|
} else {
|
|
SCHEME_VEC_ELS(t_vec)[i] = scheme_false;
|
|
}
|
|
}
|
|
}
|
|
SCHEME_VEC_ELS(vec)[delta * SFS_BRANCH_W] = scheme_make_integer(t_min_t);
|
|
SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 1] = t_vec;
|
|
SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 2] = scheme_make_integer(info->max_nontail);
|
|
SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 3] = scheme_make_integer(info->ip);
|
|
}
|
|
|
|
memset(info->max_used + info->stackpos, 0, (stackpos - info->stackpos) * sizeof(int));
|
|
memset(info->max_calls + info->stackpos, 0, (stackpos - info->stackpos) * sizeof(int));
|
|
|
|
info->stackpos = stackpos;
|
|
|
|
return tbranch;
|
|
}
|
|
|
|
static Scheme_Object *sfs_branch(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_Branch_Rec *b;
|
|
Scheme_Object *t, *tb, *fb, *vec;
|
|
int ip, min_t, max_t;
|
|
|
|
b = (Scheme_Branch_Rec *)o;
|
|
|
|
scheme_sfs_start_sequence(info, 1, 0);
|
|
|
|
t = scheme_sfs_expr(b->test, info, -1);
|
|
|
|
ip = info->ip;
|
|
info->ip++;
|
|
/* Use ip to represent all uses in the two branches.
|
|
Use ip+1 to represent all non-tail calls in the two branches. */
|
|
|
|
min_t = info->min_touch;
|
|
max_t = info->max_touch;
|
|
|
|
SFS_LOG(printf(" after test: %d %d\n", min_t, max_t));
|
|
|
|
if (!info->pass) {
|
|
vec = scheme_make_vector(SFS_BRANCH_W * 2, NULL);
|
|
scheme_sfs_save(info, vec);
|
|
} else {
|
|
vec = scheme_sfs_next_saved(info);
|
|
}
|
|
|
|
tb = sfs_one_branch(info, ip, vec, 0, b->tbranch);
|
|
|
|
if (!info->pass) {
|
|
if ((min_t == -1)
|
|
|| ((info->min_touch > -1) && (info->min_touch < min_t)))
|
|
min_t = info->min_touch;
|
|
if (info->max_touch > max_t)
|
|
max_t = info->max_touch;
|
|
if (info->max_nontail > ip + 1)
|
|
info->max_nontail = ip + 1;
|
|
}
|
|
|
|
fb = sfs_one_branch(info, ip, vec, 1, b->fbranch);
|
|
|
|
if (!info->pass) {
|
|
if ((min_t == -1)
|
|
|| ((info->min_touch > -1) && (info->min_touch < min_t)))
|
|
min_t = info->min_touch;
|
|
if (info->max_touch > max_t)
|
|
max_t = info->max_touch;
|
|
if (info->max_nontail > ip + 1)
|
|
info->max_nontail = ip + 1;
|
|
}
|
|
|
|
SFS_LOG(printf(" done if: %d %d\n", min_t, max_t));
|
|
|
|
info->min_touch = min_t;
|
|
info->max_touch = max_t;
|
|
|
|
b->test = t;
|
|
b->tbranch = tb;
|
|
b->fbranch = fb;
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_let_value(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)o;
|
|
Scheme_Object *body, *rhs, *clears = scheme_null;
|
|
int i, pos;
|
|
|
|
scheme_sfs_start_sequence(info, 2, 1);
|
|
|
|
rhs = scheme_sfs_expr(lv->value, info, -1);
|
|
|
|
if (!info->pass
|
|
|| (info->ip < info->max_nontail)) {
|
|
for (i = 0; i < lv->count; i++) {
|
|
pos = lv->position + i;
|
|
if (!info->pass)
|
|
scheme_sfs_used(info, pos);
|
|
else {
|
|
int spos;
|
|
spos = pos + info->stackpos;
|
|
if ((info->max_used[spos] == info->ip)
|
|
&& (info->max_calls[spos] > info->ip)) {
|
|
/* No one is using the id after we set it.
|
|
We still need to set it, in case it's boxed and shared,
|
|
but then remove the binding or box. */
|
|
clears = scheme_make_pair(scheme_make_integer(pos),
|
|
clears);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
body = scheme_sfs_expr(lv->body, info, -1);
|
|
|
|
body = scheme_sfs_add_clears(body, clears, 1);
|
|
|
|
lv->value = rhs;
|
|
lv->body = body;
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_let_one(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_Let_One *lo = (Scheme_Let_One *)o;
|
|
Scheme_Object *body, *rhs, *vec;
|
|
int pos, save_mnt, ip, et;
|
|
|
|
scheme_sfs_start_sequence(info, 2, 1);
|
|
|
|
scheme_sfs_push(info, 1, 1);
|
|
ip = info->ip;
|
|
pos = info->stackpos;
|
|
save_mnt = info->max_nontail;
|
|
|
|
if (!info->pass) {
|
|
vec = scheme_make_vector(3, NULL);
|
|
scheme_sfs_save(info, vec);
|
|
} else {
|
|
vec = scheme_sfs_next_saved(info);
|
|
if (SCHEME_VEC_SIZE(vec) != 3)
|
|
scheme_signal_error("internal error: bad vector length");
|
|
info->max_used[pos] = SCHEME_INT_VAL(SCHEME_VEC_ELS(vec)[0]);
|
|
info->max_calls[pos] = SCHEME_INT_VAL(SCHEME_VEC_ELS(vec)[1]);
|
|
info->max_nontail = SCHEME_INT_VAL(SCHEME_VEC_ELS(vec)[2]);
|
|
}
|
|
|
|
rhs = scheme_sfs_expr(lo->value, info, -1);
|
|
body = scheme_sfs_expr(lo->body, info, -1);
|
|
|
|
# if MAX_SFS_CLEARING
|
|
if (!info->pass)
|
|
info->max_nontail = info->ip;
|
|
# endif
|
|
|
|
if (!info->pass) {
|
|
int n;
|
|
info->max_calls[pos] = info->max_nontail;
|
|
n = info->max_used[pos];
|
|
SCHEME_VEC_ELS(vec)[0] = scheme_make_integer(n);
|
|
n = info->max_calls[pos];
|
|
SCHEME_VEC_ELS(vec)[1] = scheme_make_integer(n);
|
|
SCHEME_VEC_ELS(vec)[2] = scheme_make_integer(info->max_nontail);
|
|
} else {
|
|
info->max_nontail = save_mnt;
|
|
|
|
if (info->max_used[pos] <= ip) {
|
|
/* No one is using it, so either don't push the real value, or clear it.
|
|
The optimizer normally would have converted away the binding, but
|
|
it might not because (1) it was introduced late by inlining,
|
|
or (2) the rhs expression doesn't always produce a single
|
|
value. */
|
|
if (scheme_omittable_expr(rhs, 1, -1, 1)) {
|
|
rhs = scheme_false;
|
|
} else {
|
|
Scheme_Object *clr;
|
|
Scheme_Sequence *s;
|
|
s = malloc_sequence(2);
|
|
s->so.type = scheme_sequence_type;
|
|
s->count = 2;
|
|
clr = scheme_make_local(scheme_local_type, 0, SCHEME_LOCAL_CLEAR_ON_READ);
|
|
s->array[0] = clr;
|
|
s->array[1] = body;
|
|
body = (Scheme_Object *)s;
|
|
}
|
|
}
|
|
}
|
|
|
|
lo->value = rhs;
|
|
lo->body = body;
|
|
|
|
et = scheme_get_eval_type(lo->value);
|
|
SCHEME_LET_EVAL_TYPE(lo) = et;
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_let_void(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_Let_Void *lv = (Scheme_Let_Void *)o;
|
|
Scheme_Object *body;
|
|
int i, pos, save_mnt;
|
|
Scheme_Object *vec;
|
|
|
|
scheme_sfs_push(info, lv->count, 1);
|
|
pos = info->stackpos;
|
|
save_mnt = info->max_nontail;
|
|
|
|
if (!info->pass) {
|
|
vec = scheme_make_vector(lv->count + 1, NULL);
|
|
scheme_sfs_save(info, vec);
|
|
} else {
|
|
vec = scheme_sfs_next_saved(info);
|
|
if (!SCHEME_VECTORP(vec))
|
|
scheme_signal_error("internal error: not a vector");
|
|
for (i = 0; i < lv->count; i++) {
|
|
info->max_used[pos + i] = SCHEME_INT_VAL(SCHEME_VEC_ELS(vec)[i]);
|
|
info->max_calls[pos + i] = SCHEME_INT_VAL(SCHEME_VEC_ELS(vec)[lv->count]);
|
|
}
|
|
info->max_nontail = SCHEME_INT_VAL(SCHEME_VEC_ELS(vec)[lv->count]);
|
|
}
|
|
|
|
body = scheme_sfs_expr(lv->body, info, -1);
|
|
|
|
# if MAX_SFS_CLEARING
|
|
if (!info->pass)
|
|
info->max_nontail = info->ip;
|
|
# endif
|
|
|
|
if (!info->pass) {
|
|
int n;
|
|
SCHEME_VEC_ELS(vec)[lv->count] = scheme_make_integer(info->max_nontail);
|
|
for (i = 0; i < lv->count; i++) {
|
|
n = info->max_used[pos + i];
|
|
SCHEME_VEC_ELS(vec)[i] = scheme_make_integer(n);
|
|
}
|
|
} else {
|
|
info->max_nontail = save_mnt;
|
|
}
|
|
|
|
lv->body = body;
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_letrec(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_Letrec *lr = (Scheme_Letrec *)o;
|
|
Scheme_Object **procs, *v, *clears = scheme_null;
|
|
int i, count;
|
|
|
|
count = lr->count;
|
|
|
|
scheme_sfs_start_sequence(info, count + 1, 1);
|
|
|
|
procs = lr->procs;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
v = scheme_sfs_expr(procs[i], info, i);
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(v), scheme_syntax_type)
|
|
&& (SCHEME_PINT_VAL(v) == BEGIN0_EXPD)) {
|
|
/* Some clearing actions were added to the closure.
|
|
Lift them out. */
|
|
int j;
|
|
Scheme_Sequence *cseq = (Scheme_Sequence *)SCHEME_IPTR_VAL(v);
|
|
for (j = 1; j < cseq->count; j++) {
|
|
int pos;
|
|
pos = SCHEME_LOCAL_POS(cseq->array[j]);
|
|
clears = scheme_make_pair(scheme_make_integer(pos), clears);
|
|
}
|
|
v = cseq->array[0];
|
|
}
|
|
procs[i] = v;
|
|
}
|
|
|
|
v = scheme_sfs_expr(lr->body, info, -1);
|
|
|
|
v = scheme_sfs_add_clears(v, clears, 1);
|
|
|
|
lr->body = v;
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *sfs_wcm(Scheme_Object *o, SFS_Info *info)
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)o;
|
|
Scheme_Object *k, *v, *b;
|
|
|
|
scheme_sfs_start_sequence(info, 3, 1);
|
|
|
|
k = scheme_sfs_expr(wcm->key, info, -1);
|
|
v = scheme_sfs_expr(wcm->val, info, -1);
|
|
b = scheme_sfs_expr(wcm->body, info, -1);
|
|
|
|
wcm->key = k;
|
|
wcm->val = v;
|
|
wcm->body = b;
|
|
|
|
return o;
|
|
}
|
|
|
|
Scheme_Object *scheme_sfs_expr(Scheme_Object *expr, SFS_Info *info, int closure_self_pos)
|
|
{
|
|
Scheme_Type type = SCHEME_TYPE(expr);
|
|
int seqn, stackpos, tp;
|
|
|
|
seqn = info->seqn;
|
|
stackpos = info->stackpos;
|
|
tp = info->tail_pos;
|
|
if (seqn) {
|
|
info->seqn = 0;
|
|
info->tail_pos = 0;
|
|
}
|
|
info->ip++;
|
|
|
|
switch (type) {
|
|
case scheme_local_type:
|
|
case scheme_local_unbox_type:
|
|
if (!info->pass)
|
|
scheme_sfs_used(info, SCHEME_LOCAL_POS(expr));
|
|
else {
|
|
int pos, at_ip;
|
|
pos = SCHEME_LOCAL_POS(expr);
|
|
at_ip = info->max_used[info->stackpos + pos];
|
|
if (at_ip < info->max_calls[info->stackpos + pos]) {
|
|
if (at_ip == info->ip) {
|
|
/* Clear on read: */
|
|
expr = scheme_make_local(type, pos, SCHEME_LOCAL_CLEAR_ON_READ);
|
|
} else {
|
|
/* Someone else clears it: */
|
|
expr = scheme_make_local(type, pos, SCHEME_LOCAL_OTHER_CLEARS);
|
|
}
|
|
} else {
|
|
# if MAX_SFS_CLEARING
|
|
scheme_signal_error("should have been cleared somewhere");
|
|
# endif
|
|
}
|
|
}
|
|
break;
|
|
case scheme_syntax_type:
|
|
{
|
|
Scheme_Syntax_SFSer f;
|
|
Scheme_Object *orig, *naya;
|
|
|
|
f = scheme_syntax_sfsers[SCHEME_PINT_VAL(expr)];
|
|
orig = SCHEME_IPTR_VAL(expr);
|
|
naya = f(orig, info);
|
|
if (!SAME_OBJ(orig, naya))
|
|
expr = naya;
|
|
}
|
|
break;
|
|
case scheme_application_type:
|
|
expr = sfs_application(expr, info);
|
|
break;
|
|
case scheme_application2_type:
|
|
expr = sfs_application2(expr, info);
|
|
break;
|
|
case scheme_application3_type:
|
|
expr = sfs_application3(expr, info);
|
|
break;
|
|
case scheme_sequence_type:
|
|
expr = sfs_sequence(expr, info);
|
|
break;
|
|
case scheme_branch_type:
|
|
expr = sfs_branch(expr, info);
|
|
break;
|
|
case scheme_with_cont_mark_type:
|
|
expr = sfs_wcm(expr, info);
|
|
break;
|
|
case scheme_unclosed_procedure_type:
|
|
expr = scheme_sfs_closure(expr, info, closure_self_pos);
|
|
break;
|
|
case scheme_let_value_type:
|
|
expr = sfs_let_value(expr, info);
|
|
break;
|
|
case scheme_let_void_type:
|
|
expr = sfs_let_void(expr, info);
|
|
break;
|
|
case scheme_letrec_type:
|
|
expr = sfs_letrec(expr, info);
|
|
break;
|
|
case scheme_let_one_type:
|
|
expr = sfs_let_one(expr, info);
|
|
break;
|
|
case scheme_closure_type:
|
|
{
|
|
Scheme_Closure *c = (Scheme_Closure *)expr;
|
|
if (ZERO_SIZED_CLOSUREP(c)) {
|
|
Scheme_Object *code;
|
|
code = scheme_sfs_closure((Scheme_Object *)c->code, info, closure_self_pos);
|
|
if (SAME_TYPE(SCHEME_TYPE(code), scheme_syntax_type)
|
|
&& (SCHEME_PINT_VAL(code) == BEGIN0_EXPD)) {
|
|
Scheme_Sequence *seq = (Scheme_Sequence *)SCHEME_IPTR_VAL(code);
|
|
c->code = (Scheme_Closure_Data *)seq->array[0];
|
|
seq->array[0] = expr;
|
|
expr = code;
|
|
} else {
|
|
c->code = (Scheme_Closure_Data *)code;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case scheme_toplevel_type:
|
|
{
|
|
int c = SCHEME_TOPLEVEL_DEPTH(expr);
|
|
if (info->stackpos + c != info->tlpos)
|
|
scheme_signal_error("toplevel access not at expected place");
|
|
}
|
|
break;
|
|
case scheme_case_closure_type:
|
|
{
|
|
/* FIXME: maybe need to handle eagerly created closure */
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
info->ip++;
|
|
|
|
if (seqn) {
|
|
info->seqn = seqn - 1;
|
|
memset(info->max_used + info->stackpos, 0, (stackpos - info->stackpos) * sizeof(int));
|
|
memset(info->max_calls + info->stackpos, 0, (stackpos - info->stackpos) * sizeof(int));
|
|
info->stackpos = stackpos;
|
|
info->tail_pos = tp;
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* JIT */
|
|
/*========================================================================*/
|
|
|
|
#ifdef MZ_USE_JIT
|
|
|
|
static Scheme_Object *jit_application(Scheme_Object *o)
|
|
{
|
|
Scheme_Object *orig, *naya = NULL;
|
|
Scheme_App_Rec *app, *app2;
|
|
int i, n, size;
|
|
|
|
app = (Scheme_App_Rec *)o;
|
|
n = app->num_args + 1;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
orig = app->args[i];
|
|
naya = scheme_jit_expr(orig);
|
|
if (!SAME_OBJ(orig, naya))
|
|
break;
|
|
}
|
|
|
|
if (i >= n)
|
|
return o;
|
|
|
|
size = (sizeof(Scheme_App_Rec)
|
|
+ ((n - 1) * sizeof(Scheme_Object *))
|
|
+ n * sizeof(char));
|
|
app2 = (Scheme_App_Rec *)scheme_malloc_tagged(size);
|
|
memcpy(app2, app, size);
|
|
app2->args[i] = naya;
|
|
|
|
for (i++; i < n; i++) {
|
|
orig = app2->args[i];
|
|
naya = scheme_jit_expr(orig);
|
|
app2->args[i] = naya;
|
|
}
|
|
|
|
return (Scheme_Object *)app2;
|
|
}
|
|
|
|
static Scheme_Object *jit_application2(Scheme_Object *o)
|
|
{
|
|
Scheme_App2_Rec *app;
|
|
Scheme_Object *nrator, *nrand;
|
|
|
|
app = (Scheme_App2_Rec *)o;
|
|
|
|
nrator = scheme_jit_expr(app->rator);
|
|
nrand = scheme_jit_expr(app->rand);
|
|
|
|
if (SAME_OBJ(nrator, app->rator)
|
|
&& SAME_OBJ(nrand, app->rand))
|
|
return o;
|
|
|
|
app = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
|
|
memcpy(app, o, sizeof(Scheme_App2_Rec));
|
|
app->rator = nrator;
|
|
app->rand = nrand;
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
static Scheme_Object *jit_application3(Scheme_Object *o)
|
|
{
|
|
Scheme_App3_Rec *app;
|
|
Scheme_Object *nrator, *nrand1, *nrand2;
|
|
|
|
app = (Scheme_App3_Rec *)o;
|
|
|
|
nrator = scheme_jit_expr(app->rator);
|
|
nrand1 = scheme_jit_expr(app->rand1);
|
|
nrand2 = scheme_jit_expr(app->rand2);
|
|
|
|
if (SAME_OBJ(nrator, app->rator)
|
|
&& SAME_OBJ(nrand1, app->rand1)
|
|
&& SAME_OBJ(nrand2, app->rand2))
|
|
return o;
|
|
|
|
app = MALLOC_ONE_TAGGED(Scheme_App3_Rec);
|
|
memcpy(app, o, sizeof(Scheme_App3_Rec));
|
|
app->rator = nrator;
|
|
app->rand1 = nrand1;
|
|
app->rand2 = nrand2;
|
|
|
|
return (Scheme_Object *)app;
|
|
}
|
|
|
|
static Scheme_Object *jit_sequence(Scheme_Object *o)
|
|
{
|
|
Scheme_Object *orig, *naya = NULL;
|
|
Scheme_Sequence *seq, *seq2;
|
|
int i, n, size;
|
|
|
|
seq = (Scheme_Sequence *)o;
|
|
n = seq->count;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
orig = seq->array[i];
|
|
naya = scheme_jit_expr(orig);
|
|
if (!SAME_OBJ(orig, naya))
|
|
break;
|
|
}
|
|
|
|
if (i >= n)
|
|
return o;
|
|
|
|
size = (sizeof(Scheme_Sequence)
|
|
+ ((n - 1) * sizeof(Scheme_Object *)));
|
|
seq2 = (Scheme_Sequence *)scheme_malloc_tagged(size);
|
|
memcpy(seq2, seq, size);
|
|
seq2->array[i] = naya;
|
|
|
|
for (i++; i < n; i++) {
|
|
orig = seq2->array[i];
|
|
naya = scheme_jit_expr(orig);
|
|
seq2->array[i] = naya;
|
|
}
|
|
|
|
return (Scheme_Object *)seq2;
|
|
}
|
|
|
|
static Scheme_Object *jit_branch(Scheme_Object *o)
|
|
{
|
|
Scheme_Branch_Rec *b;
|
|
Scheme_Object *t, *tb, *fb;
|
|
|
|
b = (Scheme_Branch_Rec *)o;
|
|
|
|
t = scheme_jit_expr(b->test);
|
|
tb = scheme_jit_expr(b->tbranch);
|
|
fb = scheme_jit_expr(b->fbranch);
|
|
|
|
if (SAME_OBJ(t, b->test)
|
|
&& SAME_OBJ(tb, b->tbranch)
|
|
&& SAME_OBJ(fb, b->fbranch))
|
|
return o;
|
|
|
|
b = MALLOC_ONE_TAGGED(Scheme_Branch_Rec);
|
|
memcpy(b, o, sizeof(Scheme_Branch_Rec));
|
|
b->test = t;
|
|
b->tbranch = tb;
|
|
b->fbranch = fb;
|
|
|
|
return (Scheme_Object *)b;
|
|
}
|
|
|
|
static Scheme_Object *jit_let_value(Scheme_Object *o)
|
|
{
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)o;
|
|
Scheme_Object *body, *rhs;
|
|
|
|
rhs = scheme_jit_expr(lv->value);
|
|
body = scheme_jit_expr(lv->body);
|
|
|
|
if (SAME_OBJ(rhs, lv->value)
|
|
&& SAME_OBJ(body, lv->body))
|
|
return o;
|
|
|
|
lv = MALLOC_ONE_TAGGED(Scheme_Let_Value);
|
|
memcpy(lv, o, sizeof(Scheme_Let_Value));
|
|
lv->value = rhs;
|
|
lv->body = body;
|
|
|
|
return (Scheme_Object *)lv;
|
|
}
|
|
|
|
static Scheme_Object *jit_let_one(Scheme_Object *o)
|
|
{
|
|
Scheme_Let_One *lo = (Scheme_Let_One *)o;
|
|
Scheme_Object *body, *rhs;
|
|
|
|
rhs = scheme_jit_expr(lo->value);
|
|
body = scheme_jit_expr(lo->body);
|
|
|
|
if (SAME_OBJ(rhs, lo->value)
|
|
&& SAME_OBJ(body, lo->body))
|
|
return o;
|
|
|
|
lo = MALLOC_ONE_TAGGED(Scheme_Let_One);
|
|
memcpy(lo, o, sizeof(Scheme_Let_One));
|
|
lo->value = rhs;
|
|
lo->body = body;
|
|
|
|
return (Scheme_Object *)lo;
|
|
}
|
|
|
|
static Scheme_Object *jit_let_void(Scheme_Object *o)
|
|
{
|
|
Scheme_Let_Void *lv = (Scheme_Let_Void *)o;
|
|
Scheme_Object *body;
|
|
|
|
body = scheme_jit_expr(lv->body);
|
|
|
|
if (SAME_OBJ(body, lv->body))
|
|
return o;
|
|
|
|
lv = MALLOC_ONE_TAGGED(Scheme_Let_Void);
|
|
memcpy(lv, o, sizeof(Scheme_Let_Void));
|
|
lv->body = body;
|
|
|
|
return (Scheme_Object *)lv;
|
|
}
|
|
|
|
static Scheme_Object *jit_letrec(Scheme_Object *o)
|
|
{
|
|
Scheme_Letrec *lr = (Scheme_Letrec *)o, *lr2;
|
|
Scheme_Object **procs, **procs2, *v;
|
|
int i, count;
|
|
|
|
count = lr->count;
|
|
|
|
lr2 = MALLOC_ONE_TAGGED(Scheme_Letrec);
|
|
memcpy(lr2, lr, sizeof(Scheme_Letrec));
|
|
|
|
procs = lr->procs;
|
|
procs2 = MALLOC_N(Scheme_Object *, count);
|
|
lr2->procs = procs2;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
v = scheme_jit_closure(procs[i], (Scheme_Object *)lr2);
|
|
procs2[i] = v;
|
|
}
|
|
|
|
v = scheme_jit_expr(lr->body);
|
|
lr2->body = v;
|
|
|
|
return (Scheme_Object *)lr2;
|
|
}
|
|
|
|
static Scheme_Object *jit_wcm(Scheme_Object *o)
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)o;
|
|
Scheme_Object *k, *v, *b;
|
|
|
|
k = scheme_jit_expr(wcm->key);
|
|
v = scheme_jit_expr(wcm->val);
|
|
b = scheme_jit_expr(wcm->body);
|
|
if (SAME_OBJ(wcm->key, k)
|
|
&& SAME_OBJ(wcm->val, v)
|
|
&& SAME_OBJ(wcm->body, b))
|
|
return o;
|
|
|
|
wcm = MALLOC_ONE_TAGGED(Scheme_With_Continuation_Mark);
|
|
memcpy(wcm, o, sizeof(Scheme_With_Continuation_Mark));
|
|
|
|
wcm->key = k;
|
|
wcm->val = v;
|
|
wcm->body = b;
|
|
|
|
return (Scheme_Object *)wcm;
|
|
}
|
|
|
|
Scheme_Object *scheme_jit_expr(Scheme_Object *expr)
|
|
{
|
|
Scheme_Type type = SCHEME_TYPE(expr);
|
|
|
|
switch (type) {
|
|
case scheme_syntax_type:
|
|
{
|
|
Scheme_Syntax_Jitter f;
|
|
Scheme_Object *orig, *naya;
|
|
|
|
f = scheme_syntax_jitters[SCHEME_PINT_VAL(expr)];
|
|
orig = SCHEME_IPTR_VAL(expr);
|
|
naya = f(orig);
|
|
if (SAME_OBJ(orig, naya))
|
|
return expr;
|
|
|
|
return scheme_make_syntax_resolved(SCHEME_PINT_VAL(expr), naya);
|
|
}
|
|
case scheme_application_type:
|
|
return jit_application(expr);
|
|
case scheme_application2_type:
|
|
return jit_application2(expr);
|
|
case scheme_application3_type:
|
|
return jit_application3(expr);
|
|
case scheme_sequence_type:
|
|
return jit_sequence(expr);
|
|
case scheme_branch_type:
|
|
return jit_branch(expr);
|
|
case scheme_with_cont_mark_type:
|
|
return jit_wcm(expr);
|
|
case scheme_unclosed_procedure_type:
|
|
return scheme_jit_closure(expr, NULL);
|
|
case scheme_let_value_type:
|
|
return jit_let_value(expr);
|
|
case scheme_let_void_type:
|
|
return jit_let_void(expr);
|
|
case scheme_letrec_type:
|
|
return jit_letrec(expr);
|
|
case scheme_let_one_type:
|
|
return jit_let_one(expr);
|
|
case scheme_closure_type:
|
|
{
|
|
Scheme_Closure *c = (Scheme_Closure *)expr;
|
|
if (ZERO_SIZED_CLOSUREP(c)) {
|
|
/* JIT the closure body, producing a native closure: */
|
|
return scheme_jit_closure((Scheme_Object *)c->code, NULL);
|
|
} else
|
|
return expr;
|
|
}
|
|
case scheme_case_closure_type:
|
|
{
|
|
return scheme_unclose_case_lambda(expr, 1);
|
|
}
|
|
default:
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
Scheme_Object *scheme_jit_expr(Scheme_Object *expr)
|
|
{
|
|
return expr;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*========================================================================*/
|
|
/* compilation info management */
|
|
/*========================================================================*/
|
|
|
|
void scheme_default_compile_rec(Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
}
|
|
|
|
void scheme_init_compile_recs(Scheme_Compile_Info *src, int drec,
|
|
Scheme_Compile_Info *dest, int n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
#ifdef MZTAG_REQUIRED
|
|
dest[i].type = scheme_rt_compile_info;
|
|
#endif
|
|
dest[i].comp = 1;
|
|
dest[i].dont_mark_local_use = src[drec].dont_mark_local_use;
|
|
dest[i].resolve_module_ids = src[drec].resolve_module_ids;
|
|
dest[i].value_name = scheme_false;
|
|
dest[i].certs = src[drec].certs;
|
|
/* should be always NULL */
|
|
dest[i].observer = src[drec].observer;
|
|
}
|
|
}
|
|
|
|
void scheme_init_expand_recs(Scheme_Expand_Info *src, int drec,
|
|
Scheme_Expand_Info *dest, int n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
#ifdef MZTAG_REQUIRED
|
|
dest[i].type = scheme_rt_compile_info;
|
|
#endif
|
|
dest[i].comp = 0;
|
|
dest[i].depth = src[drec].depth;
|
|
dest[i].value_name = scheme_false;
|
|
dest[i].certs = src[drec].certs;
|
|
dest[i].observer = src[drec].observer;
|
|
}
|
|
}
|
|
|
|
void scheme_merge_compile_recs(Scheme_Compile_Info *src, int drec,
|
|
Scheme_Compile_Info *dest, int n)
|
|
{
|
|
/* Nothing to do anymore, since we moved max_let_depth to resolve phase */
|
|
}
|
|
|
|
void scheme_init_lambda_rec(Scheme_Compile_Info *src, int drec,
|
|
Scheme_Compile_Info *lam, int dlrec)
|
|
{
|
|
#ifdef MZTAG_REQUIRED
|
|
lam[dlrec].type = scheme_rt_compile_info;
|
|
#endif
|
|
lam[dlrec].comp = 1;
|
|
lam[dlrec].dont_mark_local_use = src[drec].dont_mark_local_use;
|
|
lam[dlrec].resolve_module_ids = src[drec].resolve_module_ids;
|
|
lam[dlrec].value_name = scheme_false;
|
|
lam[dlrec].certs = src[drec].certs;
|
|
lam[dlrec].observer = src[drec].observer;
|
|
}
|
|
|
|
void scheme_merge_lambda_rec(Scheme_Compile_Info *src, int drec,
|
|
Scheme_Compile_Info *lam, int dlrec)
|
|
{
|
|
}
|
|
|
|
void scheme_compile_rec_done_local(Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
rec[drec].value_name = scheme_false;
|
|
}
|
|
|
|
void scheme_rec_add_certs(Scheme_Compile_Expand_Info *src, int drec, Scheme_Object *stx)
|
|
{
|
|
Scheme_Object *certs;
|
|
certs = scheme_stx_extract_certs(stx, src[drec].certs);
|
|
src[drec].certs = certs;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* compilation dispatcher */
|
|
/*========================================================================*/
|
|
|
|
static Scheme_Object *
|
|
scheme_inner_compile_list(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Info *rec, int drec, int start_app_position)
|
|
{
|
|
int len;
|
|
|
|
len = scheme_stx_proper_list_length(form);
|
|
|
|
if (!len) {
|
|
scheme_compile_rec_done_local(rec, drec);
|
|
scheme_default_compile_rec(rec, drec);
|
|
return scheme_null;
|
|
} else if (len > 0) {
|
|
Scheme_Compile_Info *recs, quick[5];
|
|
int i;
|
|
Scheme_Object *c, *p, *comp_first, *comp_last, *name, *first, *rest;
|
|
|
|
name = rec[drec].value_name;
|
|
scheme_compile_rec_done_local(rec, drec);
|
|
|
|
if (len <= 5)
|
|
recs = quick;
|
|
else
|
|
recs = MALLOC_N_RT(Scheme_Compile_Info, len);
|
|
scheme_init_compile_recs(rec, drec, recs, len);
|
|
recs[len - 1].value_name = name;
|
|
|
|
comp_first = comp_last = NULL;
|
|
|
|
for (i = 0, rest = form; i < len; i++) {
|
|
first = SCHEME_STX_CAR(rest);
|
|
rest = SCHEME_STX_CDR(rest);
|
|
|
|
c = scheme_compile_expand_expr(first, env, recs, i,
|
|
!i && start_app_position);
|
|
|
|
p = scheme_make_pair(c, scheme_null);
|
|
if (comp_last)
|
|
SCHEME_CDR(comp_last) = p;
|
|
else
|
|
comp_first = p;
|
|
comp_last = p;
|
|
}
|
|
|
|
scheme_merge_compile_recs(rec, drec, recs, len);
|
|
|
|
return comp_first;
|
|
} else {
|
|
scheme_signal_error("internal error: compile-list on non-list");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *compile_application(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
Scheme_Object *result;
|
|
int len;
|
|
|
|
len = scheme_stx_proper_list_length(form);
|
|
|
|
if (len < 0)
|
|
scheme_wrong_syntax(scheme_application_stx_string, NULL, form, NULL);
|
|
|
|
scheme_compile_rec_done_local(rec, drec);
|
|
scheme_rec_add_certs(rec, drec, form);
|
|
form = scheme_inner_compile_list(form, scheme_no_defines(env), rec, drec, 1);
|
|
|
|
result = make_application(form);
|
|
|
|
return result;
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_compile_list(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
return scheme_inner_compile_list(form, env, rec, drec, 0);
|
|
}
|
|
|
|
static Scheme_Object *call_compile_handler(Scheme_Object *form, int immediate_eval)
|
|
{
|
|
Scheme_Object *argv[2], *o;
|
|
|
|
argv[0] = form;
|
|
argv[1] = (immediate_eval ? scheme_true : scheme_false);
|
|
o = scheme_get_param(scheme_current_config(), MZCONFIG_COMPILE_HANDLER);
|
|
o = scheme_apply(o, 2, argv);
|
|
|
|
if (!SAME_TYPE(SCHEME_TYPE(o), scheme_compilation_top_type)) {
|
|
argv[0] = o;
|
|
scheme_wrong_type("compile-handler", "compiled code", 0, -1, argv);
|
|
return NULL;
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
static Scheme_Object *add_renames_unless_module(Scheme_Object *form, Scheme_Env *genv)
|
|
{
|
|
if (genv->rename_set) {
|
|
if (SCHEME_STX_PAIRP(form)) {
|
|
Scheme_Object *a, *d;
|
|
|
|
a = SCHEME_STX_CAR(form);
|
|
if (SCHEME_STX_SYMBOLP(a)) {
|
|
a = scheme_add_rename(a, genv->rename_set);
|
|
if (scheme_stx_module_eq(a, scheme_module_stx, 0)) {
|
|
/* Don't add renames to the whole module; let the
|
|
module's language take over. */
|
|
d = SCHEME_STX_CDR(form);
|
|
a = scheme_make_pair(a, d);
|
|
form = scheme_datum_to_syntax(a, form, form, 1, 0);
|
|
return form;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (genv->rename_set)
|
|
form = scheme_add_rename(form, genv->rename_set);
|
|
|
|
return form;
|
|
}
|
|
|
|
void scheme_enable_expression_resolve_lifts(Resolve_Info *ri)
|
|
{
|
|
Scheme_Object *lift_vec;
|
|
|
|
lift_vec = scheme_make_vector(2, NULL);
|
|
SCHEME_VEC_ELS(lift_vec)[0] = scheme_null;
|
|
SCHEME_VEC_ELS(lift_vec)[1] = scheme_make_integer(0);
|
|
ri->lifts = lift_vec;
|
|
}
|
|
|
|
Scheme_Object *scheme_merge_expression_resolve_lifts(Scheme_Object *expr, Resolve_Prefix *rp, Resolve_Info *ri)
|
|
{
|
|
Scheme_Object *lift_vec, *lifts;
|
|
Scheme_Sequence *s;
|
|
int n, i;
|
|
|
|
lift_vec = ri->lifts;
|
|
n = SCHEME_INT_VAL(SCHEME_VEC_ELS(lift_vec)[1]);
|
|
if (n) {
|
|
rp->num_lifts = n;
|
|
lifts = SCHEME_VEC_ELS(lift_vec)[0];
|
|
|
|
s = malloc_sequence(n + 1);
|
|
s->so.type = scheme_sequence_type;
|
|
s->count = n + 1;
|
|
for (i = 0; i < n; i++, lifts = SCHEME_CDR(lifts)) {
|
|
s->array[i] = SCHEME_CAR(lifts);
|
|
}
|
|
s->array[i] = expr;
|
|
|
|
return (Scheme_Object *)s;
|
|
} else
|
|
return expr;
|
|
}
|
|
|
|
static void *compile_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *form;
|
|
int writeable, for_eval, rename, enforce_consts;
|
|
Scheme_Env *genv;
|
|
Scheme_Compile_Info rec, rec2;
|
|
Scheme_Object *o, *tl_queue;
|
|
Scheme_Compilation_Top *top;
|
|
Resolve_Prefix *rp;
|
|
Resolve_Info *ri;
|
|
Optimize_Info *oi;
|
|
Scheme_Object *gval, *insp;
|
|
Scheme_Comp_Env *cenv;
|
|
|
|
form = (Scheme_Object *)p->ku.k.p1;
|
|
genv = (Scheme_Env *)p->ku.k.p2;
|
|
writeable = p->ku.k.i1;
|
|
for_eval = p->ku.k.i2;
|
|
rename = p->ku.k.i3;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
|
|
if (!SCHEME_STXP(form)) {
|
|
form = scheme_datum_to_syntax(form, scheme_false, scheme_false, 1, 0);
|
|
rename = 1;
|
|
}
|
|
|
|
/* Renamings for requires: */
|
|
if (rename) {
|
|
form = add_renames_unless_module(form, genv);
|
|
if (genv->module) {
|
|
form = scheme_stx_phase_shift(form, 0,
|
|
genv->module->me->src_modidx,
|
|
genv->module->self_modidx,
|
|
genv->export_registry);
|
|
}
|
|
}
|
|
|
|
tl_queue = scheme_null;
|
|
|
|
{
|
|
Scheme_Config *config;
|
|
config = scheme_current_config();
|
|
insp = scheme_get_param(config, MZCONFIG_CODE_INSPECTOR);
|
|
enforce_consts = SCHEME_TRUEP(scheme_get_param(config, MZCONFIG_COMPILE_MODULE_CONSTS));
|
|
}
|
|
|
|
while (1) {
|
|
rec.comp = 1;
|
|
rec.dont_mark_local_use = 0;
|
|
rec.resolve_module_ids = !writeable && !genv->module;
|
|
rec.value_name = scheme_false;
|
|
rec.certs = NULL;
|
|
rec.observer = NULL;
|
|
|
|
cenv = scheme_new_comp_env(genv, insp, SCHEME_TOPLEVEL_FRAME);
|
|
|
|
if (for_eval) {
|
|
/* Need to look for top-level `begin', and if we
|
|
find one, break it up to eval first expression
|
|
before the rest. */
|
|
while (1) {
|
|
scheme_frame_captures_lifts(cenv, scheme_make_lifted_defn, scheme_sys_wraps(cenv), scheme_false, scheme_false);
|
|
form = scheme_check_immediate_macro(form,
|
|
cenv, &rec, 0,
|
|
0, &gval, NULL, NULL);
|
|
if (SAME_OBJ(gval, scheme_begin_syntax)) {
|
|
if (scheme_stx_proper_list_length(form) > 1){
|
|
form = SCHEME_STX_CDR(form);
|
|
tl_queue = scheme_append(scheme_flatten_syntax_list(form, NULL),
|
|
tl_queue);
|
|
tl_queue = scheme_append(scheme_frame_get_lifts(cenv),
|
|
tl_queue);
|
|
form = SCHEME_CAR(tl_queue);
|
|
tl_queue = SCHEME_CDR(tl_queue);
|
|
} else
|
|
break;
|
|
} else {
|
|
o = scheme_frame_get_lifts(cenv);
|
|
if (!SCHEME_NULLP(o)) {
|
|
tl_queue = scheme_make_pair(form, tl_queue);
|
|
tl_queue = scheme_append(o, tl_queue);
|
|
form = SCHEME_CAR(tl_queue);
|
|
tl_queue = SCHEME_CDR(tl_queue);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (for_eval) {
|
|
o = call_compile_handler(form, 1);
|
|
top = (Scheme_Compilation_Top *)o;
|
|
} else {
|
|
/* We want to simply compile `form', but we have to loop in case
|
|
an expression is lifted in the process of compiling: */
|
|
Scheme_Object *l, *prev_o = NULL;
|
|
|
|
while (1) {
|
|
scheme_frame_captures_lifts(cenv, scheme_make_lifted_defn, scheme_sys_wraps(cenv), scheme_false, scheme_false);
|
|
|
|
scheme_init_compile_recs(&rec, 0, &rec2, 1);
|
|
|
|
o = scheme_compile_expr(form, cenv, &rec2, 0);
|
|
|
|
/* If we had compiled an expression in a previous iteration,
|
|
combine it in a sequence: */
|
|
if (prev_o) {
|
|
Scheme_Sequence *seq;
|
|
seq = malloc_sequence(2);
|
|
seq->so.type = scheme_sequence_type;
|
|
seq->count = 2;
|
|
seq->array[0] = o;
|
|
seq->array[1] = prev_o;
|
|
o = (Scheme_Object *)seq;
|
|
}
|
|
|
|
/* If any definitions were lifted in the process of compiling o,
|
|
we need to fold them in. */
|
|
l = scheme_frame_get_lifts(cenv);
|
|
if (!SCHEME_NULLP(l)) {
|
|
l = icons(scheme_datum_to_syntax(begin_symbol, scheme_false, scheme_sys_wraps(cenv), 0, 0),
|
|
l);
|
|
form = scheme_datum_to_syntax(l, scheme_false, scheme_false, 0, 0);
|
|
prev_o = o;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
oi = scheme_optimize_info_create();
|
|
oi->enforce_const = enforce_consts;
|
|
o = scheme_optimize_expr(o, oi);
|
|
|
|
rp = scheme_resolve_prefix(0, cenv->prefix, 1);
|
|
ri = scheme_resolve_info_create(rp);
|
|
ri->enforce_const = enforce_consts;
|
|
scheme_enable_expression_resolve_lifts(ri);
|
|
|
|
o = scheme_resolve_expr(o, ri);
|
|
o = scheme_sfs(o, NULL, ri->max_let_depth);
|
|
|
|
o = scheme_merge_expression_resolve_lifts(o, rp, ri);
|
|
|
|
rp = scheme_remap_prefix(rp, ri);
|
|
|
|
top = MALLOC_ONE_TAGGED(Scheme_Compilation_Top);
|
|
top->so.type = scheme_compilation_top_type;
|
|
top->max_let_depth = ri->max_let_depth;
|
|
top->code = o;
|
|
top->prefix = rp;
|
|
|
|
if (0) { /* <- change to 1 to check compilation result */
|
|
scheme_validate_code(NULL, top->code,
|
|
top->max_let_depth,
|
|
top->prefix->num_toplevels,
|
|
top->prefix->num_stxes,
|
|
top->prefix->num_lifts,
|
|
0);
|
|
}
|
|
}
|
|
|
|
if (SCHEME_PAIRP(tl_queue)) {
|
|
/* This compile is interleaved with evaluation,
|
|
and we need to eval now before compiling more. */
|
|
_eval_compiled_multi_with_prompt((Scheme_Object *)top, genv);
|
|
|
|
form = SCHEME_CAR(tl_queue);
|
|
tl_queue = SCHEME_CDR(tl_queue);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
return (void *)top;
|
|
}
|
|
|
|
static Scheme_Object *_compile(Scheme_Object *form, Scheme_Env *env, int writeable, int for_eval, int eb, int rename)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(form), scheme_compilation_top_type))
|
|
return form;
|
|
|
|
if (SCHEME_STXP(form)) {
|
|
if (SAME_TYPE(SCHEME_TYPE(SCHEME_STX_VAL(form)), scheme_compilation_top_type))
|
|
return SCHEME_STX_VAL(form);
|
|
}
|
|
|
|
p->ku.k.p1 = form;
|
|
p->ku.k.p2 = env;
|
|
p->ku.k.i1 = writeable;
|
|
p->ku.k.i2 = for_eval;
|
|
p->ku.k.i3 = rename;
|
|
|
|
return (Scheme_Object *)scheme_top_level_do(compile_k, eb);
|
|
}
|
|
|
|
Scheme_Object *scheme_compile(Scheme_Object *form, Scheme_Env *env, int writeable)
|
|
{
|
|
return _compile(form, env, writeable, 0, 1, 1);
|
|
}
|
|
|
|
Scheme_Object *scheme_compile_for_eval(Scheme_Object *form, Scheme_Env *env)
|
|
{
|
|
return _compile(form, env, 0, 1, 1, 1);
|
|
}
|
|
|
|
Scheme_Object *scheme_check_immediate_macro(Scheme_Object *first,
|
|
Scheme_Comp_Env *env,
|
|
Scheme_Compile_Expand_Info *rec, int drec,
|
|
int internel_def_pos,
|
|
Scheme_Object **current_val,
|
|
Scheme_Comp_Env **_xenv,
|
|
Scheme_Object *ctx)
|
|
{
|
|
Scheme_Object *name, *val, *certs;
|
|
Scheme_Comp_Env *xenv = (_xenv ? *_xenv : NULL);
|
|
Scheme_Expand_Info erec1;
|
|
Scheme_Env *menv = NULL;
|
|
int need_cert;
|
|
|
|
SCHEME_EXPAND_OBSERVE_ENTER_CHECK(rec[drec].observer, first);
|
|
|
|
while (1) {
|
|
*current_val = NULL;
|
|
|
|
if (SCHEME_STX_PAIRP(first)) {
|
|
name = SCHEME_STX_CAR(first);
|
|
need_cert = 1;
|
|
} else {
|
|
name = first;
|
|
need_cert = 0;
|
|
}
|
|
|
|
if (!SCHEME_STX_SYMBOLP(name)) {
|
|
SCHEME_EXPAND_OBSERVE_EXIT_CHECK(rec[drec].observer, first);
|
|
return first;
|
|
}
|
|
|
|
while (1) {
|
|
|
|
if (need_cert) {
|
|
/* While resolving name, we need certs from `first' */
|
|
scheme_init_expand_recs(rec, drec, &erec1, 1);
|
|
scheme_rec_add_certs(&erec1, 0, first);
|
|
certs = erec1.certs;
|
|
} else
|
|
certs = rec[drec].certs;
|
|
|
|
val = scheme_lookup_binding(name, env,
|
|
SCHEME_NULL_FOR_UNBOUND
|
|
+ SCHEME_APP_POS + SCHEME_ENV_CONSTANTS_OK
|
|
+ ((rec[drec].comp && rec[drec].dont_mark_local_use)
|
|
? SCHEME_DONT_MARK_USE
|
|
: 0)
|
|
+ ((rec[drec].comp && rec[drec].resolve_module_ids)
|
|
? SCHEME_RESOLVE_MODIDS
|
|
: 0),
|
|
certs, env->in_modidx,
|
|
&menv, NULL, NULL);
|
|
|
|
if (SCHEME_STX_PAIRP(first))
|
|
*current_val = val;
|
|
|
|
if (!val) {
|
|
SCHEME_EXPAND_OBSERVE_EXIT_CHECK(rec[drec].observer, first);
|
|
return first;
|
|
} else if (SAME_TYPE(SCHEME_TYPE(val), scheme_macro_type)) {
|
|
if (SAME_TYPE(SCHEME_TYPE(SCHEME_PTR_VAL(val)), scheme_id_macro_type)) {
|
|
/* It's a rename. Look up the target name and try again. */
|
|
name = scheme_stx_cert(SCHEME_PTR_VAL(SCHEME_PTR_VAL(val)), scheme_false, menv, name, NULL, 1);
|
|
menv = NULL;
|
|
SCHEME_USE_FUEL(1);
|
|
} else {
|
|
/* It's a normal macro; expand once. Also, extend env to indicate
|
|
an internal-define position, if necessary. */
|
|
if (!xenv) {
|
|
if (internel_def_pos) {
|
|
xenv = scheme_new_compilation_frame(0, SCHEME_INTDEF_FRAME, env, NULL);
|
|
if (ctx)
|
|
xenv->intdef_name = ctx;
|
|
if (_xenv)
|
|
*_xenv = xenv;
|
|
} else
|
|
xenv = env;
|
|
}
|
|
{
|
|
scheme_init_expand_recs(rec, drec, &erec1, 1);
|
|
erec1.depth = 1;
|
|
erec1.value_name = rec[drec].value_name;
|
|
first = scheme_expand_expr(first, xenv, &erec1, 0);
|
|
}
|
|
break; /* break to outer loop */
|
|
}
|
|
} else {
|
|
SCHEME_EXPAND_OBSERVE_EXIT_CHECK(rec[drec].observer, first);
|
|
return first;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *
|
|
compile_expand_macro_app(Scheme_Object *name, Scheme_Env *menv, Scheme_Object *macro,
|
|
Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Expand_Info *rec, int drec)
|
|
{
|
|
Scheme_Object *xformer, *boundname;
|
|
|
|
xformer = (Scheme_Object *)SCHEME_PTR_VAL(macro);
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(xformer), scheme_set_macro_type)) {
|
|
/* scheme_apply_macro unwraps it */
|
|
} else {
|
|
if (!scheme_check_proc_arity(NULL, 1, 0, -1, &xformer)) {
|
|
scheme_wrong_syntax(NULL, NULL, form, "illegal use of syntax");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
boundname = rec[drec].value_name;
|
|
if (!boundname)
|
|
boundname = scheme_false;
|
|
|
|
return scheme_apply_macro(name, menv, xformer, form, env, boundname, rec, drec, 0);
|
|
|
|
/* caller expects rec[drec] to be used to compile the result... */
|
|
}
|
|
|
|
static int same_effective_env(Scheme_Comp_Env *orig, Scheme_Comp_Env *e)
|
|
{
|
|
while (1) {
|
|
if (orig == e)
|
|
return 1;
|
|
if (e && e->flags & SCHEME_FOR_STOPS)
|
|
e = e->next;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *compile_expand_expr_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *form = (Scheme_Object *)p->ku.k.p1;
|
|
Scheme_Comp_Env *env = (Scheme_Comp_Env *)p->ku.k.p2;
|
|
Scheme_Compile_Info *rec = (Scheme_Compile_Info *)p->ku.k.p3;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
p->ku.k.p3 = NULL;
|
|
|
|
return scheme_compile_expand_expr(form,
|
|
env,
|
|
rec,
|
|
p->ku.k.i3,
|
|
p->ku.k.i2);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
scheme_compile_expand_expr(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Expand_Info *rec, int drec,
|
|
int app_position)
|
|
{
|
|
Scheme_Object *name, *var, *stx, *normal, *can_recycle_stx = NULL;
|
|
Scheme_Env *menv = NULL;
|
|
GC_CAN_IGNORE char *not_allowed;
|
|
int looking_for_top;
|
|
|
|
top:
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
{
|
|
# include "mzstkchk.h"
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Compile_Expand_Info *recx;
|
|
|
|
recx = MALLOC_ONE_RT(Scheme_Compile_Expand_Info);
|
|
memcpy(recx, rec + drec, sizeof(Scheme_Compile_Expand_Info));
|
|
#ifdef MZTAG_REQUIRED
|
|
recx->type = scheme_rt_compile_info;
|
|
#endif
|
|
|
|
p->ku.k.p1 = (void *)form;
|
|
p->ku.k.p2 = (void *)env;
|
|
p->ku.k.p3 = (void *)recx;
|
|
p->ku.k.i3 = 0;
|
|
p->ku.k.i2 = app_position;
|
|
|
|
var = scheme_handle_stack_overflow(compile_expand_expr_k);
|
|
|
|
memcpy(rec + drec, recx, sizeof(Scheme_Compile_Expand_Info));
|
|
return var;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DO_CHECK_FOR_BREAK(scheme_current_thread, ;);
|
|
|
|
#if 1
|
|
if (!SCHEME_STXP(form))
|
|
scheme_signal_error("not syntax");
|
|
#endif
|
|
|
|
if (rec[drec].comp) {
|
|
scheme_default_compile_rec(rec, drec);
|
|
} else {
|
|
SCHEME_EXPAND_OBSERVE_VISIT(rec[drec].observer,form);
|
|
}
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(SCHEME_STX_VAL(form)), scheme_expanded_syntax_type)) {
|
|
var = SCHEME_STX_VAL(form);
|
|
if (scheme_stx_has_empty_wraps(form)
|
|
&& same_effective_env(SCHEME_PTR2_VAL(var), env)) {
|
|
/* FIXME: this needs EXPAND_OBSERVE callbacks. */
|
|
var = scheme_stx_track(SCHEME_PTR1_VAL(var), form, form);
|
|
form = scheme_stx_cert(var, scheme_false, NULL, form, NULL, 1);
|
|
if (!rec[drec].comp && (rec[drec].depth != -1)) {
|
|
/* Already fully expanded. */
|
|
return form;
|
|
}
|
|
} else {
|
|
scheme_wrong_syntax(NULL, NULL, SCHEME_PTR1_VAL(var),
|
|
"expanded syntax not in its original context");
|
|
}
|
|
}
|
|
|
|
looking_for_top = 0;
|
|
|
|
if (SCHEME_STX_NULLP(form)) {
|
|
stx = app_symbol;
|
|
not_allowed = "function application";
|
|
normal = app_expander;
|
|
} else if (!SCHEME_STX_PAIRP(form)) {
|
|
if (SCHEME_STX_SYMBOLP(form)) {
|
|
Scheme_Object *find_name = form, *lexical_binding_id;
|
|
int protected = 0;
|
|
|
|
while (1) {
|
|
lexical_binding_id = NULL;
|
|
var = scheme_lookup_binding(find_name, env,
|
|
SCHEME_NULL_FOR_UNBOUND
|
|
+ SCHEME_ENV_CONSTANTS_OK
|
|
+ (rec[drec].comp
|
|
? SCHEME_ELIM_CONST
|
|
: 0)
|
|
+ (app_position
|
|
? SCHEME_APP_POS
|
|
: 0)
|
|
+ ((rec[drec].comp && rec[drec].dont_mark_local_use) ?
|
|
SCHEME_DONT_MARK_USE
|
|
: 0)
|
|
+ ((rec[drec].comp && rec[drec].resolve_module_ids)
|
|
? SCHEME_RESOLVE_MODIDS
|
|
: 0),
|
|
rec[drec].certs, env->in_modidx,
|
|
&menv, &protected, &lexical_binding_id);
|
|
|
|
SCHEME_EXPAND_OBSERVE_RESOLVE(rec[drec].observer,find_name);
|
|
|
|
if (var && SAME_TYPE(SCHEME_TYPE(var), scheme_macro_type)
|
|
&& SAME_TYPE(SCHEME_TYPE(SCHEME_PTR_VAL(var)), scheme_id_macro_type)) {
|
|
/* It's a rename. Look up the target name and try again. */
|
|
Scheme_Object *new_name;
|
|
new_name = SCHEME_PTR_VAL(SCHEME_PTR_VAL(var));
|
|
if (!rec[drec].comp) {
|
|
new_name = scheme_stx_track(new_name, find_name, find_name);
|
|
}
|
|
new_name = scheme_stx_cert(new_name, scheme_false, menv, find_name, NULL, 1);
|
|
find_name = new_name;
|
|
SCHEME_USE_FUEL(1);
|
|
menv = NULL;
|
|
protected = 0;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (!var) {
|
|
/* Top variable */
|
|
stx = top_symbol;
|
|
not_allowed = "reference to top-level identifier";
|
|
normal = top_expander;
|
|
form = find_name; /* in case it was re-mapped */
|
|
looking_for_top = 1;
|
|
} else {
|
|
if (SAME_TYPE(SCHEME_TYPE(var), scheme_syntax_compiler_type)) {
|
|
if (var == stop_expander) {
|
|
SCHEME_EXPAND_OBSERVE_ENTER_PRIM(rec[drec].observer,form);
|
|
SCHEME_EXPAND_OBSERVE_PRIM_STOP(rec[drec].observer);
|
|
SCHEME_EXPAND_OBSERVE_EXIT_PRIM(rec[drec].observer,form);
|
|
SCHEME_EXPAND_OBSERVE_RETURN(rec[drec].observer,form);
|
|
return form;
|
|
} else {
|
|
scheme_wrong_syntax(NULL, NULL, form, "bad syntax");
|
|
return NULL;
|
|
}
|
|
} else if (SAME_TYPE(SCHEME_TYPE(var), scheme_macro_type)) {
|
|
name = form;
|
|
goto macro;
|
|
}
|
|
|
|
if (rec[drec].comp) {
|
|
scheme_compile_rec_done_local(rec, drec);
|
|
if (SAME_TYPE(SCHEME_TYPE(var), scheme_variable_type)
|
|
|| SAME_TYPE(SCHEME_TYPE(var), scheme_module_variable_type))
|
|
return scheme_register_toplevel_in_prefix(var, env, rec, drec);
|
|
else
|
|
return var;
|
|
} else {
|
|
SCHEME_EXPAND_OBSERVE_VARIABLE(rec[drec].observer, form, find_name);
|
|
if (lexical_binding_id) {
|
|
find_name = lexical_binding_id;
|
|
}
|
|
if (protected) {
|
|
/* Add a property to indicate that the name is protected */
|
|
find_name = scheme_stx_property(find_name, protected_symbol, scheme_true);
|
|
}
|
|
SCHEME_EXPAND_OBSERVE_RETURN(rec[drec].observer, find_name);
|
|
return find_name; /* which is usually == form */
|
|
}
|
|
}
|
|
} else {
|
|
/* A hack for handling lifted expressions. See compile_expand_lift_to_let. */
|
|
if (SAME_TYPE(SCHEME_TYPE(SCHEME_STX_VAL(form)), scheme_already_comp_type)) {
|
|
form = SCHEME_STX_VAL(form);
|
|
return SCHEME_IPTR_VAL(form);
|
|
}
|
|
|
|
stx = datum_symbol;
|
|
not_allowed = "literal data";
|
|
normal = datum_expander;
|
|
}
|
|
} else {
|
|
name = SCHEME_STX_CAR(form);
|
|
if (SCHEME_STX_SYMBOLP(name)) {
|
|
/* Check for macros: */
|
|
Scheme_Object *find_name = name;
|
|
Scheme_Expand_Info erec1;
|
|
|
|
/* While resolving name, we need certs from `form' */
|
|
scheme_init_expand_recs(rec, drec, &erec1, 1);
|
|
scheme_rec_add_certs(&erec1, 0, form);
|
|
|
|
while (1) {
|
|
var = scheme_lookup_binding(find_name, env,
|
|
SCHEME_APP_POS
|
|
+ SCHEME_NULL_FOR_UNBOUND
|
|
+ SCHEME_ENV_CONSTANTS_OK
|
|
+ (rec[drec].comp
|
|
? SCHEME_ELIM_CONST
|
|
: 0)
|
|
+ ((rec[drec].comp && rec[drec].dont_mark_local_use)
|
|
? SCHEME_DONT_MARK_USE
|
|
: 0)
|
|
+ ((rec[drec].comp && rec[drec].resolve_module_ids)
|
|
? SCHEME_RESOLVE_MODIDS
|
|
: 0),
|
|
erec1.certs, env->in_modidx,
|
|
&menv, NULL, NULL);
|
|
|
|
SCHEME_EXPAND_OBSERVE_RESOLVE(rec[drec].observer, find_name);
|
|
if (var && SAME_TYPE(SCHEME_TYPE(var), scheme_macro_type)
|
|
&& SAME_TYPE(SCHEME_TYPE(SCHEME_PTR_VAL(var)), scheme_id_macro_type)) {
|
|
/* It's a rename. Look up the target name and try again. */
|
|
Scheme_Object *new_name;
|
|
new_name = SCHEME_PTR_VAL(SCHEME_PTR_VAL(var));
|
|
if (!rec[drec].comp) {
|
|
new_name = scheme_stx_track(new_name, find_name, find_name);
|
|
}
|
|
new_name = scheme_stx_cert(new_name, scheme_false, menv, find_name, NULL, 1);
|
|
find_name = new_name;
|
|
SCHEME_USE_FUEL(1);
|
|
menv = NULL;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (!var) {
|
|
/* apply to global variable: compile it normally */
|
|
} else if (SAME_TYPE(SCHEME_TYPE(var), scheme_local_type)
|
|
|| SAME_TYPE(SCHEME_TYPE(var), scheme_local_unbox_type)) {
|
|
/* apply to local variable: compile it normally */
|
|
} else {
|
|
if (SAME_TYPE(SCHEME_TYPE(var), scheme_macro_type)) {
|
|
goto macro;
|
|
} else if (SAME_TYPE(SCHEME_TYPE(var), scheme_syntax_compiler_type)) {
|
|
if (rec[drec].comp) {
|
|
Scheme_Syntax *f;
|
|
f = (Scheme_Syntax *)SCHEME_SYNTAX(var);
|
|
return f(form, env, rec, drec);
|
|
} else {
|
|
Scheme_Syntax_Expander *f;
|
|
f = (Scheme_Syntax_Expander *)SCHEME_SYNTAX_EXP(var);
|
|
SCHEME_EXPAND_OBSERVE_ENTER_PRIM(rec[drec].observer, form);
|
|
form = f(form, env, rec, drec);
|
|
SCHEME_EXPAND_OBSERVE_EXIT_PRIM(rec[drec].observer, form);
|
|
SCHEME_EXPAND_OBSERVE_RETURN(rec[drec].observer, form);
|
|
return form;
|
|
}
|
|
}
|
|
|
|
/* Else: unknown global - must be a function: compile as application */
|
|
}
|
|
|
|
if (!SAME_OBJ(name, find_name)) {
|
|
/* the rator position was mapped */
|
|
Scheme_Object *code;
|
|
code = SCHEME_STX_CDR(form);
|
|
code = scheme_make_pair(find_name, code);
|
|
form = scheme_datum_to_syntax(code, form, form, 0, 0);
|
|
}
|
|
}
|
|
|
|
stx = app_symbol;
|
|
not_allowed = "function application";
|
|
normal = app_expander;
|
|
}
|
|
|
|
/* Compile/expand as application, datum, or top: */
|
|
if (quick_stx && rec[drec].comp) {
|
|
((Scheme_Stx *)quick_stx)->val = stx;
|
|
((Scheme_Stx *)quick_stx)->wraps = ((Scheme_Stx *)form)->wraps;
|
|
((Scheme_Stx *)quick_stx)->u.modinfo_cache = NULL;
|
|
stx = quick_stx;
|
|
quick_stx = NULL;
|
|
} else
|
|
stx = scheme_datum_to_syntax(stx, scheme_false, form, 0, 0);
|
|
if (rec[drec].comp)
|
|
can_recycle_stx = stx;
|
|
|
|
{
|
|
Scheme_Object *find_name = stx;
|
|
|
|
while (1) {
|
|
var = scheme_lookup_binding(find_name, env,
|
|
SCHEME_NULL_FOR_UNBOUND
|
|
+ SCHEME_APP_POS + SCHEME_ENV_CONSTANTS_OK
|
|
+ SCHEME_DONT_MARK_USE,
|
|
rec[drec].certs, env->in_modidx,
|
|
&menv, NULL, NULL);
|
|
|
|
SCHEME_EXPAND_OBSERVE_RESOLVE(rec[drec].observer, find_name);
|
|
|
|
if (var && SAME_TYPE(SCHEME_TYPE(var), scheme_macro_type)
|
|
&& SAME_TYPE(SCHEME_TYPE(SCHEME_PTR_VAL(var)), scheme_id_macro_type)) {
|
|
/* It's a rename. Look up the target name and try again. */
|
|
Scheme_Object *new_name;
|
|
new_name = SCHEME_PTR_VAL(SCHEME_PTR_VAL(var));
|
|
if (!rec[drec].comp) {
|
|
new_name = scheme_stx_track(new_name, find_name, find_name);
|
|
}
|
|
new_name = scheme_stx_cert(new_name, scheme_false, menv, find_name, NULL, 1);
|
|
find_name = new_name;
|
|
SCHEME_USE_FUEL(1);
|
|
menv = NULL;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!SAME_OBJ(var, normal)) {
|
|
/* Someone might keep the stx: */
|
|
can_recycle_stx = NULL;
|
|
}
|
|
|
|
if (!var && looking_for_top) {
|
|
/* If form is a marked name, then force #%top binding.
|
|
This is so temporaries can be used as defined ids. */
|
|
Scheme_Object *nm;
|
|
nm = scheme_tl_id_sym(env->genv, form, NULL, 0, NULL);
|
|
if (!SAME_OBJ(nm, SCHEME_STX_VAL(form))) {
|
|
stx = scheme_datum_to_syntax(top_symbol, scheme_false, scheme_sys_wraps(env), 0, 0);
|
|
|
|
/* Should be either top_expander or stop_expander: */
|
|
var = scheme_lookup_binding(stx, env,
|
|
SCHEME_NULL_FOR_UNBOUND
|
|
+ SCHEME_APP_POS + SCHEME_ENV_CONSTANTS_OK
|
|
+ SCHEME_DONT_MARK_USE,
|
|
rec[drec].certs, env->in_modidx,
|
|
&menv, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
if (var && (SAME_TYPE(SCHEME_TYPE(var), scheme_macro_type)
|
|
|| SAME_TYPE(SCHEME_TYPE(var), scheme_syntax_compiler_type))) {
|
|
if (SAME_OBJ(var, stop_expander)) {
|
|
/* Return original: */
|
|
SCHEME_EXPAND_OBSERVE_ENTER_PRIM(rec[drec].observer, form);
|
|
SCHEME_EXPAND_OBSERVE_PRIM_STOP(rec[drec].observer);
|
|
SCHEME_EXPAND_OBSERVE_EXIT_PRIM(rec[drec].observer, form);
|
|
SCHEME_EXPAND_OBSERVE_RETURN(rec[drec].observer, form);
|
|
return form;
|
|
} else if (rec[drec].comp && SAME_OBJ(var, normal)) {
|
|
/* Skip creation of intermediate form */
|
|
Scheme_Syntax *f;
|
|
taking_shortcut = 1;
|
|
f = (Scheme_Syntax *)SCHEME_SYNTAX(var);
|
|
if (can_recycle_stx && !quick_stx)
|
|
quick_stx = can_recycle_stx;
|
|
return f(form, env, rec, drec);
|
|
} else {
|
|
form = scheme_datum_to_syntax(scheme_make_pair(stx, form), form, form, 0, 2);
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(var), scheme_syntax_compiler_type)) {
|
|
if (rec[drec].comp) {
|
|
Scheme_Syntax *f;
|
|
f = (Scheme_Syntax *)SCHEME_SYNTAX(var);
|
|
return f(form, env, rec, drec);
|
|
} else {
|
|
Scheme_Syntax_Expander *f;
|
|
f = (Scheme_Syntax_Expander *)SCHEME_SYNTAX_EXP(var);
|
|
SCHEME_EXPAND_OBSERVE_ENTER_PRIM(rec[drec].observer, form);
|
|
form = f(form, env, rec, drec);
|
|
SCHEME_EXPAND_OBSERVE_EXIT_PRIM(rec[drec].observer, form);
|
|
SCHEME_EXPAND_OBSERVE_RETURN(rec[drec].observer, form);
|
|
return form;
|
|
}
|
|
} else {
|
|
name = stx;
|
|
goto macro;
|
|
}
|
|
}
|
|
} else {
|
|
/* Not allowed this context! */
|
|
scheme_wrong_syntax(scheme_compile_stx_string, NULL, form,
|
|
"bad syntax; %s is not allowed, "
|
|
"because no %S syntax transformer is bound",
|
|
not_allowed,
|
|
SCHEME_STX_VAL(stx));
|
|
return NULL;
|
|
}
|
|
|
|
macro:
|
|
if (!rec[drec].comp && !rec[drec].depth) {
|
|
SCHEME_EXPAND_OBSERVE_RETURN(rec[drec].observer, form);
|
|
return form; /* We've gone as deep as requested */
|
|
}
|
|
|
|
SCHEME_EXPAND_OBSERVE_ENTER_MACRO(rec[drec].observer, form);
|
|
form = compile_expand_macro_app(name, menv, var, form, env, rec, drec);
|
|
SCHEME_EXPAND_OBSERVE_EXIT_MACRO(rec[drec].observer, form);
|
|
|
|
if (rec[drec].comp)
|
|
goto top;
|
|
else {
|
|
if (rec[drec].depth > 0)
|
|
--rec[drec].depth;
|
|
if (rec[drec].depth)
|
|
goto top;
|
|
else {
|
|
SCHEME_EXPAND_OBSERVE_RETURN(rec[drec].observer, form);
|
|
return form;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int arg_count(Scheme_Object *lam, Scheme_Comp_Env *env)
|
|
{
|
|
Scheme_Object *l, *id, *form = lam;
|
|
int cnt = 0;
|
|
DupCheckRecord r;
|
|
|
|
lam = SCHEME_STX_CDR(lam);
|
|
if (!SCHEME_STX_PAIRP(lam)) return -1;
|
|
|
|
l = SCHEME_STX_CAR(lam);
|
|
|
|
lam = SCHEME_STX_CDR(lam);
|
|
if (!SCHEME_STX_PAIRP(lam)) return -1;
|
|
|
|
while (SCHEME_STX_PAIRP(lam)) { lam = SCHEME_STX_CDR(lam); }
|
|
if (!SCHEME_STX_NULLP(lam)) return -1;
|
|
|
|
|
|
scheme_begin_dup_symbol_check(&r, env);
|
|
|
|
while (SCHEME_STX_PAIRP(l)) {
|
|
id = SCHEME_STX_CAR(l);
|
|
scheme_check_identifier("lambda", id, NULL, env, form);
|
|
scheme_dup_symbol_check(&r, NULL, id, "argument", form);
|
|
l = SCHEME_STX_CDR(l);
|
|
cnt++;
|
|
}
|
|
if (!SCHEME_STX_NULLP(l)) return -1;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
static Scheme_Object *
|
|
compile_expand_app(Scheme_Object *forms, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Expand_Info *rec, int drec)
|
|
{
|
|
Scheme_Object *form, *naya;
|
|
int tsc = taking_shortcut;
|
|
|
|
taking_shortcut = 0;
|
|
|
|
scheme_rec_add_certs(rec, drec, forms);
|
|
if (tsc) {
|
|
form = forms;
|
|
} else {
|
|
form = SCHEME_STX_CDR(forms);
|
|
form = scheme_datum_to_syntax(form, forms, forms, 0, 0);
|
|
}
|
|
|
|
if (SCHEME_STX_NULLP(form)) {
|
|
/* Compile/expand empty application to null list: */
|
|
if (rec[drec].comp)
|
|
return scheme_null;
|
|
else
|
|
return scheme_datum_to_syntax(icons(quote_symbol,
|
|
icons(form, scheme_null)),
|
|
form,
|
|
scheme_sys_wraps(env),
|
|
0, 2);
|
|
} else if (!SCHEME_STX_PAIRP(form)) {
|
|
/* will end in error */
|
|
if (rec[drec].comp)
|
|
return compile_application(form, env, rec, drec);
|
|
else {
|
|
rec[drec].value_name = scheme_false;
|
|
naya = scheme_expand_list(form, scheme_no_defines(env), rec, drec);
|
|
/* naya will be prefixed and returned... */
|
|
}
|
|
} else if (rec[drec].comp) {
|
|
Scheme_Object *name, *origname, *gval, *orig_rest_form, *rest_form;
|
|
name = SCHEME_STX_CAR(form);
|
|
origname = name;
|
|
|
|
name = scheme_check_immediate_macro(name, env, rec, drec, 0, &gval, NULL, NULL);
|
|
|
|
/* look for ((lambda (x) ...) ...); */
|
|
if (SAME_OBJ(gval, scheme_lambda_syntax)) {
|
|
Scheme_Object *argsnbody;
|
|
|
|
argsnbody = SCHEME_STX_CDR(name);
|
|
if (SCHEME_STX_PAIRP(argsnbody)) {
|
|
Scheme_Object *args, *body;
|
|
|
|
args = SCHEME_STX_CAR(argsnbody);
|
|
body = SCHEME_STX_CDR(argsnbody);
|
|
|
|
if (SCHEME_STX_PAIRP(body)) {
|
|
int pl;
|
|
pl = scheme_stx_proper_list_length(args);
|
|
if (pl >= 0) {
|
|
Scheme_Object *bindings = scheme_null, *last = NULL;
|
|
Scheme_Object *rest;
|
|
int al;
|
|
|
|
rest = SCHEME_STX_CDR(form);
|
|
al = scheme_stx_proper_list_length(rest);
|
|
|
|
if (al == pl) {
|
|
DupCheckRecord r;
|
|
|
|
scheme_begin_dup_symbol_check(&r, env);
|
|
|
|
while (!SCHEME_STX_NULLP(args)) {
|
|
Scheme_Object *v, *n;
|
|
|
|
n = SCHEME_STX_CAR(args);
|
|
scheme_check_identifier("lambda", n, NULL, env, name);
|
|
|
|
/* If we don't check here, the error is in terms of `let': */
|
|
scheme_dup_symbol_check(&r, NULL, n, "argument", name);
|
|
|
|
v = SCHEME_STX_CAR(rest);
|
|
v = cons(cons(cons(n, scheme_null), cons(v, scheme_null)), scheme_null);
|
|
if (last)
|
|
SCHEME_CDR(last) = v;
|
|
else
|
|
bindings = v;
|
|
|
|
last = v;
|
|
args = SCHEME_STX_CDR(args);
|
|
rest = SCHEME_STX_CDR(rest);
|
|
}
|
|
|
|
body = scheme_datum_to_syntax(cons(let_values_symbol,
|
|
cons(bindings,
|
|
body)),
|
|
form,
|
|
scheme_sys_wraps(env),
|
|
0, 2);
|
|
|
|
/* Copy certifications from lambda to `body'. */
|
|
body = scheme_stx_cert(body, NULL, NULL, name, NULL, 1);
|
|
|
|
return scheme_compile_expand_expr(body, env, rec, drec, 0);
|
|
} else {
|
|
#if 0
|
|
scheme_wrong_syntax(scheme_application_stx_string, NULL, form,
|
|
"procedure application: bad ((lambda (...) ...) ...) syntax");
|
|
return NULL;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
orig_rest_form = SCHEME_STX_CDR(form);
|
|
|
|
/* Look for (call-with-values (lambda () M) (lambda (id ...) N)) */
|
|
if (SCHEME_STX_SYMBOLP(name)) {
|
|
Scheme_Object *at_first, *at_second, *the_end, *cwv_stx;
|
|
at_first = SCHEME_STX_CDR(form);
|
|
if (SCHEME_STX_PAIRP(at_first)) {
|
|
at_second = SCHEME_STX_CDR(at_first);
|
|
if (SCHEME_STX_PAIRP(at_second)) {
|
|
the_end = SCHEME_STX_CDR(at_second);
|
|
if (SCHEME_STX_NULLP(the_end)) {
|
|
Scheme_Object *orig_at_second = at_second;
|
|
|
|
cwv_stx = scheme_datum_to_syntax(scheme_intern_symbol("call-with-values"),
|
|
scheme_false, scheme_sys_wraps(env), 0, 0);
|
|
if (scheme_stx_module_eq(name, cwv_stx, 0)) {
|
|
Scheme_Object *first, *orig_first;
|
|
orig_first = SCHEME_STX_CAR(at_first);
|
|
first = scheme_check_immediate_macro(orig_first, env, rec, drec, 0, &gval, NULL, NULL);
|
|
if (SAME_OBJ(gval, scheme_lambda_syntax)
|
|
&& SCHEME_STX_PAIRP(first)
|
|
&& (arg_count(first, env) == 0)) {
|
|
Scheme_Object *second, *orig_second;
|
|
orig_second = SCHEME_STX_CAR(at_second);
|
|
second = scheme_check_immediate_macro(orig_second, env, rec, drec, 0, &gval, NULL, NULL);
|
|
if (SAME_OBJ(gval, scheme_lambda_syntax)
|
|
&& SCHEME_STX_PAIRP(second)
|
|
&& (arg_count(second, env) >= 0)) {
|
|
Scheme_Object *lhs;
|
|
second = SCHEME_STX_CDR(second);
|
|
lhs = SCHEME_STX_CAR(second);
|
|
second = SCHEME_STX_CDR(second);
|
|
first = SCHEME_STX_CDR(first);
|
|
first = SCHEME_STX_CDR(first);
|
|
/* Convert to let-values: */
|
|
name = icons(let_values_symbol,
|
|
icons(icons(icons(lhs, icons(icons(begin_symbol, first),
|
|
scheme_null)),
|
|
scheme_null),
|
|
second));
|
|
form = scheme_datum_to_syntax(name, forms, scheme_sys_wraps(env), 0, 2);
|
|
return scheme_compile_expand_expr(form, env, rec, drec, 0);
|
|
}
|
|
if (!SAME_OBJ(second, orig_second)) {
|
|
at_second = scheme_datum_to_syntax(icons(second, the_end), at_second, at_second, 0, 2);
|
|
}
|
|
}
|
|
if (!SAME_OBJ(first, orig_first)
|
|
|| !SAME_OBJ(at_second, orig_at_second)) {
|
|
at_first = scheme_datum_to_syntax(icons(first, at_second), at_first, at_first, 0, 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rest_form = at_first;
|
|
} else {
|
|
rest_form = orig_rest_form;
|
|
}
|
|
|
|
if (NOT_SAME_OBJ(name, origname)
|
|
|| NOT_SAME_OBJ(rest_form, orig_rest_form)) {
|
|
form = scheme_datum_to_syntax(scheme_make_pair(name, rest_form), forms, forms, 0, 2);
|
|
}
|
|
|
|
return compile_application(form, env, rec, drec);
|
|
} else {
|
|
scheme_rec_add_certs(rec, drec, form);
|
|
rec[drec].value_name = scheme_false;
|
|
naya = scheme_expand_list(form, scheme_no_defines(env), rec, drec);
|
|
/* naya will be prefixed returned... */
|
|
}
|
|
|
|
if (SAME_OBJ(form, naya))
|
|
return forms;
|
|
|
|
/* Add #%app prefix back: */
|
|
{
|
|
Scheme_Object *first;
|
|
|
|
first = SCHEME_STX_CAR(forms);
|
|
return scheme_datum_to_syntax(scheme_make_pair(first, naya),
|
|
forms,
|
|
forms, 0, 2);
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *
|
|
app_syntax(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
return compile_expand_app(form, env, rec, drec);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
app_expand(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
SCHEME_EXPAND_OBSERVE_PRIM_APP(erec[drec].observer);
|
|
return compile_expand_app(form, env, erec, drec);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
datum_syntax(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
Scheme_Object *c, *v;
|
|
|
|
if (taking_shortcut) {
|
|
c = form;
|
|
taking_shortcut = 0;
|
|
} else {
|
|
c = SCHEME_STX_CDR(form);
|
|
/* Need datum->syntax, in case c is a list: */
|
|
c = scheme_datum_to_syntax(c, form, form, 0, 2);
|
|
}
|
|
|
|
v = SCHEME_STX_VAL(c);
|
|
if (SCHEME_KEYWORDP(v)) {
|
|
scheme_wrong_syntax("#%datum", NULL, c, "keyword used as an expression");
|
|
return NULL;
|
|
}
|
|
|
|
return scheme_syntax_to_datum(c, 0, NULL);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
datum_expand(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
Scheme_Object *rest, *v;
|
|
|
|
SCHEME_EXPAND_OBSERVE_PRIM_DATUM(erec[drec].observer);
|
|
|
|
rest = SCHEME_STX_CDR(form);
|
|
|
|
v = SCHEME_STX_VAL(rest);
|
|
if (SCHEME_KEYWORDP(v)) {
|
|
scheme_wrong_syntax("#%datum", NULL, rest, "keyword used as an expression");
|
|
return NULL;
|
|
}
|
|
|
|
return scheme_datum_to_syntax(icons(quote_symbol,
|
|
icons(rest, scheme_null)),
|
|
form,
|
|
scheme_sys_wraps(env),
|
|
0, 2);
|
|
}
|
|
|
|
static Scheme_Object *check_top(const char *when, Scheme_Object *form, Scheme_Comp_Env *env)
|
|
{
|
|
Scheme_Object *c;
|
|
|
|
if (taking_shortcut) {
|
|
c = form;
|
|
taking_shortcut = 0;
|
|
} else
|
|
c = SCHEME_STX_CDR(form);
|
|
|
|
if (!SCHEME_STX_SYMBOLP(c))
|
|
scheme_wrong_syntax(NULL, NULL, form, NULL);
|
|
|
|
if (env->genv->module) {
|
|
Scheme_Object *modidx, *symbol = c, *tl_id;
|
|
int bad;
|
|
|
|
tl_id = scheme_tl_id_sym(env->genv, symbol, NULL, 0, NULL);
|
|
if (NOT_SAME_OBJ(tl_id, SCHEME_STX_SYM(symbol))) {
|
|
/* Since the module has a rename for this id, it's certainly defined. */
|
|
} else {
|
|
modidx = scheme_stx_module_name(&symbol, scheme_make_integer(env->genv->phase), NULL, NULL, NULL, NULL, NULL);
|
|
if (modidx) {
|
|
/* If it's an access path, resolve it: */
|
|
if (env->genv->module
|
|
&& SAME_OBJ(scheme_module_resolve(modidx, 1), env->genv->module->modname))
|
|
bad = 0;
|
|
else
|
|
bad = 1;
|
|
} else
|
|
bad = 1;
|
|
|
|
if (env->genv->disallow_unbound) {
|
|
if (bad || !scheme_lookup_in_table(env->genv->toplevel, (const char *)SCHEME_STX_SYM(c))) {
|
|
scheme_wrong_syntax(when, NULL, c,
|
|
(env->genv->phase
|
|
? "unbound variable in module (transformer environment)"
|
|
: "unbound variable in module"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
static Scheme_Object *
|
|
top_syntax(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
Scheme_Object *c;
|
|
|
|
c = check_top(scheme_compile_stx_string, form, env);
|
|
|
|
c = scheme_tl_id_sym(env->genv, c, NULL, 0, NULL);
|
|
|
|
if (env->genv->module && !rec[drec].resolve_module_ids) {
|
|
/* Self-reference in a module; need to remember the modidx. Don't
|
|
need a pos, because the symbol's gensym-ness (if any) will be
|
|
preserved within the module. */
|
|
c = scheme_hash_module_variable(env->genv, env->genv->module->self_modidx,
|
|
c, env->genv->module->insp,
|
|
-1, env->genv->mod_phase);
|
|
} else
|
|
c = (Scheme_Object *)scheme_global_bucket(c, env->genv);
|
|
|
|
return scheme_register_toplevel_in_prefix(c, env, rec, drec);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
top_expand(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
SCHEME_EXPAND_OBSERVE_PRIM_TOP(erec[drec].observer);
|
|
check_top(scheme_expand_stx_string, form, env);
|
|
return form;
|
|
}
|
|
|
|
Scheme_Object *scheme_compile_expr(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
return scheme_compile_expand_expr(form, env, rec, drec, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_expand_expr(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
return scheme_compile_expand_expr(form, env, erec, drec, 0);
|
|
}
|
|
|
|
static Scheme_Object *pair_lifted(Scheme_Object *_ip, Scheme_Object **_id, Scheme_Object *expr, Scheme_Comp_Env *env)
|
|
{
|
|
Scheme_Comp_Env **ip = (Scheme_Comp_Env **)_ip, *naya;
|
|
|
|
naya = scheme_new_compilation_frame(1, SCHEME_CAPTURE_LIFTED, (*ip)->next, NULL);
|
|
(*ip)->next = naya;
|
|
*ip = naya;
|
|
|
|
scheme_add_compilation_binding(0, *_id, naya);
|
|
|
|
return icons(icons(*_id, scheme_null), icons(expr, scheme_null));
|
|
}
|
|
|
|
static Scheme_Object *compile_expand_expr_lift_to_let_k(void);
|
|
|
|
static Scheme_Object *
|
|
compile_expand_expr_lift_to_let(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Expand_Info *rec, int drec)
|
|
{
|
|
Scheme_Expand_Info recs[2];
|
|
Scheme_Object *l, *orig_form = form, *context_key;
|
|
Scheme_Comp_Env *inserted, **ip;
|
|
|
|
/* This function only works when `env' has no lexical bindings,
|
|
because we might insert new ones at the beginning. In
|
|
particular, we might insert frames between `inserted' and
|
|
`env'.
|
|
|
|
This function also relies on the way that compilation of `let'
|
|
works. A let-bound variable is compiled to a count of the frames
|
|
to skip and the index within the frame, so we can insert new
|
|
frames without affecting lookups computed so far. Inserting each
|
|
new frame before any previous one turns out to be consistent with
|
|
the nested `let's that we generate at the end.
|
|
|
|
Some optimizations can happen later, for example constant
|
|
propagate. But these optimizations take place on the result of
|
|
this function, so we don't have to worry about them.
|
|
|
|
Don't generate a `let*' expression instead of nested `let's,
|
|
because the compiler actually takes shortcuts (that are
|
|
inconsistent with our frame nesting) instead of expanding `let*'
|
|
to `let'. */
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
{
|
|
# include "mzstkchk.h"
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Compile_Expand_Info *recx;
|
|
|
|
recx = MALLOC_ONE_RT(Scheme_Compile_Expand_Info);
|
|
memcpy(recx, rec + drec, sizeof(Scheme_Compile_Expand_Info));
|
|
#ifdef MZTAG_REQUIRED
|
|
recx->type = scheme_rt_compile_info;
|
|
#endif
|
|
|
|
p->ku.k.p1 = (void *)form;
|
|
p->ku.k.p2 = (void *)env;
|
|
p->ku.k.p3 = (void *)recx;
|
|
|
|
form = scheme_handle_stack_overflow(compile_expand_expr_lift_to_let_k);
|
|
|
|
memcpy(rec + drec, recx, sizeof(Scheme_Compile_Expand_Info));
|
|
return form;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
inserted = scheme_new_compilation_frame(0, 0, env, NULL);
|
|
|
|
ip = MALLOC_N(Scheme_Comp_Env *, 1);
|
|
*ip = inserted;
|
|
|
|
context_key = scheme_generate_lifts_key();
|
|
|
|
scheme_frame_captures_lifts(inserted, pair_lifted, (Scheme_Object *)ip, scheme_false, context_key);
|
|
|
|
if (rec[drec].comp) {
|
|
scheme_init_compile_recs(rec, drec, recs, 2);
|
|
form = scheme_compile_expr(form, inserted, recs, 0);
|
|
} else {
|
|
scheme_init_expand_recs(rec, drec, recs, 2);
|
|
form = scheme_expand_expr(form, inserted, recs, 0);
|
|
}
|
|
|
|
l = scheme_frame_get_lifts(inserted);
|
|
if (SCHEME_NULLP(l)) {
|
|
/* No lifts */
|
|
if (rec[drec].comp)
|
|
scheme_merge_compile_recs(rec, drec, recs, 1);
|
|
return form;
|
|
} else {
|
|
/* We have lifts, so add let* wrapper and go again */
|
|
Scheme_Object *o, *revl;
|
|
if (rec[drec].comp) {
|
|
/* Wrap compiled part so the compiler recognizes it later: */
|
|
o = scheme_alloc_object();
|
|
o->type = scheme_already_comp_type;
|
|
SCHEME_IPTR_VAL(o) = form;
|
|
} else
|
|
o = form;
|
|
for (revl = scheme_null; SCHEME_PAIRP(l); l = SCHEME_CDR(l)) {
|
|
revl = icons(SCHEME_CAR(l), revl);
|
|
}
|
|
for (; SCHEME_PAIRP(revl); revl = SCHEME_CDR(revl)) {
|
|
o = icons(scheme_datum_to_syntax(let_values_symbol, scheme_false, scheme_sys_wraps(env), 0, 0),
|
|
icons(icons(SCHEME_CAR(revl), scheme_null),
|
|
icons(o, scheme_null)));
|
|
}
|
|
form = scheme_datum_to_syntax(o, orig_form, scheme_false, 0, 0);
|
|
SCHEME_EXPAND_OBSERVE_LETLIFT_LOOP(rec[drec].observer, form);
|
|
form = compile_expand_expr_lift_to_let(form, env, recs, 1);
|
|
if (rec[drec].comp)
|
|
scheme_merge_compile_recs(rec, drec, recs, 2);
|
|
return form;
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *compile_expand_expr_lift_to_let_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *form = (Scheme_Object *)p->ku.k.p1;
|
|
Scheme_Comp_Env *env = (Scheme_Comp_Env *)p->ku.k.p2;
|
|
Scheme_Compile_Info *rec = (Scheme_Compile_Info *)p->ku.k.p3;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
p->ku.k.p3 = NULL;
|
|
|
|
return compile_expand_expr_lift_to_let(form, env, rec, 0);
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_compile_expr_lift_to_let(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
return compile_expand_expr_lift_to_let(form, env, rec, drec);
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_expand_expr_lift_to_let(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
return compile_expand_expr_lift_to_let(form, env, erec, drec);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
scheme_compile_expand_block(Scheme_Object *forms, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Expand_Info *rec, int drec)
|
|
/* This ugly code parses a block of code, transforming embedded
|
|
define-values and define-syntax into letrec and letrec-syntax.
|
|
It is espcailly ugly because we have to expand macros
|
|
before deciding what we have. */
|
|
{
|
|
Scheme_Object *first, *rib, *ctx, *ectx;
|
|
Scheme_Comp_Env *xenv = NULL;
|
|
Scheme_Compile_Info recs[2];
|
|
DupCheckRecord r;
|
|
|
|
if (rec[drec].comp) {
|
|
scheme_default_compile_rec(rec, drec);
|
|
} else {
|
|
SCHEME_EXPAND_OBSERVE_ENTER_BLOCK(rec[drec].observer, forms);
|
|
}
|
|
|
|
if (SCHEME_STX_NULLP(forms)) {
|
|
if (rec[drec].comp) {
|
|
scheme_compile_rec_done_local(rec, drec);
|
|
return scheme_null;
|
|
} else {
|
|
SCHEME_EXPAND_OBSERVE_BLOCK_TO_LIST(rec[drec].observer, forms);
|
|
SCHEME_EXPAND_OBSERVE_ENTER_LIST(rec[drec].observer, forms);
|
|
SCHEME_EXPAND_OBSERVE_EXIT_LIST(rec[drec].observer, forms);
|
|
return forms;
|
|
}
|
|
}
|
|
|
|
rib = scheme_make_rename_rib();
|
|
ctx = scheme_alloc_object();
|
|
ctx->type = scheme_intdef_context_type;
|
|
SCHEME_PTR1_VAL(ctx) = env;
|
|
SCHEME_PTR2_VAL(ctx) = rib;
|
|
ectx = scheme_make_pair(ctx, scheme_null);
|
|
scheme_begin_dup_symbol_check(&r, env);
|
|
|
|
try_again:
|
|
|
|
SCHEME_EXPAND_OBSERVE_NEXT(rec[drec].observer);
|
|
|
|
if (!SCHEME_STX_PAIRP(forms)) {
|
|
scheme_wrong_syntax(scheme_begin_stx_string, NULL, forms, "bad syntax");
|
|
return NULL;
|
|
}
|
|
|
|
first = SCHEME_STX_CAR(forms);
|
|
|
|
{
|
|
/* Need to send both parts (before & after) of block rename */
|
|
Scheme_Object *old_first;
|
|
|
|
old_first = first;
|
|
first = scheme_add_rename_rib(first, rib);
|
|
|
|
SCHEME_EXPAND_OBSERVE_BLOCK_RENAMES(rec[drec].observer,old_first,first);
|
|
}
|
|
|
|
{
|
|
Scheme_Object *gval, *result;
|
|
int more = 1;
|
|
|
|
result = forms;
|
|
|
|
/* Check for macro expansion, which could mask the real
|
|
define-values, define-syntax, etc.: */
|
|
first = scheme_check_immediate_macro(first, env, rec, drec, 1, &gval, &xenv, ectx);
|
|
|
|
if (SAME_OBJ(gval, scheme_begin_syntax)) {
|
|
/* Inline content */
|
|
Scheme_Object *orig_forms = forms;
|
|
|
|
SCHEME_EXPAND_OBSERVE_PRIM_BEGIN(rec[drec].observer);
|
|
|
|
/* FIXME: Redundant with check done by scheme_flatten_begin below? */
|
|
if (scheme_stx_proper_list_length(first) < 0)
|
|
scheme_wrong_syntax(scheme_begin_stx_string, NULL, first,
|
|
"bad syntax (" IMPROPER_LIST_FORM ")");
|
|
|
|
forms = SCHEME_STX_CDR(forms);
|
|
|
|
if (SCHEME_STX_NULLP(forms)) {
|
|
/* A `begin' that ends the block. An `inferred-name' property
|
|
attached to this begin should apply to the ultimate last
|
|
thing in the block. */
|
|
Scheme_Object *v;
|
|
v = scheme_check_name_property(first, rec[drec].value_name);
|
|
rec[drec].value_name = v;
|
|
}
|
|
|
|
forms = scheme_flatten_begin(first, forms);
|
|
|
|
SCHEME_EXPAND_OBSERVE_SPLICE(rec[drec].observer, forms);
|
|
|
|
if (SCHEME_STX_NULLP(forms)) {
|
|
scheme_wrong_syntax(scheme_begin_stx_string, NULL, first,
|
|
"bad syntax (empty form)");
|
|
}
|
|
|
|
forms = scheme_datum_to_syntax(forms, orig_forms, orig_forms, 0, 0);
|
|
|
|
goto try_again;
|
|
} else if (SAME_OBJ(gval, scheme_define_values_syntax)
|
|
|| SAME_OBJ(gval, scheme_define_syntaxes_syntax)) {
|
|
/* Turn defines into a letrec: */
|
|
Scheme_Object *var, *vars, *v, *link;
|
|
Scheme_Object *l = scheme_null, *start = NULL;
|
|
Scheme_Object *stx_l = scheme_null, *stx_start = NULL;
|
|
int is_val;
|
|
|
|
while (1) {
|
|
int cnt;
|
|
|
|
is_val = SAME_OBJ(gval, scheme_define_values_syntax);
|
|
|
|
v = SCHEME_STX_CDR(first);
|
|
|
|
if (is_val) {
|
|
SCHEME_EXPAND_OBSERVE_PRIM_DEFINE_VALUES(rec[drec].observer);
|
|
} else {
|
|
SCHEME_EXPAND_OBSERVE_PRIM_DEFINE_SYNTAXES(rec[drec].observer);
|
|
}
|
|
|
|
if (!SCHEME_STX_PAIRP(v))
|
|
scheme_wrong_syntax(NULL, NULL, first,
|
|
"bad syntax (" IMPROPER_LIST_FORM ")");
|
|
|
|
var = NULL;
|
|
vars = SCHEME_STX_CAR(v);
|
|
cnt = 0;
|
|
while (SCHEME_STX_PAIRP(vars)) {
|
|
var = SCHEME_STX_CAR(vars);
|
|
if (!SCHEME_STX_SYMBOLP(var))
|
|
scheme_wrong_syntax(NULL, var, first,
|
|
"name must be an identifier");
|
|
scheme_dup_symbol_check(&r, "internal definition", var, "binding", first);
|
|
vars = SCHEME_STX_CDR(vars);
|
|
cnt++;
|
|
}
|
|
if (!SCHEME_STX_NULLP(vars)) {
|
|
vars = SCHEME_STX_CAR(v);
|
|
scheme_wrong_syntax(NULL, vars, first,
|
|
"not a sequence of identifiers");
|
|
}
|
|
|
|
/* Preserve properties and track at the clause level: */
|
|
v = scheme_datum_to_syntax(v, first, first, 0, 0);
|
|
var = SCHEME_STX_CAR(first);
|
|
v = scheme_stx_track(v, first, var);
|
|
|
|
link = scheme_make_pair(v, scheme_null);
|
|
if (is_val) {
|
|
if (!start)
|
|
start = link;
|
|
else
|
|
SCHEME_CDR(l) = link;
|
|
l = link;
|
|
} else {
|
|
if (!stx_start)
|
|
stx_start = link;
|
|
else
|
|
SCHEME_CDR(stx_l) = link;
|
|
stx_l = link;
|
|
}
|
|
|
|
result = SCHEME_STX_CDR(result);
|
|
if (!SCHEME_STX_NULLP(result) && !SCHEME_STX_PAIRP(result))
|
|
scheme_wrong_syntax(NULL, NULL, first, NULL);
|
|
|
|
{
|
|
/* Execute internal macro definition and register non-macros */
|
|
Scheme_Comp_Env *new_env;
|
|
Scheme_Object *names, *expr, *l, *a;
|
|
int pos;
|
|
|
|
new_env = scheme_new_compilation_frame(0, SCHEME_FOR_INTDEF, env, rec[drec].certs);
|
|
|
|
names = SCHEME_STX_CAR(v);
|
|
expr = SCHEME_STX_CDR(v);
|
|
if (!SCHEME_STX_PAIRP(expr)) {
|
|
if (SCHEME_STX_NULLP(expr))
|
|
scheme_wrong_syntax(NULL, NULL, first,
|
|
"bad syntax (missing expression)");
|
|
else
|
|
scheme_wrong_syntax(NULL, NULL, first,
|
|
"bad syntax (" IMPROPER_LIST_FORM ")");
|
|
}
|
|
link = SCHEME_STX_CDR(expr);
|
|
if (!SCHEME_STX_NULLP(link)) {
|
|
scheme_wrong_syntax(NULL, NULL, first,
|
|
"bad syntax (extra data after expression)");
|
|
}
|
|
expr = SCHEME_STX_CAR(expr);
|
|
|
|
scheme_add_local_syntax(cnt, new_env);
|
|
|
|
/* Initialize environment slots to #f, which means "not syntax". */
|
|
cnt = 0;
|
|
for (l = names; SCHEME_STX_PAIRP(l); l = SCHEME_STX_CDR(l)) {
|
|
a = SCHEME_STX_CAR(l);
|
|
scheme_set_local_syntax(cnt++, a, scheme_false, new_env);
|
|
}
|
|
|
|
if (!is_val) {
|
|
/* Evaluate and bind syntaxes */
|
|
scheme_prepare_exp_env(new_env->genv);
|
|
pos = 0;
|
|
expr = scheme_add_rename_rib(expr, rib);
|
|
scheme_bind_syntaxes("local syntax definition",
|
|
names, expr,
|
|
new_env->genv->exp_env, new_env->insp, rec, drec,
|
|
new_env, new_env,
|
|
&pos);
|
|
}
|
|
|
|
/* Extend shared rib with renamings */
|
|
scheme_add_env_renames(rib, new_env, env);
|
|
|
|
/* Remember extended environment */
|
|
SCHEME_PTR1_VAL(ctx) = new_env;
|
|
env = new_env;
|
|
xenv = NULL;
|
|
}
|
|
|
|
define_try_again:
|
|
if (!SCHEME_STX_NULLP(result)) {
|
|
first = SCHEME_STX_CAR(result);
|
|
first = scheme_datum_to_syntax(first, forms, forms, 0, 0);
|
|
{
|
|
Scheme_Object *old_first;
|
|
old_first = first;
|
|
first = scheme_add_rename_rib(first, rib);
|
|
SCHEME_EXPAND_OBSERVE_NEXT(rec[drec].observer);
|
|
SCHEME_EXPAND_OBSERVE_BLOCK_RENAMES(rec[drec].observer,old_first,first);
|
|
}
|
|
first = scheme_check_immediate_macro(first, env, rec, drec, 1, &gval, &xenv, ectx);
|
|
more = 1;
|
|
if (NOT_SAME_OBJ(gval, scheme_define_values_syntax)
|
|
&& NOT_SAME_OBJ(gval, scheme_define_syntaxes_syntax)) {
|
|
if (SAME_OBJ(gval, scheme_begin_syntax)) {
|
|
/* Inline content */
|
|
result = SCHEME_STX_CDR(result);
|
|
SCHEME_EXPAND_OBSERVE_PRIM_BEGIN(rec[drec].observer);
|
|
result = scheme_flatten_begin(first, result);
|
|
SCHEME_EXPAND_OBSERVE_SPLICE(rec[drec].observer,result);
|
|
goto define_try_again;
|
|
} else {
|
|
/* Keep partially expanded `first': */
|
|
result = SCHEME_STX_CDR(result);
|
|
result = scheme_make_pair(first, result);
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (SCHEME_STX_PAIRP(result)) {
|
|
if (!start)
|
|
start = scheme_null;
|
|
|
|
/* I think the following was intended as an optimization for `expand',
|
|
since the syntax definition will be dropped. But it breaks
|
|
`local-expand':
|
|
if (stx_start && !(rec[drec].comp || (rec[drec].depth == -1)))
|
|
stx_start = scheme_null; */
|
|
if (stx_start) {
|
|
result = scheme_make_pair(letrec_syntaxes_symbol,
|
|
scheme_make_pair(stx_start,
|
|
scheme_make_pair(start, result)));
|
|
} else {
|
|
result = scheme_make_pair(letrec_values_symbol, scheme_make_pair(start, result));
|
|
}
|
|
result = scheme_datum_to_syntax(result, forms, scheme_sys_wraps(env), 0, 2);
|
|
result = scheme_add_rename_rib(result, rib);
|
|
|
|
more = 0;
|
|
} else {
|
|
/* Empty body: illegal. */
|
|
scheme_wrong_syntax(scheme_begin_stx_string, NULL, forms,
|
|
"no expression after a sequence of internal definitions");
|
|
}
|
|
}
|
|
|
|
if (!more) {
|
|
if (rec[drec].comp) {
|
|
result = scheme_compile_expr(result, env, rec, drec);
|
|
return scheme_make_pair(result, scheme_null);
|
|
} else {
|
|
if (rec[drec].depth > 0)
|
|
--rec[drec].depth;
|
|
if (rec[drec].depth) {
|
|
result = scheme_make_pair(result, scheme_null);
|
|
SCHEME_EXPAND_OBSERVE_BLOCK_TO_LETREC(rec[drec].observer, result);
|
|
return scheme_expand_list(result, env, rec, drec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rec[drec].comp) {
|
|
Scheme_Object *vname, *rest;
|
|
|
|
vname = rec[drec].value_name;
|
|
scheme_compile_rec_done_local(rec, drec);
|
|
scheme_init_compile_recs(rec, drec, recs, 2);
|
|
|
|
rest = SCHEME_STX_CDR(forms);
|
|
if (SCHEME_STX_NULLP(rest))
|
|
recs[0].value_name = vname;
|
|
else
|
|
recs[1].value_name = vname;
|
|
|
|
rest = scheme_datum_to_syntax(rest, forms, forms, 0, 0);
|
|
|
|
first = scheme_compile_expr(first, env, recs, 0);
|
|
#if EMBEDDED_DEFINES_START_ANYWHERE
|
|
forms = scheme_compile_expand_block(rest, env, recs, 1);
|
|
#else
|
|
forms = scheme_compile_list(rest, env, recs, 1);
|
|
#endif
|
|
|
|
scheme_merge_compile_recs(rec, drec, recs, 2);
|
|
return scheme_make_pair(first, forms);
|
|
} else {
|
|
#if EMBEDDED_DEFINES_START_ANYWHERE
|
|
/* Expand-observe not implemented for this case,
|
|
so fix that if it's ever enabled. */
|
|
Scheme_Object *rest, *vname;
|
|
|
|
vname = rec[drec].value_name;
|
|
rec[drec].value_name = scheme_false;
|
|
scheme_init_expand_recs(rec, drec, recs, 2);
|
|
|
|
rest = SCHEME_STX_CDR(forms);
|
|
|
|
if (SCHEME_STX_NULLP(rest))
|
|
recs[0].value_name = vname;
|
|
else
|
|
recs[1].value_name = vname;
|
|
|
|
first = scheme_expand_expr(first, env, recs, 0);
|
|
|
|
rest = scheme_datum_to_syntax(rest, forms, forms, 0, -1);
|
|
forms = scheme_compile_expand_block(rest, env, recs, 1);
|
|
return scheme_make_pair(first, forms);
|
|
#else
|
|
Scheme_Object *newforms, *vname;
|
|
|
|
vname = rec[drec].value_name;
|
|
rec[drec].value_name = scheme_false;
|
|
scheme_init_expand_recs(rec, drec, recs, 2);
|
|
|
|
recs[0].value_name = vname;
|
|
|
|
newforms = SCHEME_STX_CDR(forms);
|
|
newforms = scheme_make_pair(first, newforms);
|
|
forms = scheme_datum_to_syntax(newforms, forms, forms, 0, -1);
|
|
|
|
if (scheme_stx_proper_list_length(forms) < 0)
|
|
scheme_wrong_syntax(scheme_begin_stx_string, NULL, forms, "bad syntax");
|
|
|
|
SCHEME_EXPAND_OBSERVE_BLOCK_TO_LIST(rec[drec].observer, forms);
|
|
forms = scheme_expand_list(forms, env, recs, 0);
|
|
return forms;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_compile_block(Scheme_Object *forms, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
return scheme_compile_expand_block(forms, env, rec, drec);
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_expand_block(Scheme_Object *forms, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
return scheme_compile_expand_block(forms, env, erec, drec);
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_expand_list(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
Scheme_Object *first = NULL, *last = NULL, *fm;
|
|
|
|
SCHEME_EXPAND_OBSERVE_ENTER_LIST(erec[drec].observer, form);
|
|
|
|
if (SCHEME_STX_NULLP(form)) {
|
|
SCHEME_EXPAND_OBSERVE_EXIT_LIST(erec[drec].observer, form);
|
|
return scheme_null;
|
|
}
|
|
|
|
if (scheme_stx_proper_list_length(form) < 0) {
|
|
/* This is already checked for anything but application */
|
|
scheme_wrong_syntax(scheme_application_stx_string, NULL, form,
|
|
"bad syntax (" IMPROPER_LIST_FORM ")");
|
|
}
|
|
|
|
fm = form;
|
|
while (SCHEME_STX_PAIRP(fm)) {
|
|
Scheme_Object *r, *p;
|
|
Scheme_Expand_Info erec1;
|
|
|
|
SCHEME_EXPAND_OBSERVE_NEXT(erec[drec].observer);
|
|
|
|
p = SCHEME_STX_CDR(fm);
|
|
|
|
scheme_init_expand_recs(erec, drec, &erec1, 1);
|
|
erec1.value_name = (SCHEME_STX_NULLP(p) ? erec[drec].value_name : scheme_false);
|
|
|
|
r = SCHEME_STX_CAR(fm);
|
|
r = scheme_expand_expr(r, env, &erec1, 0);
|
|
p = scheme_make_pair(r, scheme_null);
|
|
if (last)
|
|
SCHEME_CDR(last) = p;
|
|
else
|
|
first = p;
|
|
last = p;
|
|
|
|
fm = SCHEME_STX_CDR(fm);
|
|
}
|
|
|
|
form = scheme_datum_to_syntax(first, form, form, 0, 0);
|
|
SCHEME_EXPAND_OBSERVE_EXIT_LIST(erec[drec].observer, form);
|
|
return form;
|
|
}
|
|
|
|
|
|
Scheme_Object *
|
|
scheme_flatten_begin(Scheme_Object *expr, Scheme_Object *append_onto)
|
|
{
|
|
Scheme_Object *l, *ll, *a, *name, *body;
|
|
|
|
if (scheme_stx_proper_list_length(expr) < 0)
|
|
scheme_wrong_syntax(NULL, NULL, expr, "bad syntax (" IMPROPER_LIST_FORM ")");
|
|
|
|
name = SCHEME_STX_CAR(expr);
|
|
body = SCHEME_STX_CDR(expr);
|
|
|
|
/* Extract body of `begin' and add tracking information */
|
|
l = scheme_copy_list(scheme_flatten_syntax_list(body, NULL));
|
|
for (ll = l; !SCHEME_NULLP(ll); ll = SCHEME_CDR(ll)) {
|
|
a = SCHEME_CAR(ll);
|
|
a = scheme_stx_track(a, expr, name);
|
|
SCHEME_CAR(ll) = a;
|
|
}
|
|
|
|
return scheme_append(l, append_onto);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* continuation marks */
|
|
/*========================================================================*/
|
|
|
|
void scheme_push_continuation_frame(Scheme_Cont_Frame_Data *d)
|
|
{
|
|
d->cont_mark_pos = MZ_CONT_MARK_POS;
|
|
d->cont_mark_stack = MZ_CONT_MARK_STACK;
|
|
|
|
MZ_CONT_MARK_POS += 2;
|
|
}
|
|
|
|
void scheme_pop_continuation_frame(Scheme_Cont_Frame_Data *d)
|
|
{
|
|
MZ_CONT_MARK_POS = d->cont_mark_pos;
|
|
MZ_CONT_MARK_STACK = d->cont_mark_stack;
|
|
}
|
|
|
|
static MZ_MARK_STACK_TYPE clone_meta_cont_set_mark(Scheme_Meta_Continuation *mc, Scheme_Object *val, long findpos)
|
|
{
|
|
/* Clone the meta-continuation, in case it was captured by
|
|
a continuation in its current state. */
|
|
Scheme_Meta_Continuation *naya;
|
|
Scheme_Cont_Mark *cp;
|
|
|
|
naya = MALLOC_ONE_RT(Scheme_Meta_Continuation);
|
|
memcpy(naya, mc, sizeof(Scheme_Meta_Continuation));
|
|
cp = MALLOC_N(Scheme_Cont_Mark, naya->cont_mark_total);
|
|
memcpy(cp, mc->cont_mark_stack_copied, naya->cont_mark_total * sizeof(Scheme_Cont_Mark));
|
|
naya->cont_mark_stack_copied = cp;
|
|
naya->copy_after_captured = scheme_cont_capture_count;
|
|
mc = naya;
|
|
scheme_current_thread->meta_continuation = mc;
|
|
|
|
mc->cont_mark_stack_copied[findpos].val = val;
|
|
mc->cont_mark_stack_copied[findpos].cache = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static MZ_MARK_STACK_TYPE new_segment_set_mark(long segpos, long pos, Scheme_Object *key, Scheme_Object *val)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Cont_Mark *cm = NULL;
|
|
int c = p->cont_mark_seg_count;
|
|
Scheme_Cont_Mark **segs, *seg;
|
|
long findpos;
|
|
|
|
/* Note: we perform allocations before changing p to avoid GC trouble,
|
|
since MzScheme adjusts a thread's cont_mark_stack_segments on GC. */
|
|
segs = MALLOC_N(Scheme_Cont_Mark *, c + 1);
|
|
seg = scheme_malloc_allow_interior(sizeof(Scheme_Cont_Mark) * SCHEME_MARK_SEGMENT_SIZE);
|
|
segs[c] = seg;
|
|
|
|
memcpy(segs, p->cont_mark_stack_segments, c * sizeof(Scheme_Cont_Mark *));
|
|
|
|
p->cont_mark_seg_count++;
|
|
p->cont_mark_stack_segments = segs;
|
|
|
|
seg = p->cont_mark_stack_segments[segpos];
|
|
cm = seg + pos;
|
|
findpos = MZ_CONT_MARK_STACK;
|
|
MZ_CONT_MARK_STACK++;
|
|
|
|
cm->key = key;
|
|
cm->val = val;
|
|
cm->pos = MZ_CONT_MARK_POS; /* always odd */
|
|
cm->cache = NULL;
|
|
|
|
return findpos;
|
|
}
|
|
|
|
|
|
MZ_MARK_STACK_TYPE scheme_set_cont_mark(Scheme_Object *key, Scheme_Object *val)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Cont_Mark *cm = NULL;
|
|
long findpos, bottom;
|
|
|
|
findpos = (long)MZ_CONT_MARK_STACK;
|
|
bottom = (long)p->cont_mark_stack_bottom;
|
|
while (1) {
|
|
if (findpos-- > bottom) {
|
|
Scheme_Cont_Mark *seg = p->cont_mark_stack_segments[findpos >> SCHEME_LOG_MARK_SEGMENT_SIZE];
|
|
long pos = findpos & SCHEME_MARK_SEGMENT_MASK;
|
|
Scheme_Cont_Mark *find = seg + pos;
|
|
|
|
if ((long)find->pos < (long)MZ_CONT_MARK_POS) {
|
|
break;
|
|
} else {
|
|
if (find->key == key) {
|
|
cm = find;
|
|
break;
|
|
} else {
|
|
/* Assume that we'll mutate rather than allocate a new mark record. */
|
|
/* This is a bad assumption for a nasty program that repeatedly
|
|
creates a new key for the same frame, but it's good enough. */
|
|
find->cache = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
if (MZ_CONT_MARK_POS == p->cont_mark_pos_bottom + 2) {
|
|
if (p->meta_continuation) {
|
|
if (key != scheme_stack_dump_key) {
|
|
/* Check the end of the meta-continuation's stack */
|
|
Scheme_Meta_Continuation *mc = p->meta_continuation;
|
|
for (findpos = (long)mc->cont_mark_total; findpos--; ) {
|
|
if (mc->cont_mark_stack_copied[findpos].pos != mc->cont_mark_pos)
|
|
break;
|
|
if (mc->cont_mark_stack_copied[findpos].key == key) {
|
|
if (mc->copy_after_captured < scheme_cont_capture_count) {
|
|
return clone_meta_cont_set_mark(mc, val, findpos);
|
|
}
|
|
mc->cont_mark_stack_copied[findpos].val = val;
|
|
mc->cont_mark_stack_copied[findpos].cache = NULL;
|
|
return 0;
|
|
} else {
|
|
mc->cont_mark_stack_copied[findpos].cache = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!cm) {
|
|
/* Allocate a new mark record: */
|
|
long segpos;
|
|
long pos;
|
|
Scheme_Cont_Mark *seg;
|
|
|
|
findpos = MZ_CONT_MARK_STACK;
|
|
segpos = ((long)findpos) >> SCHEME_LOG_MARK_SEGMENT_SIZE;
|
|
pos = ((long)findpos) & SCHEME_MARK_SEGMENT_MASK;
|
|
|
|
if (segpos >= p->cont_mark_seg_count) {
|
|
/* Need a new segment */
|
|
return new_segment_set_mark(segpos, pos, key, val);
|
|
}
|
|
|
|
seg = p->cont_mark_stack_segments[segpos];
|
|
cm = seg + pos;
|
|
MZ_CONT_MARK_STACK = findpos + 1;
|
|
}
|
|
|
|
cm->key = key;
|
|
cm->val = val;
|
|
cm->pos = MZ_CONT_MARK_POS; /* always odd */
|
|
cm->cache = NULL;
|
|
|
|
return findpos;
|
|
}
|
|
|
|
void scheme_temp_dec_mark_depth()
|
|
{
|
|
MZ_CONT_MARK_POS -= 2;
|
|
}
|
|
|
|
void scheme_temp_inc_mark_depth()
|
|
{
|
|
MZ_CONT_MARK_POS += 2;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* eval-apply helpers */
|
|
/*========================================================================*/
|
|
|
|
/* called in schapp.h */
|
|
|
|
static Scheme_Object *do_apply_known_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object **argv = (Scheme_Object **)p->ku.k.p2;
|
|
|
|
p->ku.k.p2 = NULL;
|
|
|
|
return _scheme_apply_known_prim_closure_multi((Scheme_Object *)p->ku.k.p1,
|
|
p->ku.k.i1,
|
|
argv);
|
|
}
|
|
|
|
#if 0
|
|
# define DEBUG_CHECK_TYPE(v) \
|
|
if ((v != SCHEME_MULTIPLE_VALUES) \
|
|
&& (v != SCHEME_TAIL_CALL_WAITING) \
|
|
&& (v != SCHEME_EVAL_WAITING) \
|
|
&& (SCHEME_TYPE(v) > (_scheme_last_type_ + 25))) \
|
|
{ Scheme_Object *o = *(Scheme_Object **)(v); \
|
|
if (SCHEME_TYPE(o) > (_scheme_last_type_ + 25))\
|
|
scheme_signal_error("bad type"); }
|
|
#else
|
|
# define DEBUG_CHECK_TYPE(v) /**/
|
|
#endif
|
|
|
|
Scheme_Object *_scheme_apply_known_prim_closure_multi(Scheme_Object *rator,
|
|
int argc,
|
|
Scheme_Object **argv)
|
|
{
|
|
#define PRIM_CHECK_ARITY 0
|
|
#define PRIM_CHECK_MULTI 0
|
|
#include "schapp.inc"
|
|
}
|
|
|
|
Scheme_Object *_scheme_apply_prim_closure_multi(Scheme_Object *rator,
|
|
int argc,
|
|
Scheme_Object **argv)
|
|
{
|
|
#define PRIM_CHECK_ARITY 1
|
|
#define PRIM_CHECK_MULTI 0
|
|
#include "schapp.inc"
|
|
}
|
|
|
|
Scheme_Object *_scheme_apply_known_prim_closure(Scheme_Object *rator,
|
|
int argc,
|
|
Scheme_Object **argv)
|
|
{
|
|
#define PRIM_CHECK_ARITY 0
|
|
#define PRIM_CHECK_MULTI 1
|
|
#include "schapp.inc"
|
|
}
|
|
|
|
Scheme_Object *_scheme_apply_prim_closure(Scheme_Object *rator,
|
|
int argc,
|
|
Scheme_Object **argv)
|
|
{
|
|
#define PRIM_CHECK_ARITY 1
|
|
#define PRIM_CHECK_MULTI 1
|
|
#include "schapp.inc"
|
|
}
|
|
|
|
|
|
#ifdef MZ_USE_JIT
|
|
|
|
# define PRIM_APPLY_NAME _scheme_apply_from_native
|
|
# define PRIM_APPLY_NAME_FAST _scheme_apply_from_native_fast
|
|
# define PRIM_CHECK_VALUE 1
|
|
# define PRIM_CHECK_MULTI 1
|
|
# include "schnapp.inc"
|
|
|
|
# define PRIM_APPLY_NAME _scheme_apply_multi_from_native
|
|
# define PRIM_APPLY_NAME_FAST _scheme_apply_multi_from_native_fast
|
|
# define PRIM_CHECK_VALUE 1
|
|
# define PRIM_CHECK_MULTI 0
|
|
# include "schnapp.inc"
|
|
|
|
# define PRIM_APPLY_NAME _scheme_tail_apply_from_native
|
|
# define PRIM_APPLY_NAME_FAST _scheme_tail_apply_from_native_fast
|
|
/* It's ok to call primitive and closed primitives directly,
|
|
since they implement further tail by trampolining. */
|
|
# define PRIM_CHECK_VALUE 0
|
|
# define PRIM_CHECK_MULTI 0
|
|
# include "schnapp.inc"
|
|
|
|
#endif
|
|
|
|
Scheme_Object *scheme_check_one_value(Scheme_Object *v)
|
|
{
|
|
if (v == SCHEME_MULTIPLE_VALUES)
|
|
scheme_wrong_return_arity(NULL, 1, scheme_multiple_count, scheme_multiple_array, NULL);
|
|
return v;
|
|
}
|
|
|
|
static Scheme_Object *do_eval_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *obj = (Scheme_Object *)p->ku.k.p1;
|
|
Scheme_Object **argv = (Scheme_Object **)p->ku.k.p2;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
|
|
return scheme_do_eval(obj,
|
|
p->ku.k.i1,
|
|
argv,
|
|
p->ku.k.i2);
|
|
}
|
|
|
|
static void unbound_global(Scheme_Object *obj)
|
|
{
|
|
Scheme_Object *tmp;
|
|
|
|
tmp = MZ_RUNSTACK[SCHEME_TOPLEVEL_DEPTH(obj)];
|
|
tmp = ((Scheme_Object **)tmp)[SCHEME_TOPLEVEL_POS(obj)];
|
|
|
|
scheme_unbound_global((Scheme_Bucket *)tmp);
|
|
}
|
|
|
|
static void make_tail_buffer_safe()
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
|
|
GC_CAN_IGNORE Scheme_Object **tb;
|
|
p->tail_buffer = NULL; /* so args aren't zeroed */
|
|
tb = MALLOC_N(Scheme_Object *, p->tail_buffer_size);
|
|
p->tail_buffer = tb;
|
|
}
|
|
|
|
static Scheme_Dynamic_Wind *intersect_dw(Scheme_Dynamic_Wind *a, Scheme_Dynamic_Wind *b,
|
|
Scheme_Object *prompt_tag, int b_has_tag, int *_common_depth)
|
|
{
|
|
int alen = 0, blen = 0;
|
|
int a_has_tag = 0, a_prompt_delta = 0, b_prompt_delta = 0;
|
|
Scheme_Dynamic_Wind *dw;
|
|
|
|
for (dw = a; dw && (dw->prompt_tag != prompt_tag); dw = dw->prev) {
|
|
}
|
|
if (dw) {
|
|
/* Cut off `a' below the prompt dw. */
|
|
a_prompt_delta = dw->depth;
|
|
a_has_tag = 1;
|
|
}
|
|
|
|
if (a_has_tag)
|
|
a_prompt_delta += 1;
|
|
if (b_has_tag)
|
|
b_prompt_delta += 1;
|
|
|
|
alen = (a ? a->depth + 1 : 0) - a_prompt_delta;
|
|
blen = (b ? b->depth + 1 : 0) - b_prompt_delta;
|
|
|
|
while (alen > blen) {
|
|
--alen;
|
|
a = a->prev;
|
|
}
|
|
if (!alen) {
|
|
*_common_depth = b_prompt_delta - 1;
|
|
return a;
|
|
}
|
|
while (blen > alen) {
|
|
--blen;
|
|
b = b->prev;
|
|
}
|
|
|
|
/* At this point, we have chains that are the same length. */
|
|
while (blen) {
|
|
if (SAME_OBJ(a->id ? a->id : (Scheme_Object *)a,
|
|
b->id ? b->id : (Scheme_Object *)b))
|
|
break;
|
|
a = a->prev;
|
|
b = b->prev;
|
|
blen--;
|
|
}
|
|
|
|
*_common_depth = (b ? b->depth : -1);
|
|
|
|
return a;
|
|
}
|
|
|
|
static Scheme_Prompt *lookup_cont_prompt(Scheme_Cont *c,
|
|
Scheme_Meta_Continuation **_prompt_mc,
|
|
MZ_MARK_POS_TYPE *_prompt_pos,
|
|
const char *msg)
|
|
{
|
|
Scheme_Prompt *prompt;
|
|
|
|
prompt = (Scheme_Prompt *)scheme_extract_one_cc_mark_with_meta(NULL,
|
|
SCHEME_PTR_VAL(c->prompt_tag),
|
|
NULL,
|
|
_prompt_mc,
|
|
_prompt_pos);
|
|
if (!prompt && !SAME_OBJ(scheme_default_prompt_tag, c->prompt_tag)) {
|
|
scheme_raise_exn(MZEXN_FAIL_CONTRACT_CONTINUATION,
|
|
msg);
|
|
}
|
|
|
|
return prompt;
|
|
}
|
|
|
|
#define LOOKUP_NO_PROMPT "continuation application: no corresponding prompt in the current continuation"
|
|
|
|
static Scheme_Prompt *check_barrier(Scheme_Prompt *prompt,
|
|
Scheme_Meta_Continuation *prompt_cont, MZ_MARK_POS_TYPE prompt_pos,
|
|
Scheme_Cont *c)
|
|
/* A continuation barrier is analogous to a dynamic-wind. A jump is
|
|
allowed if no dynamic-wind-like barriers would be executed for
|
|
the jump. */
|
|
{
|
|
Scheme_Prompt *barrier_prompt, *b1, *b2;
|
|
Scheme_Meta_Continuation *barrier_cont;
|
|
MZ_MARK_POS_TYPE barrier_pos;
|
|
|
|
barrier_prompt = scheme_get_barrier_prompt(&barrier_cont, &barrier_pos);
|
|
b1 = barrier_prompt;
|
|
if (b1) {
|
|
if (!b1->is_barrier)
|
|
b1 = NULL;
|
|
else if (prompt
|
|
&& scheme_is_cm_deeper(barrier_cont, barrier_pos,
|
|
prompt_cont, prompt_pos))
|
|
b1 = NULL;
|
|
}
|
|
b2 = c->barrier_prompt;
|
|
if (b2) {
|
|
if (!b2->is_barrier)
|
|
b2 = NULL;
|
|
}
|
|
|
|
if (b1 != b2) {
|
|
scheme_raise_exn(MZEXN_FAIL_CONTRACT_CONTINUATION,
|
|
"continuation application: attempt to cross a continuation barrier");
|
|
}
|
|
|
|
return barrier_prompt;
|
|
}
|
|
|
|
void scheme_recheck_prompt_and_barrier(Scheme_Cont *c)
|
|
/* Check for prompt & barrier, again. We need to
|
|
call this function like a d-w thunk, so that the meta
|
|
continuation is right in case of an error. */
|
|
{
|
|
Scheme_Prompt *prompt;
|
|
Scheme_Meta_Continuation *prompt_cont;
|
|
MZ_MARK_POS_TYPE prompt_pos;
|
|
prompt = lookup_cont_prompt(c, &prompt_cont, &prompt_pos,
|
|
LOOKUP_NO_PROMPT
|
|
" on return from `dynamic-wind' post thunk");
|
|
check_barrier(prompt, prompt_cont, prompt_pos, c);
|
|
}
|
|
|
|
static int exec_dyn_wind_posts(Scheme_Dynamic_Wind *common, Scheme_Cont *c, int common_depth,
|
|
Scheme_Dynamic_Wind **_common)
|
|
{
|
|
int meta_depth;
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Dynamic_Wind *dw;
|
|
int old_cac = scheme_continuation_application_count;
|
|
|
|
*_common = common;
|
|
|
|
for (dw = p->dw;
|
|
(common ? dw->depth != common->depth : dw != common); /* not id, which may be duplicated */
|
|
) {
|
|
meta_depth = p->next_meta;
|
|
p->next_meta += dw->next_meta;
|
|
p->dw = dw->prev;
|
|
if (dw->post) {
|
|
if (meta_depth > 0) {
|
|
scheme_apply_dw_in_meta(dw, 1, meta_depth, c);
|
|
} else {
|
|
DW_PrePost_Proc post = dw->post;
|
|
|
|
MZ_CONT_MARK_POS = dw->envss.cont_mark_pos;
|
|
MZ_CONT_MARK_STACK = dw->envss.cont_mark_stack;
|
|
post(dw->data);
|
|
|
|
if (scheme_continuation_application_count != old_cac) {
|
|
scheme_recheck_prompt_and_barrier(c);
|
|
}
|
|
}
|
|
p = scheme_current_thread;
|
|
/* p->dw might not match dw if the post thunk captures a
|
|
continuation that is later restored in a different
|
|
meta continuation: */
|
|
dw = p->dw;
|
|
|
|
/* If any continuations were applied, then the set of dynamic
|
|
winds may be different now than before. Re-compute the
|
|
intersection. */
|
|
if (scheme_continuation_application_count != old_cac) {
|
|
old_cac = scheme_continuation_application_count;
|
|
|
|
common = intersect_dw(p->dw, c->dw, c->prompt_tag, c->has_prompt_dw, &common_depth);
|
|
*_common = common;
|
|
}
|
|
} else
|
|
dw = dw->prev;
|
|
}
|
|
return common_depth;
|
|
}
|
|
|
|
Scheme_Object *scheme_jump_to_continuation(Scheme_Object *obj, int num_rands, Scheme_Object **rands, Scheme_Object **old_runstack)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Cont *c;
|
|
Scheme_Dynamic_Wind *common, *new_common;
|
|
Scheme_Object *value;
|
|
Scheme_Meta_Continuation *prompt_mc;
|
|
MZ_MARK_POS_TYPE prompt_pos;
|
|
Scheme_Prompt *prompt, *barrier_prompt;
|
|
int common_depth;
|
|
|
|
if (num_rands != 1) {
|
|
GC_CAN_IGNORE Scheme_Object **vals;
|
|
int i;
|
|
|
|
if (rands == p->tail_buffer)
|
|
make_tail_buffer_safe();
|
|
|
|
vals = MALLOC_N(Scheme_Object *, num_rands);
|
|
for (i = num_rands; i--; ) {
|
|
vals[i] = rands[i];
|
|
}
|
|
|
|
value = (Scheme_Object *)vals;
|
|
} else
|
|
value = rands[0];
|
|
|
|
c = (Scheme_Cont *)obj;
|
|
|
|
DO_CHECK_FOR_BREAK(p, ;);
|
|
|
|
if (!c->runstack_copied) {
|
|
/* This continuation is the same as another, except
|
|
that its mark stack is different. The different part
|
|
of the mark stack won't be visible, so we use the other. */
|
|
c = c->buf.cont;
|
|
}
|
|
|
|
if (c->composable) {
|
|
/* Composable continuation. Jump right in... */
|
|
scheme_continuation_application_count++;
|
|
MZ_RUNSTACK = old_runstack;
|
|
return scheme_compose_continuation(c, num_rands, value);
|
|
} else {
|
|
/* Aborting (Scheme-style) continuation. */
|
|
int orig_cac = scheme_continuation_application_count;
|
|
|
|
scheme_about_to_move_C_stack();
|
|
|
|
prompt = lookup_cont_prompt(c, &prompt_mc, &prompt_pos, LOOKUP_NO_PROMPT);
|
|
barrier_prompt = check_barrier(prompt, prompt_mc, prompt_pos, c);
|
|
|
|
p->suspend_break++; /* restored at call/cc destination */
|
|
|
|
/* Find `common', the intersection of dynamic-wind chain for
|
|
the current continuation and the given continuation, looking
|
|
no further back in the current continuation than a prompt. */
|
|
common = intersect_dw(p->dw, c->dw, c->prompt_tag, c->has_prompt_dw, &common_depth);
|
|
|
|
/* For dynamic-winds after `common' in this
|
|
continuation, execute the post-thunks */
|
|
common_depth = exec_dyn_wind_posts(common, c, common_depth, &new_common);
|
|
p = scheme_current_thread;
|
|
|
|
if (orig_cac != scheme_continuation_application_count) {
|
|
/* We checked for a barrier in exec_dyn_wind_posts, but
|
|
get prompt & barrier again. */
|
|
prompt = lookup_cont_prompt(c, &prompt_mc, &prompt_pos, "shouldn't fail!");
|
|
barrier_prompt = scheme_get_barrier_prompt(NULL, NULL);
|
|
common = new_common;
|
|
}
|
|
|
|
c->common_dw_depth = common_depth;
|
|
|
|
if (num_rands == 1)
|
|
c->value = value;
|
|
else {
|
|
GC_CAN_IGNORE Scheme_Object *vals;
|
|
vals = scheme_values(num_rands, (Scheme_Object **)value);
|
|
c->value = vals;
|
|
}
|
|
|
|
c->common_dw = common;
|
|
c->common_next_meta = p->next_meta;
|
|
|
|
scheme_continuation_application_count++;
|
|
|
|
if (!prompt) {
|
|
/* Invoke the continuation directly. If there's no prompt,
|
|
then the prompt's job is taken by the pseudo-prompt
|
|
created with a new thread or a barrier prompt. */
|
|
p->meta_continuation = NULL; /* since prompt wasn't in any meta-continuation */
|
|
p->meta_prompt = NULL;
|
|
if ((c->barrier_prompt == barrier_prompt) && barrier_prompt) {
|
|
/* Barrier determines continuation end. */
|
|
c->resume_to = NULL;
|
|
p->stack_start = c->stack_start;
|
|
} else {
|
|
/* Prompt is pseudo-prompt at thread beginning.
|
|
We're effectively composing the continuation,
|
|
so use it's prompt stack start. */
|
|
Scheme_Overflow *oflow;
|
|
oflow = scheme_get_thread_end_overflow();
|
|
c->resume_to = oflow;
|
|
p->stack_start = c->prompt_stack_start;
|
|
}
|
|
scheme_longjmpup(&c->buf);
|
|
} else if (prompt->id
|
|
&& (prompt->id == c->prompt_id)
|
|
&& !prompt_mc) {
|
|
/* The current prompt is the same as the one in place when
|
|
capturing the continuation, so we can jump directly. */
|
|
scheme_drop_prompt_meta_continuations(c->prompt_tag);
|
|
c->shortcut_prompt = prompt;
|
|
if ((!prompt->boundary_overflow_id && !p->overflow)
|
|
|| (prompt->boundary_overflow_id
|
|
&& (prompt->boundary_overflow_id == p->overflow->id))) {
|
|
scheme_longjmpup(&c->buf);
|
|
} else {
|
|
/* Need to unwind overflows... */
|
|
Scheme_Overflow *overflow;
|
|
overflow = p->overflow;
|
|
while (overflow->prev
|
|
&& (!overflow->prev->id
|
|
|| (overflow->prev->id != prompt->boundary_overflow_id))) {
|
|
overflow = overflow->prev;
|
|
}
|
|
/* Immediate destination is in scheme_handle_stack_overflow(). */
|
|
p->cjs.jumping_to_continuation = (Scheme_Object *)c;
|
|
p->overflow = overflow;
|
|
p->stack_start = overflow->stack_start;
|
|
scheme_longjmpup(&overflow->jmp->cont);
|
|
}
|
|
} else {
|
|
p->cjs.jumping_to_continuation = (Scheme_Object *)prompt;
|
|
p->cjs.num_vals = 1;
|
|
p->cjs.val = (Scheme_Object *)c;
|
|
p->cjs.is_escape = 1;
|
|
|
|
if (prompt_mc) {
|
|
/* The prompt is from a meta-continuation that's different
|
|
from the current one. Jump to the meta-continuation
|
|
and continue from there. Immediate destination is
|
|
in compose_continuation() in fun.c; the ultimate
|
|
destination is in scheme_finish_apply_for_prompt()
|
|
in fun.c.
|
|
We need to adjust the meta-continuation offsets in
|
|
common, based on the number that we're discarding
|
|
here. */
|
|
{
|
|
Scheme_Meta_Continuation *xmc;
|
|
int offset = 1;
|
|
for (xmc = p->meta_continuation;
|
|
xmc->prompt_tag != prompt_mc->prompt_tag;
|
|
xmc = xmc->next) {
|
|
if (xmc->overflow)
|
|
offset++;
|
|
}
|
|
c->common_next_meta -= offset;
|
|
}
|
|
p->meta_continuation = prompt_mc->next;
|
|
p->stack_start = prompt_mc->overflow->stack_start;
|
|
scheme_longjmpup(&prompt_mc->overflow->jmp->cont);
|
|
} else if ((!prompt->boundary_overflow_id && !p->overflow)
|
|
|| (prompt->boundary_overflow_id
|
|
&& (prompt->boundary_overflow_id == p->overflow->id))) {
|
|
/* Jump directly to the prompt: destination is in
|
|
scheme_finish_apply_for_prompt() in fun.c. */
|
|
scheme_drop_prompt_meta_continuations(c->prompt_tag);
|
|
scheme_longjmp(*prompt->prompt_buf, 1);
|
|
} else {
|
|
/* Need to unwind overflows to get to the prompt. */
|
|
Scheme_Overflow *overflow;
|
|
scheme_drop_prompt_meta_continuations(c->prompt_tag);
|
|
overflow = p->overflow;
|
|
while (overflow->prev
|
|
&& (!overflow->prev->id
|
|
|| (overflow->prev->id != prompt->boundary_overflow_id))) {
|
|
overflow = overflow->prev;
|
|
}
|
|
/* Immediate destination is in scheme_handle_stack_overflow().
|
|
Ultimate destination is in scheme_finish_apply_for_prompt()
|
|
in fun.c. */
|
|
p->overflow = overflow;
|
|
p->stack_start = overflow->stack_start;
|
|
scheme_longjmpup(&overflow->jmp->cont);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void scheme_escape_to_continuation(Scheme_Object *obj, int num_rands, Scheme_Object **rands)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *value;
|
|
|
|
if (num_rands != 1) {
|
|
GC_CAN_IGNORE Scheme_Object **vals;
|
|
int i;
|
|
|
|
if (rands == p->tail_buffer)
|
|
make_tail_buffer_safe();
|
|
|
|
vals = MALLOC_N(Scheme_Object *, num_rands);
|
|
for (i = num_rands; i--; ) {
|
|
vals[i] = rands[i];
|
|
}
|
|
|
|
value = (Scheme_Object *)vals;
|
|
p->cjs.num_vals = num_rands;
|
|
} else {
|
|
value = rands[0];
|
|
p->cjs.num_vals = 1;
|
|
}
|
|
|
|
if (!scheme_escape_continuation_ok(obj)) {
|
|
scheme_raise_exn(MZEXN_FAIL_CONTRACT_CONTINUATION,
|
|
"continuation application: attempt to jump into an escape continuation");
|
|
}
|
|
|
|
p->cjs.val = value;
|
|
p->cjs.jumping_to_continuation = obj;
|
|
scheme_longjmp(MZTHREADELEM(p, error_buf), 1);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* main eval-apply loop */
|
|
/*========================================================================*/
|
|
|
|
/* This is the main evaluator loop. It's roughly of the form:
|
|
|
|
while (1) {
|
|
if (now-applying) {
|
|
if (apply-type-1)
|
|
...
|
|
else if (apply-type-2)
|
|
...
|
|
else ...
|
|
} else {
|
|
switch (eval-type) {
|
|
case eval-type-1:
|
|
...
|
|
case eval-type-2:
|
|
...
|
|
...
|
|
}
|
|
}
|
|
}
|
|
|
|
The main use of #ifdefs is whether to use global variables for the
|
|
Scheme stack pointer or to use local variables. Different
|
|
architectures work best with different choices.
|
|
|
|
*/
|
|
|
|
#ifdef REGISTER_POOR_MACHINE
|
|
# define USE_LOCAL_RUNSTACK 0
|
|
# define DELAY_THREAD_RUNSTACK_UPDATE 0
|
|
#else
|
|
# define USE_LOCAL_RUNSTACK 1
|
|
# define DELAY_THREAD_RUNSTACK_UPDATE 1
|
|
#endif
|
|
|
|
Scheme_Object *
|
|
scheme_do_eval(Scheme_Object *obj, int num_rands, Scheme_Object **rands,
|
|
int get_value)
|
|
{
|
|
Scheme_Type type;
|
|
Scheme_Object *v;
|
|
GC_MAYBE_IGNORE_INTERIOR Scheme_Object **old_runstack;
|
|
GC_MAYBE_IGNORE_INTERIOR MZ_MARK_STACK_TYPE old_cont_mark_stack;
|
|
#if USE_LOCAL_RUNSTACK
|
|
GC_MAYBE_IGNORE_INTERIOR Scheme_Object **runstack;
|
|
#endif
|
|
MZ_MARK_STACK_TYPE pmstack = -1;
|
|
# define p scheme_current_thread
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
# define SCHEME_CURRENT_PROCESS p
|
|
# include "mzstkchk.h"
|
|
{
|
|
p->ku.k.p1 = (void *)obj;
|
|
p->ku.k.i1 = num_rands;
|
|
if (num_rands >= 0) {
|
|
/* Copy rands: */
|
|
GC_CAN_IGNORE void *ra;
|
|
if (rands == p->tail_buffer)
|
|
make_tail_buffer_safe();
|
|
ra = (void *)MALLOC_N(Scheme_Object*, num_rands);
|
|
p->ku.k.p2 = ra;
|
|
{
|
|
int i;
|
|
for (i = num_rands; i--; ) {
|
|
((Scheme_Object **)ra)[i] = rands[i];
|
|
}
|
|
}
|
|
} else
|
|
p->ku.k.p2 = (void *)rands;
|
|
p->ku.k.i2 = get_value;
|
|
return scheme_handle_stack_overflow(do_eval_k);
|
|
}
|
|
#endif
|
|
|
|
#if USE_LOCAL_RUNSTACK
|
|
# define RUNSTACK runstack
|
|
# if DELAY_THREAD_RUNSTACK_UPDATE
|
|
# define UPDATE_THREAD_RSPTR() (MZ_RUNSTACK = runstack)
|
|
# define RUNSTACK_CHANGED() /**/
|
|
# else
|
|
# define UPDATE_THREAD_RSPTR() /**/
|
|
# define RUNSTACK_CHANGED() (MZ_RUNSTACK = runstack)
|
|
# endif
|
|
runstack = MZ_RUNSTACK;
|
|
# define RESET_LOCAL_RUNSTACK() (runstack = MZ_RUNSTACK)
|
|
#else
|
|
# define RUNSTACK MZ_RUNSTACK
|
|
# define UPDATE_THREAD_RSPTR() /**/
|
|
# define RUNSTACK_CHANGED() /**/
|
|
# define RESET_LOCAL_RUNSTACK() /**/
|
|
#endif
|
|
|
|
#define RUNSTACK_START MZ_RUNSTACK_START
|
|
|
|
#define UPDATE_THREAD_RSPTR_FOR_GC() UPDATE_THREAD_RSPTR()
|
|
#define UPDATE_THREAD_RSPTR_FOR_ERROR() UPDATE_THREAD_RSPTR()
|
|
|
|
#define UPDATE_THREAD_RSPTR_FOR_PROC_MARK() UPDATE_THREAD_RSPTR()
|
|
|
|
MZ_CONT_MARK_POS += 2;
|
|
old_runstack = RUNSTACK;
|
|
old_cont_mark_stack = MZ_CONT_MARK_STACK;
|
|
|
|
if (num_rands >= 0) {
|
|
|
|
if ((RUNSTACK - RUNSTACK_START) < SCHEME_TAIL_COPY_THRESHOLD) {
|
|
/* It's possible that a sequence of primitive _scheme_tail_apply()
|
|
calls will exhaust the Scheme stack. Watch out for that. */
|
|
p->ku.k.p1 = (void *)obj;
|
|
p->ku.k.i1 = num_rands;
|
|
p->ku.k.p2 = (void *)rands;
|
|
p->ku.k.i2 = -1;
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
if (rands == p->tail_buffer)
|
|
make_tail_buffer_safe();
|
|
MZ_CONT_MARK_POS -= 2;
|
|
return scheme_enlarge_runstack(SCHEME_TAIL_COPY_THRESHOLD, (void *(*)(void))do_eval_k);
|
|
}
|
|
|
|
apply_top:
|
|
|
|
/* DANGER: if rands == p->tail_buffer, we have to be careful to
|
|
get the arguments out of the buffer before a GC occurs.
|
|
(Otherwise, they'll be zeroed.) One way to make things safe for
|
|
GC is to let rands have the buffer and create a new one. */
|
|
|
|
type = SCHEME_TYPE(obj);
|
|
|
|
if (type == scheme_prim_type) {
|
|
GC_CAN_IGNORE Scheme_Primitive_Proc *prim;
|
|
GC_CAN_IGNORE Scheme_Primitive_Closure_Proc *f;
|
|
|
|
#define VACATE_TAIL_BUFFER_USE_RUNSTACK() \
|
|
if (rands == p->tail_buffer) { \
|
|
if (num_rands < SCHEME_TAIL_COPY_THRESHOLD) { \
|
|
int i; \
|
|
GC_CAN_IGNORE Scheme_Object **quick_rands; \
|
|
\
|
|
quick_rands = PUSH_RUNSTACK(p, RUNSTACK, num_rands); \
|
|
RUNSTACK_CHANGED(); \
|
|
\
|
|
for (i = num_rands; i--; ) { \
|
|
quick_rands[i] = rands[i]; \
|
|
} \
|
|
rands = quick_rands; \
|
|
} else { \
|
|
UPDATE_THREAD_RSPTR_FOR_GC(); \
|
|
make_tail_buffer_safe(); \
|
|
} \
|
|
}
|
|
|
|
VACATE_TAIL_BUFFER_USE_RUNSTACK();
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
|
|
prim = (Scheme_Primitive_Proc *)obj;
|
|
|
|
if (num_rands < prim->mina
|
|
|| (num_rands > prim->mu.maxa && prim->mina >= 0)) {
|
|
scheme_wrong_count_m(prim->name, prim->mina, prim->mu.maxa,
|
|
num_rands, rands,
|
|
prim->pp.flags & SCHEME_PRIM_IS_METHOD);
|
|
return NULL; /* Shouldn't get here */
|
|
}
|
|
|
|
f = prim->prim_val;
|
|
v = f(num_rands, rands, (Scheme_Object *)prim);
|
|
|
|
DEBUG_CHECK_TYPE(v);
|
|
} else if (type == scheme_closure_type) {
|
|
Scheme_Closure_Data *data;
|
|
GC_CAN_IGNORE Scheme_Object **stack, **src;
|
|
int i, has_rest, num_params;
|
|
|
|
DO_CHECK_FOR_BREAK(p, UPDATE_THREAD_RSPTR_FOR_GC(); if (rands == p->tail_buffer) make_tail_buffer_safe(););
|
|
|
|
data = SCHEME_COMPILED_CLOS_CODE(obj);
|
|
|
|
if ((RUNSTACK - RUNSTACK_START) < data->max_let_depth) {
|
|
if (rands == p->tail_buffer) {
|
|
UPDATE_THREAD_RSPTR_FOR_GC();
|
|
make_tail_buffer_safe();
|
|
}
|
|
|
|
p->ku.k.p1 = (void *)obj;
|
|
p->ku.k.i1 = num_rands;
|
|
p->ku.k.p2 = (void *)rands;
|
|
p->ku.k.i2 = -1;
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
MZ_CONT_MARK_POS -= 2;
|
|
v = (Scheme_Object *)scheme_enlarge_runstack(data->max_let_depth, (void *(*)(void))do_eval_k);
|
|
MZ_CONT_MARK_POS += 2;
|
|
goto returnv;
|
|
}
|
|
|
|
num_params = data->num_params;
|
|
has_rest = SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST;
|
|
|
|
if (num_params) {
|
|
if (has_rest) {
|
|
int extra, n;
|
|
|
|
if (num_rands < (num_params - 1)) {
|
|
UPDATE_THREAD_RSPTR_FOR_ERROR();
|
|
/* note: scheme_wrong_count_m handles rands == p->tail_buffer */
|
|
scheme_wrong_count_m((const char *)obj,
|
|
-1, -1,
|
|
num_rands, rands,
|
|
SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_IS_METHOD);
|
|
return NULL; /* Doesn't get here */
|
|
}
|
|
|
|
n = num_params - has_rest;
|
|
|
|
RUNSTACK = old_runstack - num_params;
|
|
CHECK_RUNSTACK(p, RUNSTACK);
|
|
RUNSTACK_CHANGED();
|
|
|
|
extra = num_rands - n;
|
|
if (extra) {
|
|
Scheme_Object *rest_vals;
|
|
GC_CAN_IGNORE Scheme_Object *pairs;
|
|
|
|
/* This is a special case: GC may be triggered, but
|
|
p->runstack does not point at everything that needs
|
|
to be kept if args are lower on the stack.
|
|
That's what runstack_tmp_keep is for. Also, if
|
|
runstack_tmp_keep == tail_buffer, then the buffer
|
|
won't be zeroed. */
|
|
UPDATE_THREAD_RSPTR_FOR_GC();
|
|
p->runstack_tmp_keep = rands;
|
|
|
|
rest_vals = scheme_null;
|
|
for (i = num_rands - 1; i >= n; --i) {
|
|
pairs = scheme_alloc_object();
|
|
pairs->type = scheme_pair_type;
|
|
SCHEME_CDR(pairs) = rest_vals;
|
|
SCHEME_CAR(pairs) = rands[i];
|
|
rest_vals = pairs;
|
|
}
|
|
|
|
p->runstack_tmp_keep = NULL;
|
|
|
|
stack = RUNSTACK;
|
|
stack[n] = rest_vals;
|
|
while (n--) {
|
|
stack[n] = rands[n];
|
|
}
|
|
} else {
|
|
stack = RUNSTACK;
|
|
/* Possibly, but not necessarily, rands > stack: */
|
|
if ((unsigned long)rands > (unsigned long)stack) {
|
|
int i;
|
|
for (i = 0; i < n; i++) {
|
|
stack[i] = rands[i];
|
|
}
|
|
stack[n] = scheme_null;
|
|
} else {
|
|
stack[n] = scheme_null;
|
|
while (n--) {
|
|
stack[n] = rands[n];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (num_rands != num_params) {
|
|
UPDATE_THREAD_RSPTR_FOR_ERROR();
|
|
/* note: scheme_wrong_count_m handles rands == p->tail_buffer */
|
|
scheme_wrong_count_m((const char *)obj,
|
|
-1, -1,
|
|
num_rands, rands,
|
|
SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_IS_METHOD);
|
|
return NULL; /* Doesn't get here */
|
|
}
|
|
|
|
stack = RUNSTACK = old_runstack - num_params;
|
|
CHECK_RUNSTACK(p, RUNSTACK);
|
|
RUNSTACK_CHANGED();
|
|
|
|
if (rands != stack) {
|
|
int n = num_params;
|
|
while (n--) {
|
|
stack[n] = rands[n];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (num_rands) {
|
|
if (has_rest) {
|
|
/* 0 params and has_rest => (lambda args E) where args is not in E,
|
|
so accept any number of arguments and ignore them. */
|
|
|
|
} else {
|
|
UPDATE_THREAD_RSPTR_FOR_ERROR();
|
|
/* note: scheme_wrong_count handles rands == p->tail_buffer */
|
|
scheme_wrong_count((const char *)obj, -1, -1, num_rands, rands);
|
|
return NULL; /* Doesn't get here */
|
|
}
|
|
}
|
|
RUNSTACK = old_runstack;
|
|
RUNSTACK_CHANGED();
|
|
}
|
|
|
|
{
|
|
int n = data->closure_size;
|
|
|
|
if (n) {
|
|
src = SCHEME_COMPILED_CLOS_ENV(obj);
|
|
stack = PUSH_RUNSTACK(p, RUNSTACK, n);
|
|
RUNSTACK_CHANGED();
|
|
|
|
while (n--) {
|
|
stack[n] = src[n];
|
|
}
|
|
}
|
|
}
|
|
|
|
obj = data->code;
|
|
|
|
if (SCHEME_RPAIRP(obj)) {
|
|
UPDATE_THREAD_RSPTR_FOR_GC();
|
|
make_tail_buffer_safe();
|
|
scheme_delay_load_closure(data);
|
|
obj = data->code;
|
|
}
|
|
|
|
if (pmstack >= 0) {
|
|
long segpos = ((long)pmstack) >> SCHEME_LOG_MARK_SEGMENT_SIZE;
|
|
long pos = ((long)pmstack) & SCHEME_MARK_SEGMENT_MASK;
|
|
GC_CAN_IGNORE Scheme_Cont_Mark *pm = NULL;
|
|
|
|
pm = p->cont_mark_stack_segments[segpos] + pos;
|
|
|
|
if (!pm->cache)
|
|
pm->val = data->name;
|
|
else {
|
|
/* Need to clear caches, so do it the slow way */
|
|
UPDATE_THREAD_RSPTR_FOR_PROC_MARK();
|
|
pmstack = scheme_set_cont_mark(scheme_stack_dump_key, data->name);
|
|
}
|
|
} else {
|
|
/* Allocate a new mark record: */
|
|
long segpos = ((long)MZ_CONT_MARK_STACK) >> SCHEME_LOG_MARK_SEGMENT_SIZE;
|
|
if (segpos >= p->cont_mark_seg_count) {
|
|
UPDATE_THREAD_RSPTR_FOR_PROC_MARK();
|
|
pmstack = scheme_set_cont_mark(scheme_stack_dump_key, data->name);
|
|
} else {
|
|
long pos = ((long)MZ_CONT_MARK_STACK) & SCHEME_MARK_SEGMENT_MASK;
|
|
GC_CAN_IGNORE Scheme_Cont_Mark *pm;
|
|
GC_CAN_IGNORE Scheme_Cont_Mark *seg;
|
|
|
|
pmstack = MZ_CONT_MARK_STACK;
|
|
|
|
seg = p->cont_mark_stack_segments[segpos];
|
|
pm = seg + pos;
|
|
MZ_CONT_MARK_STACK++;
|
|
|
|
pm->key = scheme_stack_dump_key;
|
|
pm->val = data->name;
|
|
pm->pos = MZ_CONT_MARK_POS;
|
|
pm->cache = NULL;
|
|
}
|
|
}
|
|
|
|
goto eval_top;
|
|
} else if (type == scheme_case_closure_type) {
|
|
Scheme_Case_Lambda *seq;
|
|
Scheme_Closure_Data *data;
|
|
|
|
int i;
|
|
|
|
seq = (Scheme_Case_Lambda *)obj;
|
|
for (i = 0; i < seq->count; i++) {
|
|
data = SCHEME_COMPILED_CLOS_CODE(seq->array[i]);
|
|
if ((!(SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST)
|
|
&& (data->num_params == num_rands))
|
|
|| ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST)
|
|
&& (data->num_params - 1 <= num_rands))) {
|
|
obj = seq->array[i];
|
|
goto apply_top;
|
|
}
|
|
}
|
|
|
|
UPDATE_THREAD_RSPTR_FOR_ERROR();
|
|
/* note: scheme_wrong_count handles rands == p->tail_buffer */
|
|
scheme_wrong_count((char *)seq, -1, -1, num_rands, rands);
|
|
|
|
return NULL; /* Doesn't get here. */
|
|
#ifdef MZ_USE_JIT
|
|
} else if (type == scheme_native_closure_type) {
|
|
GC_CAN_IGNORE Scheme_Native_Closure_Data *data;
|
|
|
|
VACATE_TAIL_BUFFER_USE_RUNSTACK();
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
|
|
DO_CHECK_FOR_BREAK(p, );
|
|
|
|
if (!scheme_native_arity_check(obj, num_rands)) {
|
|
scheme_wrong_count_m((const char *)obj, -1, -1,
|
|
num_rands, rands, 0);
|
|
return NULL;
|
|
}
|
|
|
|
data = ((Scheme_Native_Closure *)obj)->code;
|
|
|
|
/* Enlarge the runstack? This max_let_depth is in bytes instead of words. */
|
|
if ((unsigned long)data->max_let_depth > ((unsigned long)RUNSTACK - (unsigned long)RUNSTACK_START)) {
|
|
p->ku.k.p1 = (void *)obj;
|
|
p->ku.k.i1 = num_rands;
|
|
p->ku.k.p2 = (void *)rands;
|
|
p->ku.k.i2 = -1;
|
|
|
|
MZ_CONT_MARK_POS -= 2;
|
|
v = (Scheme_Object *)scheme_enlarge_runstack(data->max_let_depth / sizeof(void *),
|
|
(void *(*)(void))do_eval_k);
|
|
MZ_CONT_MARK_POS += 2;
|
|
goto returnv;
|
|
}
|
|
|
|
v = data->code(obj, num_rands, rands);
|
|
|
|
DEBUG_CHECK_TYPE(v);
|
|
#endif
|
|
} else if (type == scheme_cont_type) {
|
|
UPDATE_THREAD_RSPTR();
|
|
v = scheme_jump_to_continuation(obj, num_rands, rands, old_runstack);
|
|
} else if (type == scheme_escaping_cont_type) {
|
|
UPDATE_THREAD_RSPTR();
|
|
scheme_escape_to_continuation(obj, num_rands, rands);
|
|
return NULL;
|
|
} else if (type == scheme_proc_struct_type) {
|
|
int is_method;
|
|
int check_rands = num_rands;
|
|
|
|
do {
|
|
VACATE_TAIL_BUFFER_USE_RUNSTACK();
|
|
|
|
UPDATE_THREAD_RSPTR_FOR_ERROR(); /* in case */
|
|
|
|
v = obj;
|
|
obj = scheme_extract_struct_procedure(obj, check_rands, rands, &is_method);
|
|
if (is_method) {
|
|
/* Have to add an extra argument to the front of rands */
|
|
if ((rands == RUNSTACK) && (RUNSTACK != RUNSTACK_START)){
|
|
/* Common case: we can just push self onto the front: */
|
|
rands = PUSH_RUNSTACK(p, RUNSTACK, 1);
|
|
rands[0] = v;
|
|
} else {
|
|
int i;
|
|
Scheme_Object **a;
|
|
|
|
if (p->tail_buffer && (num_rands < p->tail_buffer_size)) {
|
|
/* Use tail-call buffer. Shift in such a way that this works if
|
|
rands == p->tail_buffer */
|
|
a = p->tail_buffer;
|
|
} else {
|
|
/* Uncommon general case --- allocate an array */
|
|
UPDATE_THREAD_RSPTR_FOR_GC();
|
|
a = MALLOC_N(Scheme_Object *, num_rands + 1);
|
|
}
|
|
|
|
for (i = num_rands; i--; ) {
|
|
a[i + 1] = rands[i];
|
|
}
|
|
a[0] = v;
|
|
rands = a;
|
|
}
|
|
num_rands++;
|
|
}
|
|
|
|
/* After we check arity once, no need to check again
|
|
(which would lead to O(n^2) checking for nested
|
|
struct procs): */
|
|
check_rands = -1;
|
|
|
|
DO_CHECK_FOR_BREAK(p, UPDATE_THREAD_RSPTR_FOR_GC(); if (rands == p->tail_buffer) make_tail_buffer_safe(););
|
|
} while (SAME_TYPE(scheme_proc_struct_type, SCHEME_TYPE(obj)));
|
|
|
|
goto apply_top;
|
|
} else if (type == scheme_closed_prim_type) {
|
|
GC_CAN_IGNORE Scheme_Closed_Primitive_Proc *prim;
|
|
|
|
DO_CHECK_FOR_BREAK(p, UPDATE_THREAD_RSPTR_FOR_GC(); if (rands == p->tail_buffer) make_tail_buffer_safe(););
|
|
|
|
VACATE_TAIL_BUFFER_USE_RUNSTACK();
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
|
|
prim = (Scheme_Closed_Primitive_Proc *)obj;
|
|
|
|
if (num_rands < prim->mina
|
|
|| (num_rands > prim->maxa && prim->maxa >= 0)) {
|
|
scheme_wrong_count_m(prim->name, prim->mina, prim->maxa,
|
|
num_rands, rands,
|
|
prim->pp.flags & SCHEME_PRIM_IS_METHOD);
|
|
return NULL; /* Shouldn't get here */
|
|
}
|
|
|
|
v = prim->prim_val(prim->data, num_rands, rands);
|
|
|
|
DEBUG_CHECK_TYPE(v);
|
|
} else {
|
|
UPDATE_THREAD_RSPTR_FOR_ERROR();
|
|
if (rands == p->tail_buffer)
|
|
make_tail_buffer_safe();
|
|
scheme_wrong_rator(obj, num_rands, rands);
|
|
return NULL; /* Doesn't get here. */
|
|
}
|
|
} else {
|
|
|
|
eval_top:
|
|
|
|
if (SCHEME_INTP(obj)) {
|
|
v = obj;
|
|
goto returnv_never_multi;
|
|
}
|
|
|
|
type = _SCHEME_TYPE(obj);
|
|
switch (type)
|
|
{
|
|
case scheme_toplevel_type:
|
|
{
|
|
/* Make sure that the GC can ignore tmp: */
|
|
#define global_lookup(prefix, _obj, tmp) \
|
|
tmp = RUNSTACK[SCHEME_TOPLEVEL_DEPTH(_obj)]; \
|
|
tmp = ((Scheme_Object **)tmp)[SCHEME_TOPLEVEL_POS(_obj)]; \
|
|
tmp = (Scheme_Object *)(SCHEME_VAR_BUCKET(tmp))->val; \
|
|
if (!tmp) { \
|
|
UPDATE_THREAD_RSPTR_FOR_ERROR(); \
|
|
unbound_global(_obj); \
|
|
return NULL; \
|
|
} \
|
|
prefix tmp
|
|
|
|
global_lookup(v = , obj, v);
|
|
goto returnv_never_multi;
|
|
}
|
|
case scheme_local_type:
|
|
{
|
|
v = RUNSTACK[SCHEME_LOCAL_POS(obj)];
|
|
goto returnv_never_multi;
|
|
}
|
|
case scheme_local_unbox_type:
|
|
{
|
|
v = SCHEME_ENVBOX_VAL(RUNSTACK[SCHEME_LOCAL_POS(obj)]);
|
|
goto returnv_never_multi;
|
|
}
|
|
case scheme_syntax_type:
|
|
{
|
|
Scheme_Syntax_Executer f;
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
f = scheme_syntax_executers[SCHEME_PINT_VAL(obj)];
|
|
v = f((Scheme_Object *)SCHEME_IPTR_VAL(obj));
|
|
break;
|
|
}
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app;
|
|
GC_CAN_IGNORE Scheme_Object *tmpv;
|
|
GC_MAYBE_IGNORE_INTERIOR Scheme_Object **randsp;
|
|
Scheme_Object **stack;
|
|
int k;
|
|
int d_evals;
|
|
#ifdef MZ_XFORM
|
|
# define GET_FIRST_EVAL ((char *)app)[d_evals]
|
|
#else
|
|
char *evals;
|
|
Scheme_Object **args;
|
|
# define GET_FIRST_EVAL evals[0]
|
|
#endif
|
|
|
|
app = (Scheme_App_Rec *)obj;
|
|
num_rands = app->num_args;
|
|
|
|
d_evals = sizeof(Scheme_App_Rec) + (num_rands * sizeof(Scheme_Object *));
|
|
#ifndef MZ_XFORM
|
|
evals = ((char *)obj) + d_evals;
|
|
#endif
|
|
|
|
obj = app->args[0];
|
|
|
|
stack = PUSH_RUNSTACK(p, RUNSTACK, num_rands);
|
|
RUNSTACK_CHANGED();
|
|
UPDATE_THREAD_RSPTR();
|
|
|
|
/* Inline local & global variable lookups for speed */
|
|
switch (GET_FIRST_EVAL) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
global_lookup(obj =, obj, tmpv);
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
obj = stack[SCHEME_LOCAL_POS(obj)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
obj = SCHEME_ENVBOX_VAL(stack[SCHEME_LOCAL_POS(obj)]);
|
|
break;
|
|
default:
|
|
obj = _scheme_eval_linked_expr_wp(obj, p);
|
|
break;
|
|
}
|
|
|
|
if (num_rands) {
|
|
#ifdef MZ_XFORM
|
|
int evalpos = 1;
|
|
#endif
|
|
|
|
rands = stack;
|
|
|
|
/* Inline local & global variable lookups for speed */
|
|
#ifdef MZ_XFORM
|
|
# define GET_NEXT_EVAL ((char *)app)[d_evals + evalpos++]
|
|
# define GET_NEXT_ARG app->args[evalpos]
|
|
#else
|
|
evals++;
|
|
args = app->args + 1;
|
|
# define GET_NEXT_EVAL *(evals++)
|
|
# define GET_NEXT_ARG *(args++)
|
|
#endif
|
|
randsp = rands;
|
|
for (k = num_rands; k--; ) {
|
|
v = GET_NEXT_ARG;
|
|
switch (GET_NEXT_EVAL) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
*(randsp++) = v;
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
global_lookup(*(randsp++) =, v, tmpv);
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
*(randsp++) = stack[SCHEME_LOCAL_POS(v)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
*(randsp++) = SCHEME_ENVBOX_VAL(stack[SCHEME_LOCAL_POS(v)]);
|
|
break;
|
|
default:
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object *er;
|
|
er = _scheme_eval_linked_expr_wp(v, p);
|
|
*(randsp++) = er;
|
|
}
|
|
break;
|
|
}
|
|
|
|
DEBUG_CHECK_TYPE(randsp[-1]);
|
|
}
|
|
} else
|
|
rands = &zero_rands_ptr;
|
|
|
|
goto apply_top;
|
|
}
|
|
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app;
|
|
GC_CAN_IGNORE Scheme_Object *arg;
|
|
short flags;
|
|
|
|
app = (Scheme_App2_Rec *)obj;
|
|
|
|
obj = app->rator;
|
|
flags = SCHEME_APPN_FLAGS(app);
|
|
|
|
rands = PUSH_RUNSTACK(p, RUNSTACK, 1);
|
|
RUNSTACK_CHANGED();
|
|
UPDATE_THREAD_RSPTR();
|
|
|
|
/* Inline local & global variable lookups for speed */
|
|
switch (flags & 0x7) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object *tmpv;
|
|
global_lookup(obj =, obj, tmpv);
|
|
}
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
obj = rands[SCHEME_LOCAL_POS(obj)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
obj = SCHEME_ENVBOX_VAL(rands[SCHEME_LOCAL_POS(obj)]);
|
|
break;
|
|
default:
|
|
obj = _scheme_eval_linked_expr_wp(obj, p);
|
|
break;
|
|
}
|
|
|
|
arg = app->rand;
|
|
|
|
switch (flags >> 3) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object *tmpv;
|
|
global_lookup(arg =, arg, tmpv);
|
|
}
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
arg = rands[SCHEME_LOCAL_POS(arg)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
arg = SCHEME_ENVBOX_VAL(rands[SCHEME_LOCAL_POS(arg)]);
|
|
break;
|
|
default:
|
|
arg = _scheme_eval_linked_expr_wp(arg, p);
|
|
break;
|
|
}
|
|
|
|
rands[0] = arg;
|
|
num_rands = 1;
|
|
|
|
goto apply_top;
|
|
}
|
|
|
|
case scheme_application3_type:
|
|
{
|
|
Scheme_App3_Rec *app;
|
|
GC_CAN_IGNORE Scheme_Object *arg;
|
|
short flags;
|
|
GC_CAN_IGNORE Scheme_Object *tmpv;
|
|
|
|
app = (Scheme_App3_Rec *)obj;
|
|
|
|
obj = app->rator;
|
|
flags = SCHEME_APPN_FLAGS(app);
|
|
|
|
rands = PUSH_RUNSTACK(p, RUNSTACK, 2);
|
|
RUNSTACK_CHANGED();
|
|
UPDATE_THREAD_RSPTR();
|
|
|
|
/* Inline local & global variable lookups for speed */
|
|
switch (flags & 0x7) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
global_lookup(obj =, obj, tmpv);
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
obj = rands[SCHEME_LOCAL_POS(obj)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
obj = SCHEME_ENVBOX_VAL(rands[SCHEME_LOCAL_POS(obj)]);
|
|
break;
|
|
default:
|
|
obj = _scheme_eval_linked_expr_wp(obj, p);
|
|
break;
|
|
}
|
|
|
|
arg = app->rand1;
|
|
|
|
switch ((flags >> 3) & 0x7) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
global_lookup(arg =, arg, tmpv);
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
arg = rands[SCHEME_LOCAL_POS(arg)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
arg = SCHEME_ENVBOX_VAL(rands[SCHEME_LOCAL_POS(arg)]);
|
|
break;
|
|
default:
|
|
arg = _scheme_eval_linked_expr_wp(arg, p);
|
|
break;
|
|
}
|
|
|
|
rands[0] = arg;
|
|
|
|
arg = app->rand2;
|
|
|
|
switch (SCHEME_APPN_FLAGS(app) >> 6) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
global_lookup(arg =, arg, tmpv);
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
arg = rands[SCHEME_LOCAL_POS(arg)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
arg = SCHEME_ENVBOX_VAL(rands[SCHEME_LOCAL_POS(arg)]);
|
|
break;
|
|
default:
|
|
arg = _scheme_eval_linked_expr_wp(arg, p);
|
|
break;
|
|
}
|
|
|
|
rands[1] = arg;
|
|
|
|
num_rands = 2;
|
|
|
|
goto apply_top;
|
|
}
|
|
|
|
case scheme_sequence_type:
|
|
{
|
|
int cnt;
|
|
int i;
|
|
|
|
cnt = ((Scheme_Sequence *)obj)->count - 1;
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
for (i = 0; i < cnt; i++) {
|
|
(void)_scheme_eval_linked_expr_multi_wp(((Scheme_Sequence *)obj)->array[i], p);
|
|
}
|
|
|
|
obj = ((Scheme_Sequence *)obj)->array[cnt];
|
|
goto eval_top;
|
|
}
|
|
case scheme_branch_type:
|
|
{
|
|
UPDATE_THREAD_RSPTR();
|
|
obj = (NOT_SAME_OBJ(_scheme_eval_linked_expr_wp(((Scheme_Branch_Rec *)obj)->test, p),
|
|
scheme_false)
|
|
? ((Scheme_Branch_Rec *)obj)->tbranch
|
|
: ((Scheme_Branch_Rec *)obj)->fbranch);
|
|
|
|
goto eval_top;
|
|
}
|
|
case scheme_unclosed_procedure_type:
|
|
UPDATE_THREAD_RSPTR();
|
|
v = scheme_make_closure(p, obj, 1);
|
|
goto returnv_never_multi;
|
|
|
|
case scheme_let_value_type:
|
|
{
|
|
GC_CAN_IGNORE Scheme_Let_Value *lv;
|
|
GC_CAN_IGNORE Scheme_Object *value, **values;
|
|
int i, c, ab;
|
|
|
|
lv = (Scheme_Let_Value *)obj;
|
|
|
|
c = lv->count;
|
|
|
|
i = lv->position;
|
|
ab = SCHEME_LET_AUTOBOX(lv);
|
|
value = lv->value;
|
|
obj = lv->body;
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
|
|
if (c == 1) {
|
|
value = _scheme_eval_linked_expr_wp(value, p);
|
|
if (ab)
|
|
SCHEME_ENVBOX_VAL(RUNSTACK[i]) = value;
|
|
else
|
|
RUNSTACK[i] = value;
|
|
} else {
|
|
int c2;
|
|
GC_CAN_IGNORE Scheme_Object **stack;
|
|
|
|
value = _scheme_eval_linked_expr_multi_wp(value, p);
|
|
c2 = (SAME_OBJ(value, SCHEME_MULTIPLE_VALUES) ? p->ku.multiple.count : 1);
|
|
if (c2 != c) {
|
|
scheme_wrong_return_arity(NULL, c, c2,
|
|
(c2 == 1) ? (Scheme_Object **)value : p->ku.multiple.array,
|
|
"lexical binding");
|
|
return NULL;
|
|
}
|
|
|
|
/* Precise GC: values++ is ok because we exit the block
|
|
before any GC can happen. Also, GC would zero `values'
|
|
if it turns out to be p->values_buffer. */
|
|
|
|
values = p->ku.multiple.array;
|
|
p->ku.multiple.array = NULL;
|
|
stack = RUNSTACK;
|
|
if (ab) {
|
|
while (c--) {
|
|
SCHEME_ENVBOX_VAL(stack[i]) = *values;
|
|
values++;
|
|
i++;
|
|
}
|
|
} else {
|
|
while (c--) {
|
|
stack[i] = *values;
|
|
values++;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto eval_top;
|
|
}
|
|
|
|
case scheme_let_void_type:
|
|
{
|
|
GC_CAN_IGNORE Scheme_Let_Void *lv;
|
|
int c;
|
|
|
|
lv = (Scheme_Let_Void *)obj;
|
|
c = lv->count;
|
|
obj = lv->body;
|
|
|
|
PUSH_RUNSTACK(p, RUNSTACK, c);
|
|
RUNSTACK_CHANGED();
|
|
|
|
if (SCHEME_LET_AUTOBOX(lv)) {
|
|
Scheme_Object **stack = RUNSTACK;
|
|
|
|
UPDATE_THREAD_RSPTR_FOR_GC();
|
|
|
|
while (c--) {
|
|
GC_CAN_IGNORE Scheme_Object *ub;
|
|
ub = scheme_make_envunbox(scheme_undefined);
|
|
stack[c] = ub;
|
|
}
|
|
}
|
|
|
|
goto eval_top;
|
|
}
|
|
|
|
case scheme_letrec_type:
|
|
{
|
|
Scheme_Letrec *l = (Scheme_Letrec *)obj;
|
|
Scheme_Object **a, **stack;
|
|
int i;
|
|
|
|
stack = RUNSTACK;
|
|
a = l->procs;
|
|
i = l->count;
|
|
|
|
UPDATE_THREAD_RSPTR_FOR_GC();
|
|
|
|
/* Create unfinished closures */
|
|
while (i--) {
|
|
Scheme_Object *uc;
|
|
uc = scheme_make_closure(p, a[i], 0);
|
|
stack[i] = uc;
|
|
}
|
|
|
|
/* Close them: */
|
|
i = l->count;
|
|
while (i--) {
|
|
GC_CAN_IGNORE Scheme_Object *clos;
|
|
GC_CAN_IGNORE Scheme_Object **dest;
|
|
GC_CAN_IGNORE mzshort *map;
|
|
GC_CAN_IGNORE Scheme_Closure_Data *data;
|
|
int j;
|
|
|
|
clos = stack[i];
|
|
|
|
#ifdef MZ_USE_JIT
|
|
if (SAME_TYPE(_SCHEME_TYPE(clos), scheme_closure_type)) {
|
|
dest = ((Scheme_Closure *)clos)->vals;
|
|
} else {
|
|
dest = ((Scheme_Native_Closure *)clos)->vals;
|
|
}
|
|
#else
|
|
dest = ((Scheme_Closure *)clos)->vals;
|
|
#endif
|
|
|
|
data = (Scheme_Closure_Data *)a[i];
|
|
|
|
map = data->closure_map;
|
|
j = data->closure_size;
|
|
|
|
/* Beware - dest points to the middle of a block */
|
|
|
|
while (j--) {
|
|
dest[j] = stack[map[j]];
|
|
}
|
|
}
|
|
|
|
obj = l->body;
|
|
goto eval_top;
|
|
}
|
|
|
|
case scheme_let_one_type:
|
|
{
|
|
/* Macro instead of var for efficient precise GC conversion */
|
|
# define lo ((Scheme_Let_One *)obj)
|
|
|
|
PUSH_RUNSTACK(p, RUNSTACK, 1);
|
|
RUNSTACK_CHANGED();
|
|
|
|
switch (SCHEME_LET_EVAL_TYPE(lo)) {
|
|
case SCHEME_EVAL_CONSTANT:
|
|
RUNSTACK[0] = lo->value;
|
|
break;
|
|
case SCHEME_EVAL_GLOBAL:
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object *tmpv;
|
|
global_lookup(RUNSTACK[0] =, lo->value, tmpv);
|
|
}
|
|
break;
|
|
case SCHEME_EVAL_LOCAL:
|
|
RUNSTACK[0] = RUNSTACK[SCHEME_LOCAL_POS(lo->value)];
|
|
break;
|
|
case SCHEME_EVAL_LOCAL_UNBOX:
|
|
RUNSTACK[0] = SCHEME_ENVBOX_VAL(RUNSTACK[SCHEME_LOCAL_POS(lo->value)]);
|
|
break;
|
|
default:
|
|
UPDATE_THREAD_RSPTR();
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object *val;
|
|
val = _scheme_eval_linked_expr_wp(lo->value, p);
|
|
RUNSTACK[0] = val;
|
|
}
|
|
break;
|
|
}
|
|
|
|
obj = lo->body;
|
|
#undef lo
|
|
goto eval_top;
|
|
}
|
|
|
|
case scheme_with_cont_mark_type:
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)obj;
|
|
GC_CAN_IGNORE Scheme_Object *key, *val;
|
|
|
|
UPDATE_THREAD_RSPTR();
|
|
key = wcm->key;
|
|
if (SCHEME_TYPE(key) < _scheme_values_types_)
|
|
key = _scheme_eval_linked_expr_wp(wcm->key, p);
|
|
val = wcm->val;
|
|
if (SCHEME_TYPE(val) < _scheme_values_types_)
|
|
val = _scheme_eval_linked_expr_wp(wcm->val, p);
|
|
|
|
scheme_set_cont_mark(key, val);
|
|
|
|
obj = wcm->body;
|
|
|
|
goto eval_top;
|
|
}
|
|
|
|
case scheme_quote_syntax_type:
|
|
{
|
|
GC_CAN_IGNORE Scheme_Quote_Syntax *qs = (Scheme_Quote_Syntax *)obj;
|
|
Scheme_Object **globs;
|
|
int i, c, p;
|
|
|
|
i = qs->position;
|
|
c = qs->depth;
|
|
p = qs->midpoint;
|
|
|
|
globs = (Scheme_Object **)RUNSTACK[c];
|
|
v = globs[i+p+1];
|
|
if (!v) {
|
|
v = globs[p];
|
|
v = scheme_delayed_rename((Scheme_Object **)v, i);
|
|
globs[i+p+1] = v;
|
|
}
|
|
|
|
goto returnv_never_multi;
|
|
}
|
|
|
|
default:
|
|
v = obj;
|
|
goto returnv_never_multi;
|
|
}
|
|
}
|
|
|
|
if (SAME_OBJ(v, SCHEME_TAIL_CALL_WAITING)) {
|
|
obj = p->ku.apply.tail_rator;
|
|
num_rands = p->ku.apply.tail_num_rands;
|
|
rands = p->ku.apply.tail_rands;
|
|
p->ku.apply.tail_rator = NULL;
|
|
p->ku.apply.tail_rands = NULL;
|
|
RUNSTACK = old_runstack;
|
|
RUNSTACK_CHANGED();
|
|
goto apply_top;
|
|
}
|
|
|
|
if (SAME_OBJ(v, SCHEME_EVAL_WAITING)) {
|
|
RESET_LOCAL_RUNSTACK();
|
|
obj = p->ku.eval.wait_expr;
|
|
p->ku.eval.wait_expr = NULL;
|
|
goto eval_top;
|
|
}
|
|
|
|
returnv:
|
|
|
|
if (SAME_OBJ(v, SCHEME_MULTIPLE_VALUES))
|
|
if (get_value > 0) {
|
|
scheme_wrong_return_arity(NULL, 1, p->ku.multiple.count,
|
|
p->ku.multiple.array,
|
|
NULL);
|
|
return NULL;
|
|
}
|
|
|
|
returnv_never_multi:
|
|
|
|
MZ_RUNSTACK = old_runstack;
|
|
MZ_CONT_MARK_STACK = old_cont_mark_stack;
|
|
MZ_CONT_MARK_POS -= 2;
|
|
|
|
DEBUG_CHECK_TYPE(v);
|
|
|
|
return v;
|
|
|
|
#ifdef p
|
|
# undef p
|
|
#endif
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* eval/compile/expand starting points */
|
|
/*========================================================================*/
|
|
|
|
Scheme_Object *scheme_eval(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return scheme_eval_compiled(scheme_compile_for_eval(obj, env), env);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_multi(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return scheme_eval_compiled_multi(scheme_compile_for_eval(obj, env), env);
|
|
}
|
|
|
|
static Scheme_Object *finish_eval_with_prompt(void *_data, int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Object *data = (Scheme_Object *)_data;
|
|
return _scheme_eval_compiled(SCHEME_CAR(data), (Scheme_Env *)SCHEME_CDR(data));
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_with_prompt(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
Scheme_Object *expr;
|
|
expr = scheme_compile_for_eval(obj, env);
|
|
return scheme_call_with_prompt(finish_eval_with_prompt,
|
|
scheme_make_pair(expr, (Scheme_Object *)env));
|
|
}
|
|
|
|
static Scheme_Object *finish_eval_multi_with_prompt(void *_data, int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Object *data = (Scheme_Object *)_data;
|
|
return _scheme_eval_compiled_multi(SCHEME_CAR(data), (Scheme_Env *)SCHEME_CDR(data));
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_multi_with_prompt(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
Scheme_Object *expr;
|
|
expr = scheme_compile_for_eval(obj, env);
|
|
return scheme_call_with_prompt_multi(finish_eval_multi_with_prompt,
|
|
scheme_make_pair(expr, (Scheme_Object *)env));
|
|
}
|
|
|
|
static void *eval_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *v, **save_runstack;
|
|
Scheme_Env *env;
|
|
int isexpr, multi, use_jit, as_tail;
|
|
|
|
v = (Scheme_Object *)p->ku.k.p1;
|
|
env = (Scheme_Env *)p->ku.k.p2;
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
multi = p->ku.k.i1;
|
|
isexpr = p->ku.k.i2;
|
|
as_tail = p->ku.k.i3;
|
|
|
|
{
|
|
Scheme_Object *b;
|
|
b = scheme_get_param(scheme_current_config(), MZCONFIG_USE_JIT);
|
|
use_jit = SCHEME_TRUEP(b);
|
|
}
|
|
|
|
if (isexpr) {
|
|
if (multi)
|
|
v = _scheme_eval_linked_expr_multi_wp(v, p);
|
|
else
|
|
v = _scheme_eval_linked_expr_wp(v, p);
|
|
} else if (SAME_TYPE(SCHEME_TYPE(v), scheme_compilation_top_type)) {
|
|
Scheme_Compilation_Top *top = (Scheme_Compilation_Top *)v;
|
|
int depth;
|
|
|
|
depth = top->max_let_depth + scheme_prefix_depth(top->prefix);
|
|
if (!scheme_check_runstack(depth)) {
|
|
p->ku.k.p1 = top;
|
|
p->ku.k.p2 = env;
|
|
p->ku.k.i1 = multi;
|
|
p->ku.k.i2 = 0;
|
|
return (Scheme_Object *)scheme_enlarge_runstack(depth, eval_k);
|
|
}
|
|
|
|
v = top->code;
|
|
|
|
if (use_jit)
|
|
v = scheme_jit_expr(v);
|
|
|
|
save_runstack = scheme_push_prefix(env, top->prefix, NULL, NULL, 0, env->phase);
|
|
|
|
if (as_tail) {
|
|
/* Cons up a closure to capture the prefix */
|
|
Scheme_Closure_Data *data;
|
|
mzshort *map;
|
|
int i, sz;
|
|
|
|
sz = (save_runstack XFORM_OK_MINUS MZ_RUNSTACK);
|
|
map = (mzshort *)scheme_malloc_atomic(sizeof(mzshort) * sz);
|
|
for (i = 0; i < sz; i++) {
|
|
map[i] = i;
|
|
}
|
|
|
|
data = MALLOC_ONE_TAGGED(Scheme_Closure_Data);
|
|
data->iso.so.type = scheme_compiled_unclosed_procedure_type;
|
|
data->num_params = 0;
|
|
data->max_let_depth = top->max_let_depth + sz;
|
|
data->closure_size = sz;
|
|
data->closure_map = map;
|
|
data->code = v;
|
|
|
|
v = scheme_make_closure(p, (Scheme_Object *)data, 1);
|
|
|
|
v = _scheme_tail_apply(v, 0, NULL);
|
|
} else if (multi)
|
|
v = _scheme_eval_linked_expr_multi_wp(v, p);
|
|
else
|
|
v = _scheme_eval_linked_expr_wp(v, p);
|
|
|
|
scheme_pop_prefix(save_runstack);
|
|
} else {
|
|
v = scheme_void;
|
|
}
|
|
|
|
return (void *)v;
|
|
}
|
|
|
|
static Scheme_Object *_eval(Scheme_Object *obj, Scheme_Env *env,
|
|
int isexpr, int multi, int top, int as_tail)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
|
|
p->ku.k.p1 = obj;
|
|
p->ku.k.p2 = env;
|
|
p->ku.k.i1 = multi;
|
|
p->ku.k.i2 = isexpr;
|
|
p->ku.k.i3 = as_tail;
|
|
|
|
if (top)
|
|
return (Scheme_Object *)scheme_top_level_do(eval_k, 1);
|
|
else
|
|
return (Scheme_Object *)eval_k();
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_compiled(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return _eval(obj, env, 0, 0, 1, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_compiled_multi(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return _eval(obj, env, 0, 1, 1, 0);
|
|
}
|
|
|
|
Scheme_Object *_scheme_eval_compiled(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return _eval(obj, env, 0, 0, 0, 0);
|
|
}
|
|
|
|
Scheme_Object *_scheme_eval_compiled_multi(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return _eval(obj, env, 0, 1, 0, 0);
|
|
}
|
|
|
|
static Scheme_Object *finish_compiled_multi_with_prompt(void *_data, int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Object *data = (Scheme_Object *)_data;
|
|
return _eval(SCHEME_CAR(data), (Scheme_Env *)SCHEME_CDR(data), 0, 1, 0, 0);
|
|
}
|
|
|
|
Scheme_Object *_eval_compiled_multi_with_prompt(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return _scheme_call_with_prompt_multi(finish_compiled_multi_with_prompt,
|
|
scheme_make_pair(obj, (Scheme_Object *)env));
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_linked_expr(Scheme_Object *obj)
|
|
{
|
|
return _eval(obj, NULL, 1, 0, 1, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_linked_expr_multi(Scheme_Object *obj)
|
|
{
|
|
return _eval(obj, NULL, 1, 1, 1, 0);
|
|
}
|
|
|
|
/* for mzc: */
|
|
Scheme_Object *scheme_load_compiled_stx_string(const char *str, long len)
|
|
{
|
|
Scheme_Object *port, *expr;
|
|
|
|
port = scheme_make_sized_byte_string_input_port(str, -len);
|
|
|
|
expr = scheme_internal_read(port, NULL, 1, 0, 0, 0, 0, -1, NULL, NULL, NULL, NULL);
|
|
|
|
expr = _scheme_eval_compiled(expr, scheme_get_env(NULL));
|
|
|
|
/* Unwrap syntax once; */
|
|
expr = SCHEME_STX_VAL(expr);
|
|
|
|
return expr;
|
|
}
|
|
|
|
/* for mzc: */
|
|
Scheme_Object *scheme_compiled_stx_symbol(Scheme_Object *stx)
|
|
{
|
|
return SCHEME_STX_VAL(stx);
|
|
}
|
|
|
|
/* for mzc: */
|
|
Scheme_Object *scheme_eval_compiled_stx_string(Scheme_Object *expr, Scheme_Env *env,
|
|
long shift, Scheme_Object *modidx)
|
|
{
|
|
/* If modidx, then last element is a module index; shift the rest. */
|
|
if (modidx) {
|
|
int i, len = SCHEME_VEC_SIZE(expr);
|
|
Scheme_Object *orig = SCHEME_VEC_ELS(expr)[len - 1], *s, *result;
|
|
|
|
orig = SCHEME_STX_VAL(orig);
|
|
result = scheme_make_vector(len - 1, NULL);
|
|
|
|
for (i = 0; i < len - 1; i++) {
|
|
s = scheme_stx_phase_shift(SCHEME_VEC_ELS(expr)[i], shift, orig, modidx, env->export_registry);
|
|
SCHEME_VEC_ELS(result)[i] = s;
|
|
}
|
|
|
|
return result;
|
|
} else
|
|
return expr;
|
|
}
|
|
|
|
static Scheme_Object *add_lifts_as_begin(Scheme_Object *obj, Scheme_Object *l, Scheme_Comp_Env *env)
|
|
{
|
|
obj = scheme_append(l, scheme_make_pair(obj, scheme_null));
|
|
obj = icons(scheme_datum_to_syntax(begin_symbol, scheme_false, scheme_sys_wraps(env), 0, 0),
|
|
obj);
|
|
obj = scheme_datum_to_syntax(obj, scheme_false, scheme_false, 0, 0);
|
|
return obj;
|
|
|
|
}
|
|
|
|
static void *expand_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *obj, *certs, *observer, *catch_lifts_key;
|
|
Scheme_Comp_Env *env;
|
|
Scheme_Expand_Info erec1;
|
|
int depth, rename, just_to_top, as_local;
|
|
|
|
obj = (Scheme_Object *)p->ku.k.p1;
|
|
env = (Scheme_Comp_Env *)p->ku.k.p2;
|
|
depth = p->ku.k.i1;
|
|
rename = p->ku.k.i2;
|
|
just_to_top = p->ku.k.i3;
|
|
catch_lifts_key = p->ku.k.p4;
|
|
certs = (Scheme_Object *)p->ku.k.p3;
|
|
as_local = p->ku.k.i4;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
p->ku.k.p3 = NULL;
|
|
p->ku.k.p4 = NULL;
|
|
|
|
if (!SCHEME_STXP(obj))
|
|
obj = scheme_datum_to_syntax(obj, scheme_false, scheme_false, 1, 0);
|
|
|
|
if (rename > 0) {
|
|
/* Renamings for requires: */
|
|
obj = add_renames_unless_module(obj, env->genv);
|
|
}
|
|
|
|
observer = scheme_get_expand_observe();
|
|
SCHEME_EXPAND_OBSERVE_START_EXPAND(observer);
|
|
|
|
/* Loop for lifted expressions: */
|
|
while (1) {
|
|
erec1.comp = 0;
|
|
erec1.depth = depth;
|
|
erec1.value_name = scheme_false;
|
|
erec1.certs = certs;
|
|
erec1.observer = observer;
|
|
|
|
if (catch_lifts_key)
|
|
scheme_frame_captures_lifts(env, scheme_make_lifted_defn, scheme_sys_wraps(env), scheme_false, catch_lifts_key);
|
|
|
|
if (just_to_top) {
|
|
Scheme_Object *gval;
|
|
obj = scheme_check_immediate_macro(obj, env, &erec1, 0, 0, &gval, NULL, NULL);
|
|
} else
|
|
obj = scheme_expand_expr(obj, env, &erec1, 0);
|
|
|
|
if (catch_lifts_key) {
|
|
Scheme_Object *l;
|
|
l = scheme_frame_get_lifts(env);
|
|
if (SCHEME_PAIRP(l)) {
|
|
obj = add_lifts_as_begin(obj, l, env);
|
|
SCHEME_EXPAND_OBSERVE_LIFT_LOOP(erec1.observer,obj);
|
|
if ((depth >= 0) || as_local)
|
|
break;
|
|
} else {
|
|
if (as_local)
|
|
obj = add_lifts_as_begin(obj, scheme_null, env);
|
|
break;
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (rename && !just_to_top) {
|
|
/* scheme_simplify_stx(obj, scheme_new_stx_simplify_cache()); */ /* too expensive */
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
static Scheme_Object *_expand(Scheme_Object *obj, Scheme_Comp_Env *env,
|
|
int depth, int rename, int just_to_top,
|
|
Scheme_Object *catch_lifts_key, int eb,
|
|
Scheme_Object *certs, int as_local)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
|
|
p->ku.k.p1 = obj;
|
|
p->ku.k.p2 = env;
|
|
p->ku.k.i1 = depth;
|
|
p->ku.k.i2 = rename;
|
|
p->ku.k.i3 = just_to_top;
|
|
p->ku.k.p4 = catch_lifts_key;
|
|
p->ku.k.p3 = certs;
|
|
p->ku.k.i4 = as_local;
|
|
|
|
return (Scheme_Object *)scheme_top_level_do(expand_k, eb);
|
|
}
|
|
|
|
Scheme_Object *scheme_expand(Scheme_Object *obj, Scheme_Env *env)
|
|
{
|
|
return _expand(obj, scheme_new_expand_env(env, NULL, SCHEME_TOPLEVEL_FRAME),
|
|
-1, 1, 0, scheme_true, -1, NULL, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_tail_eval_expr(Scheme_Object *obj)
|
|
{
|
|
return scheme_tail_eval(obj);
|
|
}
|
|
|
|
/* local functions */
|
|
|
|
static Scheme_Object *
|
|
sch_eval(const char *who, int argc, Scheme_Object *argv[])
|
|
{
|
|
if (argc == 1) {
|
|
return _scheme_tail_apply(scheme_get_param(scheme_current_config(), MZCONFIG_EVAL_HANDLER),
|
|
1, argv);
|
|
} else {
|
|
Scheme_Config *config;
|
|
|
|
if (SCHEME_TYPE(argv[1]) != scheme_namespace_type)
|
|
scheme_wrong_type(who, "namespace", 1, argc, argv);
|
|
|
|
config = scheme_extend_config(scheme_current_config(),
|
|
MZCONFIG_ENV,
|
|
argv[1]);
|
|
scheme_set_cont_mark(scheme_parameterization_key, (Scheme_Object *)config);
|
|
|
|
return _scheme_tail_apply(scheme_get_param(config, MZCONFIG_EVAL_HANDLER),
|
|
1, argv);
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *
|
|
eval(int argc, Scheme_Object *argv[])
|
|
{
|
|
Scheme_Object *a[2], *form;
|
|
|
|
form = argv[0];
|
|
if (SCHEME_STXP(form)
|
|
&& !SAME_TYPE(SCHEME_TYPE(SCHEME_STX_VAL(form)), scheme_compilation_top_type)) {
|
|
Scheme_Env *genv;
|
|
if (argc > 1) {
|
|
if (SCHEME_TYPE(argv[1]) != scheme_namespace_type)
|
|
scheme_wrong_type("eval", "namespace", 1, argc, argv);
|
|
genv = (Scheme_Env *)argv[1];
|
|
} else
|
|
genv = scheme_get_env(NULL);
|
|
form = add_renames_unless_module(form, genv);
|
|
}
|
|
|
|
a[0] = form;
|
|
if (argc > 1)
|
|
a[1] = argv[1];
|
|
return sch_eval("eval", argc, a);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
eval_stx(int argc, Scheme_Object *argv[])
|
|
{
|
|
if (!SCHEME_STXP(argv[0])) {
|
|
scheme_wrong_type("eval-syntax", "syntax", 0, argc, argv);
|
|
return NULL;
|
|
}
|
|
|
|
return sch_eval("eval-syntax", argc, argv);
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_default_eval_handler(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
Scheme_Object *v;
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
v = _compile(argv[0], env, 0, 1, 0, 0);
|
|
|
|
/* Returns a tail apply: */
|
|
return _eval(v, env, 0, 1, 0, 1);
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_default_compile_handler(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
return _compile(argv[0], env, SCHEME_FALSEP(argv[1]), 0, 0, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
current_eval(int argc, Scheme_Object **argv)
|
|
{
|
|
return scheme_param_config("current-eval",
|
|
scheme_make_integer(MZCONFIG_EVAL_HANDLER),
|
|
argc, argv,
|
|
1, NULL, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
current_compile(int argc, Scheme_Object **argv)
|
|
{
|
|
return scheme_param_config("current-compile",
|
|
scheme_make_integer(MZCONFIG_COMPILE_HANDLER),
|
|
argc, argv,
|
|
2, NULL, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
top_introduce_stx(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Object *form;
|
|
|
|
if (!SCHEME_STXP(argv[0])) {
|
|
scheme_wrong_type("namespace-syntax-introduce", "syntax", 0, argc, argv);
|
|
return NULL;
|
|
}
|
|
|
|
form = argv[0];
|
|
|
|
if (!SAME_TYPE(SCHEME_TYPE(SCHEME_STX_VAL(form)), scheme_compilation_top_type)) {
|
|
Scheme_Env *genv;
|
|
genv = (Scheme_Env *)scheme_get_param(scheme_current_config(), MZCONFIG_ENV);
|
|
form = add_renames_unless_module(form, genv);
|
|
}
|
|
|
|
return form;
|
|
}
|
|
|
|
Scheme_Object *scheme_datum_to_kernel_stx(Scheme_Object *e)
|
|
{
|
|
return scheme_datum_to_syntax(e, scheme_false, scheme_sys_wraps(NULL), 0, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
compile(int argc, Scheme_Object *argv[])
|
|
{
|
|
Scheme_Object *form = argv[0];
|
|
Scheme_Env *genv;
|
|
|
|
if (!SCHEME_STXP(form))
|
|
form = scheme_datum_to_syntax(form, scheme_false, scheme_false, 1, 0);
|
|
|
|
genv = scheme_get_env(NULL);
|
|
form = add_renames_unless_module(form, genv);
|
|
|
|
return call_compile_handler(form, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
compile_stx(int argc, Scheme_Object *argv[])
|
|
{
|
|
if (!SCHEME_STXP(argv[0]))
|
|
scheme_wrong_type("compile-syntax", "syntax", 0, argc, argv);
|
|
|
|
return call_compile_handler(argv[0], 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
compiled_p(int argc, Scheme_Object *argv[])
|
|
{
|
|
return (SAME_TYPE(SCHEME_TYPE(argv[0]), scheme_compilation_top_type)
|
|
? scheme_true
|
|
: scheme_false);
|
|
}
|
|
|
|
static Scheme_Object *expand(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
return _expand(argv[0], scheme_new_expand_env(env, NULL, SCHEME_TOPLEVEL_FRAME),
|
|
-1, 1, 0, scheme_true, 0, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *expand_stx(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
|
|
if (!SCHEME_STXP(argv[0]))
|
|
scheme_wrong_type("expand-syntax", "syntax", 0, argc, argv);
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
return _expand(argv[0], scheme_new_expand_env(env, NULL, SCHEME_TOPLEVEL_FRAME),
|
|
-1, -1, 0, scheme_true, 0, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *stop_syntax(Scheme_Object *form, Scheme_Comp_Env *env,
|
|
Scheme_Compile_Info *rec, int drec)
|
|
{
|
|
scheme_signal_error("internal error: shouldn't get to stop syntax");
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *stop_expand(Scheme_Object *form, Scheme_Comp_Env *env, Scheme_Expand_Info *erec, int drec)
|
|
{
|
|
SCHEME_EXPAND_OBSERVE_PRIM_STOP(erec[drec].observer);
|
|
return form;
|
|
}
|
|
|
|
Scheme_Object *scheme_get_stop_expander(void)
|
|
{
|
|
if (!stop_expander) {
|
|
REGISTER_SO(stop_expander);
|
|
stop_expander = scheme_make_compiled_syntax(stop_syntax,
|
|
stop_expand);
|
|
}
|
|
|
|
return stop_expander;
|
|
}
|
|
|
|
Scheme_Object *scheme_generate_lifts_key(void)
|
|
{
|
|
static int cnt = 0;
|
|
char buf[20];
|
|
sprintf(buf, "lifts%d", cnt++);
|
|
return scheme_make_symbol(buf); /* uninterned */
|
|
}
|
|
|
|
Scheme_Object *
|
|
scheme_make_lifted_defn(Scheme_Object *sys_wraps, Scheme_Object **_id, Scheme_Object *expr, Scheme_Comp_Env *env)
|
|
{
|
|
Scheme_Object *l;
|
|
|
|
/* Registers marked id: */
|
|
scheme_tl_id_sym(env->genv, *_id, scheme_false, 2, NULL);
|
|
|
|
l = icons(scheme_datum_to_syntax(define_values_symbol, scheme_false, sys_wraps, 0, 0),
|
|
icons(scheme_make_pair(*_id, scheme_null),
|
|
icons(expr,
|
|
scheme_null)));
|
|
|
|
return scheme_datum_to_syntax(l, scheme_false, scheme_false, 0, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
do_local_expand(const char *name, int for_stx, int catch_lifts, int for_expr, int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Comp_Env *env, *orig_env;
|
|
Scheme_Object *l, *local_mark, *renaming = NULL, *orig_l, *exp_expr = NULL;
|
|
int cnt, pos, kind;
|
|
int bad_sub_env = 0;
|
|
Scheme_Object *observer, *catch_lifts_key = NULL;
|
|
|
|
env = scheme_current_thread->current_local_env;
|
|
orig_env = env;
|
|
|
|
if (!env)
|
|
scheme_raise_exn(MZEXN_FAIL_CONTRACT, "%s: not currently transforming", name);
|
|
|
|
if (for_stx) {
|
|
scheme_prepare_exp_env(env->genv);
|
|
env = scheme_new_comp_env(env->genv->exp_env, env->insp, 0);
|
|
}
|
|
|
|
if (for_expr)
|
|
kind = 0; /* expression */
|
|
else if (SAME_OBJ(argv[1], module_symbol))
|
|
kind = SCHEME_MODULE_BEGIN_FRAME; /* name is backwards compared to symbol! */
|
|
else if (SAME_OBJ(argv[1], module_begin_symbol))
|
|
kind = SCHEME_MODULE_FRAME; /* name is backwards compared to symbol! */
|
|
else if (SAME_OBJ(argv[1], top_level_symbol))
|
|
kind = SCHEME_TOPLEVEL_FRAME;
|
|
else if (SAME_OBJ(argv[1], expression_symbol))
|
|
kind = 0;
|
|
else if (scheme_proper_list_length(argv[1]) > 0)
|
|
kind = SCHEME_INTDEF_FRAME;
|
|
else {
|
|
scheme_wrong_type(name,
|
|
"'expression, 'module, 'module-begin, 'top-level, or non-empty list",
|
|
1, argc, argv);
|
|
return NULL;
|
|
}
|
|
|
|
if (argc > 3) {
|
|
if (SCHEME_TRUEP(argv[3])) {
|
|
if (SAME_TYPE(scheme_intdef_context_type, SCHEME_TYPE(argv[3]))) {
|
|
Scheme_Comp_Env *stx_env;
|
|
stx_env = (Scheme_Comp_Env *)SCHEME_PTR1_VAL(argv[3]);
|
|
renaming = SCHEME_PTR2_VAL(argv[3]);
|
|
if (!scheme_is_sub_env(stx_env, env))
|
|
bad_sub_env = 1;
|
|
env = stx_env;
|
|
}
|
|
}
|
|
|
|
if (argc > 4) {
|
|
/* catch_lifts */
|
|
catch_lifts_key = argv[4];
|
|
}
|
|
}
|
|
|
|
if (catch_lifts && !catch_lifts_key)
|
|
catch_lifts_key = scheme_generate_lifts_key();
|
|
|
|
/* For each given stop-point identifier, shadow any potential syntax
|
|
in the environment with an identity-expanding syntax expander. */
|
|
|
|
(void)scheme_get_stop_expander();
|
|
|
|
env = scheme_new_compilation_frame(0, (SCHEME_CAPTURE_WITHOUT_RENAME
|
|
| SCHEME_FOR_STOPS
|
|
| kind),
|
|
env, NULL);
|
|
if (kind == SCHEME_INTDEF_FRAME)
|
|
env->intdef_name = argv[1];
|
|
env->in_modidx = scheme_current_thread->current_local_modidx;
|
|
|
|
local_mark = scheme_current_thread->current_local_mark;
|
|
|
|
if (for_expr) {
|
|
} else if (SCHEME_TRUEP(argv[2])) {
|
|
cnt = scheme_stx_proper_list_length(argv[2]);
|
|
if (cnt > 0)
|
|
scheme_add_local_syntax(cnt, env);
|
|
pos = 0;
|
|
|
|
for (l = argv[2]; SCHEME_PAIRP(l); l = SCHEME_CDR(l)) {
|
|
Scheme_Object *i;
|
|
|
|
i = SCHEME_CAR(l);
|
|
if (!SCHEME_STX_SYMBOLP(i)) {
|
|
scheme_wrong_type(name, "#f or list of identifier syntax", 2, argc, argv);
|
|
return NULL;
|
|
}
|
|
|
|
if (cnt > 0)
|
|
scheme_set_local_syntax(pos++, i, stop_expander, env);
|
|
}
|
|
if (!SCHEME_NULLP(l)) {
|
|
scheme_wrong_type(name, "#f or list of identifier syntax", 2, argc, argv);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Report errors related to 3rd argument, finally */
|
|
if (argc > 3) {
|
|
if (SCHEME_TRUEP(argv[3])) {
|
|
if (SAME_TYPE(scheme_intdef_context_type, SCHEME_TYPE(argv[3]))) {
|
|
if (bad_sub_env) {
|
|
scheme_raise_exn(MZEXN_FAIL_CONTRACT, "%s: transforming context does "
|
|
"not match internal-definition context at the front of the context list",
|
|
name);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
scheme_wrong_type(name, "internal-definition context or #f", 3, argc, argv);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
l = argv[0];
|
|
|
|
if (!SCHEME_STXP(l))
|
|
l = scheme_datum_to_syntax(l, scheme_false, scheme_false, 1, 0);
|
|
|
|
orig_l = l;
|
|
|
|
observer = scheme_get_expand_observe();
|
|
if (observer) {
|
|
if (for_expr) {
|
|
SCHEME_EXPAND_OBSERVE_ENTER_LOCAL_EXPR(observer, l);
|
|
} else {
|
|
SCHEME_EXPAND_OBSERVE_ENTER_LOCAL(observer, l);
|
|
}
|
|
if (for_stx) {
|
|
SCHEME_EXPAND_OBSERVE_PHASE_UP(observer);
|
|
}
|
|
}
|
|
|
|
if (local_mark) {
|
|
/* Since we have an expression from local context,
|
|
we need to remove the temporary mark... */
|
|
l = scheme_add_remove_mark(l, local_mark);
|
|
}
|
|
|
|
l = scheme_stx_activate_certs(l);
|
|
|
|
if (renaming)
|
|
l = scheme_add_rename(l, renaming);
|
|
|
|
SCHEME_EXPAND_OBSERVE_LOCAL_PRE(observer, l);
|
|
|
|
if (SCHEME_FALSEP(argv[2])) {
|
|
Scheme_Object *xl, *gval;
|
|
Scheme_Compile_Expand_Info drec[1];
|
|
|
|
if (catch_lifts_key)
|
|
scheme_frame_captures_lifts(env, scheme_make_lifted_defn, scheme_sys_wraps(env), scheme_false,
|
|
catch_lifts_key);
|
|
|
|
memset(drec, 0, sizeof(drec));
|
|
drec[0].value_name = scheme_false; /* or scheme_current_thread->current_local_name ? */
|
|
drec[0].certs = scheme_current_thread->current_local_certs;
|
|
drec[0].depth = -2;
|
|
|
|
xl = scheme_check_immediate_macro(l, env, drec, 0, 0, &gval, NULL, NULL);
|
|
|
|
if (SAME_OBJ(xl, l))
|
|
return orig_l;
|
|
|
|
if (catch_lifts_key)
|
|
xl = add_lifts_as_begin(xl, scheme_frame_get_lifts(env), env);
|
|
|
|
l = xl;
|
|
} else {
|
|
/* Expand the expression. depth = -2 means expand all the way, but
|
|
preserve letrec-syntax. */
|
|
l = _expand(l, env, -2, 0, 0, catch_lifts_key, 0, scheme_current_thread->current_local_certs, 1);
|
|
}
|
|
|
|
SCHEME_EXPAND_OBSERVE_LOCAL_POST(observer, l);
|
|
|
|
if (renaming)
|
|
l = scheme_add_rename(l, renaming);
|
|
|
|
if (for_expr) {
|
|
/* Package up expanded expr with the enviornment. */
|
|
while (1) {
|
|
if (orig_env->flags & SCHEME_FOR_STOPS)
|
|
orig_env = orig_env->next;
|
|
else if ((orig_env->flags & SCHEME_INTDEF_FRAME)
|
|
&& !orig_env->num_bindings)
|
|
orig_env = orig_env->next;
|
|
else
|
|
break;
|
|
}
|
|
exp_expr = scheme_alloc_object();
|
|
exp_expr->type = scheme_expanded_syntax_type;
|
|
SCHEME_PTR1_VAL(exp_expr) = l;
|
|
SCHEME_PTR2_VAL(exp_expr) = orig_env;
|
|
exp_expr = scheme_datum_to_syntax(exp_expr, l, scheme_false, 0, 0);
|
|
exp_expr = scheme_add_remove_mark(exp_expr, local_mark);
|
|
}
|
|
|
|
if (local_mark) {
|
|
/* Put the temporary mark back: */
|
|
l = scheme_add_remove_mark(l, local_mark);
|
|
}
|
|
|
|
if (for_expr) {
|
|
Scheme_Object *a[2];
|
|
SCHEME_EXPAND_OBSERVE_EXIT_LOCAL_EXPR(observer, l, exp_expr);
|
|
a[0] = l;
|
|
a[1] = exp_expr;
|
|
return scheme_values(2, a);
|
|
} else
|
|
SCHEME_EXPAND_OBSERVE_EXIT_LOCAL(observer, l);
|
|
return l;
|
|
}
|
|
|
|
static Scheme_Object *
|
|
local_expand(int argc, Scheme_Object **argv)
|
|
{
|
|
return do_local_expand("local-expand", 0, 0, 0, argc, argv);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
local_expand_expr(int argc, Scheme_Object **argv)
|
|
{
|
|
return do_local_expand("syntax-local-expand-expression", 0, 0, 1, argc, argv);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
local_transformer_expand(int argc, Scheme_Object **argv)
|
|
{
|
|
return do_local_expand("local-transformer-expand", 1, 0, 0, argc, argv);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
local_expand_catch_lifts(int argc, Scheme_Object **argv)
|
|
{
|
|
return do_local_expand("local-expand/capture-lifts", 0, 1, 0, argc, argv);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
local_transformer_expand_catch_lifts(int argc, Scheme_Object **argv)
|
|
{
|
|
return do_local_expand("local-transformer-expand/capture-lifts", 1, 1, 0, argc, argv);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
expand_once(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
return _expand(argv[0], scheme_new_expand_env(env, NULL, SCHEME_TOPLEVEL_FRAME),
|
|
1, 1, 0, scheme_true, 0, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
expand_stx_once(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
|
|
if (!SCHEME_STXP(argv[0]))
|
|
scheme_wrong_type("expand-syntax-once", "syntax", 0, argc, argv);
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
return _expand(argv[0], scheme_new_expand_env(env, NULL, SCHEME_TOPLEVEL_FRAME),
|
|
1, -1, 0, scheme_true, 0, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
expand_to_top_form(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
return _expand(argv[0], scheme_new_expand_env(env, NULL, SCHEME_TOPLEVEL_FRAME),
|
|
1, 1, 1, scheme_true, 0, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
expand_stx_to_top_form(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Env *env;
|
|
|
|
if (!SCHEME_STXP(argv[0]))
|
|
scheme_wrong_type("expand-syntax-to-top", "syntax", 0, argc, argv);
|
|
|
|
env = scheme_get_env(NULL);
|
|
|
|
return _expand(argv[0], scheme_new_expand_env(env, NULL, SCHEME_TOPLEVEL_FRAME),
|
|
1, -1, 1, scheme_true, 0, NULL, 0);
|
|
}
|
|
|
|
static Scheme_Object *do_eval_string_all(const char *str, Scheme_Env *env, int cont, int w_prompt)
|
|
/* cont == -2 => module (no result)
|
|
cont == -1 => single result
|
|
cont == 1 -> multiple result ok
|
|
cont == 2 -> multiple result ok, use current_print to show results */
|
|
{
|
|
Scheme_Object *port, *expr, *result = scheme_void;
|
|
|
|
port = scheme_make_byte_string_input_port(str);
|
|
do {
|
|
expr = scheme_read_syntax(port, scheme_false);
|
|
|
|
if (cont == -2) {
|
|
if (SCHEME_STXP(expr)) {
|
|
Scheme_Object *m;
|
|
m = SCHEME_STX_VAL(expr);
|
|
if (SCHEME_PAIRP(m)) {
|
|
m = scheme_make_pair(scheme_datum_to_syntax(module_symbol,
|
|
SCHEME_CAR(m),
|
|
scheme_sys_wraps(NULL),
|
|
0, 0),
|
|
SCHEME_CDR(m));
|
|
expr = scheme_datum_to_syntax(m, expr, expr, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SAME_OBJ(expr, scheme_eof))
|
|
cont = 0;
|
|
else if (cont < 0) {
|
|
if (w_prompt)
|
|
result = scheme_eval_with_prompt(expr, env);
|
|
else
|
|
result = scheme_eval(expr, env);
|
|
} else {
|
|
if (w_prompt)
|
|
result = scheme_eval_multi_with_prompt(expr, env);
|
|
else
|
|
result = scheme_eval_multi(expr, env);
|
|
|
|
if (cont == 2) {
|
|
Scheme_Object **a, *_a[1], *arg[1], *printer;
|
|
int cnt, i;
|
|
|
|
if (result == SCHEME_MULTIPLE_VALUES) {
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
if (SAME_OBJ(p->ku.multiple.array, p->values_buffer))
|
|
p->values_buffer = NULL;
|
|
a = p->ku.multiple.array;
|
|
cnt = p->ku.multiple.count;
|
|
} else {
|
|
_a[0] = result;
|
|
a = _a;
|
|
cnt = 1;
|
|
}
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
printer = scheme_get_param(scheme_current_config(), MZCONFIG_PRINT_HANDLER);
|
|
arg[0] = a[i];
|
|
scheme_apply(printer, 1, arg);
|
|
}
|
|
}
|
|
}
|
|
} while (cont > 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_string_all(const char *str, Scheme_Env *env, int cont)
|
|
{
|
|
return do_eval_string_all(str, env, cont, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_string(const char *str, Scheme_Env *env)
|
|
{
|
|
return do_eval_string_all(str, env, -1, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_module_string(const char *str, Scheme_Env *env)
|
|
{
|
|
return do_eval_string_all(str, env, -2, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_string_multi(const char *str, Scheme_Env *env)
|
|
{
|
|
return do_eval_string_all(str, env, 0, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_string_all_with_prompt(const char *str, Scheme_Env *env, int cont)
|
|
{
|
|
return do_eval_string_all(str, env, cont, 1);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_string_with_prompt(const char *str, Scheme_Env *env)
|
|
{
|
|
return do_eval_string_all(str, env, -1, 1);
|
|
}
|
|
|
|
Scheme_Object *scheme_eval_string_multi_with_prompt(const char *str, Scheme_Env *env)
|
|
{
|
|
return do_eval_string_all(str, env, 0, 1);
|
|
}
|
|
|
|
void scheme_init_collection_paths(Scheme_Env *global_env, Scheme_Object *extra_dirs)
|
|
{
|
|
mz_jmp_buf * volatile save, newbuf;
|
|
Scheme_Thread * volatile p;
|
|
p = scheme_get_current_thread();
|
|
save = p->error_buf;
|
|
p->error_buf = &newbuf;
|
|
if (!scheme_setjmp(newbuf)) {
|
|
Scheme_Object *clcp, *flcp, *a[1];
|
|
|
|
clcp = scheme_builtin_value("current-library-collection-paths");
|
|
flcp = scheme_builtin_value("find-library-collection-paths");
|
|
|
|
if (clcp && flcp) {
|
|
a[0] = extra_dirs;
|
|
a[0] = _scheme_apply(flcp, 1, a);
|
|
_scheme_apply(clcp, 1, a);
|
|
}
|
|
}
|
|
p->error_buf = save;
|
|
}
|
|
|
|
static Scheme_Object *allow_set_undefined(int argc, Scheme_Object **argv)
|
|
{
|
|
return scheme_param_config("compile-allow-set!-undefined",
|
|
scheme_make_integer(MZCONFIG_ALLOW_SET_UNDEFINED),
|
|
argc, argv,
|
|
-1, NULL, NULL, 1);
|
|
}
|
|
|
|
static Scheme_Object *compile_module_constants(int argc, Scheme_Object **argv)
|
|
{
|
|
return scheme_param_config("compile-enforce-module-constants",
|
|
scheme_make_integer(MZCONFIG_COMPILE_MODULE_CONSTS),
|
|
argc, argv,
|
|
-1, NULL, NULL, 1);
|
|
}
|
|
|
|
static Scheme_Object *use_jit(int argc, Scheme_Object **argv)
|
|
{
|
|
return scheme_param_config("eval-jit-enabled",
|
|
scheme_make_integer(MZCONFIG_USE_JIT),
|
|
argc, argv,
|
|
-1, NULL, NULL, 1);
|
|
}
|
|
|
|
static Scheme_Object *
|
|
enable_break(int argc, Scheme_Object *argv[])
|
|
{
|
|
if (argc == 1) {
|
|
scheme_set_can_break(SCHEME_TRUEP(argv[0]));
|
|
if (SCHEME_TRUEP(argv[0])) {
|
|
if (scheme_current_thread->external_break && scheme_can_break(scheme_current_thread)) {
|
|
scheme_thread_block(0.0);
|
|
scheme_current_thread->ran_some = 1;
|
|
}
|
|
}
|
|
return scheme_void;
|
|
} else {
|
|
return scheme_can_break(scheme_current_thread) ? scheme_true : scheme_false;
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *
|
|
local_eval(int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Comp_Env *env, *stx_env, *old_stx_env;
|
|
Scheme_Object *l, *a, *rib, *expr, *certs, *names;
|
|
int cnt = 0, pos;
|
|
|
|
names = argv[0];
|
|
for (l = names; SCHEME_PAIRP(l); l = SCHEME_CDR(l)) {
|
|
a = SCHEME_CAR(l);
|
|
if (!SCHEME_STX_SYMBOLP(a))
|
|
break;
|
|
cnt++;
|
|
}
|
|
if (!SCHEME_NULLP(l))
|
|
scheme_wrong_type("syntax-local-bind-syntaxes", "list of syntax identifieres", 0, argc, argv);
|
|
|
|
expr = argv[1];
|
|
if (!SCHEME_FALSEP(expr) && !SCHEME_STXP(expr))
|
|
scheme_wrong_type("syntax-local-bind-syntaxes", "syntax or #f", 1, argc, argv);
|
|
if (!SAME_TYPE(scheme_intdef_context_type, SCHEME_TYPE(argv[2])))
|
|
scheme_wrong_type("syntax-local-bind-syntaxes", "internal-definition context", 2, argc, argv);
|
|
|
|
env = scheme_current_thread->current_local_env;
|
|
if (!env)
|
|
scheme_raise_exn(MZEXN_FAIL_CONTRACT, "syntax-local-bind-syntaxes: not currently transforming");
|
|
|
|
stx_env = (Scheme_Comp_Env *)SCHEME_PTR1_VAL(argv[2]);
|
|
rib = SCHEME_PTR2_VAL(argv[2]);
|
|
|
|
if (!scheme_is_sub_env(stx_env, env)) {
|
|
scheme_raise_exn(MZEXN_FAIL_CONTRACT, "syntax-local-bind-syntaxes: transforming context does "
|
|
"not match given internal-definition context");
|
|
}
|
|
|
|
certs = scheme_current_thread->current_local_certs;
|
|
old_stx_env = stx_env;
|
|
stx_env = scheme_new_compilation_frame(0, SCHEME_FOR_INTDEF, stx_env, certs);
|
|
scheme_add_local_syntax(cnt, stx_env);
|
|
|
|
/* Mark names */
|
|
names = scheme_named_map_1(NULL, scheme_add_remove_mark, names,
|
|
scheme_current_thread->current_local_mark);
|
|
|
|
/* Initialize environment slots to #f, which means "not syntax". */
|
|
cnt = 0;
|
|
for (l = names; SCHEME_PAIRP(l); l = SCHEME_CDR(l)) {
|
|
scheme_set_local_syntax(cnt++, SCHEME_CAR(l), scheme_false, stx_env);
|
|
}
|
|
|
|
stx_env->in_modidx = scheme_current_thread->current_local_modidx;
|
|
if (!SCHEME_FALSEP(expr)) {
|
|
|
|
Scheme_Compile_Expand_Info rec;
|
|
rec.comp = 0;
|
|
rec.depth = -1;
|
|
rec.value_name = scheme_false;
|
|
rec.certs = certs;
|
|
rec.observer = scheme_get_expand_observe();
|
|
|
|
/* Evaluate and bind syntaxes */
|
|
expr = scheme_add_remove_mark(expr, scheme_current_thread->current_local_mark);
|
|
|
|
scheme_prepare_exp_env(stx_env->genv);
|
|
pos = 0;
|
|
expr = scheme_add_rename_rib(expr, rib);
|
|
scheme_bind_syntaxes("local syntax definition", names, expr,
|
|
stx_env->genv->exp_env, stx_env->insp, &rec, 0,
|
|
stx_env, stx_env,
|
|
&pos);
|
|
}
|
|
|
|
/* Extend shared rib with renamings */
|
|
scheme_add_env_renames(rib, stx_env, old_stx_env);
|
|
|
|
/* Remember extended environment */
|
|
SCHEME_PTR1_VAL(argv[2]) = stx_env;
|
|
|
|
return scheme_void;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* creating/pushing prefix for top-levels and syntax objects */
|
|
/*========================================================================*/
|
|
|
|
int scheme_prefix_depth(Resolve_Prefix *rp)
|
|
{
|
|
if (rp->num_toplevels || rp->num_stxes || rp->num_lifts)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
Scheme_Object **scheme_push_prefix(Scheme_Env *genv, Resolve_Prefix *rp,
|
|
Scheme_Object *src_modidx, Scheme_Object *now_modidx,
|
|
int src_phase, int now_phase)
|
|
{
|
|
Scheme_Object **rs_save, **rs, *v, **a;
|
|
int i, j;
|
|
|
|
rs_save = rs = MZ_RUNSTACK;
|
|
|
|
if (rp->num_toplevels || rp->num_stxes || rp->num_lifts) {
|
|
i = rp->num_toplevels;
|
|
if (rp->num_stxes) {
|
|
i += rp->num_stxes + 1;
|
|
}
|
|
i += rp->num_lifts;
|
|
|
|
a = MALLOC_N(Scheme_Object *, i);
|
|
--rs;
|
|
MZ_RUNSTACK = rs;
|
|
rs[0] = (Scheme_Object *)a;
|
|
|
|
for (i = 0; i < rp->num_toplevels; i++) {
|
|
v = rp->toplevels[i];
|
|
if (genv)
|
|
v = link_toplevel(rp->toplevels[i], genv, src_modidx, now_modidx);
|
|
a[i] = v;
|
|
}
|
|
|
|
if (rp->num_stxes) {
|
|
i = rp->num_toplevels;
|
|
v = scheme_stx_phase_shift_as_rename(now_phase - src_phase, src_modidx, now_modidx,
|
|
genv ? genv->export_registry : NULL);
|
|
if (v || rp->delay_info) {
|
|
/* Put lazy-shift info in a[i]: */
|
|
Scheme_Object **ls;
|
|
ls = MALLOC_N(Scheme_Object *, 2);
|
|
ls[0] = v;
|
|
ls[1] = (Scheme_Object *)rp;
|
|
a[i] = (Scheme_Object *)ls;
|
|
/* Rest of a left zeroed, to be filled in lazily by quote-syntax evaluation */
|
|
} else {
|
|
/* No shift, so fill in stxes immediately */
|
|
i++;
|
|
for (j = 0; j < rp->num_stxes; j++) {
|
|
a[i + j] = rp->stxes[j];
|
|
}
|
|
}
|
|
j = rp->num_stxes + 1;
|
|
} else
|
|
j = 0;
|
|
|
|
if (rp->num_lifts) {
|
|
Scheme_Object *sym;
|
|
sym = scheme_make_symbol("<lifted>"); /* uninterned! */
|
|
j += rp->num_toplevels;
|
|
for (i = 0; i < rp->num_lifts; i++, j++) {
|
|
v = (Scheme_Object *)MALLOC_ONE_TAGGED(Scheme_Bucket_With_Home);
|
|
v->type = scheme_variable_type;
|
|
((Scheme_Bucket_With_Flags *)v)->flags = GLOB_HAS_HOME_PTR;
|
|
((Scheme_Bucket_With_Home *)v)->home = genv;
|
|
((Scheme_Bucket *)v)->key = (char *)sym;
|
|
a[j] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rs_save;
|
|
}
|
|
|
|
void scheme_pop_prefix(Scheme_Object **rs)
|
|
{
|
|
/* This function must not allocate, since a relevant multiple-values
|
|
result may be in the thread record (and we don't want it zerod) */
|
|
MZ_RUNSTACK = rs;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* bytecode validation */
|
|
/*========================================================================*/
|
|
|
|
/* Bytecode validation is an abstract interpretation on the stack,
|
|
where the abstract values are "not available", "value", "boxed
|
|
value", "syntax object", or "global array". */
|
|
|
|
/* FIXME: validation doesn't check CLOS_SINGLE_RESULT or
|
|
CLOS_PRESERVES_MARKS. (Maybe check them in the JIT pass?) */
|
|
|
|
#define VALID_NOT 0
|
|
#define VALID_VAL 1
|
|
#define VALID_BOX 2
|
|
#define VALID_TOPLEVELS 3
|
|
#define VALID_VAL_NOCLEAR 4
|
|
#define VALID_BOX_NOCLEAR 5
|
|
|
|
typedef struct Validate_Clearing {
|
|
MZTAG_IF_REQUIRED
|
|
int stackpos, stacksize;
|
|
int *stack;
|
|
int ncstackpos, ncstacksize;
|
|
int *ncstack;
|
|
int self_pos, self_count, self_start;
|
|
} Validate_Clearing;
|
|
|
|
static struct Validate_Clearing *make_clearing_stack()
|
|
{
|
|
Validate_Clearing *vc;
|
|
vc = MALLOC_ONE_RT(Validate_Clearing);
|
|
SET_REQUIRED_TAG(vc->type = scheme_rt_validate_clearing);
|
|
vc->self_pos = -1;
|
|
return vc;
|
|
}
|
|
|
|
static void reset_clearing(struct Validate_Clearing *vc)
|
|
{
|
|
vc->stackpos = 0;
|
|
vc->ncstackpos = 0;
|
|
}
|
|
|
|
static void clearing_stack_push(struct Validate_Clearing *vc, int pos, int val)
|
|
{
|
|
if (vc->stackpos + 2 > vc->stacksize) {
|
|
int *a, sz;
|
|
sz = (vc->stacksize ? 2 * vc->stacksize : 32);
|
|
a = (int *)scheme_malloc_atomic(sizeof(int) * sz);
|
|
memcpy(a, vc->stack, vc->stacksize * sizeof(int));
|
|
vc->stacksize = sz;
|
|
vc->stack = a;
|
|
}
|
|
vc->stack[vc->stackpos] = pos;
|
|
vc->stack[vc->stackpos + 1] = val;
|
|
vc->stackpos += 2;
|
|
}
|
|
|
|
static void noclear_stack_push(struct Validate_Clearing *vc, int pos)
|
|
{
|
|
if (vc->ncstackpos + 1 > vc->ncstacksize) {
|
|
int *a, sz;
|
|
sz = (vc->ncstacksize ? 2 * vc->ncstacksize : 32);
|
|
a = (int *)scheme_malloc_atomic(sizeof(int) * sz);
|
|
memcpy(a, vc->ncstack, vc->ncstacksize * sizeof(int));
|
|
vc->ncstacksize = sz;
|
|
vc->ncstack = a;
|
|
}
|
|
vc->ncstack[vc->ncstackpos] = pos;
|
|
vc->ncstackpos += 1;
|
|
}
|
|
|
|
void scheme_validate_code(Mz_CPort *port, Scheme_Object *code,
|
|
int depth,
|
|
int num_toplevels, int num_stxes, int num_lifts,
|
|
int code_vec)
|
|
{
|
|
char *stack;
|
|
int delta;
|
|
struct Validate_Clearing *vc;
|
|
Validate_TLS tls;
|
|
|
|
depth += ((num_toplevels || num_stxes || num_lifts) ? 1 : 0);
|
|
|
|
stack = scheme_malloc_atomic(depth);
|
|
memset(stack, VALID_NOT, depth);
|
|
|
|
if (num_toplevels || num_stxes || num_lifts) {
|
|
stack[depth - 1] = VALID_TOPLEVELS;
|
|
}
|
|
|
|
delta = depth - ((num_toplevels || num_stxes || num_lifts) ? 1 : 0);
|
|
|
|
tls = MALLOC_N(mzshort*, num_lifts);
|
|
|
|
vc = make_clearing_stack();
|
|
|
|
if (code_vec) {
|
|
int i, cnt;
|
|
cnt = SCHEME_VEC_SIZE(code);
|
|
for (i = 0; i < cnt; i++) {
|
|
reset_clearing(vc);
|
|
scheme_validate_expr(port, SCHEME_VEC_ELS(code)[i],
|
|
stack, tls,
|
|
depth, delta, delta,
|
|
num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0,
|
|
vc, 1);
|
|
}
|
|
} else {
|
|
scheme_validate_expr(port, code,
|
|
stack, tls,
|
|
depth, delta, delta,
|
|
num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0,
|
|
vc, 1);
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *validate_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Mz_CPort *port = (Mz_CPort *)p->ku.k.p1;
|
|
Scheme_Object *expr = (Scheme_Object *)p->ku.k.p2;
|
|
char *stack = (char *)p->ku.k.p3;
|
|
int *args = (int *)(((void **)p->ku.k.p5)[0]);
|
|
Scheme_Object *app_rator = (Scheme_Object *)(((void **)p->ku.k.p5)[1]);
|
|
Validate_TLS tls = (Validate_TLS)(((void **)p->ku.k.p5)[2]);
|
|
struct Validate_Clearing *vc = (struct Validate_Clearing *)p->ku.k.p4;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
p->ku.k.p3 = NULL;
|
|
p->ku.k.p4 = NULL;
|
|
p->ku.k.p5 = NULL;
|
|
|
|
scheme_validate_expr(port, expr, stack, tls,
|
|
args[0], args[1], args[2],
|
|
args[3], args[4], args[5],
|
|
app_rator, args[6], args[7], vc, args[8]);
|
|
|
|
return scheme_true;
|
|
}
|
|
|
|
int scheme_validate_rator_wants_box(Scheme_Object *app_rator, int pos,
|
|
int hope,
|
|
Validate_TLS tls,
|
|
int num_toplevels, int num_stxes, int num_lifts)
|
|
{
|
|
Scheme_Closure_Data *data = NULL;
|
|
|
|
while (1) {
|
|
if (SAME_TYPE(SCHEME_TYPE(app_rator), scheme_closure_type)) {
|
|
data = SCHEME_COMPILED_CLOS_CODE(app_rator);
|
|
break;
|
|
} else if (SAME_TYPE(SCHEME_TYPE(app_rator), scheme_unclosed_procedure_type)) {
|
|
data = (Scheme_Closure_Data *)app_rator;
|
|
break;
|
|
} else if (SAME_TYPE(SCHEME_TYPE(app_rator), scheme_toplevel_type)) {
|
|
int p;
|
|
p = SCHEME_TOPLEVEL_POS(app_rator);
|
|
while (1) {
|
|
if (p >= (num_toplevels + num_stxes + (num_stxes ? 1 : 0))) {
|
|
/* It's a lift. Check that the lift is defined, and that it
|
|
doesn't want reference arguments. */
|
|
mzshort *a; /* 0x1 => no ref args,
|
|
ptr with pos length => expected (0 => don't care, 1 => want not, 2 => want is),
|
|
ptr with neg length => actual
|
|
ptr with 0 => another top-level */
|
|
int tp;
|
|
|
|
tp = (p - (num_toplevels + num_stxes + (num_stxes ? 1 : 0)));
|
|
if (tp >= num_lifts)
|
|
return 0;
|
|
|
|
a = tls[tp];
|
|
if (a == (mzshort *)0x1) {
|
|
return 0;
|
|
} else if (!a || (a[0] > 0)) {
|
|
/* The lift isn't ready.
|
|
Record what we expect to find when it is ready. */
|
|
if (!a || (a[0] < (pos + 1))) {
|
|
mzshort *naya;
|
|
int sz;
|
|
if (a)
|
|
sz = a[0];
|
|
else
|
|
sz = 3;
|
|
sz *= 2;
|
|
if (sz <= pos)
|
|
sz = pos + 1;
|
|
naya = scheme_malloc_atomic((sz + 1) * sizeof(mzshort));
|
|
memset(naya, 0, (sz + 1) * sizeof(mzshort));
|
|
if (a)
|
|
memcpy(naya, a, (a[0] + 1) * sizeof(mzshort));
|
|
naya[0] = sz;
|
|
a = naya;
|
|
tls[tp] = a;
|
|
}
|
|
|
|
if (!a[pos + 1]) {
|
|
a[pos + 1] = hope ? 2 : 1;
|
|
return hope;
|
|
} else if (a[pos + 1] == 2)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
} else if (!a[0]) {
|
|
/* try again */
|
|
p = a[1];
|
|
} else {
|
|
return a[pos + 1];
|
|
}
|
|
} else
|
|
return 0;
|
|
}
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REF_ARGS) {
|
|
if (pos < data->num_params) {
|
|
int bit = ((mzshort)1 << (pos & (BITS_PER_MZSHORT - 1)));
|
|
if (data->closure_map[data->closure_size + (pos / BITS_PER_MZSHORT)] & bit)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int argument_to_arity_error(Scheme_Object *app_rator, int proc_with_refs_ok)
|
|
{
|
|
/* Since `raise-arity-error' doesn't actually apply its argument,
|
|
it's ok to pass any procedure. In particular, the compiler generates
|
|
calls to converted procedures. */
|
|
return ((proc_with_refs_ok == 2)
|
|
&& SAME_OBJ(app_rator, scheme_raise_arity_error_proc));
|
|
}
|
|
|
|
void scheme_validate_closure(Mz_CPort *port, Scheme_Object *expr,
|
|
char *closure_stack, Validate_TLS tls,
|
|
int num_toplevels, int num_stxes, int num_lifts,
|
|
int self_pos_in_closure)
|
|
{
|
|
Scheme_Closure_Data *data = (Scheme_Closure_Data *)expr;
|
|
int i, sz, cnt, base, base2;
|
|
char *new_stack;
|
|
struct Validate_Clearing *vc;
|
|
|
|
sz = data->max_let_depth;
|
|
new_stack = scheme_malloc_atomic(sz);
|
|
memset(new_stack, VALID_NOT, sz - data->num_params - data->closure_size);
|
|
|
|
cnt = data->num_params;
|
|
base = sz - cnt;
|
|
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REF_ARGS) {
|
|
base2 = data->closure_size;
|
|
for (i = 0; i < cnt; i++) {
|
|
new_stack[base + i] = closure_stack[base2 + i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < cnt; i++) {
|
|
new_stack[i + base] = VALID_VAL;
|
|
}
|
|
}
|
|
|
|
cnt = data->closure_size;
|
|
base = base - cnt;
|
|
for (i = 0; i < cnt; i++) {
|
|
new_stack[i + base] = closure_stack[i];
|
|
}
|
|
|
|
vc = make_clearing_stack();
|
|
if (self_pos_in_closure >= 0) {
|
|
vc->self_pos = base + self_pos_in_closure;
|
|
vc->self_count = data->closure_size;
|
|
vc->self_start = base;
|
|
}
|
|
|
|
scheme_validate_expr(port, data->code, new_stack, tls, sz, sz, base, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0, vc, 1);
|
|
}
|
|
|
|
|
|
static void validate_unclosed_procedure(Mz_CPort *port, Scheme_Object *expr,
|
|
char *stack, Validate_TLS tls,
|
|
int depth, int delta,
|
|
int num_toplevels, int num_stxes, int num_lifts,
|
|
Scheme_Object *app_rator, int proc_with_refs_ok,
|
|
int self_pos)
|
|
{
|
|
Scheme_Closure_Data *data = (Scheme_Closure_Data *)expr;
|
|
int i, cnt, q, p, sz, base, vld, self_pos_in_closure = -1;
|
|
mzshort *map;
|
|
char *closure_stack;
|
|
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REF_ARGS) {
|
|
sz = data->closure_size + data->num_params;
|
|
} else {
|
|
sz = data->closure_size;
|
|
}
|
|
map = data->closure_map;
|
|
|
|
if (sz)
|
|
closure_stack = scheme_malloc_atomic(sz);
|
|
else
|
|
closure_stack = NULL;
|
|
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REF_ARGS) {
|
|
cnt = data->num_params;
|
|
base = sz - cnt;
|
|
for (i = 0; i < cnt; i++) {
|
|
int bit = ((mzshort)1 << (i & (BITS_PER_MZSHORT - 1)));
|
|
if (map[data->closure_size + (i / BITS_PER_MZSHORT)] & bit)
|
|
vld = VALID_BOX;
|
|
else
|
|
vld = VALID_VAL;
|
|
closure_stack[i + base] = vld;
|
|
}
|
|
} else {
|
|
base = sz;
|
|
}
|
|
|
|
cnt = data->closure_size;
|
|
base = base - cnt;
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
q = map[i];
|
|
if (q == self_pos)
|
|
self_pos_in_closure = i;
|
|
p = q + delta;
|
|
if ((q < 0) || (p > depth) || (stack[p] == VALID_NOT))
|
|
scheme_ill_formed_code(port);
|
|
vld = stack[p];
|
|
if (vld == VALID_VAL_NOCLEAR)
|
|
vld = VALID_VAL;
|
|
else if (vld == VALID_BOX_NOCLEAR)
|
|
vld = VALID_BOX;
|
|
closure_stack[i + base] = vld;
|
|
}
|
|
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REF_ARGS) {
|
|
if ((proc_with_refs_ok != 1)
|
|
&& !argument_to_arity_error(app_rator, proc_with_refs_ok))
|
|
scheme_ill_formed_code(port);
|
|
}
|
|
|
|
if (SCHEME_RPAIRP(data->code)) {
|
|
/* Delay validation */
|
|
Scheme_Object *vec;
|
|
vec = scheme_make_vector(7, NULL);
|
|
SCHEME_VEC_ELS(vec)[0] = SCHEME_CAR(data->code);
|
|
SCHEME_VEC_ELS(vec)[1] = (Scheme_Object *)closure_stack;
|
|
SCHEME_VEC_ELS(vec)[2] = (Scheme_Object *)tls;
|
|
SCHEME_VEC_ELS(vec)[3] = scheme_make_integer(num_toplevels);
|
|
SCHEME_VEC_ELS(vec)[4] = scheme_make_integer(num_stxes);
|
|
SCHEME_VEC_ELS(vec)[5] = scheme_make_integer(num_lifts);
|
|
SCHEME_VEC_ELS(vec)[6] = scheme_make_integer(self_pos_in_closure);
|
|
SCHEME_CAR(data->code) = vec;
|
|
} else
|
|
scheme_validate_closure(port, expr, closure_stack, tls, num_toplevels, num_stxes, num_lifts, self_pos_in_closure);
|
|
}
|
|
|
|
static void check_self_call_valid(Scheme_Object *rator, Mz_CPort *port, struct Validate_Clearing *vc,
|
|
int delta, char *stack)
|
|
{
|
|
if ((vc->self_pos >= 0)
|
|
&& SAME_TYPE(SCHEME_TYPE(rator), scheme_local_type)
|
|
&& !(SCHEME_LOCAL_FLAGS(rator) & SCHEME_LOCAL_CLEARING_MASK)
|
|
&& ((SCHEME_LOCAL_POS(rator) + delta) == vc->self_pos)) {
|
|
/* For a self call, the JIT needs the closure data to be intact. */
|
|
int i, pos;
|
|
for (i = vc->self_count; i--; ) {
|
|
pos = i + vc->self_start;
|
|
if (stack[pos] == VALID_NOT)
|
|
scheme_ill_formed_code(port);
|
|
}
|
|
}
|
|
}
|
|
|
|
void scheme_validate_expr(Mz_CPort *port, Scheme_Object *expr,
|
|
char *stack, Validate_TLS tls,
|
|
int depth, int letlimit, int delta,
|
|
int num_toplevels, int num_stxes, int num_lifts,
|
|
Scheme_Object *app_rator, int proc_with_refs_ok,
|
|
int result_ignored,
|
|
struct Validate_Clearing *vc, int tailpos)
|
|
{
|
|
Scheme_Type type;
|
|
int did_one = 0, vc_merge = 0, vc_merge_start = 0;
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
# include "mzstkchk.h"
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
void **pr;
|
|
int *args;
|
|
|
|
args = MALLOC_N_ATOMIC(int, 8);
|
|
|
|
p->ku.k.p1 = (void *)port;
|
|
p->ku.k.p2 = (void *)expr;
|
|
p->ku.k.p3 = (void *)stack;
|
|
p->ku.k.p4 = (void *)vc;
|
|
|
|
args[0] = depth;
|
|
args[1] = letlimit;
|
|
args[2] = delta;
|
|
args[3] = num_toplevels;
|
|
args[4] = num_stxes;
|
|
args[5] = num_lifts;
|
|
args[6] = proc_with_refs_ok;
|
|
args[7] = result_ignored;
|
|
|
|
pr = MALLOC_N(void*, 3);
|
|
pr[0] = (void *)args;
|
|
pr[1] = (void *)app_rator;
|
|
pr[2] = (void *)tls;
|
|
|
|
p->ku.k.p5 = (void *)pr;
|
|
|
|
(void)scheme_handle_stack_overflow(validate_k);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
top:
|
|
if (did_one) {
|
|
if (app_rator) {
|
|
if (scheme_validate_rator_wants_box(app_rator, proc_with_refs_ok - 2, 0,
|
|
tls, num_toplevels, num_stxes, num_lifts))
|
|
scheme_ill_formed_code(port);
|
|
app_rator = NULL;
|
|
}
|
|
proc_with_refs_ok = 0;
|
|
} else
|
|
did_one = 1;
|
|
|
|
type = SCHEME_TYPE(expr);
|
|
|
|
switch (type) {
|
|
case scheme_toplevel_type:
|
|
{
|
|
int c = SCHEME_TOPLEVEL_DEPTH(expr);
|
|
int d = c + delta;
|
|
int p = SCHEME_TOPLEVEL_POS(expr);
|
|
|
|
if ((c < 0) || (p < 0) || (d >= depth)
|
|
|| (stack[d] != VALID_TOPLEVELS)
|
|
|| (p >= (num_toplevels + num_lifts + num_stxes + (num_stxes ? 1 : 0)))
|
|
|| ((p >= num_toplevels) && (p < num_toplevels + num_stxes + (num_stxes ? 1 : 0))))
|
|
scheme_ill_formed_code(port);
|
|
|
|
if ((proc_with_refs_ok != 1)
|
|
&& !argument_to_arity_error(app_rator, proc_with_refs_ok)) {
|
|
if (p >= (num_toplevels + num_stxes + (num_stxes ? 1 : 0))) {
|
|
/* It's a lift. Check that the lift is defined, and that it
|
|
doesn't want reference arguments. */
|
|
int tp;
|
|
mzshort *a;
|
|
tp = p - (num_toplevels + num_stxes + (num_stxes ? 1 : 0));
|
|
a = tls[tp];
|
|
if (a) {
|
|
if (a == (mzshort *)0x1) {
|
|
/* Ok */
|
|
} else if (a[0] > 0) {
|
|
int i, cnt;
|
|
cnt = a[0];
|
|
for (i = 0; i < cnt; i++) {
|
|
if (a[i] == 2)
|
|
scheme_ill_formed_code(port);
|
|
}
|
|
tls[tp] = (mzshort *)0x1;
|
|
} else {
|
|
/* a[0] is either 0 (top-level ref; shouldn't happen) or < 0 (wants some ref args) */
|
|
scheme_ill_formed_code(port);
|
|
}
|
|
} else {
|
|
tls[tp] = (mzshort *)0x1; /* means "no ref args anywhere" */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case scheme_local_type:
|
|
{
|
|
int q = SCHEME_LOCAL_POS(expr);
|
|
int p = q + delta;
|
|
|
|
if ((q < 0) || (p >= depth))
|
|
scheme_ill_formed_code(port);
|
|
|
|
if ((stack[p] != VALID_VAL) && (stack[p] != VALID_VAL_NOCLEAR)) {
|
|
if (result_ignored && ((stack[p] == VALID_BOX) || (stack[p] == VALID_BOX_NOCLEAR))) {
|
|
/* ok to look up and ignore box */
|
|
} else if ((proc_with_refs_ok >= 2)
|
|
&& ((stack[p] == VALID_BOX) || (stack[p] == VALID_BOX_NOCLEAR))
|
|
&& scheme_validate_rator_wants_box(app_rator, proc_with_refs_ok - 2, 1,
|
|
tls, num_toplevels, num_stxes, num_lifts)) {
|
|
/* It's ok - the function wants us to pass it a box, and
|
|
we did. */
|
|
app_rator = NULL;
|
|
} else
|
|
scheme_ill_formed_code(port);
|
|
}
|
|
|
|
if (SCHEME_LOCAL_FLAGS(expr) & SCHEME_LOCAL_CLEAR_ON_READ) {
|
|
if ((stack[p] == VALID_VAL_NOCLEAR) || (stack[p] == VALID_BOX_NOCLEAR))
|
|
scheme_ill_formed_code(port);
|
|
if (p >= letlimit)
|
|
clearing_stack_push(vc, p, stack[p]);
|
|
stack[p] = VALID_NOT;
|
|
} else if (!(SCHEME_LOCAL_FLAGS(expr) & SCHEME_LOCAL_OTHER_CLEARS)) {
|
|
if (stack[p] == VALID_BOX) {
|
|
if (p >= letlimit)
|
|
noclear_stack_push(vc, p);
|
|
stack[p] = VALID_BOX_NOCLEAR;
|
|
} else if (stack[p] == VALID_VAL) {
|
|
if (p >= letlimit)
|
|
noclear_stack_push(vc, p);
|
|
stack[p] = VALID_VAL_NOCLEAR;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case scheme_local_unbox_type:
|
|
{
|
|
int q = SCHEME_LOCAL_POS(expr);
|
|
int p = q + delta;
|
|
|
|
if ((q < 0) || (p >= depth) || ((stack[p] != VALID_BOX)
|
|
&& (stack[p] != VALID_BOX_NOCLEAR)))
|
|
scheme_ill_formed_code(port);
|
|
|
|
if (SCHEME_LOCAL_FLAGS(expr) & SCHEME_LOCAL_CLEAR_ON_READ) {
|
|
if (stack[p] == VALID_BOX_NOCLEAR)
|
|
scheme_ill_formed_code(port);
|
|
if (p >= letlimit)
|
|
clearing_stack_push(vc, p, stack[p]);
|
|
stack[p] = VALID_NOT;
|
|
} else if (!(SCHEME_LOCAL_FLAGS(expr) & SCHEME_LOCAL_OTHER_CLEARS)) {
|
|
if (stack[p] == VALID_BOX) {
|
|
if (p >= letlimit)
|
|
noclear_stack_push(vc, p);
|
|
stack[p] = VALID_BOX_NOCLEAR;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case scheme_syntax_type:
|
|
{
|
|
Scheme_Syntax_Validater f;
|
|
int p = SCHEME_PINT_VAL(expr);
|
|
|
|
if ((p < 0) || (p >= _COUNT_EXPD_))
|
|
scheme_ill_formed_code(port);
|
|
|
|
f = scheme_syntax_validaters[p];
|
|
f((Scheme_Object *)SCHEME_IPTR_VAL(expr), port, stack, tls, depth, letlimit, delta,
|
|
num_toplevels, num_stxes, num_lifts, vc, tailpos);
|
|
}
|
|
break;
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
|
|
int i, n;
|
|
|
|
n = app->num_args + 1;
|
|
|
|
delta -= (n - 1);
|
|
if (delta < 0)
|
|
scheme_ill_formed_code(port);
|
|
memset(stack + delta, VALID_NOT, n - 1);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
scheme_validate_expr(port, app->args[i], stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
i ? app->args[0] : NULL, i + 1, 0, vc, 0);
|
|
}
|
|
|
|
if (tailpos)
|
|
check_self_call_valid(app->args[0], port, vc, delta, stack);
|
|
}
|
|
break;
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
|
|
|
|
delta -= 1;
|
|
if (delta < 0)
|
|
scheme_ill_formed_code(port);
|
|
stack[delta] = VALID_NOT;
|
|
|
|
scheme_validate_expr(port, app->rator, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 1, 0, vc, 0);
|
|
scheme_validate_expr(port, app->rand, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
app->rator, 2, 0, vc, 0);
|
|
|
|
if (tailpos)
|
|
check_self_call_valid(app->rator, port, vc, delta, stack);
|
|
}
|
|
break;
|
|
case scheme_application3_type:
|
|
{
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
|
|
|
|
delta -= 2;
|
|
if (delta < 0)
|
|
scheme_ill_formed_code(port);
|
|
stack[delta] = VALID_NOT;
|
|
stack[delta+1] = VALID_NOT;
|
|
|
|
scheme_validate_expr(port, app->rator, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 1, 0, vc, 0);
|
|
scheme_validate_expr(port, app->rand1, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
app->rator, 2, 0, vc, 0);
|
|
scheme_validate_expr(port, app->rand2, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
app->rator, 3, 0, vc, 0);
|
|
|
|
if (tailpos)
|
|
check_self_call_valid(app->rator, port, vc, delta, stack);
|
|
}
|
|
break;
|
|
case scheme_sequence_type:
|
|
{
|
|
Scheme_Sequence *seq = (Scheme_Sequence *)expr;
|
|
int cnt;
|
|
int i;
|
|
|
|
cnt = seq->count;
|
|
|
|
for (i = 0; i < cnt - 1; i++) {
|
|
scheme_validate_expr(port, seq->array[i], stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 1, vc, 0);
|
|
}
|
|
|
|
expr = seq->array[cnt - 1];
|
|
goto top;
|
|
}
|
|
break;
|
|
case scheme_branch_type:
|
|
{
|
|
Scheme_Branch_Rec *b;
|
|
int vc_pos, vc_ncpos;
|
|
|
|
b = (Scheme_Branch_Rec *)expr;
|
|
scheme_validate_expr(port, b->test, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0, vc, 0);
|
|
/* This is where letlimit is useful. It prevents let-assignment in the
|
|
"then" branch that could permit bad code in the "else" branch (or the
|
|
same thing with either branch affecting later code in a sequence). */
|
|
letlimit = delta;
|
|
vc_pos = vc->stackpos;
|
|
vc_ncpos = vc->ncstackpos;
|
|
scheme_validate_expr(port, b->tbranch, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, result_ignored, vc, tailpos);
|
|
|
|
/* Rewind clears and noclears, but also save the clears,
|
|
so that the branches' effects can be merged. */
|
|
{
|
|
int i, j;
|
|
|
|
if (!vc_merge) {
|
|
vc_merge = 1;
|
|
vc_merge_start = vc_pos;
|
|
}
|
|
|
|
for (i = vc->stackpos - 2; i >= vc_pos; i -= 2) {
|
|
stack[vc->stack[i]] = vc->stack[i + 1];
|
|
}
|
|
|
|
for (i = vc->ncstackpos - 1; i >= vc_ncpos; i--) {
|
|
j = vc->ncstack[i];
|
|
if (stack[j] == VALID_VAL_NOCLEAR)
|
|
stack[j] = VALID_VAL;
|
|
else if (stack[j] == VALID_BOX_NOCLEAR)
|
|
stack[j] = VALID_BOX;
|
|
}
|
|
vc->ncstackpos = vc_ncpos;
|
|
}
|
|
|
|
expr = b->fbranch;
|
|
goto top;
|
|
}
|
|
break;
|
|
case scheme_with_cont_mark_type:
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)expr;
|
|
|
|
scheme_validate_expr(port, wcm->key, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0, vc, 0);
|
|
scheme_validate_expr(port, wcm->val, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0, vc, 0);
|
|
expr = wcm->body;
|
|
goto top;
|
|
}
|
|
break;
|
|
case scheme_quote_syntax_type:
|
|
{
|
|
Scheme_Quote_Syntax *qs = (Scheme_Quote_Syntax *)expr;
|
|
int c = qs->depth;
|
|
int i = qs->position;
|
|
int p = qs->midpoint;
|
|
int d = c + delta;
|
|
|
|
if ((c < 0) || (p < 0) || (d >= depth)
|
|
|| (stack[d] != VALID_TOPLEVELS)
|
|
|| (p != num_toplevels)
|
|
|| (i >= num_stxes))
|
|
scheme_ill_formed_code(port);
|
|
}
|
|
break;
|
|
case scheme_unclosed_procedure_type:
|
|
{
|
|
validate_unclosed_procedure(port, expr, stack, tls,
|
|
depth, delta, num_toplevels, num_stxes, num_lifts,
|
|
app_rator, proc_with_refs_ok, -1);
|
|
}
|
|
break;
|
|
case scheme_let_value_type:
|
|
{
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)expr;
|
|
int q, p, c, i;
|
|
|
|
scheme_validate_expr(port, lv->value, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0, vc, 0);
|
|
memset(stack, VALID_NOT, delta);
|
|
|
|
c = lv->count;
|
|
q = lv->position;
|
|
p = q + delta;
|
|
|
|
for (i = 0; i < c; i++, p++) {
|
|
if ((q < 0)
|
|
|| (SCHEME_LET_AUTOBOX(lv) && ((p >= depth)
|
|
|| ((stack[p] != VALID_BOX)
|
|
&& (stack[p] != VALID_BOX_NOCLEAR))))
|
|
|| (!SCHEME_LET_AUTOBOX(lv) && ((p >= letlimit)
|
|
|| ((stack[p] != VALID_VAL)
|
|
&& (stack[p] != VALID_VAL_NOCLEAR)
|
|
&& (stack[p] != VALID_NOT)))))
|
|
scheme_ill_formed_code(port);
|
|
|
|
if (!SCHEME_LET_AUTOBOX(lv)) {
|
|
if (stack[p] != VALID_VAL_NOCLEAR)
|
|
stack[p] = VALID_VAL;
|
|
}
|
|
}
|
|
|
|
expr = lv->body;
|
|
goto top;
|
|
}
|
|
break;
|
|
case scheme_let_void_type:
|
|
{
|
|
Scheme_Let_Void *lv = (Scheme_Let_Void *)expr;
|
|
int c, i;
|
|
|
|
c = lv->count;
|
|
|
|
if ((c < 0) || (c > delta))
|
|
scheme_ill_formed_code(port);
|
|
|
|
if (SCHEME_LET_AUTOBOX(lv)) {
|
|
for (i = 0; i < c; i++) {
|
|
stack[--delta] = VALID_BOX;
|
|
}
|
|
} else {
|
|
delta -= c;
|
|
memset(stack + delta, VALID_NOT, c);
|
|
}
|
|
|
|
|
|
expr = lv->body;
|
|
goto top;
|
|
}
|
|
break;
|
|
case scheme_letrec_type:
|
|
{
|
|
Scheme_Letrec *l = (Scheme_Letrec *)expr;
|
|
int i, c;
|
|
|
|
c = l->count;
|
|
|
|
if ((c < 0) || (c + delta > depth))
|
|
scheme_ill_formed_code(port);
|
|
|
|
for (i = 0; i < c; i++) {
|
|
if (!SAME_TYPE(SCHEME_TYPE(l->procs[i]), scheme_unclosed_procedure_type))
|
|
scheme_ill_formed_code(port);
|
|
}
|
|
|
|
for (i = 0; i < c; i++) {
|
|
stack[delta + i] = VALID_VAL;
|
|
}
|
|
|
|
for (i = 0; i < c; i++) {
|
|
validate_unclosed_procedure(port, l->procs[i], stack, tls,
|
|
depth, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, i);
|
|
}
|
|
|
|
expr = l->body;
|
|
goto top;
|
|
}
|
|
break;
|
|
case scheme_let_one_type:
|
|
{
|
|
Scheme_Let_One *lo = (Scheme_Let_One *)expr;
|
|
|
|
--delta;
|
|
if (delta < 0)
|
|
scheme_ill_formed_code(port);
|
|
stack[delta] = VALID_NOT;
|
|
|
|
scheme_validate_expr(port, lo->value, stack, tls, depth, letlimit, delta, num_toplevels, num_stxes, num_lifts,
|
|
NULL, 0, 0, vc, 0);
|
|
stack[delta] = VALID_VAL;
|
|
|
|
expr = lo->body;
|
|
goto top;
|
|
}
|
|
break;
|
|
default:
|
|
/* All values are definitely ok, except pre-closed closures.
|
|
Such a closure can refer back to itself, so we use a flag
|
|
to track cycles. */
|
|
if (SAME_TYPE(type, scheme_closure_type)) {
|
|
Scheme_Closure_Data *data;
|
|
expr = (Scheme_Object *)SCHEME_COMPILED_CLOS_CODE(expr);
|
|
data = (Scheme_Closure_Data *)expr;
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_VALIDATED) {
|
|
/* Done with this one. */
|
|
} else {
|
|
SCHEME_CLOSURE_DATA_FLAGS(data) |= CLOS_VALIDATED;
|
|
did_one = 0;
|
|
goto top;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (app_rator)
|
|
if (scheme_validate_rator_wants_box(app_rator, proc_with_refs_ok - 2, 0,
|
|
tls, num_toplevels, num_stxes, num_lifts))
|
|
scheme_ill_formed_code(port);
|
|
|
|
if (vc_merge) {
|
|
/* Re-clear to merge effects from branches */
|
|
int i, p;
|
|
for (i = vc_merge_start; i < vc->stackpos; i += 2) {
|
|
p = vc->stack[i];
|
|
stack[p] = VALID_NOT;
|
|
}
|
|
}
|
|
}
|
|
|
|
void scheme_validate_toplevel(Scheme_Object *expr, Mz_CPort *port,
|
|
char *stack, Validate_TLS tls,
|
|
int depth, int delta,
|
|
int num_toplevels, int num_stxes, int num_lifts,
|
|
int skip_refs_check)
|
|
{
|
|
if (!SAME_TYPE(scheme_toplevel_type, SCHEME_TYPE(expr)))
|
|
scheme_ill_formed_code(port);
|
|
|
|
scheme_validate_expr(port, expr, stack, tls,
|
|
depth, delta, delta,
|
|
num_toplevels, num_stxes, num_lifts,
|
|
NULL, skip_refs_check ? 1 : 0, 0,
|
|
make_clearing_stack(), 0);
|
|
}
|
|
|
|
void scheme_validate_boxenv(int p, Mz_CPort *port, char *stack, int depth, int delta)
|
|
{
|
|
p += delta;
|
|
|
|
if ((p < 0) || (p >= depth) || (stack[p] != VALID_VAL))
|
|
scheme_ill_formed_code(port);
|
|
|
|
stack[p] = VALID_BOX;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* [un]marshalling application, branch, sequence, wcm bytecode */
|
|
/*========================================================================*/
|
|
|
|
#define BOOL(x) (x ? scheme_true : scheme_false)
|
|
|
|
static Scheme_Object *write_application(Scheme_Object *obj)
|
|
{
|
|
scheme_signal_error("app writer shouldn't be used");
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *read_application(Scheme_Object *obj)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *write_sequence(Scheme_Object *obj)
|
|
{
|
|
Scheme_Object *l;
|
|
int i;
|
|
|
|
i = ((Scheme_Sequence *)obj)->count;
|
|
|
|
l = scheme_null;
|
|
for (; i--; ) {
|
|
l = cons(scheme_protect_quote(((Scheme_Sequence *)obj)->array[i]), l);
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
static Scheme_Object *read_sequence(Scheme_Object *obj)
|
|
{
|
|
return scheme_make_sequence_compilation(obj, 1);
|
|
}
|
|
|
|
static Scheme_Object *read_sequence_save_first(Scheme_Object *obj)
|
|
{
|
|
return scheme_make_sequence_compilation(obj, -1);
|
|
}
|
|
|
|
static Scheme_Object *write_branch(Scheme_Object *obj)
|
|
{
|
|
scheme_signal_error("branch writer shouldn't be used");
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *read_branch(Scheme_Object *obj)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static Scheme_Object *write_with_cont_mark(Scheme_Object *obj)
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm;
|
|
|
|
wcm = (Scheme_With_Continuation_Mark *)obj;
|
|
|
|
return cons(scheme_protect_quote(wcm->key),
|
|
cons(scheme_protect_quote(wcm->val),
|
|
scheme_protect_quote(wcm->body)));
|
|
}
|
|
|
|
static Scheme_Object *read_with_cont_mark(Scheme_Object *obj)
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm;
|
|
|
|
if (!SCHEME_PAIRP(obj) || !SCHEME_PAIRP(SCHEME_CDR(obj)))
|
|
return NULL; /* bad .zo */
|
|
|
|
wcm = MALLOC_ONE_TAGGED(Scheme_With_Continuation_Mark);
|
|
wcm->so.type = scheme_with_cont_mark_type;
|
|
wcm->key = SCHEME_CAR(obj);
|
|
wcm->val = SCHEME_CADR(obj);
|
|
wcm->body = SCHEME_CDR(SCHEME_CDR(obj));
|
|
|
|
return (Scheme_Object *)wcm;
|
|
}
|
|
|
|
static Scheme_Object *write_syntax(Scheme_Object *obj)
|
|
{
|
|
Scheme_Object *idx, *rest, *l;
|
|
int protect_after, c;
|
|
|
|
c = SCHEME_PINT_VAL(obj);
|
|
idx = scheme_make_integer(c);
|
|
protect_after = scheme_syntax_protect_afters[c];
|
|
|
|
if (c == BEGIN0_EXPD) {
|
|
Scheme_Object *v;
|
|
v = SCHEME_PTR_VAL(obj);
|
|
switch (SCHEME_TYPE(v)) {
|
|
case scheme_sequence_type:
|
|
case scheme_begin0_sequence_type:
|
|
break;
|
|
default:
|
|
*(long *)0x0 = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
l = rest = (Scheme_Object *)SCHEME_IPTR_VAL(obj);
|
|
if (protect_after == -2) {
|
|
/* -2 => protect first element of vector */
|
|
if (SCHEME_VECTORP(l)) {
|
|
l = scheme_protect_quote(SCHEME_VEC_ELS(rest)[0]);
|
|
if (!SAME_OBJ(l, SCHEME_VEC_ELS(rest)[0])) {
|
|
Scheme_Object *vec;
|
|
long i, len;
|
|
len = SCHEME_VEC_SIZE(rest);
|
|
vec = scheme_make_vector(len, NULL);
|
|
SCHEME_VEC_ELS(vec)[0] = l;
|
|
for (i = 1; i < len; i++) {
|
|
SCHEME_VEC_ELS(vec)[i] = SCHEME_VEC_ELS(rest)[i];
|
|
}
|
|
rest = vec;
|
|
}
|
|
} else {
|
|
scheme_signal_error("expected a vector for syntax");
|
|
}
|
|
} else {
|
|
for (c = 0; SCHEME_PAIRP(l) && (c < protect_after); c++) {
|
|
l = SCHEME_CDR(l);
|
|
}
|
|
if (!SCHEME_NULLP(l) && (c == protect_after)) {
|
|
Scheme_Object *new_l;
|
|
|
|
new_l = scheme_protect_quote(l);
|
|
|
|
if (new_l != l) {
|
|
Scheme_Object *first = NULL, *last = NULL;
|
|
|
|
while (rest != l) {
|
|
Scheme_Object *p;
|
|
|
|
p = scheme_make_pair(SCHEME_CAR(rest), scheme_null);
|
|
if (last)
|
|
SCHEME_CDR(last) = p;
|
|
else
|
|
first = p;
|
|
last = p;
|
|
|
|
rest = SCHEME_CDR(rest);
|
|
}
|
|
|
|
if (last)
|
|
SCHEME_CDR(last) = new_l;
|
|
else
|
|
first = new_l;
|
|
|
|
rest = first;
|
|
}
|
|
}
|
|
}
|
|
|
|
return cons(idx, rest);
|
|
}
|
|
|
|
static Scheme_Object *read_syntax(Scheme_Object *obj)
|
|
{
|
|
Scheme_Object *idx;
|
|
Scheme_Object *first = NULL, *last = NULL;
|
|
int limit;
|
|
|
|
if (!SCHEME_PAIRP(obj) || !SCHEME_INTP(SCHEME_CAR(obj)))
|
|
return NULL; /* bad .zo */
|
|
|
|
idx = SCHEME_CAR(obj);
|
|
|
|
/* Copy obj, up to number of cons cells before a "protected" value: */
|
|
limit = scheme_syntax_protect_afters[SCHEME_INT_VAL(idx)];
|
|
obj = SCHEME_CDR(obj);
|
|
while (SCHEME_PAIRP(obj) && (limit > 0)) {
|
|
Scheme_Object *p;
|
|
p = scheme_make_pair(SCHEME_CAR(obj), scheme_null);
|
|
if (last)
|
|
SCHEME_CDR(last) = p;
|
|
else
|
|
first = p;
|
|
last = p;
|
|
obj = SCHEME_CDR(obj);
|
|
limit--;
|
|
}
|
|
|
|
if (last)
|
|
SCHEME_CDR(last) = obj;
|
|
else
|
|
first = obj;
|
|
|
|
return scheme_make_syntax_resolved(SCHEME_INT_VAL(idx), first);
|
|
}
|
|
|
|
static Scheme_Object *write_quote_syntax(Scheme_Object *obj)
|
|
{
|
|
Scheme_Quote_Syntax *qs = (Scheme_Quote_Syntax *)obj;
|
|
|
|
return cons(scheme_make_integer(qs->depth),
|
|
cons(scheme_make_integer(qs->position),
|
|
scheme_make_integer(qs->midpoint)));
|
|
}
|
|
|
|
static Scheme_Object *read_quote_syntax(Scheme_Object *obj)
|
|
{
|
|
Scheme_Quote_Syntax *qs;
|
|
Scheme_Object *a;
|
|
int c, i, p;
|
|
|
|
if (!SCHEME_PAIRP(obj)) return NULL;
|
|
|
|
a = SCHEME_CAR(obj);
|
|
c = SCHEME_INT_VAL(a);
|
|
|
|
obj = SCHEME_CDR(obj);
|
|
if (!SCHEME_PAIRP(obj)) return NULL;
|
|
|
|
a = SCHEME_CAR(obj);
|
|
i = SCHEME_INT_VAL(a);
|
|
|
|
a = SCHEME_CDR(obj);
|
|
p = SCHEME_INT_VAL(a);
|
|
|
|
qs = MALLOC_ONE_TAGGED(Scheme_Quote_Syntax);
|
|
qs->so.type = scheme_quote_syntax_type;
|
|
qs->depth = c;
|
|
qs->position = i;
|
|
qs->midpoint = p;
|
|
|
|
return (Scheme_Object *)qs;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* precise GC traversers */
|
|
/*========================================================================*/
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
|
|
START_XFORM_SKIP;
|
|
|
|
#define MARKS_FOR_EVAL_C
|
|
#include "mzmark.c"
|
|
|
|
static void register_traversers(void)
|
|
{
|
|
GC_REG_TRAV(scheme_rt_compile_info, mark_comp_info);
|
|
GC_REG_TRAV(scheme_rt_saved_stack, mark_saved_stack);
|
|
GC_REG_TRAV(scheme_rt_validate_clearing, mark_validate_clearing);
|
|
}
|
|
|
|
END_XFORM_SKIP;
|
|
|
|
#endif
|