1 /* This file implements bytecode optimization.
2 
3    See "eval.c" for an overview of compilation passes. */
4 
5 #include "schpriv.h"
6 #include "schrunst.h"
7 #include "schmach.h"
8 
9 /* Controls for inlining algorithm: */
10 #define OPT_ESTIMATE_FUTURE_SIZES   1
11 #define OPT_DISCOURAGE_EARLY_INLINE 1
12 #define OPT_LIMIT_FUNCTION_RESIZE   0
13 #define OPT_BRANCH_ADDS_NO_SIZE     1
14 #define OPT_DELAY_GROUP_PROPAGATE   0
15 #define OPT_PRE_OPTIMIZE_FOR_CROSS_LINKLET(size_override) (size_override)
16 
17 #define MAX_PROC_INLINE_SIZE     256
18 #define CROSS_LINKLET_INLINE_SIZE 8
19 
20 /* Various kinds of fuel ensure that
21    the compiler doesn't go into a loop
22    or take non-linear time */
23 #define INITIAL_INLINING_FUEL   32
24 #define INITIAL_FLATTENING_FUEL 16
25 
26 
27 #define SCHEME_LAMBDA_FRAME 1
28 
29 typedef struct Cross_Linklet_Info
30 {
31   /* Must be all pointers; allocated with scheme_malloc() */
32   Scheme_Object *get_import; /* NULL or (key -> linklet (vector key ...)) */
33   Scheme_Hash_Tree *import_keys; /* import-position -> key */
34   Scheme_Hash_Tree *rev_import_keys; /* key -> import-position */
35   Scheme_Hash_Tree *linklets; /* key -> linklet-or-instance */
36   Scheme_Hash_Tree *import_next_keys; /* key -> (vector key ...) */
37   Scheme_Hash_Tree *inline_variants; /* key -> symbol -> value */
38   Scheme_Hash_Tree *import_syms; /* import-position -> ((symbol -> variable-position)
39                                     .                   + (variable-position -> symbol)) */
40   int used_import_shape;
41 } Cross_Linklet_Info;
42 
43 /* Clasification for predicates.
44    Each one implies the smaller.  */
45 #define RLV_IS_RELEVANT   1 /* The predicate is remembered by the optimizer */
46 #define RLV_EQV_TESTEABLE 2 /* (equal? x <pred>) can be replaced by (eqv? x <pred>) */
47 #define RLV_EQ_TESTEABLE  3 /* (equal? x <pred>) can be replaced by (eq? x <pred>) */
48 #define RLV_SINGLETON     4 /* Recognizes a single value */
49 
50 struct Optimize_Info
51 {
52   MZTAG_IF_REQUIRED
53   short flags;
54   struct Optimize_Info *next;
55   struct Scheme_Linklet *linklet;
56   int init_kclock;
57 
58   /* For cross-linklet inlining: */
59   Cross_Linklet_Info *cross;
60 
61   /* Track which imports are still used after optimization */
62   Scheme_Hash_Tree **imports_used; /* import position -> variable position -> true */
63 
64   /* Propagated up and down the chain: */
65   int size;
66   int vclock; /* virtual clock that ticks for a side effect, a branch,
67                  observation of a side effect (such as an unbox),
68                  or a dependency on an earlier side effect (such as a
69                  previous guard on an unsafe operation's argument);
70                  the clock is only compared between binding sites and
71                  uses, so we can rewind the clock at a join after an
72                  increment that models a branch (if the branch is not
73                  taken or doesn't increment the clock) */
74   int aclock; /* virtual clock that ticks for allocation without side effects,
75                  for constraining the reordering of operations that might
76                  capture a continuation */
77   int kclock; /* virtual clock that ticks for a potential continuation capture,
78                  for constraining the movement of allocation operations */
79   int sclock; /* virtual clock that ticks when space consumption is potentially observed */
80   int psize;
81   short inline_fuel, flatten_fuel;
82   char letrec_not_twice, enforce_const, unsafe_mode, use_psize, has_nonleaf;
83   Scheme_Hash_Table *top_level_consts;
84 
85   int maybe_values_argument; /* triggers an approximation for clock increments */
86 
87   /* Set by expression optimization: */
88   int single_result, preserves_marks; /* negative means "tentative", due to fixpoint in progress */
89   int escapes; /* flag to signal that the expression always escapes. When escapes is 1, it's assumed
90                   that single_result and preserves_marks are also 1, and that it's not necessary to
91                   use optimize_ignored before including the expression. */
92 
93   int lambda_depth; /* counts nesting depth under `lambda`s */
94   int used_toplevel; /* tracks whether any non-local variables or syntax-object literals are used */
95 
96   Scheme_Hash_Table *uses; /* used variables, accumulated for closures */
97 
98   Scheme_IR_Local *transitive_use_var; /* set when optimizing a letrec-bound procedure
99                                           to record variables that were added to `uses` */
100 
101   Scheme_Object *context; /* for logging */
102   Scheme_Logger *logger;
103   Scheme_Hash_Tree *types; /* maps position (from this frame) to predicate */
104   int no_types; /* disables use of type info */
105 };
106 
107 typedef struct Optimize_Info_Sequence {
108   int init_flatten_fuel, min_flatten_fuel;
109 } Optimize_Info_Sequence;
110 
111 static Scheme_Object *optimize_expr(Scheme_Object *expr, Optimize_Info *info, int context);
112 
113 static int get_rator_flags(Scheme_Object *rator, int num_args, Optimize_Info *info);
114 Scheme_Object *lookup_constant_proc(Optimize_Info *info, Scheme_Object *le, int argc);
115 static void merge_lambda_arg_types(Scheme_Lambda *lam1, Scheme_Lambda *lam2);
116 static void check_lambda_arg_types_registered(Scheme_Lambda *lam, int app_count);
117 static int lambda_body_size_plus_info(Scheme_Lambda *lam, int check_assign,
118                                       Optimize_Info *info, int *is_leaf);
119 static int lambda_has_top_level(Scheme_Lambda *lam);
120 
121 static Scheme_Object *make_sequence_2(Scheme_Object *a, Scheme_Object *b);
122 
123 XFORM_NONGCING static int wants_local_type_arguments(Scheme_Object *rator, int argpos);
124 
125 static void add_types_for_f_branch(Scheme_Object *t, Optimize_Info *info, int fuel);
126 
127 static void register_use(Scheme_IR_Local *var, Optimize_Info *info);
128 static Scheme_Object *optimize_info_lookup(Scheme_Object *var);
129 static Scheme_Object *optimize_info_propagate_local(Scheme_Object *var);
130 static void optimize_info_used_top(Optimize_Info *info);
131 static Scheme_Object *optimize_get_predicate(Optimize_Info *info, Scheme_Object *var, int ignore_no_types);
132 static void add_type(Optimize_Info *info, Scheme_Object *var, Scheme_Object *pred);
133 static void merge_types(Optimize_Info *src_info, Optimize_Info *info, Scheme_Hash_Tree *skip_vars);
134 
135 static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info *info);
136 static Scheme_Object *do_expr_implies_predicate(Scheme_Object *expr, Optimize_Info *info,
137                                                 int *_involves_k_cross, int fuel,
138                                                 Scheme_Hash_Tree *ignore_vars);
139 static int produces_local_type(Scheme_Object *rator, int argc);
140 static int optimize_any_uses(Optimize_Info *info, Scheme_IR_Let_Value *at_irlv, int n);
141 static void optimize_uses_of_mutable_imply_early_alloc(Scheme_IR_Let_Value *at_irlv, int n);
142 static void propagate_used_variables(Optimize_Info *info);
143 static int env_uses_toplevel(Optimize_Info *frame);
144 static Scheme_IR_Local *clone_variable(Scheme_IR_Local *var);
145 static void increment_use_count(Scheme_IR_Local *var, int as_rator);
146 
147 static Optimize_Info *optimize_info_create(Scheme_Linklet *linklet,
148                                            int enforce_const, int can_inline, int unsafe_mode);
149 static Optimize_Info *optimize_info_add_frame(Optimize_Info *info, int flags);
150 static void optimize_info_done(Optimize_Info *info, Optimize_Info *parent);
151 
152 static void register_transitive_uses(Scheme_IR_Local *var, Optimize_Info *info);
153 
154 static void optimize_info_seq_init(Optimize_Info *info, Optimize_Info_Sequence *info_seq);
155 static void optimize_info_seq_step(Optimize_Info *info, Optimize_Info_Sequence *info_seq);
156 static void optimize_info_seq_done(Optimize_Info *info, Optimize_Info_Sequence *info_seq);
157 
158 static int ir_propagate_ok(Scheme_Object *o, Optimize_Info *info, int used_once, Scheme_IR_Local *once_var);
159 
160 static Scheme_Object *estimate_closure_size(Scheme_Object *e);
161 static Scheme_Object *no_potential_size(Scheme_Object *value);
162 
163 static Scheme_Object *optimize_lets(Scheme_Object *form, Optimize_Info *info, int context);
164 
165 static Scheme_Object *optimize_clone(int single_use, Scheme_Object *obj, Optimize_Info *info, Scheme_Hash_Tree *var_map, int as_rator);
166 
167 static Scheme_Object *get_import_shape(Optimize_Info *info, Scheme_IR_Toplevel *var);
168 static Scheme_Object *get_import_inline(Optimize_Info *info, Scheme_IR_Toplevel *var, int argc, int case_ok);
169 static void register_import_used(Optimize_Info *info, Scheme_IR_Toplevel *expr);
170 static void record_optimize_shapes(Optimize_Info *info, Scheme_Linklet *linklet, Scheme_Object **_import_keys);
171 static Scheme_Object *get_value_shape(Scheme_Object *v, int imprecise);
172 
173 XFORM_NONGCING static int relevant_predicate(Scheme_Object *pred);
174 XFORM_NONGCING static int predicate_implies(Scheme_Object *pred1, Scheme_Object *pred2);
175 XFORM_NONGCING static int predicate_implies_not(Scheme_Object *pred1, Scheme_Object *pred2);
176 static int single_valued_expression(Scheme_Object *expr, Optimize_Info *info, int fuel);
177 static int single_valued_noncm_expression(Scheme_Object *expr, Optimize_Info *info, int fuel);
178 static int noncm_expression(Scheme_Object *expr, Optimize_Info *info, int fuel);
179 static Scheme_Object *optimize_ignored(Scheme_Object *e, Optimize_Info *info,
180                                        int expected_vals, int maybe_omittable,
181                                        int fuel);
182 static Scheme_Object *equivalent_exprs(Scheme_Object *a, Scheme_Object *b,
183                                        Optimize_Info *a_info, Optimize_Info *b_info, int context);
184 static int movable_expression(Scheme_Object *expr, Optimize_Info *info,
185                               int cross_lambda, int cross_k, int cross_s,
186                               int check_space, int fuel);
187 Scheme_Object *optimize_apply_values(Scheme_Object *f, Scheme_Object *e,
188                                      Optimize_Info *info,
189                                      int e_single_result,
190                                      int context);
191 
192 #define SCHEME_LAMBDAP(vals_expr) (SAME_TYPE(SCHEME_TYPE(vals_expr), scheme_ir_lambda_type) \
193                                    || SAME_TYPE(SCHEME_TYPE(vals_expr), scheme_case_lambda_sequence_type))
194 
195 #define SCHEME_WILL_BE_LAMBDAP(v)     SAME_TYPE(SCHEME_TYPE(v), scheme_will_be_lambda_type)
196 #define SCHEME_WILL_BE_LAMBDA_SIZE(v) SCHEME_PINT_VAL(v)
197 #define SCHEME_WILL_BE_LAMBDA(v)      SCHEME_IPTR_VAL(v)
198 
199 static int lambda_body_size(Scheme_Object *o, int less_args);
200 
201 typedef struct Scheme_Once_Used {
202   Scheme_Object so;
203   Scheme_Object *expr;
204   Scheme_IR_Local *var;
205   int vclock; /* record clocks at binding site */
206   int aclock;
207   int kclock;
208   int sclock;
209   int spans_k; /* potentially captures a continuation */
210   int moved;
211 } Scheme_Once_Used;
212 
213 static Scheme_Once_Used *make_once_used(Scheme_Object *val, Scheme_IR_Local *var,
214                                         int vclock, int aclock, int kclock, int sclock, int spans_k);
215 
216 static ROSYM Scheme_Hash_Tree *empty_eq_hash_tree;
217 
218 #ifdef MZ_PRECISE_GC
219 static void register_traversers(void);
220 #endif
221 
scheme_init_optimize()222 void scheme_init_optimize()
223 {
224   REGISTER_SO(empty_eq_hash_tree);
225   empty_eq_hash_tree = scheme_make_hash_tree(SCHEME_hashtr_eq);
226 
227 #ifdef MZ_PRECISE_GC
228   register_traversers();
229 #endif
230 }
231 
232 /*========================================================================*/
233 /*                                logging                                 */
234 /*========================================================================*/
235 
note_match(int actual,int expected,Optimize_Info * warn_info)236 static void note_match(int actual, int expected, Optimize_Info *warn_info)
237 {
238   if (!warn_info || (expected == -1))
239     return;
240 
241   if (actual != expected) {
242     scheme_log(warn_info->logger,
243                SCHEME_LOG_WARNING,
244                0,
245                "warning%s: %d values produced when %d expected",
246                scheme_optimize_context_to_string(warn_info->context),
247                actual, expected);
248   }
249 }
250 
scheme_optimize_context_to_string(Scheme_Object * context)251 char *scheme_optimize_context_to_string(Scheme_Object *context)
252 /* Convert a context to a string that is suitable for use in logging */
253 {
254   if (context) {
255     Scheme_Object *linklet, *func;
256     const char *ctx, *prefix, *mctx, *mprefix;
257     char *all;
258     int clen, plen, mclen, mplen, len;
259 
260     if (SCHEME_PAIRP(context)) {
261       func = SCHEME_CAR(context);
262       linklet = SCHEME_CDR(context);
263     } else if (SAME_TYPE(SCHEME_TYPE(context), scheme_linklet_type)) {
264       func = scheme_false;
265       linklet = context;
266     } else {
267       func = context;
268       linklet = scheme_false;
269     }
270 
271     if (SAME_TYPE(SCHEME_TYPE(func), scheme_ir_lambda_type)) {
272       Scheme_Object *name;
273 
274       name = ((Scheme_Lambda *)func)->name;
275       if (name) {
276         if (SCHEME_VECTORP(name)) {
277           Scheme_Object *port;
278           int print_width = 1024;
279           intptr_t plen;
280 
281           port = scheme_make_byte_string_output_port();
282 
283           scheme_write_proc_context(port, print_width,
284                                     SCHEME_VEC_ELS(name)[0],
285                                     SCHEME_VEC_ELS(name)[1], SCHEME_VEC_ELS(name)[2],
286                                     SCHEME_VEC_ELS(name)[3], SCHEME_VEC_ELS(name)[4],
287                                     SCHEME_TRUEP(SCHEME_VEC_ELS(name)[6]));
288 
289           ctx = scheme_get_sized_byte_string_output(port, &plen);
290           prefix = " in: ";
291         } else {
292           ctx = scheme_get_proc_name(func, &len, 0);
293           prefix = " in: ";
294         }
295       } else {
296         ctx = "";
297         prefix = "";
298       }
299     } else {
300       ctx = "";
301       prefix = "";
302     }
303 
304     if (SAME_TYPE(SCHEME_TYPE(linklet), scheme_linklet_type)) {
305       mctx = scheme_display_to_string(((Scheme_Linklet *)linklet)->name, NULL);
306       mprefix = " in module: ";
307     } else {
308       mctx = "";
309       mprefix = "";
310     }
311 
312     clen = strlen(ctx);
313     plen = strlen(prefix);
314     mclen = strlen(mctx);
315     mplen = strlen(mprefix);
316 
317     if (!clen && !mclen)
318       return "";
319 
320     all = scheme_malloc_atomic(clen + plen + mclen + mplen + 1);
321     memcpy(all, prefix, plen);
322     memcpy(all + plen, ctx, clen);
323     memcpy(all + plen + clen, mprefix, mplen);
324     memcpy(all + plen + clen + mplen, mctx, mclen);
325     all[clen + plen + mclen + mplen] = 0;
326     return all;
327   } else
328     return "";
329 }
330 
scheme_optimize_info_context(Optimize_Info * info)331 char *scheme_optimize_info_context(Optimize_Info *info)
332 {
333   return scheme_optimize_context_to_string(info->context);
334 }
335 
scheme_optimize_info_logger(Optimize_Info * info)336 Scheme_Logger *scheme_optimize_info_logger(Optimize_Info *info)
337 {
338   return info->logger;
339 }
340 
341 /*========================================================================*/
342 /*                                  utils                                 */
343 /*========================================================================*/
344 
set_optimize_mode(Scheme_IR_Local * var)345 static void set_optimize_mode(Scheme_IR_Local *var)
346 {
347   MZ_ASSERT(SAME_TYPE(var->so.type, scheme_ir_local_type));
348   memset(&var->optimize, 0, sizeof(var->optimize));
349   var->mode = SCHEME_VAR_MODE_OPTIMIZE;
350 }
351 
352 #define SCHEME_PRIM_IS_UNSAFE_NONMUTATING (SCHEME_PRIM_IS_UNSAFE_FUNCTIONAL | SCHEME_PRIM_IS_UNSAFE_OMITABLE)
353 
scheme_is_functional_nonfailing_primitive(Scheme_Object * rator,int num_args,int expected_vals)354 int scheme_is_functional_nonfailing_primitive(Scheme_Object *rator, int num_args, int expected_vals)
355 /* A call to a functional, non-failing primitive (i.e., it accepts any argument)
356    can be discarded if its results are ignored.
357    Return 2 => true, and results are a constant when arguments are constants. */
358 {
359   if (SCHEME_PRIMP(rator)
360       && ((SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_IS_UNSAFE_NONMUTATING)
361           || scheme_is_omitable_primitive(rator, num_args))
362       && (num_args >= ((Scheme_Primitive_Proc *)rator)->mina)
363       && (num_args <= ((Scheme_Primitive_Proc *)rator)->mu.maxa)
364       && ((expected_vals < 0)
365           || ((expected_vals == 1) && !(SCHEME_PRIM_PROC_FLAGS(rator) & SCHEME_PRIM_IS_MULTI_RESULT))
366           || (SAME_OBJ(scheme_values_proc, rator)
367               && (expected_vals == num_args)))) {
368     if (SAME_OBJ(scheme_values_proc, rator))
369       return 2;
370     return 1;
371   } else
372     return 0;
373 }
374 
scheme_is_omitable_primitive(Scheme_Object * rator,int num_args)375 int scheme_is_omitable_primitive(Scheme_Object *rator, int num_args)
376 {
377   if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & (SCHEME_PRIM_IS_OMITABLE
378                                            | SCHEME_PRIM_IS_OMITABLE_ALLOCATION
379                                            | SCHEME_PRIM_IS_UNSAFE_OMITABLE))
380     return 1;
381 
382   if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_IS_ARITY_0_OMITABLE_ALLOCATION)
383     return (num_args == 0);
384 
385   if ((SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_IS_EVEN_ARITY_OMITABLE_ALLOCATION))
386     return !(num_args & 0x1);
387 
388   return 0;
389 }
390 
391 int scheme_is_functional_nonfailing_primitive(Scheme_Object *rator, int num_args, int expected_vals);
392 
get_defn_shape(Optimize_Info * info,Scheme_IR_Toplevel * var)393 static Scheme_Object *get_defn_shape(Optimize_Info *info, Scheme_IR_Toplevel *var)
394 {
395   Scheme_Object *v;
396 
397   if (info->top_level_consts && (var->instance_pos == -1)) {
398     v = scheme_hash_get(info->top_level_consts, scheme_make_integer(var->variable_pos));
399     if (v) return v;
400 
401     v = scheme_hash_get(info->top_level_consts, scheme_false);
402     if (v && scheme_hash_get((Scheme_Hash_Table *)v, scheme_make_integer(var->variable_pos)))
403       return scheme_fixed_key;
404   }
405 
406   return NULL;
407 }
408 
get_struct_proc_shape(Scheme_Object * rator,Optimize_Info * info,int prop_ok)409 static Scheme_Object *get_struct_proc_shape(Scheme_Object *rator, Optimize_Info *info, int prop_ok)
410 /* Determines whether `rator` is known to be a struct accessor, etc. */
411 {
412   Scheme_Object *c;
413 
414   if (info && SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_toplevel_type)) {
415     c = get_defn_shape(info, (Scheme_IR_Toplevel *)rator);
416     if (!c)
417       c = get_import_shape(info, (Scheme_IR_Toplevel *)rator);
418 
419     if (c && (SAME_TYPE(SCHEME_TYPE(c), scheme_struct_proc_shape_type)
420               || (prop_ok && SAME_TYPE(SCHEME_TYPE(c), scheme_struct_prop_proc_shape_type))))
421       return c;
422   }
423 
424   return NULL;
425 }
426 
scheme_is_struct_functional(Scheme_Object * rator,int num_args,Optimize_Info * info,int vals)427 int scheme_is_struct_functional(Scheme_Object *rator, int num_args, Optimize_Info *info, int vals)
428 /* Determines whether `rator` is a functional, non-failing struct operation */
429 {
430   Scheme_Object *c;
431 
432   if ((vals == 1) || (vals == -1)) {
433     c = get_struct_proc_shape(rator, info, 1);
434     if (c) {
435       if (SAME_TYPE(SCHEME_TYPE(c), scheme_struct_proc_shape_type)) {
436         int mode = (SCHEME_PROC_SHAPE_MODE(c) & STRUCT_PROC_SHAPE_MASK);
437         int field_count = (SCHEME_PROC_SHAPE_MODE(c) >> STRUCT_PROC_SHAPE_SHIFT);
438         if (((num_args == 1) && (mode == STRUCT_PROC_SHAPE_PRED))
439             || ((num_args == field_count)
440                 && (mode == STRUCT_PROC_SHAPE_CONSTR)
441                 && (SCHEME_PROC_SHAPE_MODE(c) & STRUCT_PROC_SHAPE_NONFAIL_CONSTR))) {
442           return 1;
443         }
444       } else if (SAME_TYPE(SCHEME_TYPE(c), scheme_struct_prop_proc_shape_type)) {
445         int mode = (SCHEME_PROP_PROC_SHAPE_MODE(c) & STRUCT_PROC_SHAPE_MASK);
446         if ((mode == STRUCT_PROP_PROC_SHAPE_PRED)
447             && (num_args == 1))
448           return 1;
449       }
450     }
451   }
452 
453   return 0;
454 }
455 
extract_specialized_proc(Scheme_Object * le,Scheme_Object * default_val)456 static Scheme_Object *extract_specialized_proc(Scheme_Object *le, Scheme_Object *default_val)
457 /* Look through `(procedure-specialize <e>)` to get `<e>` */
458 {
459   if (SAME_TYPE(SCHEME_TYPE(le), scheme_application2_type)) {
460     Scheme_App2_Rec *app = (Scheme_App2_Rec *)le;
461     if (SAME_OBJ(app->rator, scheme_procedure_specialize_proc)) {
462       if (SCHEME_PROCP(app->rand) || SCHEME_LAMBDAP(app->rand))
463         return app->rand;
464     }
465   }
466 
467   return default_val;
468 }
469 
scheme_omittable_expr(Scheme_Object * o,int vals,int fuel,int flags,Optimize_Info * opt_info,Optimize_Info * warn_info)470 int scheme_omittable_expr(Scheme_Object *o, int vals, int fuel, int flags,
471                           Optimize_Info *opt_info, Optimize_Info *warn_info)
472      /* Checks whether the bytecode `o` returns `vals` values with no
473         side-effects and without pushing and using continuation marks.
474         A -1 for `vals` means that any return count is ok.
475         Also used with fully resolved expression by `linklet` to check
476         for "functional" bodies, in which case `flags` includes
477         `OMITTABLE_RESOLVED`.
478         The `opt_info` argument is used only to access linklet-level
479         information, not local bindings.
480         If `warn_info` is supplied, complain when a mismatch is detected.
481         We rely on the letrec-check pass to avoid omitting early references
482         to letrec-bound variables, but `flags` can include `OMITTABLE_KEEP_VARS`
483         to keep all variable references.
484         If flags includes `OMITTABLE_KEEP_MUTABLE_VARS`, then references
485         to mutable variables are kept, which allows this function to be
486         a conservative approximation for "reorderable". */
487 {
488   Scheme_Type vtype;
489 
490   /* FIXME: can overflow the stack */
491 
492  try_again:
493 
494   vtype = SCHEME_TYPE(o);
495 
496   if ((vtype > _scheme_ir_values_types_)
497       || ((vtype == scheme_ir_local_type)
498           && !(flags & OMITTABLE_KEEP_VARS)
499           && (!(flags & OMITTABLE_KEEP_MUTABLE_VARS)
500               || !SCHEME_VAR(o)->mutated))
501       || ((vtype == scheme_local_type)
502           && !(flags & OMITTABLE_KEEP_VARS)
503           && !(SCHEME_GET_LOCAL_FLAGS(o) == SCHEME_LOCAL_CLEAR_ON_READ))
504       || ((vtype == scheme_local_unbox_type)
505           && !(flags & (OMITTABLE_KEEP_VARS | OMITTABLE_KEEP_MUTABLE_VARS))
506           && !(SCHEME_GET_LOCAL_FLAGS(o) == SCHEME_LOCAL_CLEAR_ON_READ))
507       || (vtype == scheme_lambda_type)
508       || (vtype == scheme_ir_lambda_type)
509       || (vtype == scheme_inline_variant_type)
510       || (vtype == scheme_case_lambda_sequence_type)
511       || (vtype == scheme_varref_form_type)) {
512     note_match(1, vals, warn_info);
513     return ((vals == 1) || (vals < 0));
514   }
515 
516   if ((vtype == scheme_toplevel_type) || (vtype == scheme_static_toplevel_type)) {
517     note_match(1, vals, warn_info);
518     if (!(flags & OMITTABLE_KEEP_VARS) && (flags & OMITTABLE_RESOLVED) && ((vals == 1) || (vals < 0))) {
519       int tl_flags = SCHEME_TOPLEVEL_FLAGS(o);
520       if (tl_flags & SCHEME_TOPLEVEL_FLAGS_MASK)
521         return 1;
522       else
523         return 0;
524     }
525   }
526 
527   if (vtype == scheme_ir_toplevel_type) {
528     note_match(1, vals, warn_info);
529     if ((vals == 1) || (vals < 0)) {
530       if (!(flags & OMITTABLE_KEEP_VARS)
531           && ((SCHEME_IR_TOPLEVEL_FLAGS((Scheme_IR_Toplevel *)o) & SCHEME_TOPLEVEL_FLAGS_MASK) >= SCHEME_TOPLEVEL_READY))
532         return 1;
533       else if ((SCHEME_IR_TOPLEVEL_FLAGS((Scheme_IR_Toplevel *)o) & SCHEME_TOPLEVEL_FLAGS_MASK) >= SCHEME_TOPLEVEL_CONST)
534         return 1;
535       else
536         return 0;
537     }
538   }
539 
540   /* check for struct-type declaration: */
541   if (!(flags & OMITTABLE_IGNORE_MAKE_STRUCT_TYPE)) {
542     Scheme_Object *auto_e;
543     int auto_e_depth;
544     auto_e = scheme_is_simple_make_struct_type(o, vals,
545                                                (((flags & OMITTABLE_RESOLVED) ? CHECK_STRUCT_TYPE_RESOLVED : 0)
546                                                 | CHECK_STRUCT_TYPE_ALWAYS_SUCCEED
547                                                 | CHECK_STRUCT_TYPE_DELAY_AUTO_CHECK),
548                                                &auto_e_depth,
549                                                NULL, NULL,
550                                                opt_info,
551                                                NULL, NULL, 0, NULL, NULL,
552                                                5);
553     if (auto_e) {
554       if (scheme_omittable_expr(auto_e, 1, fuel - 1, flags, opt_info, warn_info))
555         return 1;
556     }
557   }
558 
559   if (vtype == scheme_branch_type) {
560     Scheme_Branch_Rec *b;
561     b = (Scheme_Branch_Rec *)o;
562     return (scheme_omittable_expr(b->test, 1, fuel - 1, flags, opt_info, warn_info)
563 	    && scheme_omittable_expr(b->tbranch, vals, fuel - 1, flags, opt_info, warn_info)
564 	    && scheme_omittable_expr(b->fbranch, vals, fuel - 1, flags, opt_info, warn_info));
565   }
566 
567   if (vtype == scheme_let_one_type) {
568     Scheme_Let_One *lo = (Scheme_Let_One *)o;
569     return (scheme_omittable_expr(lo->value, 1, fuel - 1, flags, opt_info, warn_info)
570 	    && scheme_omittable_expr(lo->body, vals, fuel - 1, flags, opt_info, warn_info));
571   }
572 
573   if (vtype == scheme_let_void_type) {
574     Scheme_Let_Void *lv = (Scheme_Let_Void *)o;
575     /* recognize (letrec ([x <omittable>]) ...): */
576     MZ_ASSERT(flags & OMITTABLE_RESOLVED);
577     if (SAME_TYPE(SCHEME_TYPE(lv->body), scheme_let_value_type)) {
578       Scheme_Let_Value *lv2 = (Scheme_Let_Value *)lv->body;
579       if ((lv2->count == 1)
580           && (lv2->position == 0)
581           && scheme_omittable_expr(lv2->value, 1, fuel - 1, flags, opt_info, warn_info)) {
582         o = lv2->body;
583       } else
584         o = lv->body;
585     } else
586       o = lv->body;
587     goto try_again;
588   }
589 
590   if (vtype == scheme_ir_let_header_type) {
591     /* recognize another (let ([x <omittable>]) ...) pattern: */
592     Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)o;
593     int i;
594     MZ_ASSERT(!(flags & OMITTABLE_RESOLVED));
595     o = lh->body;
596     for (i = 0; i < lh->num_clauses; i++) {
597       Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)o;
598       if (!scheme_omittable_expr(lv->value, lv->count, fuel - 1, flags, opt_info, warn_info))
599         return 0;
600       o = lv->body;
601     }
602     goto try_again;
603   }
604 
605   if (vtype == scheme_letrec_type) {
606     MZ_ASSERT(flags & OMITTABLE_RESOLVED);
607     o = ((Scheme_Letrec *)o)->body;
608     goto try_again;
609   }
610 
611   if (vtype == scheme_application_type) {
612     Scheme_App_Rec *app = (Scheme_App_Rec *)o;
613 
614     if ((app->num_args >= 4) && (app->num_args <= 11)
615         && SAME_OBJ(scheme_make_struct_type_proc, app->args[0])) {
616       note_match(5, vals, warn_info);
617     }
618 
619     if (scheme_is_functional_nonfailing_primitive(app->args[0], app->num_args, vals)
620         || scheme_is_struct_functional(app->args[0], app->num_args, opt_info, vals)
621         || ((SCHEME_APPN_FLAGS(app) & APPN_FLAG_OMITTABLE) && !(flags & OMITTABLE_IGNORE_APPN_OMIT))) {
622       int i;
623       for (i = app->num_args; i--; ) {
624         if (!scheme_omittable_expr(app->args[i + 1], 1, fuel - 1, flags, opt_info, warn_info))
625           return 0;
626       }
627       return 1;
628     } else if (SCHEME_PRIMP(app->args[0])) {
629       if (!(SCHEME_PRIM_PROC_FLAGS(app->args[0]) & SCHEME_PRIM_IS_MULTI_RESULT)) {
630         note_match(1, vals, warn_info);
631       } else if (SAME_OBJ(scheme_values_proc, app->args[0])) {
632         note_match(app->num_args, vals, warn_info);
633       }
634     }
635 
636     if (!SAME_OBJ(scheme_make_struct_type_proc, app->args[0]))
637       return 0;
638   }
639 
640   if (vtype == scheme_application2_type) {
641     Scheme_App2_Rec *app = (Scheme_App2_Rec *)o;
642     if (scheme_is_functional_nonfailing_primitive(app->rator, 1, vals)
643         || scheme_is_struct_functional(app->rator, 1, opt_info, vals)
644         || ((SCHEME_APPN_FLAGS(app) & APPN_FLAG_OMITTABLE) && !(flags & OMITTABLE_IGNORE_APPN_OMIT))) {
645       if (scheme_omittable_expr(app->rand, 1, fuel - 1, flags, opt_info, warn_info))
646         return 1;
647     } else if (SAME_OBJ(app->rator, scheme_make_vector_proc)
648                && (vals == 1 || vals == -1)
649                && (SCHEME_INTP(app->rand)
650                    && (SCHEME_INT_VAL(app->rand) >= 0))
651                    && IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand))) {
652       return 1;
653     } else if (SAME_OBJ(app->rator, scheme_procedure_specialize_proc)) {
654       if ((vals == 1 || vals == -1) && extract_specialized_proc(o, NULL))
655         return 1;
656     } else if (SCHEME_PRIMP(app->rator)) {
657       if (!(SCHEME_PRIM_PROC_FLAGS(app->rator) & SCHEME_PRIM_IS_MULTI_RESULT)
658           || SAME_OBJ(scheme_values_proc, app->rator)) {
659         note_match(1, vals, warn_info);
660       }
661     }
662 
663     if (!SAME_OBJ(scheme_make_struct_type_property_proc, app->rator))
664       return 0;
665   }
666 
667   if (vtype == scheme_application3_type) {
668     Scheme_App3_Rec *app = (Scheme_App3_Rec *)o;
669     if (scheme_is_functional_nonfailing_primitive(app->rator, 2, vals)
670         || scheme_is_struct_functional(app->rator, 2, opt_info, vals)
671         || ((SCHEME_APPN_FLAGS(app) & APPN_FLAG_OMITTABLE) && !(flags & OMITTABLE_IGNORE_APPN_OMIT))) {
672       if (scheme_omittable_expr(app->rand1, 1, fuel - 1, flags, opt_info, warn_info)
673           && scheme_omittable_expr(app->rand2, 1, fuel - 1, flags, opt_info, warn_info))
674         return 1;
675     } else if (SAME_OBJ(app->rator, scheme_make_vector_proc)
676                && (vals == 1 || vals == -1)
677                && (SCHEME_INTP(app->rand1)
678                    && (SCHEME_INT_VAL(app->rand1) >= 0)
679                    && IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand1)))
680                && scheme_omittable_expr(app->rand2, 1, fuel - 1, flags, opt_info, warn_info)) {
681       return 1;
682     } else if (SCHEME_PRIMP(app->rator)) {
683       if (!(SCHEME_PRIM_PROC_FLAGS(app->rator) & SCHEME_PRIM_IS_MULTI_RESULT)) {
684         note_match(1, vals, warn_info);
685       } else if (SAME_OBJ(scheme_values_proc, app->rator)) {
686         note_match(2, vals, warn_info);
687       }
688     }
689 
690     if (!SAME_OBJ(scheme_make_struct_type_property_proc, app->rator))
691       return 0;
692   }
693 
694   /* check for (set! x x) */
695   if (vtype == scheme_set_bang_type) {
696     Scheme_Set_Bang *sb = (Scheme_Set_Bang *)o;
697     if (SAME_TYPE(scheme_local_type, SCHEME_TYPE(sb->var))
698         && SAME_TYPE(scheme_local_type, SCHEME_TYPE(sb->val))
699         && (SCHEME_LOCAL_POS(sb->var) == SCHEME_LOCAL_POS(sb->val)))
700       return 1;
701     else if (SAME_TYPE(scheme_ir_local_type, SCHEME_TYPE(sb->var))
702              && SAME_OBJ(sb->var, sb->val)
703              && ((((Scheme_IR_Local *)sb->var)->mode != SCHEME_VAR_MODE_COMPILE)
704                  || !((Scheme_IR_Local *)sb->var)->compile.keep_assignment))
705       return 1;
706   }
707 
708   /* check for struct-type property declaration: */
709   if (!(flags & OMITTABLE_IGNORE_MAKE_STRUCT_TYPE)) {
710     if (scheme_is_simple_make_struct_type_property(o, vals,
711                                                    (((flags & OMITTABLE_RESOLVED) ? CHECK_STRUCT_TYPE_RESOLVED : 0)
712                                                     | CHECK_STRUCT_TYPE_ALWAYS_SUCCEED),
713                                                    NULL,
714                                                    opt_info,
715                                                    NULL, NULL, 0, NULL,
716                                                    5))
717       return 1;
718   }
719 
720   return 0;
721 }
722 
ensure_single_value(Scheme_Object * e,Optimize_Info * info)723 static Scheme_Object *ensure_single_value(Scheme_Object *e, Optimize_Info *info)
724 /* Wrap `e` so that it either produces a single value or fails */
725 {
726   Scheme_App2_Rec *app2;
727   if (single_valued_expression(e, info, 5))
728     return e;
729 
730   app2 = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
731   app2->iso.so.type = scheme_application2_type;
732   app2->rator = scheme_values_proc;
733   app2->rand = e;
734   SCHEME_APPN_FLAGS(app2) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
735 
736   return (Scheme_Object *)app2;
737 }
738 
ensure_single_value_noncm(Scheme_Object * e,Optimize_Info * info)739 static Scheme_Object *ensure_single_value_noncm(Scheme_Object *e, Optimize_Info *info)
740 /* Wrap `e` so that it either produces a single value or fails.
741    Also, wrap `e` in case it may have a `with-continuation-mark`
742    in tail position. */
743 {
744   Scheme_App2_Rec *app2;
745   if (single_valued_noncm_expression(e, info, 5))
746     return e;
747 
748   app2 = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
749   app2->iso.so.type = scheme_application2_type;
750   app2->rator = scheme_values_proc;
751   app2->rand = e;
752   SCHEME_APPN_FLAGS(app2) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
753 
754   return (Scheme_Object *)app2;
755 }
756 
ensure_noncm(Scheme_Object * e,Optimize_Info * info)757 static Scheme_Object *ensure_noncm(Scheme_Object *e, Optimize_Info *info)
758 /* Wrap `e` in case it may have a `with-continuation-mark` form in tail
759    position. This is useful when `e` escapes, and it is lifted and the
760    surrounding is discarded, in which case the shift out of a nested
761    position is observable. */
762 {
763   Scheme_Sequence *seq;
764 
765   if (noncm_expression(e, info, 5))
766     return e;
767 
768   seq = scheme_malloc_sequence(1);
769   seq->so.type = scheme_begin0_sequence_type;
770   seq->count = 1;
771   seq->array[0] = e;
772 
773   return (Scheme_Object *)seq;
774 }
775 
do_make_discarding_sequence(Scheme_Object * e1,Scheme_Object * e2,Optimize_Info * info,int ignored,int rev)776 static Scheme_Object *do_make_discarding_sequence(Scheme_Object *e1, Scheme_Object *e2,
777                                                   Optimize_Info *info,
778                                                   int ignored, int rev)
779 /* Evaluate `e1` then `e2` (or opposite order if rev), and each must
780    produce a single value. The result of `e1` is ignored and the
781    result is `e2` --- except that `e2` is ignored, too, if
782    `ignored`. */
783 {
784   if (ignored)
785     e2 = optimize_ignored(e2, info, 1, 0, 5);
786 
787   e2 = ensure_single_value_noncm(e2, info);
788 
789   if (scheme_omittable_expr(e1, 1, 5, 0, info, NULL))
790     return e2;
791 
792   e1 = ensure_single_value(optimize_ignored(e1, info, 1, 0, 5), info);
793 
794   if (ignored && scheme_omittable_expr(e2, 1, 5, 0, info, NULL))
795     return ensure_single_value_noncm(e1, info);
796 
797   /* use `begin` instead of `begin0` if we can swap the order: */
798   if (rev && movable_expression(e2, info, 0, 1, 1, 0, 50))
799     rev = 0;
800 
801   if (!rev && SAME_TYPE(SCHEME_TYPE(e1), scheme_sequence_type)) {
802     Scheme_Sequence *seq = (Scheme_Sequence *)e1;
803 
804     if (SCHEME_TYPE(seq->array[seq->count - 1]) > _scheme_ir_values_types_) {
805       seq->array[seq->count - 1] = e2;
806       return e1;
807     }
808   }
809 
810   return scheme_make_sequence_compilation(scheme_make_pair((rev ? e2 : e1),
811                                                            scheme_make_pair((rev ? e1 : e2), scheme_null)),
812                                           rev ? -1 : 1,
813                                           0);
814 }
815 
make_discarding_sequence(Scheme_Object * e1,Scheme_Object * e2,Optimize_Info * info)816 static Scheme_Object *make_discarding_sequence(Scheme_Object *e1, Scheme_Object *e2,
817                                                Optimize_Info *info)
818 {
819   return do_make_discarding_sequence(e1, e2, info, 0, 0);
820 }
821 
make_discarding_reverse_sequence(Scheme_Object * e1,Scheme_Object * e2,Optimize_Info * info)822 static Scheme_Object *make_discarding_reverse_sequence(Scheme_Object *e1, Scheme_Object *e2,
823                                                        Optimize_Info *info)
824 {
825   return do_make_discarding_sequence(e1, e2, info, 0, 1);
826 }
827 
make_discarding_sequence_3(Scheme_Object * e1,Scheme_Object * e2,Scheme_Object * e3,Optimize_Info * info)828 static Scheme_Object *make_discarding_sequence_3(Scheme_Object *e1, Scheme_Object *e2, Scheme_Object *e3,
829                                                  Optimize_Info *info)
830 {
831   return make_discarding_sequence(e1, make_discarding_sequence(e2, e3, info), info);
832 }
833 
make_discarding_app_sequence(Scheme_App_Rec * appr,int result_pos,Scheme_Object * result,Optimize_Info * info)834 static Scheme_Object *make_discarding_app_sequence(Scheme_App_Rec *appr, int result_pos, Scheme_Object *result,
835                                                    Optimize_Info *info)
836 /* Generalize do_make_discarding_sequence() to a sequence of argument
837    expressions, where `result_pos` is the position of the returned
838    argument. If `result_pos` is -1, then all argument results will be
839    ignored. If `result`, then it is used as the result after all
840    arguments are evaluated.*/
841 {
842   int i;
843   Scheme_Object *l = scheme_null;
844 
845   result_pos = result_pos + 1;
846   if (result)
847     l = scheme_make_pair(result, l);
848 
849   for (i = appr->num_args; i; i--) {
850     Scheme_Object *e;
851     e = appr->args[i];
852     e = ensure_single_value(e, info);
853     if (i == result_pos) {
854       if (SCHEME_NULLP(l)) {
855         e = ensure_single_value_noncm(e, info);
856         l = scheme_make_pair(e, scheme_null);
857       } else {
858         l = scheme_make_sequence_compilation(scheme_make_pair(e, l), -1, 0);
859         l = scheme_make_pair(l, scheme_null);
860       }
861     } else {
862       e = optimize_ignored(e, info, 1, 1, 5);
863       if (e)
864         l = scheme_make_pair(e, l);
865     }
866   }
867 
868   if (SCHEME_NULLP(l))
869     return scheme_void;
870 
871   if (SCHEME_NULLP(SCHEME_CDR(l)))
872     return SCHEME_CAR(l);
873 
874   return scheme_make_sequence_compilation(l, 1, 0);
875 }
876 
optimize_ignored(Scheme_Object * e,Optimize_Info * info,int expected_vals,int maybe_omittable,int fuel)877 static Scheme_Object *optimize_ignored(Scheme_Object *e, Optimize_Info *info,
878                                        int expected_vals, int maybe_omittable,
879                                        int fuel)
880 /* Simplify an expression whose result will be ignored.  The
881    `expected_vals` is 1 or -1. If `maybe_omittable`, the result can be
882    NULL to indicate that it can be omitted. */
883 {
884   if (scheme_omittable_expr(e, expected_vals, 5, 0, info, NULL))
885     return maybe_omittable? NULL : scheme_false;
886 
887   if (fuel) {
888     /* We could do a lot more here, but for now, we just avoid purely
889        functional, always successful operations --- especially allocating ones. */
890     switch (SCHEME_TYPE(e)) {
891     case scheme_application2_type:
892       {
893         Scheme_App2_Rec *app = (Scheme_App2_Rec *)e;
894 
895         if (!SAME_OBJ(app->rator, scheme_values_proc)) /* `values` is probably here to ensure a single result */
896           if (scheme_is_functional_nonfailing_primitive(app->rator, 1, expected_vals))
897             return do_make_discarding_sequence(app->rand, scheme_void, info, 1, 0);
898 
899         /* (make-vector <num>) => <void> */
900         if (SAME_OBJ(app->rator, scheme_make_vector_proc)
901             && (SCHEME_INTP(app->rand)
902                 && (SCHEME_INT_VAL(app->rand) >= 0))
903                 && IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand)))
904           return (maybe_omittable ? NULL : scheme_void);
905       }
906       break;
907     case scheme_application3_type:
908       {
909         Scheme_App3_Rec *app = (Scheme_App3_Rec *)e;
910 
911         if (scheme_is_functional_nonfailing_primitive(app->rator, 2, expected_vals))
912           return do_make_discarding_sequence(app->rand1,
913                                              do_make_discarding_sequence(app->rand2,
914                                                                          scheme_void,
915                                                                          info,
916                                                                          1, 0),
917                                              info,
918                                              1, 0);
919 
920         /* (make-vector <num> <expr>) => <expr> */
921         if (SAME_OBJ(app->rator, scheme_make_vector_proc)
922             && (SCHEME_INTP(app->rand1)
923                 && (SCHEME_INT_VAL(app->rand1) >= 0))
924                 && IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand1))) {
925           Scheme_Object *val;
926           val = ensure_single_value_noncm(app->rand2, info);
927           return optimize_ignored(val, info, 1, maybe_omittable, 5);
928         }
929       }
930       break;
931     case scheme_application_type:
932       {
933         Scheme_App_Rec *app = (Scheme_App_Rec *)e;
934 
935         if (scheme_is_functional_nonfailing_primitive(app->args[0], app->num_args, expected_vals))
936           return make_discarding_app_sequence(app, -1, NULL, info);
937       }
938       break;
939     case scheme_branch_type:
940       {
941         Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)e;
942         Scheme_Object *tb, *fb;
943 
944         tb = optimize_ignored(b->tbranch, info, expected_vals, 1, fuel - 1);
945         fb = optimize_ignored(b->fbranch, info, expected_vals, 1, fuel - 1);
946 
947         if (tb || fb) {
948           b->tbranch = tb ? tb : scheme_false;
949           b->fbranch = fb ? fb : scheme_false;
950           return (Scheme_Object*)b;
951         } else {
952           Scheme_Object *val;
953           val = ensure_single_value_noncm(b->test, info);
954           return optimize_ignored(val, info, 1, maybe_omittable, 5);
955         }
956       }
957       break;
958     case scheme_sequence_type:
959       {
960         Scheme_Sequence *seq = (Scheme_Sequence *)e;
961         Scheme_Object *last;
962 
963         last = optimize_ignored(seq->array[seq->count - 1], info, expected_vals, 1, fuel - 1);
964 
965         if (last) {
966           seq->array[seq->count - 1] = last;
967           return (Scheme_Object*)seq;
968         } else if (seq->count == 2
969                    && (expected_vals == -1
970                        || single_valued_noncm_expression(seq->array[0], info, 5))) {
971           return seq->array[0];
972         } else {
973           seq->array[seq->count - 1] = scheme_false;
974           return (Scheme_Object*)seq;
975         }
976       }
977     case scheme_begin0_sequence_type:
978       {
979         Scheme_Sequence *seq = (Scheme_Sequence *)e;
980         Scheme_Object *first;
981 
982         first = optimize_ignored(seq->array[0], info, expected_vals, 1, fuel - 1);
983 
984         if (first) {
985           seq->array[0] = first;
986           return (Scheme_Object*)seq;
987         } else if (seq->count == 2
988                    && (expected_vals == -1
989                        || single_valued_noncm_expression(seq->array[1], info, 5))) {
990           return seq->array[1];
991         } else {
992           seq->array[0] = scheme_false;
993           return (Scheme_Object*)seq;
994         }
995       }
996       break;
997     case scheme_ir_let_header_type:
998       {
999         Scheme_IR_Let_Header *head = (Scheme_IR_Let_Header *)e;
1000         Scheme_IR_Let_Value *lv;
1001         Scheme_Object *body;
1002         int i;
1003 
1004         body = head->body;
1005         if (0 == head->num_clauses)
1006           lv = (Scheme_IR_Let_Value *)body;
1007         for (i = head->num_clauses; i--; ) {
1008           lv = (Scheme_IR_Let_Value *)body;
1009           body = lv->body;
1010         }
1011         body = optimize_ignored(body, info, expected_vals, 0, fuel - 1);
1012         lv->body = body;
1013         return (Scheme_Object*)head;
1014       }
1015       break;
1016     }
1017   }
1018 
1019   return e;
1020 }
1021 
make_sequence_2(Scheme_Object * a,Scheme_Object * b)1022 static Scheme_Object *make_sequence_2(Scheme_Object *a, Scheme_Object *b)
1023 {
1024   return scheme_make_sequence_compilation(scheme_make_pair(a, scheme_make_pair(b, scheme_null)), 1, 0);
1025 }
1026 
make_discarding_first_sequence(Scheme_Object * e1,Scheme_Object * e2,Optimize_Info * info)1027 static Scheme_Object *make_discarding_first_sequence(Scheme_Object *e1, Scheme_Object *e2,
1028                                                      Optimize_Info *info)
1029 /* Like make_discarding_sequence(), but second expression is not constrained to
1030    a single result. */
1031 {
1032   e1 = optimize_ignored(e1, info, 1, 1, 5);
1033   if (!e1)
1034     return e2;
1035   e1 = ensure_single_value(e1, info);
1036   return make_sequence_2(e1, e2);
1037 }
1038 
make_application_2(Scheme_Object * a,Scheme_Object * b,Optimize_Info * info)1039 static Scheme_Object *make_application_2(Scheme_Object *a, Scheme_Object *b, Optimize_Info *info)
1040 {
1041   return scheme_make_application(scheme_make_pair(a, scheme_make_pair(b, scheme_null)), info);
1042 }
1043 
make_application_3(Scheme_Object * a,Scheme_Object * b,Scheme_Object * c,Optimize_Info * info)1044 static Scheme_Object *make_application_3(Scheme_Object *a, Scheme_Object *b, Scheme_Object *c,
1045                                          Optimize_Info *info)
1046 {
1047   return scheme_make_application(scheme_make_pair(a, scheme_make_pair(b, scheme_make_pair(c, scheme_null))),
1048                                  info);
1049 }
1050 
replace_tail_inside(Scheme_Object * alt,Scheme_Object * inside,Scheme_Object * orig)1051 static Scheme_Object *replace_tail_inside(Scheme_Object *alt, Scheme_Object *inside, Scheme_Object *orig)
1052 /* Installs a new expression in the result position of various forms, such as `begin`;
1053    extract_tail_inside() needs to be consistent with this function */
1054 {
1055   if (inside) {
1056     switch (SCHEME_TYPE(inside)) {
1057     case scheme_sequence_type:
1058       if (((Scheme_Sequence *)inside)->count)
1059         ((Scheme_Sequence *)inside)->array[((Scheme_Sequence *)inside)->count-1] = alt;
1060       else
1061         scheme_signal_error("internal error: strange inside replacement");
1062       break;
1063     case scheme_ir_let_header_type:
1064       ((Scheme_IR_Let_Header *)inside)->body = alt;
1065       break;
1066     case scheme_ir_let_value_type:
1067       ((Scheme_IR_Let_Value *)inside)->body = alt;
1068       break;
1069     case scheme_with_cont_mark_type:
1070       ((Scheme_With_Continuation_Mark *)inside)->body = alt;
1071       break;
1072     default:
1073       scheme_signal_error("internal error: strange inside replacement");
1074     }
1075     return orig;
1076   }
1077   return alt;
1078 }
1079 
extract_tail_inside(Scheme_Object ** _t2,Scheme_Object ** _inside,int for_immediate_body)1080 static void extract_tail_inside(Scheme_Object **_t2, Scheme_Object **_inside, int for_immediate_body)
1081 /* Looks through various forms, like `begin` to extract a result expression;
1082    replace_tail_inside() needs to be consistent with this function */
1083 {
1084   while (1) {
1085     if (SAME_TYPE(SCHEME_TYPE(*_t2), scheme_ir_let_header_type)) {
1086       Scheme_IR_Let_Header *head = (Scheme_IR_Let_Header *)*_t2;
1087       int i;
1088       *_inside = *_t2;
1089       *_t2 = head->body;
1090       for (i = head->num_clauses; i--; ) {
1091         *_inside = *_t2;
1092         *_t2 = ((Scheme_IR_Let_Value *)*_t2)->body;
1093       }
1094     } else if (SAME_TYPE(SCHEME_TYPE(*_t2), scheme_sequence_type)) {
1095       Scheme_Sequence *seq = (Scheme_Sequence *)*_t2;
1096       if (seq->count) {
1097         *_inside = *_t2;
1098         *_t2 = seq->array[seq->count-1];
1099       } else
1100         break;
1101     } else if (SAME_TYPE(SCHEME_TYPE(*_t2), scheme_with_cont_mark_type)
1102                && for_immediate_body) {
1103       Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)*_t2;
1104       *_inside = *_t2;
1105       *_t2 = wcm->body;
1106     } else
1107       break;
1108   }
1109 }
1110 
scheme_optimize_extract_tail_inside(Scheme_Object * t2)1111 Scheme_Object *scheme_optimize_extract_tail_inside(Scheme_Object *t2)
1112 {
1113   Scheme_Object *inside;
1114   extract_tail_inside(&t2, &inside, 0);
1115   return t2;
1116 }
1117 
1118 /*========================================================================*/
1119 /*        detecting `make-struct-type` calls and struct shapes            */
1120 /*========================================================================*/
1121 
is_inspector_call(Scheme_Object * a)1122 static int is_inspector_call(Scheme_Object *a)
1123 /* Does `a` produce an inspector? */
1124 {
1125   if (SAME_TYPE(SCHEME_TYPE(a), scheme_application_type)) {
1126     Scheme_App_Rec *app = (Scheme_App_Rec *)a;
1127     if (!app->num_args
1128         && (SAME_OBJ(app->args[0], scheme_current_inspector_proc)
1129             || SAME_OBJ(app->args[0], scheme_make_inspector_proc)))
1130       return 1;
1131   }
1132   return 0;
1133 }
1134 
is_proc_spec_proc(Scheme_Object * p,int init_field_count)1135 static int is_proc_spec_proc(Scheme_Object *p, int init_field_count)
1136 /* Does `p` produce a good `prop:procedure` value? */
1137 {
1138   Scheme_Type vtype;
1139 
1140   if (SCHEME_INTP(p)
1141       && (SCHEME_INT_VAL(p) >= 0)
1142       && (SCHEME_INT_VAL(p) < init_field_count))
1143     return 1;
1144 
1145   if (SCHEME_PROCP(p)) {
1146     p = scheme_get_or_check_arity(p, -1);
1147     if (SCHEME_INTP(p)) {
1148       return (SCHEME_INT_VAL(p) >= 1);
1149     } else if (SCHEME_STRUCTP(p)
1150                && scheme_is_struct_instance(scheme_arity_at_least, p)) {
1151       p = ((Scheme_Structure *)p)->slots[0];
1152       if (SCHEME_INTP(p))
1153         return (SCHEME_INT_VAL(p) >= 1);
1154     }
1155     return 0;
1156   }
1157 
1158   vtype = SCHEME_TYPE(p);
1159 
1160   if ((vtype == scheme_lambda_type) || (vtype == scheme_ir_lambda_type)) {
1161     if (((Scheme_Lambda *)p)->num_params >= 1)
1162       return 1;
1163   }
1164 
1165   return 0;
1166 }
1167 
is_local_ref(Scheme_Object * e,int p,int r,Scheme_IR_Local ** vars)1168 static int is_local_ref(Scheme_Object *e, int p, int r, Scheme_IR_Local **vars)
1169 /* Does `e` refer to...
1170     In resolved mode: variables at offset `p` though `p+r`?
1171     In optimizer IR mode: variables in `vars`? */
1172 {
1173   if (!vars && SAME_TYPE(SCHEME_TYPE(e), scheme_local_type)) {
1174     if ((SCHEME_LOCAL_POS(e) >= p)
1175         && (SCHEME_LOCAL_POS(e) < (p + r)))
1176       return 1;
1177   } else if (vars && SAME_TYPE(SCHEME_TYPE(e), scheme_ir_local_type)) {
1178     int i;
1179     for (i = p; i < p + r; i++) {
1180       if (SAME_OBJ(e, (Scheme_Object *)vars[i]))
1181         return 1;
1182     }
1183   }
1184 
1185   return 0;
1186 }
1187 
is_int_list(Scheme_Object * o,int up_to)1188 static int is_int_list(Scheme_Object *o, int up_to)
1189 /* Is `o` a list of distinct integers that are less than `up_to`? */
1190 {
1191   if (SCHEME_PAIRP(o)) {
1192     char *s, quick[8];
1193     Scheme_Object *e;
1194     if (up_to <= 8)
1195       s = quick;
1196     else
1197       s = (char *)scheme_malloc_atomic(up_to);
1198     memset(s, 0, up_to);
1199     while (SCHEME_PAIRP(o)) {
1200       e = SCHEME_CAR(o);
1201       o = SCHEME_CDR(o);
1202       if (!SCHEME_INTP(e)
1203           || (SCHEME_INT_VAL(e) < 0)
1204           || (SCHEME_INT_VAL(e) > up_to)
1205           || s[SCHEME_INT_VAL(e)])
1206         return 0;
1207       s[SCHEME_INT_VAL(e)] = 1;
1208     }
1209   }
1210 
1211   return SCHEME_NULLP(o);
1212 }
1213 
ok_proc_creator_args(Scheme_Object * rator,Scheme_Object * rand1,Scheme_Object * rand2,Scheme_Object * rand3,int delta2,int field_count,Scheme_IR_Local ** vars)1214 static int ok_proc_creator_args(Scheme_Object *rator, Scheme_Object *rand1, Scheme_Object *rand2, Scheme_Object *rand3,
1215                                 int delta2, int field_count, Scheme_IR_Local **vars)
1216 /* Does `rator` plus `rand1` and `rand2` create a struct accessor or mutator? */
1217 {
1218   if ((SAME_OBJ(rator, scheme_make_struct_field_accessor_proc)
1219        && is_local_ref(rand1, delta2+3, 1, vars))
1220       || (SAME_OBJ(rator, scheme_make_struct_field_mutator_proc)
1221           && is_local_ref(rand1, delta2+4, 1, vars))) {
1222     if (SCHEME_INTP(rand2)
1223         && (SCHEME_INT_VAL(rand2) >= 0)
1224         && (SCHEME_INT_VAL(rand2) < field_count)
1225         && (!rand3 || SCHEME_SYMBOLP(rand3))) {
1226       return 1;
1227     }
1228   }
1229 
1230   return 0;
1231 }
1232 
is_values_with_accessors_and_mutators(Scheme_Object * e,int vals,int resolved,Simple_Struct_Type_Info * _stinfo,Scheme_IR_Local ** vars)1233 static int is_values_with_accessors_and_mutators(Scheme_Object *e, int vals, int resolved,
1234                                                  Simple_Struct_Type_Info *_stinfo,
1235                                                  Scheme_IR_Local **vars)
1236 /* Does `e` produce values for a structure type, mutators, and accessors in the
1237    usual order? */
1238 {
1239   if (SAME_TYPE(SCHEME_TYPE(e), scheme_application_type)) {
1240     Scheme_App_Rec *app = (Scheme_App_Rec *)e;
1241     int delta = (resolved ? app->num_args : 0);
1242     if (SAME_OBJ(app->args[0], scheme_values_proc)
1243         && (app->num_args == vals)
1244         && (app->num_args >= 3)
1245         && is_local_ref(app->args[1], delta, 1, vars)
1246         && is_local_ref(app->args[2], delta+1, 1, vars)
1247         && is_local_ref(app->args[3], delta+2, 1, vars)) {
1248       int i, num_gets = 0, num_sets = 0, normal_ops = 1;
1249       int setter_fields = 0, normal_sets = 1;
1250       int prev_setter_pos = app->num_args; /* bigger than any setter index can be */
1251       for (i = app->num_args; i > 3; i--) {
1252         if (is_local_ref(app->args[i], delta, 5, vars)) {
1253           normal_ops = 0;
1254         } else if (SAME_TYPE(SCHEME_TYPE(app->args[i]), scheme_application_type)
1255                    && _stinfo->normal_ops && !_stinfo->indexed_ops) {
1256           Scheme_App_Rec *app3 = (Scheme_App_Rec *)app->args[i];
1257           int delta2 = delta + (resolved ? app3->num_args : 0);
1258           if (app3->num_args == 3) {
1259             if (!ok_proc_creator_args(app3->args[0], app3->args[1], app3->args[2], app3->args[3],
1260                                       delta2, _stinfo->field_count, vars))
1261               break;
1262             if (SAME_OBJ(app3->args[0], scheme_make_struct_field_mutator_proc)) {
1263               int pos = SCHEME_INT_VAL(app3->args[2]);
1264               if (num_gets) {
1265                 /* Since we're walking backwards, it's not normal to hit a mutator
1266                    after (i.e., before in argument order) a selector */
1267                 normal_ops = 0;
1268               }
1269               if (normal_sets) {
1270                 if (pos >= prev_setter_pos) {
1271                   /* setters are not in the usual order; zero out the mask */
1272                   normal_sets = 0;
1273                   setter_fields = 0;
1274                 } else if (pos < (31 - STRUCT_PROC_SHAPE_SHIFT)) {
1275                   setter_fields |= (1 << pos);
1276                   prev_setter_pos = pos;
1277                 }
1278               }
1279               num_sets++;
1280             } else {
1281               if (SCHEME_INT_VAL(app3->args[2]) != (i - 4)) {
1282                 /* selectors are not in the usual order */
1283                 normal_ops = 0;
1284               }
1285               num_gets++;
1286             }
1287           } else
1288             break;
1289         } else if (SAME_TYPE(SCHEME_TYPE(app->args[i]), scheme_application3_type)
1290                    && _stinfo->normal_ops && !_stinfo->indexed_ops) {
1291           Scheme_App3_Rec *app3 = (Scheme_App3_Rec *)app->args[i];
1292           int delta2 = delta + (resolved ? 2 : 0);
1293           if (!ok_proc_creator_args(app3->rator, app3->rand1, app3->rand2, NULL,
1294                                     delta2, _stinfo->field_count, vars))
1295             break;
1296           if (SAME_OBJ(app3->rator, scheme_make_struct_field_mutator_proc)) {
1297             if (num_gets) normal_ops = 0;
1298             num_sets++;
1299           } else {
1300             if (SCHEME_INT_VAL(app3->rand2) != (i - 4)) normal_ops = 0;
1301             num_gets++;
1302           }
1303         } else
1304           break;
1305       }
1306       if (i <= 3) {
1307         _stinfo->normal_ops = normal_ops;
1308         _stinfo->indexed_ops = 1;
1309         _stinfo->num_gets = num_gets;
1310         _stinfo->num_sets = num_sets;
1311         _stinfo->setter_fields = setter_fields;
1312         return 1;
1313       }
1314     }
1315   }
1316 
1317   return 0;
1318 }
1319 
skip_clears(Scheme_Object * body)1320 static Scheme_Object *skip_clears(Scheme_Object *body)
1321 /* If `body` is a `begin` form that exists only to clear variables
1322    as installed by the SFS pass, then extract the result form. */
1323 {
1324   if (SAME_TYPE(SCHEME_TYPE(body), scheme_sequence_type)) {
1325     Scheme_Sequence *seq = (Scheme_Sequence *)body;
1326     int i;
1327     for (i = seq->count - 1; i--; ) {
1328       if (!SAME_TYPE(SCHEME_TYPE(seq->array[i]), scheme_local_type))
1329         break;
1330     }
1331     if (i < 0)
1332       return seq->array[seq->count-1];
1333   }
1334   return body;
1335 }
1336 
1337 typedef int (*Ok_Value_Callback)(void *data, Scheme_Object *v, int mode);
1338 #define OK_CONSTANT_SHAPE          1
1339 #define OK_CONSTANT_ENCODED_SHAPE  2
1340 #define OK_CONSTANT_VALIDATE_SHAPE 3
1341 #define OK_CONSTANT_VARIANT        4
1342 #define OK_CONSTANT_VALUE          5
1343 
is_ok_value(Ok_Value_Callback ok_value,void * data,Scheme_Object * arg,Optimize_Info * info,Scheme_Hash_Table * top_level_table,Scheme_Object ** runstack,int rs_delta,Scheme_Linklet * enclosing_linklet)1344 static int is_ok_value(Ok_Value_Callback ok_value, void *data,
1345                        Scheme_Object *arg,
1346                        Optimize_Info *info,
1347                        Scheme_Hash_Table *top_level_table,
1348                        Scheme_Object **runstack, int rs_delta,
1349                        Scheme_Linklet *enclosing_linklet)
1350 /* Does `arg` produce a value that satisfies `ok_value`? */
1351 {
1352   int pos;
1353   Scheme_Object *v;
1354 
1355   if (SAME_TYPE(SCHEME_TYPE(arg), scheme_ir_toplevel_type)) {
1356     if (info) {
1357       /* This is optimize mode */
1358       v = get_defn_shape(info, (Scheme_IR_Toplevel *)arg);
1359       if (!v)
1360         v = get_import_shape(info, (Scheme_IR_Toplevel *)arg);
1361       if (v)
1362         return ok_value(data, v, OK_CONSTANT_SHAPE);
1363     }
1364   } else if (SAME_TYPE(SCHEME_TYPE(arg), scheme_toplevel_type)
1365              || SAME_TYPE(SCHEME_TYPE(arg), scheme_static_toplevel_type)) {
1366     pos = SCHEME_TOPLEVEL_POS(arg);
1367     if (runstack) {
1368       /* This is eval mode; conceptually, this code belongs in
1369          define_execute_with_dynamic_state() */
1370       Scheme_Bucket *b;
1371       Scheme_Prefix *toplevels;
1372       if (SAME_TYPE(SCHEME_TYPE(arg), scheme_toplevel_type))
1373         toplevels = (Scheme_Prefix *)runstack[SCHEME_TOPLEVEL_DEPTH(arg) - rs_delta];
1374       else
1375         toplevels = SCHEME_STATIC_TOPLEVEL_PREFIX(arg);
1376       b = (Scheme_Bucket *)toplevels->a[pos];
1377       if (b->val && (((Scheme_Bucket_With_Flags *)b)->flags & GLOB_IS_CONSISTENT))
1378         return ok_value(data, b->val, OK_CONSTANT_VALUE);
1379     } else if (enclosing_linklet) {
1380       /* This is linklet-export mode; conceptually, this code belongs in
1381          linklet_setup_constants() */
1382       if (pos > enclosing_linklet->num_total_imports) {
1383         Scheme_Object *name;
1384         pos -= (enclosing_linklet->num_total_imports + 1);
1385         name = SCHEME_VEC_ELS(enclosing_linklet->defns)[pos];
1386         v = scheme_hash_get(enclosing_linklet->constants, name);
1387         if (v)
1388           return ok_value(data, v, OK_CONSTANT_VARIANT);
1389       } else if (pos >= 1
1390                  && (pos <= enclosing_linklet->num_total_imports)
1391                  && enclosing_linklet->import_shapes) {
1392         pos -= 1;
1393         return ok_value(data, SCHEME_VEC_ELS(enclosing_linklet->import_shapes)[pos], OK_CONSTANT_ENCODED_SHAPE);
1394       }
1395     } else if (top_level_table) {
1396       /* This is validate mode; conceptually, this code belongs in
1397          define_values_validate() */
1398       v = scheme_hash_get(top_level_table, scheme_make_integer(pos));
1399       if (v) {
1400         return ok_value(data, v, OK_CONSTANT_VALIDATE_SHAPE);
1401       }
1402     }
1403   } else if (SCHEME_TYPE(arg) > _scheme_ir_values_types_)
1404     return ok_value(data, arg, OK_CONSTANT_VALUE);
1405 
1406   return 0;
1407 }
1408 
ok_constant_super_value(void * data,Scheme_Object * v,int mode)1409 static int ok_constant_super_value(void *data, Scheme_Object *v, int mode)
1410 /* Is `v` a structure type (which can serve as a supertype)? */
1411 {
1412   Scheme_Object **_parent_identity = (Scheme_Object **)((void **)data)[0];
1413   int *_nonfail_constr = (int *)((void **)data)[1];
1414   int *_prefab = (int *)((void **)data)[2];
1415 
1416   if (mode == OK_CONSTANT_SHAPE) {
1417     if (SAME_TYPE(SCHEME_TYPE(v), scheme_struct_proc_shape_type)) {
1418       int mode = (SCHEME_PROC_SHAPE_MODE(v) & STRUCT_PROC_SHAPE_MASK);
1419       int field_count = (SCHEME_PROC_SHAPE_MODE(v) >> STRUCT_PROC_SHAPE_SHIFT);
1420       if (mode == STRUCT_PROC_SHAPE_STRUCT) {
1421         if (_parent_identity)
1422           *_parent_identity = SCHEME_PROC_SHAPE_IDENTITY(v);
1423         if (_nonfail_constr)
1424           *_nonfail_constr = SCHEME_PROC_SHAPE_MODE(v) & STRUCT_PROC_SHAPE_NONFAIL_CONSTR;
1425         if (_prefab)
1426           *_prefab = SCHEME_PROC_SHAPE_MODE(v) & STRUCT_PROC_SHAPE_PREFAB;
1427         return field_count + 1;
1428       }
1429     }
1430   } else if (mode == OK_CONSTANT_ENCODED_SHAPE) {
1431     intptr_t k;
1432     if (scheme_decode_struct_shape(v, &k)) {
1433       if ((k & STRUCT_PROC_SHAPE_MASK) == STRUCT_PROC_SHAPE_STRUCT) {
1434         if (_nonfail_constr)
1435           *_nonfail_constr = k & STRUCT_PROC_SHAPE_NONFAIL_CONSTR;
1436         if (_prefab)
1437           *_prefab = k & STRUCT_PROC_SHAPE_PREFAB;
1438         return (k >> STRUCT_PROC_SHAPE_SHIFT) + 1;
1439       }
1440     }
1441   } else if (mode == OK_CONSTANT_VALIDATE_SHAPE) {
1442     int k = SCHEME_INT_VAL(v);
1443     if ((k >= 0)
1444         && (k & STRUCT_PROC_SHAPE_MASK) == STRUCT_PROC_SHAPE_STRUCT) {
1445       if (_nonfail_constr)
1446         *_nonfail_constr = k & STRUCT_PROC_SHAPE_NONFAIL_CONSTR;
1447       if (_prefab)
1448         *_prefab = k & STRUCT_PROC_SHAPE_PREFAB;
1449       return (k >> STRUCT_PROC_SHAPE_SHIFT) + 1;
1450     }
1451   } else if (mode == OK_CONSTANT_VARIANT) {
1452     if (SCHEME_VECTORP(v) && (SCHEME_VEC_SIZE(v) == 3)) {
1453       if (_parent_identity)
1454         *_parent_identity = SCHEME_VEC_ELS(v)[2];
1455       v = SCHEME_VEC_ELS(v)[1];
1456       if (v && SCHEME_INTP(v)) {
1457         int mode = (SCHEME_INT_VAL(v) & STRUCT_PROC_SHAPE_MASK);
1458         int field_count = (SCHEME_INT_VAL(v) >> STRUCT_PROC_SHAPE_SHIFT);
1459         if (mode == STRUCT_PROC_SHAPE_STRUCT) {
1460           if (_nonfail_constr)
1461             *_nonfail_constr = SCHEME_INT_VAL(v) & STRUCT_PROC_SHAPE_NONFAIL_CONSTR;
1462           if (_prefab)
1463             *_prefab = SCHEME_INT_VAL(v) & STRUCT_PROC_SHAPE_PREFAB;
1464           return field_count + 1;
1465         }
1466       }
1467     }
1468   } else if (mode == OK_CONSTANT_VALUE) {
1469     if (SCHEME_STRUCT_TYPEP(v)) {
1470       Scheme_Struct_Type *st = (Scheme_Struct_Type *)v;
1471       if (st->num_slots == st->num_islots) {
1472         if (_nonfail_constr)
1473           *_nonfail_constr = (st->more_flags & STRUCT_TYPE_FLAG_NONFAIL_CONSTRUCTOR);
1474         if (_prefab)
1475           *_prefab = !!st->prefab_key;
1476         return st->num_slots + 1;
1477       }
1478     }
1479   }
1480 
1481   return 0;
1482 }
1483 
is_constant_super(Scheme_Object * arg,Optimize_Info * info,Scheme_Hash_Table * top_level_table,Scheme_Object ** runstack,int rs_delta,Scheme_Linklet * enclosing_linklet,Scheme_Object ** _parent_identity,int * _nonfail_constr,int * _prefab)1484 static int is_constant_super(Scheme_Object *arg,
1485                              Optimize_Info *info,
1486                              Scheme_Hash_Table *top_level_table,
1487                              Scheme_Object **runstack, int rs_delta,
1488                              Scheme_Linklet *enclosing_linklet,
1489                              Scheme_Object **_parent_identity,
1490                              int *_nonfail_constr,
1491                              int *_prefab)
1492 /* Does `arg` produce another structure type (which can serve as a supertype)? */
1493 {
1494   void *data[3];
1495 
1496   data[0] = _parent_identity;
1497   data[1] = _nonfail_constr;
1498   data[2] = _prefab;
1499 
1500   return is_ok_value(ok_constant_super_value, data,
1501                      arg,
1502                      info,
1503                      top_level_table,
1504                      runstack, rs_delta,
1505                      enclosing_linklet);
1506 }
1507 
ok_constant_property_without_guard(void * data,Scheme_Object * v,int mode)1508 static int ok_constant_property_without_guard(void *data, Scheme_Object *v, int mode)
1509 {
1510   intptr_t k = -1;
1511 
1512   if (mode == OK_CONSTANT_SHAPE) {
1513     if (SAME_TYPE(SCHEME_TYPE(v), scheme_struct_prop_proc_shape_type)) {
1514       k = SCHEME_PROP_PROC_SHAPE_MODE(v);
1515     }
1516   } else if (mode == OK_CONSTANT_ENCODED_SHAPE) {
1517     if (!scheme_decode_struct_prop_shape(v, &k))
1518       k = 0;
1519   } else if (mode == OK_CONSTANT_VALIDATE_SHAPE) {
1520     int k = SCHEME_INT_VAL(v);
1521     if (k < 0)
1522       k = -(k+1);
1523     else
1524       k = 0;
1525   } else if (mode == OK_CONSTANT_VARIANT) {
1526     if (SCHEME_VECTORP(v) && (SCHEME_VEC_SIZE(v) == 4)) {
1527       v = SCHEME_VEC_ELS(v)[1];
1528       if (v && SCHEME_INTP(v))
1529         k = SCHEME_INT_VAL(v);
1530     }
1531   } else if (mode == OK_CONSTANT_VALUE) {
1532     if (SAME_TYPE(SCHEME_TYPE(v), scheme_struct_property_type)) {
1533       if (!((Scheme_Struct_Property *)v)->guard)
1534         return 1;
1535     }
1536   }
1537 
1538   return (k == STRUCT_PROP_PROC_SHAPE_PROP);
1539 }
1540 
is_struct_type_property_without_guard(Scheme_Object * arg,Optimize_Info * info,Scheme_Hash_Table * top_level_table,Scheme_Object ** runstack,int rs_delta,Scheme_Linklet * enclosing_linklet)1541 static int is_struct_type_property_without_guard(Scheme_Object *arg,
1542                                                  Optimize_Info *info,
1543                                                  Scheme_Hash_Table *top_level_table,
1544                                                  Scheme_Object **runstack, int rs_delta,
1545                                                  Scheme_Linklet *enclosing_linklet)
1546 /* Does `arg` produce a structure type property that has no guard (so that any value is ok)? */
1547 {
1548   return is_ok_value(ok_constant_property_without_guard, NULL,
1549                      arg,
1550                      info,
1551                      top_level_table,
1552                      runstack, rs_delta,
1553                      enclosing_linklet);
1554 }
1555 
is_simple_property_list(Scheme_Object * a,int resolved,Optimize_Info * info,Scheme_Hash_Table * top_level_table,Scheme_Object ** runstack,int rs_delta,Scheme_Linklet * enclosing_linklet,int just_for_authentic,int * _authentic)1556 static int is_simple_property_list(Scheme_Object *a, int resolved,
1557                                    Optimize_Info *info,
1558                                    Scheme_Hash_Table *top_level_table,
1559                                    Scheme_Object **runstack, int rs_delta,
1560                                    Scheme_Linklet *enclosing_linklet,
1561                                    int just_for_authentic, int *_authentic)
1562 /* Does `a` produce a property list that always lets `make-struct-type` succeed? */
1563 {
1564   Scheme_Object *arg;
1565   int i, count;
1566 
1567   if (SAME_TYPE(SCHEME_TYPE(a), scheme_application_type)) {
1568     if (!SAME_OBJ(((Scheme_App_Rec *)a)->args[0], scheme_list_proc))
1569       return 0;
1570     count = ((Scheme_App_Rec *)a)->num_args;
1571   } else if (SAME_TYPE(SCHEME_TYPE(a), scheme_application2_type)) {
1572     if (!SAME_OBJ(((Scheme_App2_Rec *)a)->rator, scheme_list_proc))
1573       return 0;
1574     count = 1;
1575   } else if (SAME_TYPE(SCHEME_TYPE(a), scheme_application3_type)) {
1576     if (!SAME_OBJ(((Scheme_App3_Rec *)a)->rator, scheme_list_proc))
1577       return 0;
1578     count = 2;
1579   } else
1580     return 0;
1581 
1582   for (i = 0; i < count; i++) {
1583     if (SAME_TYPE(SCHEME_TYPE(a), scheme_application_type))
1584       arg = ((Scheme_App_Rec *)a)->args[i+1];
1585     else if (SAME_TYPE(SCHEME_TYPE(a), scheme_application2_type))
1586       arg = ((Scheme_App2_Rec *)a)->rand;
1587     else {
1588       if (i == 0)
1589         arg = ((Scheme_App3_Rec *)a)->rand1;
1590       else
1591         arg = ((Scheme_App3_Rec *)a)->rand2;
1592     }
1593 
1594     if (SAME_TYPE(SCHEME_TYPE(arg), scheme_application3_type)) {
1595       Scheme_App3_Rec *a3 = (Scheme_App3_Rec *)arg;
1596 
1597       if (!SAME_OBJ(a3->rator, scheme_cons_proc)) {
1598         if (!just_for_authentic)
1599           return 0;
1600       } else {
1601         if (_authentic && SAME_OBJ(a3->rand1, scheme_authentic_property))
1602           *_authentic = 1;
1603         if (!just_for_authentic) {
1604           if (is_struct_type_property_without_guard(a3->rand1,
1605                                                     info,
1606                                                     top_level_table,
1607                                                     runstack, rs_delta,
1608                                                     enclosing_linklet)) {
1609             if (!scheme_omittable_expr(a3->rand2, 1, 3, (resolved ? OMITTABLE_RESOLVED : 0), NULL, NULL))
1610               return 0;
1611           } else
1612             return 0;
1613         }
1614       }
1615     } else {
1616       if (!just_for_authentic)
1617         return 0;
1618     }
1619   }
1620 
1621   return 1;
1622 }
1623 
scheme_is_simple_make_struct_type(Scheme_Object * e,int vals,int flags,GC_CAN_IGNORE int * _auto_e_depth,Simple_Struct_Type_Info * _stinfo,Scheme_Object ** _parent_identity,Optimize_Info * info,Scheme_Hash_Table * top_level_table,Scheme_Object ** runstack,int rs_delta,Scheme_Linklet * enclosing_linklet,Scheme_Object ** _name,int fuel)1624 Scheme_Object *scheme_is_simple_make_struct_type(Scheme_Object *e, int vals, int flags,
1625                                                  GC_CAN_IGNORE int *_auto_e_depth,
1626                                                  Simple_Struct_Type_Info *_stinfo,
1627                                                  Scheme_Object **_parent_identity,
1628                                                  Optimize_Info *info,
1629                                                  Scheme_Hash_Table *top_level_table,
1630                                                  Scheme_Object **runstack, int rs_delta,
1631                                                  Scheme_Linklet *enclosing_linklet,
1632                                                  Scheme_Object **_name,
1633                                                  int fuel)
1634 /* Checks whether it's a `make-struct-type' call --- that, if `flags` includes
1635    `CHECK_STRUCT_TYPE_ALWAYS_SUCCEED`, certainly succeeds (i.e., no exception) ---
1636    pending a check of the auto-value argument if `flags` includes `CHECK_STRUCT_TYPE_DELAY_AUTO_CHECK`.
1637    The expression itself must have no side-effects except for errors (but the possibility
1638    of errors means that the expression is not necessarily omittable).
1639    The result is the auto-value argument or scheme_true if it's simple, NULL if not.
1640    The first result of `e` will be a struct type, the second a constructor, and the third a predicate;
1641    the rest are selectors and mutators. */
1642 {
1643   int resolved = (flags & CHECK_STRUCT_TYPE_RESOLVED);
1644 
1645   if (!fuel) return NULL;
1646 
1647   if (SAME_TYPE(SCHEME_TYPE(e), scheme_application_type)) {
1648     if ((vals == 5) || (vals < 0)) {
1649       Scheme_App_Rec *app = (Scheme_App_Rec *)e;
1650 
1651       if ((app->num_args >= 4) && (app->num_args <= 11)
1652           && SAME_OBJ(scheme_make_struct_type_proc, app->args[0])) {
1653         int super_count_plus_one, super_nonfail_constr = 1, super_prefab = 1;
1654 
1655         if (_parent_identity)
1656           *_parent_identity = scheme_null;
1657         if (!SCHEME_FALSEP(app->args[2]))
1658           super_count_plus_one = is_constant_super(app->args[2],
1659                                                    info, top_level_table, runstack,
1660                                                    rs_delta + app->num_args,
1661                                                    enclosing_linklet, _parent_identity,
1662                                                    &super_nonfail_constr,
1663                                                    &super_prefab);
1664         else
1665           super_count_plus_one = 0;
1666 
1667         if (SCHEME_SYMBOLP(app->args[1])
1668             && (SCHEME_FALSEP(app->args[2]) /* super */
1669                 || super_count_plus_one)
1670             && SCHEME_INTP(app->args[3])
1671             && (SCHEME_INT_VAL(app->args[3]) >= 0)
1672             && SCHEME_INTP(app->args[4])
1673             && (SCHEME_INT_VAL(app->args[4]) >= 0)
1674             && ((app->num_args < 5)
1675                 /* auto-field value: */
1676                 || (flags & CHECK_STRUCT_TYPE_DELAY_AUTO_CHECK)
1677                 || scheme_omittable_expr(app->args[5], 1, 3, (resolved ? OMITTABLE_RESOLVED : 0), NULL, NULL))
1678             && ((app->num_args < 6)
1679                 /* no properties... */
1680                 || SCHEME_NULLP(app->args[6])
1681                 /* ... or properties that might make the `make-struct-type`
1682                    call itself fail, but otherwise don't affect the constructor
1683                    or selectors in a way that matters (although supplying the
1684                    `prop:chaperone-unsafe-undefined` property can affect the
1685                    constructor in an optimizer-irrelevant way) */
1686                 || (!(flags & CHECK_STRUCT_TYPE_ALWAYS_SUCCEED)
1687                     && scheme_omittable_expr(app->args[6], 1, 4, (resolved ? OMITTABLE_RESOLVED : 0), NULL, NULL))
1688                 || ((flags & CHECK_STRUCT_TYPE_ALWAYS_SUCCEED)
1689                     && is_simple_property_list(app->args[6], resolved,
1690                                                info,
1691                                                top_level_table,
1692                                                runstack, rs_delta,
1693                                                enclosing_linklet,
1694                                                0, NULL)))
1695             && ((app->num_args < 7)
1696                 /* inspector: */
1697                 || SCHEME_FALSEP(app->args[7])
1698                 || (super_prefab
1699                     && SCHEME_SYMBOLP(app->args[7])
1700                     && !strcmp("prefab", SCHEME_SYM_VAL(app->args[7]))
1701                     && !SCHEME_SYM_WEIRDP(app->args[7]))
1702                 || is_inspector_call(app->args[7]))
1703             && ((app->num_args < 8)
1704                 /* procedure property: */
1705                 || SCHEME_FALSEP(app->args[8])
1706                 || is_proc_spec_proc(app->args[8], SCHEME_INT_VAL(app->args[3])))
1707             && ((app->num_args < 9)
1708                 /* immutables: */
1709                 || is_int_list(app->args[9],
1710                                SCHEME_INT_VAL(app->args[3])))
1711             && ((app->num_args < 10)
1712                 /* guard: */
1713                 || SCHEME_FALSEP(app->args[10])
1714                 /* Could try to check for procedure with correct arity: */
1715                 || !(flags & CHECK_STRUCT_TYPE_ALWAYS_SUCCEED))
1716             && ((app->num_args < 11)
1717                 /* constructor name: */
1718                 || SCHEME_FALSEP(app->args[11])
1719                 || SCHEME_SYMBOLP(app->args[11]))) {
1720           if (_auto_e_depth)
1721             *_auto_e_depth = (resolved ? app->num_args : 0);
1722           if (_name)
1723             *_name = app->args[1];
1724           if (_stinfo) {
1725             int authentic = 0;
1726             int super_count = (super_count_plus_one
1727                                ? (super_count_plus_one - 1)
1728                                : 0);
1729             _stinfo->init_field_count = SCHEME_INT_VAL(app->args[3]) + super_count;
1730             _stinfo->field_count = (SCHEME_INT_VAL(app->args[3])
1731                                     + SCHEME_INT_VAL(app->args[4])
1732                                     + super_count);
1733             _stinfo->uses_super = (super_count_plus_one ? 1 : 0);
1734             _stinfo->super_field_count = (super_count_plus_one ? (super_count_plus_one - 1) : 0);
1735             _stinfo->normal_ops = 1;
1736             _stinfo->indexed_ops = 0;
1737             _stinfo->authentic = 0;
1738             if ((app->num_args > 6)
1739                 && is_simple_property_list(app->args[6], resolved,
1740                                            info,
1741                                            top_level_table,
1742                                            runstack, rs_delta,
1743                                            enclosing_linklet,
1744                                            1, &authentic))
1745               _stinfo->authentic = authentic;
1746             _stinfo->nonfail_constructor = (super_nonfail_constr
1747                                             && ((app->num_args < 10) || SCHEME_FALSEP(app->args[10])));
1748             _stinfo->prefab = ((app->num_args > 7)
1749                                && SCHEME_SYMBOLP(app->args[7]));
1750             _stinfo->num_gets = 1;
1751             _stinfo->num_sets = 1;
1752           }
1753           return ((app->num_args < 5) ? scheme_true : app->args[5]);
1754         }
1755       }
1756     }
1757   }
1758 
1759   if (SAME_TYPE(SCHEME_TYPE(e), scheme_ir_let_header_type)) {
1760     /* check for (let-values ([(: mk ? ref- set-!) (make-struct-type ...)]) (values ...))
1761        as generated by the expansion of `struct' */
1762     Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)e;
1763     if ((lh->count == 5) && (lh->num_clauses == 1)) {
1764       if (SAME_TYPE(SCHEME_TYPE(lh->body), scheme_ir_let_value_type)) {
1765         Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)lh->body;
1766         if (SAME_TYPE(SCHEME_TYPE(lv->value), scheme_application_type)) {
1767           Scheme_Object *auto_e;
1768           Simple_Struct_Type_Info stinfo;
1769           if (!_stinfo) _stinfo = &stinfo;
1770           auto_e = scheme_is_simple_make_struct_type(lv->value, 5, flags,
1771                                                      _auto_e_depth, _stinfo, _parent_identity,
1772                                                      info, top_level_table,
1773                                                      runstack, rs_delta,
1774                                                      enclosing_linklet,
1775                                                      _name,
1776                                                      fuel-1);
1777           if (auto_e) {
1778             /* We have (let-values ([... (make-struct-type)]) ....), so make sure body
1779                just uses `make-struct-field-{accessor,mutator}'. */
1780             if (is_values_with_accessors_and_mutators(lv->body, vals, resolved, _stinfo, lv->vars)) {
1781               return auto_e;
1782             }
1783           }
1784         }
1785       }
1786     }
1787   }
1788 
1789   if (SAME_TYPE(SCHEME_TYPE(e), scheme_let_void_type)) {
1790     /* same thing, but in resolved form */
1791     Scheme_Let_Void *lvd = (Scheme_Let_Void *)e;
1792     if (lvd->count == 5) {
1793       if (SAME_TYPE(SCHEME_TYPE(lvd->body), scheme_let_value_type)) {
1794         Scheme_Let_Value *lv = (Scheme_Let_Value *)lvd->body;
1795         if ((lv->position == 0) && (lv->count == 5)) {
1796           Scheme_Object *e2;
1797           e2 = skip_clears(lv->value);
1798           if (SAME_TYPE(SCHEME_TYPE(e2), scheme_application_type)) {
1799             Scheme_Object *auto_e;
1800             Simple_Struct_Type_Info stinfo;
1801             if (!_stinfo) _stinfo = &stinfo;
1802             auto_e = scheme_is_simple_make_struct_type(e2, 5, flags,
1803                                                        _auto_e_depth, _stinfo, _parent_identity,
1804                                                        info, top_level_table,
1805                                                        runstack, rs_delta + lvd->count,
1806                                                        enclosing_linklet,
1807                                                        _name,
1808                                                        fuel-1);
1809             if (auto_e) {
1810               /* We have (let-values ([... (make-struct-type)]) ....), so make sure body
1811                  just uses `make-struct-field-{accessor,mutator}'. */
1812               e2 = skip_clears(lv->body);
1813               if (is_values_with_accessors_and_mutators(e2, vals, resolved, _stinfo, NULL)) {
1814                 if (_auto_e_depth) *_auto_e_depth += lvd->count;
1815                 return auto_e;
1816               }
1817             }
1818           }
1819         }
1820       }
1821     }
1822   }
1823 
1824   return NULL;
1825 }
1826 
scheme_is_simple_make_struct_type_property(Scheme_Object * e,int vals,int flags,int * _has_guard,Optimize_Info * info,Scheme_Hash_Table * top_level_table,Scheme_Object ** runstack,int rs_delta,Scheme_Linklet * enclosing_linklet,int fuel)1827 int scheme_is_simple_make_struct_type_property(Scheme_Object *e, int vals, int flags,
1828                                                int *_has_guard,
1829                                                Optimize_Info *info,
1830                                                Scheme_Hash_Table *top_level_table,
1831                                                Scheme_Object **runstack, int rs_delta,
1832                                                Scheme_Linklet *enclosing_linklet,
1833                                                int fuel)
1834 /* Reports whether `app` is a call to `make-struct-type-property` to
1835    produce a property. The `flag` argument can indicate further that the
1836    expression must always succeed without raising an exception. */
1837 {
1838   int resolved = (flags & CHECK_STRUCT_TYPE_RESOLVED);
1839 
1840   if ((vals != 3) && (vals >= 0)) return 0;
1841 
1842   if (SAME_TYPE(SCHEME_TYPE(e), scheme_application2_type)) {
1843     Scheme_App2_Rec *app = (Scheme_App2_Rec *)e;
1844     if (SAME_OBJ(app->rator, scheme_make_struct_type_property_proc)) {
1845       if (SCHEME_SYMBOLP(app->rand)) {
1846         if (_has_guard) *_has_guard = 0;
1847         return 1;
1848       }
1849     }
1850   }
1851 
1852   if (SAME_TYPE(SCHEME_TYPE(e), scheme_application3_type)) {
1853     Scheme_App3_Rec *app = (Scheme_App3_Rec *)e;
1854     if (SAME_OBJ(app->rator, scheme_make_struct_type_property_proc)) {
1855       if (SCHEME_SYMBOLP(app->rand1)
1856           && (!(flags & CHECK_STRUCT_TYPE_ALWAYS_SUCCEED)
1857               || SCHEME_FALSEP(app->rand2)
1858               || (SCHEME_LAMBDAP(app->rand2)
1859                   && (((Scheme_Lambda *)app->rand2)->num_params == 2)))
1860           && (scheme_omittable_expr(app->rator, 1, 4, (resolved ? OMITTABLE_RESOLVED : 0), NULL, NULL))) {
1861         if (_has_guard) *_has_guard = 1;
1862         return 1;
1863       }
1864     }
1865   }
1866 
1867   return 0;
1868 }
1869 
1870 /*========================================================================*/
1871 /*                             more utils                                 */
1872 /*========================================================================*/
1873 
scheme_get_struct_proc_shape(int k,Simple_Struct_Type_Info * stinfo)1874 intptr_t scheme_get_struct_proc_shape(int k, Simple_Struct_Type_Info *stinfo)
1875 {
1876   switch (k) {
1877   case 0:
1878     if (stinfo->field_count == stinfo->init_field_count)
1879       return (STRUCT_PROC_SHAPE_STRUCT
1880               | (stinfo->authentic ? STRUCT_PROC_SHAPE_AUTHENTIC : 0)
1881               | (stinfo->nonfail_constructor ? STRUCT_PROC_SHAPE_NONFAIL_CONSTR : 0)
1882               | (stinfo->prefab ? STRUCT_PROC_SHAPE_PREFAB : 0)
1883               | (stinfo->field_count << STRUCT_PROC_SHAPE_SHIFT));
1884     else
1885       return STRUCT_PROC_SHAPE_OTHER;
1886     break;
1887   case 1:
1888     return (STRUCT_PROC_SHAPE_CONSTR
1889             | (stinfo->init_field_count << STRUCT_PROC_SHAPE_SHIFT)
1890             | (stinfo->nonfail_constructor ? STRUCT_PROC_SHAPE_NONFAIL_CONSTR : 0));
1891     break;
1892   case 2:
1893     return (STRUCT_PROC_SHAPE_PRED
1894             | (stinfo->authentic ? STRUCT_PROC_SHAPE_AUTHENTIC : 0));
1895     break;
1896   default:
1897     if (stinfo && stinfo->normal_ops && stinfo->indexed_ops) {
1898       if (k - 3 < stinfo->num_gets) {
1899         /* record index of field */
1900         return (STRUCT_PROC_SHAPE_GETTER
1901                 | (stinfo->authentic ? STRUCT_PROC_SHAPE_AUTHENTIC : 0)
1902                 | ((stinfo->super_field_count + (k - 3)) << STRUCT_PROC_SHAPE_SHIFT));
1903       } else {
1904         int idx = (k - 3 - stinfo->num_gets), setter_fields = stinfo->setter_fields, pos = 0;
1905 
1906         /* setter_fields is a bitmap for first (31-STRUCT_PROC_SHAPE_SHIFT) fields that may have a setter */
1907         while ((idx > 0) || !(setter_fields & 1)) {
1908           if (setter_fields & 1) {
1909             idx--;
1910           }
1911           setter_fields = setter_fields >> 1;
1912           pos++;
1913           if (!setter_fields) break;
1914         }
1915 
1916         if (!idx && (setter_fields & 1))
1917           pos += stinfo->super_field_count + 1;
1918         else {
1919           /* represent "unknown" by zero */
1920           pos = 0;
1921         }
1922 
1923         return (STRUCT_PROC_SHAPE_SETTER
1924                 | (stinfo->authentic ? STRUCT_PROC_SHAPE_AUTHENTIC : 0)
1925                 | (pos << STRUCT_PROC_SHAPE_SHIFT));
1926       }
1927     }
1928   }
1929 
1930   return STRUCT_PROC_SHAPE_OTHER;
1931 }
1932 
scheme_make_struct_proc_shape(intptr_t k,Scheme_Object * identity)1933 Scheme_Object *scheme_make_struct_proc_shape(intptr_t k, Scheme_Object *identity)
1934 {
1935   Scheme_Object *ps;
1936 
1937   ps = scheme_malloc_small_tagged(sizeof(Scheme_Simple_Object));
1938   ps->type = scheme_struct_proc_shape_type;
1939   SCHEME_PROC_SHAPE_MODE(ps) = k;
1940   SCHEME_PROC_SHAPE_IDENTITY(ps) = identity;
1941 
1942   return ps;
1943 }
1944 
scheme_get_struct_property_proc_shape(int k,int has_guard)1945 intptr_t scheme_get_struct_property_proc_shape(int k, int has_guard)
1946 {
1947   switch (k) {
1948   case 0:
1949     if (has_guard)
1950       return STRUCT_PROP_PROC_SHAPE_GUARDED_PROP;
1951     else
1952       return STRUCT_PROP_PROC_SHAPE_PROP;
1953   case 1:
1954     return STRUCT_PROP_PROC_SHAPE_PRED;
1955   case 2:
1956   default:
1957     return STRUCT_PROP_PROC_SHAPE_GETTER;
1958   }
1959 }
1960 
scheme_make_struct_property_proc_shape(intptr_t k)1961 Scheme_Object *scheme_make_struct_property_proc_shape(intptr_t k)
1962 {
1963   Scheme_Object *ps;
1964 
1965   ps = scheme_alloc_small_object();
1966   ps->type = scheme_struct_prop_proc_shape_type;
1967   SCHEME_PROP_PROC_SHAPE_MODE(ps) = k;
1968 
1969   return ps;
1970 }
1971 
is_struct_identity_subtype(Scheme_Object * sub,Scheme_Object * sup)1972 XFORM_NONGCING static int is_struct_identity_subtype(Scheme_Object *sub, Scheme_Object *sup)
1973 {
1974   /* A structure identity is typically a list of symbols, but the symbols are
1975      just for debugging. Instead, the address of each pair forming the
1976      list represents an identiity. */
1977   while (SCHEME_PAIRP(sub)) {
1978     if (SAME_OBJ(sub, sup))
1979       return 1;
1980     sub = SCHEME_CDR(sub);
1981   }
1982   return 0;
1983 }
1984 
single_valued_noncm_function(Scheme_Object * rator,int num_args,Optimize_Info * info,int s_v,int non_cm)1985 static int single_valued_noncm_function(Scheme_Object *rator, int num_args,
1986                                         Optimize_Info *info, int s_v, int non_cm)
1987 {
1988   int flags;
1989 
1990   if (!s_v && !non_cm)
1991     return 1;
1992 
1993   flags = get_rator_flags(rator, num_args, info);
1994   if (s_v && !(flags & LAMBDA_SINGLE_RESULT))
1995     return 0;
1996   if (non_cm && !(flags & LAMBDA_PRESERVES_MARKS))
1997     return 0;
1998 
1999   return 1;
2000 }
2001 
do_single_valued_noncm_expression(Scheme_Object * expr,Optimize_Info * info,int fuel,int s_v,int non_cm)2002 static int do_single_valued_noncm_expression(Scheme_Object *expr, Optimize_Info *info, int fuel, int s_v, int non_cm)
2003 /* Not necessarily omittable or copyable expression.
2004    If `s_v`, the expression must not be single-valued.
2005    If `non_cm`, the expression must be not sensitive to tail position. In particular,
2006    it has no with-continuation-mark in tail position, unless the body is omittable.
2007    The conservative answer is 0. */
2008 {
2009   if (!s_v && !non_cm)
2010     return 1;
2011 
2012   while (fuel) {
2013     switch (SCHEME_TYPE(expr)) {
2014     case scheme_ir_local_type:
2015     case scheme_local_type:
2016     case scheme_local_unbox_type:
2017     case scheme_ir_toplevel_type:
2018       return 1;
2019       break;
2020     case scheme_application_type:
2021       {
2022         Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
2023         return single_valued_noncm_function(app->args[0], app->num_args, info, s_v, non_cm);
2024       }
2025       break;
2026     case scheme_application2_type:
2027       {
2028         Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
2029         return single_valued_noncm_function(app->rator, 1, info, s_v, non_cm);
2030       }
2031       break;
2032     case scheme_application3_type:
2033       {
2034         Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
2035         return single_valued_noncm_function(app->rator, 2, info, s_v, non_cm);
2036       }
2037       break;
2038     case scheme_branch_type:
2039       {
2040         Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr;
2041         return (do_single_valued_noncm_expression(b->tbranch, info, fuel - 1, s_v, non_cm)
2042                 && do_single_valued_noncm_expression(b->fbranch, info, fuel - 1, s_v, non_cm));
2043       }
2044       break;
2045     case scheme_ir_let_header_type:
2046       {
2047         Scheme_IR_Let_Header *hl = (Scheme_IR_Let_Header *)expr;
2048         expr = hl->body;
2049       }
2050       break;
2051     case scheme_ir_let_value_type:
2052       {
2053         Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)expr;
2054         expr = lv->body;
2055       }
2056       break;
2057     case scheme_sequence_type:
2058       {
2059         Scheme_Sequence *seq = (Scheme_Sequence *)expr;
2060         expr = seq->array[seq->count-1];
2061       }
2062       break;
2063     case scheme_begin0_sequence_type:
2064       {
2065          Scheme_Sequence *seq = (Scheme_Sequence *)expr;
2066       expr = seq->array[0];
2067       }
2068       break;
2069     case scheme_with_cont_mark_type:
2070       {
2071         Scheme_With_Continuation_Mark * wcm = (Scheme_With_Continuation_Mark *)expr;
2072         if (non_cm) {
2073           /* To avoid being sensitive to tail position, the body must not inspect
2074              the continuation at all. */
2075           return scheme_omittable_expr(wcm->body, s_v ? 1 : -1, 5, 0, NULL, NULL);
2076         } else {
2077           expr = wcm->body;
2078         }
2079       }
2080       break;
2081     case scheme_ir_lambda_type:
2082     case scheme_case_lambda_sequence_type:
2083     case scheme_set_bang_type:
2084       return 1;
2085       break;
2086     default:
2087       if (SCHEME_TYPE(expr) > _scheme_ir_values_types_)
2088         return 1;
2089       break;
2090     }
2091     fuel--;
2092   }
2093 
2094   return 0;
2095 }
2096 
single_valued_noncm_expression(Scheme_Object * expr,Optimize_Info * info,int fuel)2097 static int single_valued_noncm_expression(Scheme_Object *expr, Optimize_Info *info, int fuel)
2098 {
2099   return do_single_valued_noncm_expression(expr, info, fuel, 1, 1);
2100 }
2101 
single_valued_expression(Scheme_Object * expr,Optimize_Info * info,int fuel)2102 static int single_valued_expression(Scheme_Object *expr, Optimize_Info *info, int fuel)
2103 {
2104   return do_single_valued_noncm_expression(expr, info, fuel, 1, 0);
2105 }
2106 
noncm_expression(Scheme_Object * expr,Optimize_Info * info,int fuel)2107 static int noncm_expression(Scheme_Object *expr, Optimize_Info *info, int fuel)
2108 {
2109   return do_single_valued_noncm_expression(expr, info, fuel, 0, 1);
2110 }
2111 
is_movable_prim(Scheme_Object * rator,int n,int cross_lambda,int cross_k,Optimize_Info * info)2112 static int is_movable_prim(Scheme_Object *rator, int n, int cross_lambda, int cross_k, Optimize_Info *info)
2113 /* Can we move a call to `rator` relative to other function calls?
2114    A -1 return means that the arguments must be movable without
2115    changing space complexity (which is the case for `cons`, for example). */
2116 {
2117   if (rator && SCHEME_PRIMP(rator)) {
2118     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_IS_UNSAFE_FUNCTIONAL) {
2119       /* Although it's semantically ok to return -1 even when cross_lambda,
2120          doing so risks duplicating a computation if the relevant `lambda'
2121          is later inlined. */
2122       if (cross_lambda) return 0;
2123       if (cross_k
2124           && !(SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_IS_UNSAFE_NONALLOCATE)
2125           && (produces_local_type(rator, n) != SCHEME_LOCAL_TYPE_FIXNUM)) {
2126         return 0;
2127       }
2128       return -1;
2129     }
2130   }
2131 
2132   if (SAME_OBJ(scheme_void_proc, rator))
2133     return -1;
2134 
2135   if (!cross_lambda
2136       && !cross_k /* because all calls below allocate */
2137       /* Note that none of these have space-safety issues, since they
2138          return values that contain all arguments: */
2139       && (SAME_OBJ(scheme_list_proc, rator)
2140           || (SAME_OBJ(scheme_cons_proc, rator) && (n == 2))
2141           || (SAME_OBJ(scheme_mcons_proc, rator) && (n == 2))
2142           || (SAME_OBJ(scheme_unsafe_cons_list_proc, rator) && (n == 2))
2143           || SAME_OBJ(scheme_list_star_proc, rator)
2144           || SAME_OBJ(scheme_vector_proc, rator)
2145           || SAME_OBJ(scheme_vector_immutable_proc, rator)
2146           || (SAME_OBJ(scheme_box_proc, rator) && (n == 1))
2147           || (SAME_OBJ(scheme_box_immutable_proc, rator) && (n == 1))))
2148     return 1;
2149 
2150   return 0;
2151 }
2152 
movable_expression(Scheme_Object * expr,Optimize_Info * info,int cross_lambda,int cross_k,int cross_s,int check_space,int fuel)2153 static int movable_expression(Scheme_Object *expr, Optimize_Info *info,
2154                               int cross_lambda, int cross_k, int cross_s,
2155                               int check_space, int fuel)
2156 /* A movable expression can't necessarily be constant-folded,
2157    but can be delayed because it has no side-effects (or is unsafe),
2158    produces a single value,
2159    and is not sensitive to being in tail position */
2160 {
2161   int can_move;
2162 
2163   if (fuel < 0) return 0;
2164 
2165   switch (SCHEME_TYPE(expr)) {
2166   case scheme_toplevel_type:
2167   case scheme_static_toplevel_type:
2168     return ((SCHEME_TOPLEVEL_FLAGS(expr) & SCHEME_TOPLEVEL_FLAGS_MASK) >= SCHEME_TOPLEVEL_FIXED);
2169   case scheme_ir_local_type:
2170     {
2171       /* Ok if not mutable */
2172       if (!SCHEME_VAR(expr)->mutated) {
2173         if (check_space) {
2174           if (SCHEME_VAR(expr)->val_type)
2175             return 1;
2176           /* the value of the identifier might be something that would
2177              retain significant memory, so we can't delay evaluation */
2178           return 0;
2179         }
2180         return 1;
2181       }
2182     }
2183     break;
2184   case scheme_application_type:
2185     if (!cross_lambda
2186         && !cross_k
2187         && (SCHEME_APPN_FLAGS((Scheme_App_Rec *)expr) & APPN_FLAG_OMITTABLE))
2188       can_move = -1;
2189     else
2190       can_move = is_movable_prim(((Scheme_App_Rec *)expr)->args[0], ((Scheme_App_Rec *)expr)->num_args,
2191                                  cross_lambda, cross_k, info);
2192     if (can_move) {
2193       int i;
2194       for (i = ((Scheme_App_Rec *)expr)->num_args; i--; ) {
2195         if (!movable_expression(((Scheme_App_Rec *)expr)->args[i+1], info,
2196                                 cross_lambda, cross_k, cross_s,
2197                                 check_space || (cross_s && (can_move < 0)), fuel - 1))
2198           return 0;
2199       }
2200       return 1;
2201     }
2202     break;
2203   case scheme_application2_type:
2204     if (!cross_lambda
2205         && !cross_k
2206         && (SCHEME_APPN_FLAGS((Scheme_App2_Rec *)expr) & APPN_FLAG_OMITTABLE))
2207       can_move = -1;
2208     else
2209       can_move = is_movable_prim(((Scheme_App2_Rec *)expr)->rator, 1, cross_lambda, cross_k, info);
2210     if (can_move) {
2211       if (movable_expression(((Scheme_App2_Rec *)expr)->rand, info,
2212                              cross_lambda, cross_k, cross_s,
2213                              check_space || (cross_s && (can_move < 0)), fuel - 1))
2214         return 1;
2215     }
2216     break;
2217   case scheme_application3_type:
2218     if (!cross_lambda
2219         && !cross_k
2220         && (SCHEME_APPN_FLAGS((Scheme_App3_Rec *)expr) & APPN_FLAG_OMITTABLE))
2221       can_move = -1;
2222     else
2223       can_move = is_movable_prim(((Scheme_App3_Rec *)expr)->rator, 2, cross_lambda, cross_k, info);
2224     if (can_move) {
2225       if (movable_expression(((Scheme_App3_Rec *)expr)->rand1, info,
2226                              cross_lambda, cross_k, cross_s,
2227                              check_space || (cross_s && (can_move < 0)), fuel - 1)
2228           && movable_expression(((Scheme_App3_Rec *)expr)->rand2, info,
2229                                 cross_lambda, cross_k, cross_s,
2230                                 check_space || (cross_s && (can_move < 0)), fuel - 1))
2231         return 1;
2232     }
2233     break;
2234   case scheme_branch_type:
2235     {
2236       Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr;
2237       if (movable_expression(b->test, info, cross_lambda, cross_k, cross_s, check_space, fuel-1)
2238           /* Check space for branches if cross_s, because evaluating `if` eliminates one of them */
2239           && movable_expression(b->tbranch, info, cross_lambda, cross_k, cross_s, check_space || cross_s, fuel-1)
2240           && movable_expression(b->fbranch, info, cross_lambda, cross_k, cross_s, check_space || cross_s, fuel-1))
2241         return 1;
2242     }
2243     break;
2244   case scheme_ir_lambda_type:
2245   case scheme_case_lambda_sequence_type:
2246     /* Can't move across lambda or continuation if not closed, since
2247        that changes allocation of a closure (i.e., might allocate the
2248        closure multiple times). */
2249     return !cross_lambda && !cross_k;
2250   default:
2251     if (SCHEME_TYPE(expr) > _scheme_ir_values_types_)
2252       return 1;
2253   }
2254 
2255   return 0;
2256 }
2257 
scheme_is_ir_lambda(Scheme_Object * o,int can_be_closed,int can_be_liftable)2258 int scheme_is_ir_lambda(Scheme_Object *o, int can_be_closed, int can_be_liftable)
2259 {
2260   if (SAME_TYPE(SCHEME_TYPE(o), scheme_ir_lambda_type)) {
2261     if (!can_be_closed || !can_be_liftable) {
2262       Scheme_Lambda *lam;
2263       lam = (Scheme_Lambda *)o;
2264       /* Because == 0 is like a constant */
2265       if (!can_be_closed && !lam->closure_size)
2266         return 0;
2267       /* Because procs that reference only globals are lifted: */
2268       if (!can_be_liftable && (lam->closure_size == 1) && lambda_has_top_level(lam))
2269         return 0;
2270     }
2271     return 1;
2272   } else
2273     return 0;
2274 }
2275 
small_inline_number(Scheme_Object * o)2276 XFORM_NONGCING static int small_inline_number(Scheme_Object *o)
2277 {
2278   if (SCHEME_BIGNUMP(o))
2279     return SCHEME_BIGLEN(o) < 32;
2280   else if (SCHEME_COMPLEXP(o))
2281     return (small_inline_number(scheme_complex_real_part(o))
2282             && small_inline_number(scheme_complex_imaginary_part(o)));
2283   else if (SCHEME_RATIONALP(o))
2284     return (small_inline_number(scheme_rational_numerator(o))
2285             && small_inline_number(scheme_rational_denominator(o)));
2286   else
2287     return 1;
2288 }
2289 
2290 #define STR_INLINE_LIMIT 256
2291 
scheme_ir_duplicate_ok(Scheme_Object * fb,int cross_linklet)2292 int scheme_ir_duplicate_ok(Scheme_Object *fb, int cross_linklet)
2293 /* Is the constant a value that we can "copy" in the code? */
2294 {
2295   return (SCHEME_VOIDP(fb)
2296           || SAME_OBJ(fb, scheme_true)
2297           || SAME_OBJ(fb, scheme_undefined)
2298           || SCHEME_FALSEP(fb)
2299           || (SCHEME_SYMBOLP(fb)
2300               && (!cross_linklet || (!SCHEME_SYM_WEIRDP(fb)
2301                                     && (SCHEME_SYM_LEN(fb) < STR_INLINE_LIMIT))))
2302           || (SCHEME_KEYWORDP(fb)
2303               && (!cross_linklet || (SCHEME_KEYWORD_LEN(fb) < STR_INLINE_LIMIT)))
2304           || SCHEME_EOFP(fb)
2305           || SCHEME_INTP(fb)
2306           || SCHEME_NULLP(fb)
2307           || (SCHEME_HASHTRP(fb) && !((Scheme_Hash_Tree *)fb)->count)
2308           || (!cross_linklet && SAME_TYPE(SCHEME_TYPE(fb), scheme_ir_toplevel_type))
2309           || (!cross_linklet && SAME_TYPE(SCHEME_TYPE(fb), scheme_ir_local_type))
2310           || SCHEME_PRIMP(fb)
2311           /* Values that are hashed by the printer and/or interned on
2312              read to avoid duplication: */
2313           || SCHEME_CHARP(fb)
2314           || (SCHEME_CHAR_STRINGP(fb)
2315               && (!cross_linklet || (SCHEME_CHAR_STRLEN_VAL(fb) < STR_INLINE_LIMIT)))
2316           || (SCHEME_BYTE_STRINGP(fb)
2317               && (!cross_linklet || (SCHEME_BYTE_STRLEN_VAL(fb) < STR_INLINE_LIMIT)))
2318           || SAME_TYPE(SCHEME_TYPE(fb), scheme_regexp_type)
2319           || (SCHEME_NUMBERP(fb)
2320               && (!cross_linklet || small_inline_number(fb)))
2321           || SAME_TYPE(SCHEME_TYPE(fb), scheme_ctype_type));
2322 }
2323 
2324 /*========================================================================*/
2325 /*                   applications, branches, sequences                    */
2326 /*========================================================================*/
2327 
2328 static Scheme_Object *finish_optimize_application(Scheme_App_Rec *app, Optimize_Info *info, int context);
2329 static Scheme_Object *finish_optimize_application2(Scheme_App2_Rec *app, Optimize_Info *info, int context);
2330 static Scheme_Object *finish_optimize_application3(Scheme_App3_Rec *app, Optimize_Info *info, int context);
2331 
try_optimize_fold(Scheme_Object * f,Scheme_Object * args,Scheme_Object * o,Optimize_Info * info)2332 static Scheme_Object *try_optimize_fold(Scheme_Object *f, Scheme_Object *args, Scheme_Object *o, Optimize_Info *info)
2333 /* If `args` is NULL, extract arguments from `o` */
2334 {
2335   if (scheme_is_foldable_prim(f)) {
2336 
2337     if (!args) {
2338       switch (SCHEME_TYPE(o)) {
2339       case scheme_application_type:
2340         {
2341           Scheme_App_Rec *app = (Scheme_App_Rec *)o;
2342           int i;
2343 
2344           args = scheme_null;
2345           for (i = app->num_args; i--; ) {
2346             args = scheme_make_pair(app->args[i + 1], args);
2347           }
2348         }
2349         break;
2350       case scheme_application2_type:
2351         {
2352           Scheme_App2_Rec *app = (Scheme_App2_Rec *)o;
2353           args = scheme_make_pair(app->rand, scheme_null);
2354         }
2355         break;
2356       case scheme_application3_type:
2357       default:
2358         {
2359           Scheme_App3_Rec *app = (Scheme_App3_Rec *)o;
2360           args = scheme_make_pair(app->rand1,
2361                                   scheme_make_pair(app->rand2,
2362                                                    scheme_null));
2363         }
2364         break;
2365       }
2366     }
2367 
2368     return scheme_try_apply(f, args, info);
2369   }
2370 
2371   return NULL;
2372 }
2373 
estimate_expr_size(Scheme_Object * expr,int sz,int fuel)2374 static int estimate_expr_size(Scheme_Object *expr, int sz, int fuel)
2375 {
2376   Scheme_Type t;
2377 
2378   if (sz > 128)
2379     return sz;
2380   if (fuel < 0)
2381     return sz + 128;
2382 
2383   t = SCHEME_TYPE(expr);
2384 
2385   switch(t) {
2386   case scheme_ir_local_type:
2387     {
2388       sz += 1;
2389       break;
2390     }
2391   case scheme_case_lambda_sequence_type:
2392     {
2393       int max_sz = sz + 1, a_sz;
2394       Scheme_Case_Lambda *cl = (Scheme_Case_Lambda *)expr;
2395       int i;
2396       for (i = cl->count; i--; ) {
2397         a_sz = estimate_expr_size(cl->array[i], sz, fuel);
2398         if (a_sz > max_sz) max_sz = a_sz;
2399       }
2400       sz = max_sz;
2401     }
2402     break;
2403   case scheme_application2_type:
2404     {
2405       Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
2406 
2407       sz = estimate_expr_size(app->rator, sz, fuel - 1);
2408       sz = estimate_expr_size(app->rand, sz, fuel - 1);
2409       sz++;
2410 
2411       break;
2412     }
2413   case scheme_application_type:
2414     {
2415       Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
2416       int i;
2417 
2418       for (i = app->num_args + 1; i--; ) {
2419         sz = estimate_expr_size(app->args[i], sz, fuel - 1);
2420       }
2421       sz++;
2422 
2423       break;
2424     }
2425   case scheme_application3_type:
2426     {
2427       Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
2428 
2429       sz = estimate_expr_size(app->rator, sz, fuel - 1);
2430       sz = estimate_expr_size(app->rand1, sz, fuel - 1);
2431       sz = estimate_expr_size(app->rand2, sz, fuel - 1);
2432       sz++;
2433 
2434       break;
2435     }
2436   case scheme_ir_let_header_type:
2437     {
2438       Scheme_IR_Let_Header *head = (Scheme_IR_Let_Header *)expr;
2439       Scheme_Object *body;
2440       Scheme_IR_Let_Value *lv;
2441       int i;
2442 
2443       body = head->body;
2444       for (i = head->num_clauses; i--; ) {
2445 	lv = (Scheme_IR_Let_Value *)body;
2446         sz = estimate_expr_size(lv->value, sz, fuel - 1);
2447 	body = lv->body;
2448         sz++;
2449       }
2450       sz = estimate_expr_size(body, sz, fuel - 1);
2451       break;
2452     }
2453   case scheme_sequence_type:
2454   case scheme_begin0_sequence_type:
2455     {
2456       Scheme_Sequence *seq = (Scheme_Sequence *)expr;
2457       int i;
2458 
2459       for (i = seq->count; i--; ) {
2460 	sz = estimate_expr_size(seq->array[i], sz, fuel - 1);
2461       }
2462 
2463       break;
2464     }
2465   case scheme_branch_type:
2466     {
2467       Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr;
2468 
2469       sz = estimate_expr_size(b->test, sz, fuel - 1);
2470       sz = estimate_expr_size(b->tbranch, sz, fuel - 1);
2471       sz = estimate_expr_size(b->fbranch, sz, fuel - 1);
2472       break;
2473     }
2474   case scheme_ir_lambda_type:
2475     {
2476       sz = estimate_expr_size(((Scheme_Lambda *)expr)->body, sz, fuel - 1);
2477       sz++;
2478       break;
2479     }
2480   case scheme_ir_toplevel_type:
2481     /* FIXME: other syntax types not covered */
2482   default:
2483     sz += 1;
2484     break;
2485   }
2486 
2487   return sz;
2488 }
2489 
estimate_closure_size(Scheme_Object * e)2490 static Scheme_Object *estimate_closure_size(Scheme_Object *e)
2491 {
2492   Scheme_Object *wbl;
2493   int sz;
2494   sz = estimate_expr_size(e, 0, 32);
2495 
2496   wbl = scheme_alloc_object();
2497   wbl->type = scheme_will_be_lambda_type;
2498   SCHEME_WILL_BE_LAMBDA_SIZE(wbl) = sz;
2499   SCHEME_WILL_BE_LAMBDA(wbl) = e;
2500 
2501   return wbl;
2502 }
2503 
no_potential_size(Scheme_Object * v)2504 static Scheme_Object *no_potential_size(Scheme_Object *v)
2505 {
2506   if (v && SCHEME_WILL_BE_LAMBDAP(v))
2507     return NULL;
2508   else
2509     return v;
2510 }
2511 
apply_inlined(Scheme_Lambda * lam,Optimize_Info * info,int argc,Scheme_App_Rec * app,Scheme_App2_Rec * app2,Scheme_App3_Rec * app3,int context,Scheme_Object * orig,Scheme_Object * le_prev,int single_use)2512 static Scheme_Object *apply_inlined(Scheme_Lambda *lam, Optimize_Info *info,
2513 				    int argc, Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3,
2514                                     int context, Scheme_Object *orig, Scheme_Object *le_prev,
2515                                     int single_use)
2516 /* Optimize the body of `lam` given the known arguments in `app`, `app2`, or `app3` */
2517 {
2518   Scheme_IR_Let_Header *lh;
2519   Scheme_IR_Let_Value *lv, *prev = NULL;
2520   Scheme_Object *val;
2521   int i, expected;
2522   Optimize_Info *sub_info;
2523   Scheme_IR_Local **vars;
2524   Scheme_Object *p = lam->body;
2525 
2526   expected = lam->num_params;
2527 
2528   if (!expected) {
2529     /* No arguments, so no need for a `let` wrapper: */
2530     sub_info = optimize_info_add_frame(info, 0);
2531     if (!single_use || lam->ir_info->is_dup)
2532       sub_info->inline_fuel >>= 1;
2533     p = optimize_expr(p, sub_info, context);
2534     info->single_result = sub_info->single_result;
2535     info->preserves_marks = sub_info->preserves_marks;
2536     optimize_info_done(sub_info, NULL);
2537     merge_types(sub_info, info, NULL);
2538 
2539     return replace_tail_inside(p, le_prev, orig);
2540   }
2541 
2542   lh = MALLOC_ONE_TAGGED(Scheme_IR_Let_Header);
2543   lh->iso.so.type = scheme_ir_let_header_type;
2544   lh->count = expected;
2545   lh->num_clauses = expected;
2546 
2547   for (i = 0; i < expected; i++) {
2548     lv = MALLOC_ONE_TAGGED(Scheme_IR_Let_Value);
2549     lv->iso.so.type = scheme_ir_let_value_type;
2550     lv->count = 1;
2551 
2552     vars = MALLOC_N(Scheme_IR_Local*, 1);
2553     vars[0] = lam->ir_info->vars[i];
2554     lv->vars = vars;
2555 
2556     if ((i == expected - 1)
2557         && (SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_HAS_REST)) {
2558       int j;
2559       Scheme_Object *l = scheme_null;
2560 
2561       for (j = argc; j-- > i; ) {
2562         if (app)
2563           val = app->args[j + 1];
2564         else if (app3)
2565           val = (j ? app3->rand2 : app3->rand1);
2566         else if (app2)
2567           val = app2->rand;
2568         else
2569           val = scheme_false;
2570 
2571         l = scheme_make_pair(val, l);
2572       }
2573       l = scheme_make_pair(scheme_list_proc, l);
2574       val = scheme_make_application(l, info);
2575     } else if (app)
2576       val = app->args[i + 1];
2577     else if (app3)
2578       val = (i ? app3->rand2 : app3->rand1);
2579     else
2580       val = app2->rand;
2581 
2582     lv->value = val;
2583 
2584     if (prev)
2585       prev->body = (Scheme_Object *)lv;
2586     else
2587       lh->body = (Scheme_Object *)lv;
2588     prev = lv;
2589   }
2590 
2591   if (prev)
2592     prev->body = p;
2593   else
2594     lh->body = p;
2595 
2596   sub_info = optimize_info_add_frame(info, 0);
2597   if (!single_use || lam->ir_info->is_dup)
2598     sub_info->inline_fuel >>= 1;
2599 
2600   p = optimize_lets((Scheme_Object *)lh, sub_info, context);
2601 
2602   info->single_result = sub_info->single_result;
2603   info->preserves_marks = sub_info->preserves_marks;
2604   optimize_info_done(sub_info, NULL);
2605   merge_types(sub_info, info, NULL);
2606 
2607   return replace_tail_inside(p, le_prev, orig);
2608 }
2609 
scheme_check_leaf_rator(Scheme_Object * le)2610 int scheme_check_leaf_rator(Scheme_Object *le)
2611 {
2612   if (le && SCHEME_PRIMP(le)) {
2613     int opt;
2614     opt = ((Scheme_Prim_Proc_Header *)le)->flags & SCHEME_PRIM_OPT_MASK;
2615     if (opt >= SCHEME_PRIM_OPT_IMMEDIATE)
2616       return 1;
2617   }
2618   return 0;
2619 }
2620 
get_rator_flags(Scheme_Object * rator,int num_args,Optimize_Info * info)2621 static int get_rator_flags(Scheme_Object *rator, int num_args, Optimize_Info *info)
2622 {
2623   rator = lookup_constant_proc(info, rator, num_args);
2624   if (!rator) {
2625     return 0;
2626   } else if (SAME_OBJ(rator, scheme_true)) {
2627     /* wrong arity */
2628     return (LAMBDA_PRESERVES_MARKS | LAMBDA_SINGLE_RESULT);
2629   } else if (SAME_TYPE(SCHEME_TYPE(rator), scheme_struct_proc_shape_type)) {
2630     return (LAMBDA_PRESERVES_MARKS | LAMBDA_SINGLE_RESULT);
2631   } else if (SAME_TYPE(SCHEME_TYPE(rator), scheme_struct_prop_proc_shape_type)) {
2632     switch (SCHEME_PROP_PROC_SHAPE_MODE(rator)) {
2633     case STRUCT_PROP_PROC_SHAPE_PRED:
2634       return (LAMBDA_PRESERVES_MARKS | LAMBDA_SINGLE_RESULT);
2635     case STRUCT_PROP_PROC_SHAPE_GETTER:
2636       if (num_args == 1)
2637         return (LAMBDA_PRESERVES_MARKS | LAMBDA_SINGLE_RESULT);
2638     }
2639   } else if (SCHEME_PRIMP(rator)) {
2640     int opt;
2641     /* special cases for values */
2642     if (SAME_OBJ(rator, scheme_values_proc) && num_args == 1) {
2643       return (LAMBDA_PRESERVES_MARKS | LAMBDA_SINGLE_RESULT);
2644     }
2645     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_ALWAYS_ESCAPES) {
2646       return (LAMBDA_PRESERVES_MARKS | LAMBDA_SINGLE_RESULT);
2647     }
2648     opt = ((Scheme_Prim_Proc_Header *)rator)->flags & SCHEME_PRIM_OPT_MASK;
2649     if (opt >= SCHEME_PRIM_OPT_NONCM) {
2650       return (LAMBDA_PRESERVES_MARKS | LAMBDA_SINGLE_RESULT);
2651     }
2652   } else if (SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_lambda_type)) {
2653     Scheme_Lambda *lam = (Scheme_Lambda *)rator;
2654     return SCHEME_LAMBDA_FLAGS(lam);
2655   }
2656   return 0;
2657 }
2658 
check_single_use(Scheme_Object * var)2659 int check_single_use(Scheme_Object *var)
2660 {
2661    Scheme_IR_Local *v = SCHEME_VAR(var);
2662 
2663   return ((v->use_count == 1)
2664           /* If we're outside the binding, then the binding
2665              itself will remain as a used: */
2666           && !v->optimize_outside_binding
2667           /* To help avoid infinite unrolling,
2668              don't count a self use as "single" use. */
2669           && !v->optimize_unready);
2670 }
2671 
check_potential_size(Scheme_Object * var)2672 int check_potential_size(Scheme_Object *var)
2673 {
2674   Scheme_Object* n;
2675 
2676   n = SCHEME_VAR(var)->optimize.known_val;
2677   if (n && SCHEME_WILL_BE_LAMBDAP(n)) {
2678     return SCHEME_WILL_BE_LAMBDA_SIZE(n);
2679   }
2680 
2681   return 0;
2682 }
2683 
do_lookup_constant_proc(Optimize_Info * info,Scheme_Object * le,int argc,int for_inline,int for_props,int * _single_use,Scheme_Object ** _single_use_var)2684 Scheme_Object *do_lookup_constant_proc(Optimize_Info *info, Scheme_Object *le,
2685                                        int argc, int for_inline, int for_props,
2686                                        int *_single_use, Scheme_Object **_single_use_var)
2687 /* Return a known procedure, if any.
2688    When argc == -1, the result may be a case-lambda or `scheme_constant_key`;
2689    otherwise, unless `for_props`, the arity is used to split a case-lambda to extact
2690    the relevant lambda, and if the arity is wrong, the result is `scheme_true`.
2691    If `for_inline`, the result may be a potential size, otherwise this function
2692    goes inside potential sizes, noinline procedures, lets, begins and other construction,
2693    so the result can't be inlined and must be used only to get the properties
2694    of the actual procedure. */
2695 {
2696   Scheme_Object *prev = NULL;
2697 
2698   *_single_use = 0;
2699 
2700   /* Move inside `let' bindings to get the inner procedure */
2701   if (!for_inline)
2702     extract_tail_inside(&le, &prev, 0);
2703 
2704   le = extract_specialized_proc(le, le);
2705 
2706   if (SCHEME_LAMBDAP(le)) {
2707     /* Found a `((lambda' */
2708     *_single_use = 1;
2709   }
2710 
2711   if (SAME_TYPE(SCHEME_TYPE(le), scheme_ir_local_type)) {
2712     int tmp;
2713     tmp = check_single_use(le);
2714     *_single_use = tmp;
2715     if (tmp)
2716       *_single_use_var = le;
2717     if ((SCHEME_VAR(le)->mode != SCHEME_VAR_MODE_OPTIMIZE)) {
2718       /* We got a local that is bound in a let that is not yet optimized. */
2719       return NULL;
2720     }
2721     le = SCHEME_VAR(le)->optimize.known_val;
2722     if (!le)
2723       return NULL;
2724   }
2725 
2726   if (SAME_TYPE(SCHEME_TYPE(le), scheme_ir_toplevel_type)) {
2727     Scheme_Object *inl;
2728     *_single_use = 0;
2729     do {
2730       inl = get_import_inline(info, (Scheme_IR_Toplevel *)le, argc, for_props);
2731       if ((argc < 0) && SAME_OBJ(inl, scheme_constant_key))
2732         return inl;
2733       if (!inl) inl = get_defn_shape(info, (Scheme_IR_Toplevel *)le);
2734       if (inl) le = inl;
2735     } while (inl && SAME_TYPE(SCHEME_TYPE(le), scheme_ir_toplevel_type));
2736   }
2737 
2738   if (SCHEME_WILL_BE_LAMBDAP(le)) {
2739     if (for_inline)
2740       return le;
2741     else
2742       le = SCHEME_WILL_BE_LAMBDA(le);
2743   }
2744 
2745   if (!for_inline && SAME_TYPE(scheme_noninline_proc_type, SCHEME_TYPE(le))) {
2746     le = SCHEME_BOX_VAL(le);
2747   }
2748 
2749   if (SAME_TYPE(SCHEME_TYPE(le), scheme_struct_proc_shape_type)) {
2750     int ok_arity;
2751     switch (SCHEME_PROC_SHAPE_MODE(le) & STRUCT_PROC_SHAPE_MASK) {
2752     case STRUCT_PROC_SHAPE_CONSTR:
2753       ok_arity = (argc == (SCHEME_PROC_SHAPE_MODE(le) >> STRUCT_PROC_SHAPE_SHIFT));
2754       break;
2755     case STRUCT_PROC_SHAPE_PRED:
2756       ok_arity = (argc == 1);
2757       break;
2758     case STRUCT_PROC_SHAPE_GETTER:
2759       ok_arity = (argc == 1);
2760       break;
2761     case STRUCT_PROC_SHAPE_SETTER:
2762       ok_arity = (argc == 2);
2763       break;
2764     default:
2765       return NULL;
2766     }
2767     if (ok_arity || (argc == -1)) {
2768       return for_inline ? NULL : le;
2769     } else if (for_props)
2770       return le;
2771     else
2772       return scheme_true;
2773   }
2774 
2775   if (SAME_TYPE(SCHEME_TYPE(le), scheme_struct_prop_proc_shape_type)) {
2776     int ok_arity;
2777     switch (SCHEME_PROP_PROC_SHAPE_MODE(le)) {
2778     case STRUCT_PROP_PROC_SHAPE_PRED:
2779       ok_arity = (argc == 1);
2780       break;
2781     case STRUCT_PROP_PROC_SHAPE_GETTER:
2782       ok_arity = (argc == 1) || (argc == 2);
2783       break;
2784     default:
2785       return NULL;
2786     }
2787     if (ok_arity || (argc == -1)) {
2788       return for_inline ? NULL : le;
2789     } else if (for_props)
2790       return le;
2791     else
2792       return scheme_true;
2793   }
2794 
2795   if (SAME_TYPE(SCHEME_TYPE(le), scheme_case_lambda_sequence_type)) {
2796     Scheme_Case_Lambda *cl = (Scheme_Case_Lambda *)le;
2797     Scheme_Object *cp;
2798     int i, count;
2799 
2800     if ((argc == -1) || for_props)
2801       return le;
2802 
2803     count = cl->count;
2804     for (i = 0; i < count; i++) {
2805       cp = cl->array[i];
2806       if (SAME_TYPE(SCHEME_TYPE(cp), scheme_ir_lambda_type)) {
2807         Scheme_Lambda *lam = (Scheme_Lambda *)cp;
2808         if ((lam->num_params == argc)
2809             || ((SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_HAS_REST)
2810                 && (argc + 1 >= lam->num_params))) {
2811           return cp;
2812         }
2813       } else {
2814         scheme_signal_error("internal error: strange case-lambda");
2815       }
2816     }
2817     if (i >= count) {
2818       return scheme_true;
2819     }
2820   }
2821 
2822   if (SAME_TYPE(SCHEME_TYPE(le), scheme_ir_lambda_type)) {
2823     Scheme_Lambda *lam = (Scheme_Lambda *)le;
2824 
2825     if ((argc == -1) || for_props)
2826       return le;
2827 
2828     if ((lam->num_params == argc)
2829         || ((SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_HAS_REST)
2830             && (argc + 1 >= lam->num_params))) {
2831       return le;
2832     } else {
2833       return scheme_true;
2834     }
2835   }
2836 
2837   if (SCHEME_PROCP(le)) {
2838     Scheme_Object *a[1];
2839 
2840     if ((argc == -1) || for_props)
2841       return le;
2842 
2843     a[0] = le;
2844     if (scheme_check_proc_arity(NULL, argc, 0, 1, a))
2845       return le;
2846     else
2847       return scheme_true;
2848   }
2849 
2850   if (for_props
2851       && le
2852       && (SAME_TYPE(SCHEME_TYPE(le), scheme_lambda_type)
2853           || SAME_TYPE(SCHEME_TYPE(le), scheme_case_lambda_sequence_type)))
2854     return le;
2855 
2856   return NULL;
2857 }
2858 
lookup_constant_proc(Optimize_Info * info,Scheme_Object * le,int argc)2859 Scheme_Object *lookup_constant_proc(Optimize_Info *info, Scheme_Object *le, int argc)
2860 {
2861   int single_use = 0;
2862   Scheme_Object *single_use_var;
2863   return do_lookup_constant_proc(info, le, argc, 0, 0, &single_use, &single_use_var);
2864 }
2865 
2866 #if 0
2867 # define LOG_INLINE(x) x
2868 #else
2869 # define LOG_INLINE(x) /*empty*/
2870 #endif
2871 
optimize_for_inline(Optimize_Info * info,Scheme_Object * le,int argc,Scheme_App_Rec * app,Scheme_App2_Rec * app2,Scheme_App3_Rec * app3,int context,int optimized_rator)2872 Scheme_Object *optimize_for_inline(Optimize_Info *info, Scheme_Object *le, int argc,
2873                                    Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3,
2874                                    int context, int optimized_rator)
2875 /* One of app, app2 and app3 should be non-NULL.
2876    If app, we're inlining a general application. If app2, we're inlining an
2877    application with a single argument and if app3, we're inlining an
2878    application with two arguments. */
2879 {
2880   int single_use = 0, psize = 0;
2881   Scheme_Object *prev = NULL, *orig_le = le, *le2, *single_use_var = NULL;
2882   int already_opt = optimized_rator;
2883 
2884   if ((info->inline_fuel < 0) && info->has_nonleaf)
2885     return NULL;
2886 
2887   /* Move inside `let' bindings, so we can convert ((let (....) proc) arg ...)
2888      to (let (....) (proc arg ...)) */
2889   if (already_opt)
2890     extract_tail_inside(&le, &prev, 0);
2891 
2892   le = extract_specialized_proc(le, le);
2893 
2894   if (!already_opt
2895       && SCHEME_LAMBDAP(le)) {
2896     /* We have an immediate `lambda' that wasn't optimized, yet.
2897        Go optimize it, first. */
2898     return NULL;
2899   }
2900 
2901   le2 = le;
2902   le = do_lookup_constant_proc(info, le, argc, 1, 0, &single_use, &single_use_var);
2903 
2904   if (!le) {
2905     info->has_nonleaf = 1;
2906     return NULL;
2907   }
2908 
2909   if (SCHEME_WILL_BE_LAMBDAP(le)) {
2910     psize = SCHEME_WILL_BE_LAMBDA_SIZE(le);
2911     LOG_INLINE(fprintf(stderr, "Potential inline %d %d\n", psize, info->inline_fuel * (argc + 2)));
2912     /* If we inline, the enclosing function will get larger, so we increase
2913        its potential size. */
2914     if (psize <= (info->inline_fuel * (argc + 2)))
2915       info->psize += psize;
2916     info->has_nonleaf = 1;
2917     return NULL;
2918   }
2919 
2920   if (SAME_OBJ(le, scheme_true)) {
2921     /* wrong arity */
2922     int len;
2923     const char *pname = NULL, *context;
2924     info->escapes = 1;
2925     le2 = do_lookup_constant_proc(info, le2, argc, 1, 1, &single_use, &single_use_var);
2926     if (!SAME_TYPE(SCHEME_TYPE(le2), scheme_struct_proc_shape_type)
2927         && !SAME_TYPE(SCHEME_TYPE(le2), scheme_struct_prop_proc_shape_type)){
2928       pname = scheme_get_proc_name(le2, &len, 0);
2929     }
2930     context = scheme_optimize_context_to_string(info->context);
2931     scheme_log(info->logger,
2932                SCHEME_LOG_WARNING,
2933                0,
2934                "warning%s: optimizer detects procedure incorrectly applied to %d arguments%s%s",
2935                context,
2936                argc,
2937                pname ? ": " : "",
2938                pname ? pname : "");
2939     return NULL;
2940   }
2941 
2942   if (SAME_TYPE(SCHEME_TYPE(le), scheme_ir_lambda_type) && (info->inline_fuel >= 0)) {
2943     Scheme_Lambda *lam = (Scheme_Lambda *)le;
2944     int sz, threshold, is_leaf = 0;
2945 
2946     sz = lambda_body_size_plus_info(lam, 1, info, &is_leaf);
2947     if (is_leaf) {
2948       /* encourage inlining of leaves: */
2949       sz >>= 2;
2950     }
2951     threshold = info->inline_fuel * (2 + argc);
2952 
2953     /* Do we have enough fuel? */
2954     if ((sz >= 0) && (single_use || (sz <= threshold))) {
2955       Optimize_Info *sub_info;
2956       sub_info = info;
2957 
2958       /* If optimize_clone succeeds, inlining succeeds. */
2959       le = optimize_clone(single_use, (Scheme_Object *)lam, sub_info, empty_eq_hash_tree, 0);
2960 
2961       if (le) {
2962         LOG_INLINE(fprintf(stderr, "Inline %d[%d]<=%d@%d %d %s\n", sz, is_leaf, threshold, info->inline_fuel,
2963                            single_use, scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL)));
2964         if (scheme_log_level_p(info->logger, SCHEME_LOG_DEBUG))
2965           scheme_log(info->logger,
2966                      SCHEME_LOG_DEBUG,
2967                      0,
2968                      "inlining %s size: %d threshold: %d#<separator>%s",
2969                      scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL),
2970                      sz,
2971                      threshold,
2972                      scheme_optimize_context_to_string(info->context));
2973         if (single_use_var)
2974           SCHEME_VAR(single_use_var)->optimize_used = 0; /* just in case tentatively used */
2975         le = apply_inlined((Scheme_Lambda *)le, sub_info, argc, app, app2, app3, context,
2976                            orig_le, prev, single_use);
2977         return le;
2978       } else {
2979         LOG_INLINE(fprintf(stderr, "No inline %s\n", scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL)));
2980         if (scheme_log_level_p(info->logger, SCHEME_LOG_DEBUG))
2981           scheme_log(info->logger,
2982                      SCHEME_LOG_DEBUG,
2983                      0,
2984                      "no-inlining %s size: %d threshold: %d#<separator>%s",
2985                      scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL),
2986                      sz,
2987                      threshold,
2988                      scheme_optimize_context_to_string(info->context));
2989       }
2990     } else {
2991       LOG_INLINE(fprintf(stderr, "No fuel %s %d[%d]>%d@%d %d\n", scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL),
2992                          sz, is_leaf, threshold,
2993                          info->inline_fuel, info->use_psize));
2994       if (scheme_log_level_p(info->logger, SCHEME_LOG_DEBUG))
2995         scheme_log(info->logger,
2996                    SCHEME_LOG_DEBUG,
2997                    0,
2998                    "out-of-fuel %s size: %d threshold: %d#<separator>%s",
2999                    scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL),
3000                    sz,
3001                    threshold,
3002                    scheme_optimize_context_to_string(info->context));
3003     }
3004   }
3005 
3006   if (!scheme_check_leaf_rator(le))
3007     info->has_nonleaf = 1;
3008 
3009   return NULL;
3010 }
3011 
is_local_type_expression(Scheme_Object * expr,Optimize_Info * info)3012 static int is_local_type_expression(Scheme_Object *expr, Optimize_Info *info)
3013 /* Get an unboxing type (e.g., flonum) for `expr` */
3014 {
3015   return scheme_predicate_to_local_type(expr_implies_predicate(expr, info));
3016 }
3017 
register_local_argument_types(Scheme_App_Rec * app,Scheme_App2_Rec * app2,Scheme_App3_Rec * app3,Optimize_Info * info)3018 static void register_local_argument_types(Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3,
3019                                           Optimize_Info *info)
3020 /* If `rator` is a variable bound to a `lambda`, record the types of actual arguments
3021    provided in a function call. If all calls are consistent with unboxing, then the
3022    procedure will accept unboxed arguments at run time. */
3023 {
3024   Scheme_Object *rator, *rand, *le;
3025   int n, i, nth_app;
3026 
3027   if (app) {
3028     rator = app->args[0];
3029     n = app->num_args;
3030     nth_app = SCHEME_APPN_FLAGS(app) & APPN_POSITION_MASK;
3031   } else if (app2) {
3032     rator = app2->rator;
3033     n = 1;
3034     nth_app = SCHEME_APPN_FLAGS(app2) & APPN_POSITION_MASK;
3035   } else {
3036     rator = app3->rator;
3037     n = 2;
3038     nth_app = SCHEME_APPN_FLAGS(app3) & APPN_POSITION_MASK;
3039   }
3040 
3041   if (SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_local_type)) {
3042     le = optimize_info_lookup(rator);
3043     if (le && SCHEME_WILL_BE_LAMBDAP(le))
3044       le = SCHEME_WILL_BE_LAMBDA(le);
3045 
3046     if (le && SAME_TYPE(SCHEME_TYPE(le), scheme_ir_lambda_type)) {
3047       Scheme_Lambda *lam = (Scheme_Lambda *)le;
3048       if ((lam->num_params == n)
3049           && !(SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_HAS_REST)) {
3050         Scheme_Object *pred;
3051 
3052         if (!lam->ir_info->arg_types) {
3053           Scheme_Object **arg_types;
3054           short *contributors;
3055           arg_types = MALLOC_N(Scheme_Object*, n);
3056           lam->ir_info->arg_types = arg_types;
3057           contributors = MALLOC_N_ATOMIC(short, n);
3058           memset(contributors, 0, sizeof(short) * n);
3059           lam->ir_info->arg_type_contributors = contributors;
3060         }
3061 
3062         for (i = 0; i < n; i++) {
3063           if (app)
3064             rand = app->args[i+1];
3065           else if (app2)
3066             rand = app2->rand;
3067           else {
3068             if (!i)
3069               rand = app3->rand1;
3070             else
3071               rand = app3->rand2;
3072           }
3073 
3074           if (lam->ir_info->arg_types[i]
3075               || !lam->ir_info->arg_type_contributors[i]) {
3076             int widen_to_top = 0;
3077 
3078             pred = expr_implies_predicate(rand, info);
3079 
3080             if (pred) {
3081               if (!lam->ir_info->arg_type_contributors[i]) {
3082                 lam->ir_info->arg_types[i] = pred;
3083                 if (nth_app)
3084                   lam->ir_info->arg_type_contributors[i] |= (1 << (nth_app-1));
3085               } else if (predicate_implies(pred, lam->ir_info->arg_types[i])) {
3086                 /* ok */
3087                 if (nth_app)
3088                   lam->ir_info->arg_type_contributors[i] |= (1 << (nth_app-1));
3089               } else if (predicate_implies(lam->ir_info->arg_types[i], pred)) {
3090                 /* widen */
3091                 lam->ir_info->arg_types[i] = pred;
3092                 if (nth_app)
3093                   lam->ir_info->arg_type_contributors[i] |= (1 << (nth_app-1));
3094               } else
3095                 widen_to_top = 1;
3096             } else
3097               widen_to_top = 1;
3098 
3099             if (widen_to_top) {
3100               if (nth_app) {
3101                 /* Since we cant provide a nice type right now, just
3102                    don't check in, in case a future iteration provides
3103                    better information. If we never check in with a type,
3104                    it will count as widening in the end. */
3105               } else {
3106                 /* since we don't have an identity, the lambda won't
3107                    be able to tell whether all apps have checked in,
3108                    so we have to registers a "top" as an anonymous
3109                    contributor. */
3110                 lam->ir_info->arg_type_contributors[i] |= (1 << (SCHEME_USE_COUNT_INF-1));
3111                 lam->ir_info->arg_types[i] = NULL;
3112               }
3113             }
3114           }
3115         }
3116       }
3117     }
3118   }
3119 }
3120 
reset_rator(Scheme_Object * app,Scheme_Object * a)3121 static void reset_rator(Scheme_Object *app, Scheme_Object *a)
3122 {
3123   switch (SCHEME_TYPE(app)) {
3124   case scheme_application_type:
3125     ((Scheme_App_Rec *)app)->args[0] = a;
3126     break;
3127   case scheme_application2_type:
3128     ((Scheme_App2_Rec *)app)->rator = a;
3129     break;
3130   case scheme_application3_type:
3131     ((Scheme_App3_Rec *)app)->rator = a;
3132     break;
3133   }
3134 }
3135 
set_application_omittable(Scheme_Object * app,Scheme_Object * a)3136 static void set_application_omittable(Scheme_Object *app, Scheme_Object *a)
3137 {
3138   switch (SCHEME_TYPE(app)) {
3139   case scheme_application_type:
3140     SCHEME_APPN_FLAGS((Scheme_App_Rec *)app) |= APPN_FLAG_OMITTABLE;
3141     break;
3142   case scheme_application2_type:
3143     SCHEME_APPN_FLAGS((Scheme_App2_Rec *)app) |= APPN_FLAG_OMITTABLE;
3144     break;
3145   case scheme_application3_type:
3146     SCHEME_APPN_FLAGS((Scheme_App3_Rec *)app) |= APPN_FLAG_OMITTABLE;
3147     break;
3148   }
3149 }
3150 
check_app_let_rator(Scheme_Object * app,Scheme_Object * rator,Optimize_Info * info,int argc,int context)3151 static Scheme_Object *check_app_let_rator(Scheme_Object *app, Scheme_Object *rator, Optimize_Info *info,
3152                                           int argc, int context)
3153 /* Convert ((let (....) E) arg ...) to (let (....) (E arg ...)) and
3154    ((begin .... E) arg ...) to (begin .... (E arg ...)), in case
3155    the `let' or `begin' is immediately apparent. We check for this
3156    pattern again in optimize_for_inline() after optimizing a rator. */
3157 {
3158   Scheme_Object *orig_rator = rator, *inside = NULL;
3159 
3160   extract_tail_inside(&rator, &inside, 0);
3161 
3162   if (!inside)
3163     return NULL;
3164 
3165   /* Moving a variable into application position: */
3166   if (SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_local_type)) {
3167     Scheme_IR_Local *var = SCHEME_VAR(rator);
3168     if (var->non_app_count < SCHEME_USE_COUNT_INF)
3169       --var->non_app_count;
3170   }
3171 
3172   reset_rator(app, rator);
3173   orig_rator = replace_tail_inside(app, inside, orig_rator);
3174 
3175   return optimize_expr(orig_rator, info, context);
3176 }
3177 
is_primitive_allocating(Scheme_Object * rator,int argc)3178 XFORM_NONGCING static int is_primitive_allocating(Scheme_Object *rator, int argc)
3179 {
3180   if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & (SCHEME_PRIM_IS_OMITABLE_ALLOCATION
3181                                            | SCHEME_PRIM_IS_ARITY_0_OMITABLE_ALLOCATION
3182                                            | SCHEME_PRIM_IS_EVEN_ARITY_OMITABLE_ALLOCATION))
3183     return scheme_is_omitable_primitive(rator, argc);
3184 
3185   return 0;
3186 }
3187 
is_nonmutating_nondependant_primitive(Scheme_Object * rator,int argc)3188 XFORM_NONGCING static int is_nonmutating_nondependant_primitive(Scheme_Object *rator, int argc)
3189 /* Does not include SCHEME_PRIM_IS_UNSAFE_OMITABLE, because those can
3190    depend on earlier tests (explicit or implicit) for whether the
3191    unsafe operation is defined */
3192 {
3193   if (SCHEME_PRIMP(rator)
3194       && (((SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_IS_OMITABLE)
3195            || is_primitive_allocating(rator, argc))
3196           && !(SCHEME_PRIM_PROC_OPT_FLAGS(rator) & (SCHEME_PRIM_IS_UNSAFE_OMITABLE))
3197           && !((SAME_OBJ(scheme_values_proc, rator) && (argc != 1))))
3198       && (argc >= ((Scheme_Primitive_Proc *)rator)->mina)
3199       && (argc <= ((Scheme_Primitive_Proc *)rator)->mu.maxa))
3200     return 1;
3201 
3202   return 0;
3203 }
3204 
is_noncapturing_primitive(Scheme_Object * rator,int n)3205 XFORM_NONGCING static int is_noncapturing_primitive(Scheme_Object *rator, int n)
3206 {
3207   if (SCHEME_PRIMP(rator)) {
3208     int opt, t;
3209     opt = ((Scheme_Prim_Proc_Header *)rator)->flags & SCHEME_PRIM_OPT_MASK;
3210     if (opt >= SCHEME_PRIM_OPT_IMMEDIATE)
3211       return 1;
3212     if (opt >= SCHEME_PRIM_OPT_NONCM) {
3213       if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_ALWAYS_ESCAPES) {
3214         /* even if a continuation is captured, it won't get back */
3215         return 1;
3216       }
3217     }
3218     t = (((Scheme_Primitive_Proc *)rator)->pp.flags & SCHEME_PRIM_OTHER_TYPE_MASK);
3219     if (!n && (t == SCHEME_PRIM_TYPE_PARAMETER))
3220       return 1;
3221     if (SAME_OBJ(rator, scheme_values_proc))
3222       return 1;
3223   }
3224 
3225   return 0;
3226 }
3227 
is_nonsaving_primitive(Scheme_Object * rator,int n)3228 XFORM_NONGCING static int is_nonsaving_primitive(Scheme_Object *rator, int n)
3229 {
3230   if (SCHEME_PRIMP(rator)) {
3231     int opt;
3232     opt = ((Scheme_Prim_Proc_Header *)rator)->flags & SCHEME_PRIM_OPT_MASK;
3233     if (opt >= SCHEME_PRIM_OPT_IMMEDIATE)
3234       return 1;
3235     if (SAME_OBJ(rator, scheme_values_proc))
3236       return 1;
3237   }
3238 
3239   return 0;
3240 }
3241 
is_always_escaping_primitive(Scheme_Object * rator)3242 XFORM_NONGCING static int is_always_escaping_primitive(Scheme_Object *rator)
3243 {
3244   if (SCHEME_PRIMP(rator)
3245       && (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_ALWAYS_ESCAPES)) {
3246     return 1;
3247   }
3248   return 0;
3249 }
3250 
3251 #define IS_NAMED_PRIM(p, nm) (!strcmp(((Scheme_Primitive_Proc *)p)->name, nm))
3252 
wants_local_type_arguments(Scheme_Object * rator,int argpos)3253 XFORM_NONGCING static int wants_local_type_arguments(Scheme_Object *rator, int argpos)
3254 {
3255   if (SCHEME_PRIMP(rator)) {
3256     int flags;
3257     flags = SCHEME_PRIM_PROC_OPT_FLAGS(rator);
3258 
3259     if (argpos == 0) {
3260       if (flags & SCHEME_PRIM_WANTS_FLONUM_FIRST)
3261         return SCHEME_LOCAL_TYPE_FLONUM;
3262       if (flags & SCHEME_PRIM_WANTS_EXTFLONUM_FIRST)
3263         return SCHEME_LOCAL_TYPE_EXTFLONUM;
3264     } else if (argpos == 1) {
3265       if (flags & SCHEME_PRIM_WANTS_FLONUM_SECOND)
3266         return SCHEME_LOCAL_TYPE_FLONUM;
3267       if (flags & SCHEME_PRIM_WANTS_EXTFLONUM_SECOND)
3268         return SCHEME_LOCAL_TYPE_EXTFLONUM;
3269     } else if (argpos == 2) {
3270       if (flags & SCHEME_PRIM_WANTS_FLONUM_THIRD)
3271         return SCHEME_LOCAL_TYPE_FLONUM;
3272       if (flags & SCHEME_PRIM_WANTS_EXTFLONUM_THIRD)
3273         return SCHEME_LOCAL_TYPE_EXTFLONUM;
3274     }
3275   }
3276 
3277   return 0;
3278 }
3279 
produces_local_type(Scheme_Object * rator,int argc)3280 static int produces_local_type(Scheme_Object *rator, int argc)
3281 {
3282   if (SCHEME_PRIMP(rator)
3283       && (argc >= ((Scheme_Primitive_Proc *)rator)->mina)
3284       && (argc <= ((Scheme_Primitive_Proc *)rator)->mu.maxa)) {
3285     int flags;
3286     flags = SCHEME_PRIM_PROC_OPT_FLAGS(rator);
3287     return SCHEME_PRIM_OPT_TYPE(flags);
3288   }
3289 
3290   return 0;
3291 }
3292 
local_type_to_predicate(int t)3293 static Scheme_Object *local_type_to_predicate(int t)
3294 {
3295   switch (t) {
3296   case SCHEME_LOCAL_TYPE_FLONUM:
3297     return scheme_flonum_p_proc;
3298   case SCHEME_LOCAL_TYPE_FIXNUM:
3299     return scheme_fixnum_p_proc;
3300   case SCHEME_LOCAL_TYPE_EXTFLONUM:
3301     return scheme_extflonum_p_proc;
3302   }
3303   return NULL;
3304 }
3305 
scheme_predicate_to_local_type(Scheme_Object * pred)3306 int scheme_predicate_to_local_type(Scheme_Object *pred)
3307 {
3308   if (!pred)
3309     return 0;
3310   if (SAME_OBJ(scheme_flonum_p_proc, pred))
3311     return SCHEME_LOCAL_TYPE_FLONUM;
3312   if (SAME_OBJ(scheme_fixnum_p_proc, pred))
3313     return SCHEME_LOCAL_TYPE_FIXNUM;
3314   if (SAME_OBJ(scheme_extflonum_p_proc, pred))
3315     return SCHEME_LOCAL_TYPE_EXTFLONUM;
3316   return 0;
3317 }
3318 
scheme_expr_produces_local_type(Scheme_Object * expr,int * _involves_k_cross)3319 int scheme_expr_produces_local_type(Scheme_Object *expr, int *_involves_k_cross)
3320 {
3321   if (_involves_k_cross) *_involves_k_cross = 0;
3322   return scheme_predicate_to_local_type(do_expr_implies_predicate(expr, NULL, _involves_k_cross,
3323                                                                   10, empty_eq_hash_tree));
3324 }
3325 
rator_implies_predicate(Scheme_Object * rator,Optimize_Info * info,int argc)3326 static Scheme_Object *rator_implies_predicate(Scheme_Object *rator, Optimize_Info *info, int argc)
3327 {
3328   if (SCHEME_PRIMP(rator)) {
3329     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_PRODUCES_REAL)
3330       return scheme_real_p_proc;
3331     else if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_PRODUCES_NUMBER)
3332       return scheme_number_p_proc;
3333     else if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_PRODUCES_BOOL)
3334       return scheme_boolean_p_proc;
3335     else if (SAME_OBJ(rator, scheme_cons_proc))
3336       return scheme_pair_p_proc;
3337     else if (SAME_OBJ(rator, scheme_unsafe_cons_list_proc))
3338       return scheme_list_pair_p_proc;
3339     else if (SAME_OBJ(rator, scheme_mcons_proc))
3340       return scheme_mpair_p_proc;
3341     else if (SAME_OBJ(rator, scheme_list_proc)) {
3342       if (argc >= 1)
3343         return scheme_list_pair_p_proc;
3344       else
3345         return scheme_null_p_proc;
3346     } else if (SAME_OBJ(rator, scheme_list_star_proc)) {
3347       if (argc > 2)
3348         return scheme_pair_p_proc;
3349     } else if (IS_NAMED_PRIM(rator, "vector->list")
3350                || IS_NAMED_PRIM(rator, "map")) {
3351       return scheme_list_p_proc;
3352     } else if (IS_NAMED_PRIM(rator, "string-ref")) {
3353       return scheme_char_p_proc;
3354     } else if (IS_NAMED_PRIM(rator, "string-append")
3355                || IS_NAMED_PRIM(rator, "string-append-immutable")
3356                || IS_NAMED_PRIM(rator, "string->immutable-string")
3357                || IS_NAMED_PRIM(rator, "symbol->string")
3358                || IS_NAMED_PRIM(rator, "symbol->immutable-string")
3359                || IS_NAMED_PRIM(rator, "keyword->string")
3360                || IS_NAMED_PRIM(rator, "keyword->immutable-string")) {
3361         return scheme_string_p_proc;
3362     } else if (IS_NAMED_PRIM(rator, "bytes-append")
3363                || IS_NAMED_PRIM(rator, "bytes->immutable-bytes")) {
3364         return scheme_byte_string_p_proc;
3365     } else if (SAME_OBJ(rator, scheme_vector_proc)
3366                || SAME_OBJ(rator, scheme_vector_immutable_proc)
3367                || SAME_OBJ(rator, scheme_make_vector_proc)
3368                || SAME_OBJ(rator, scheme_list_to_vector_proc)
3369                || SAME_OBJ(rator, scheme_struct_to_vector_proc)
3370                || IS_NAMED_PRIM(rator, "vector->immutable-vector"))
3371       return scheme_vector_p_proc;
3372     else if (SAME_OBJ(rator, scheme_box_proc)
3373              || SAME_OBJ(rator, scheme_box_immutable_proc))
3374       return scheme_box_p_proc;
3375     else if (SAME_OBJ(rator, scheme_void_proc))
3376       return scheme_void_p_proc;
3377     else if (SAME_OBJ(rator, scheme_procedure_specialize_proc))
3378       return scheme_procedure_p_proc;
3379     else if (IS_NAMED_PRIM(rator, "vector-set!")
3380              || IS_NAMED_PRIM(rator, "string-set!")
3381              || IS_NAMED_PRIM(rator, "bytes-set!")
3382              || IS_NAMED_PRIM(rator, "set-box!"))
3383       return scheme_void_p_proc;
3384     else if (IS_NAMED_PRIM(rator, "string->symbol")
3385              || IS_NAMED_PRIM(rator, "gensym"))
3386       return scheme_symbol_p_proc;
3387     else if (IS_NAMED_PRIM(rator, "string->keyword"))
3388       return scheme_keyword_p_proc;
3389 
3390     {
3391       Scheme_Object *p;
3392       p = local_type_to_predicate(produces_local_type(rator, argc));
3393       if (p)
3394         return p;
3395     }
3396   }
3397 
3398   {
3399     Scheme_Object *shape;
3400     shape = get_struct_proc_shape(rator, info, 1);
3401     if (shape) {
3402       if (SAME_TYPE(SCHEME_TYPE(shape), scheme_struct_proc_shape_type)) {
3403         if (((SCHEME_PROC_SHAPE_MODE(shape) & STRUCT_PROC_SHAPE_MASK) == STRUCT_PROC_SHAPE_PRED)) {
3404           return scheme_boolean_p_proc;
3405         }
3406       } else if (SAME_TYPE(SCHEME_TYPE(shape), scheme_struct_prop_proc_shape_type)) {
3407         if (SCHEME_PROP_PROC_SHAPE_MODE(shape) == STRUCT_PROP_PROC_SHAPE_PRED) {
3408           return scheme_boolean_p_proc;
3409         }
3410       }
3411     }
3412   }
3413 
3414   return NULL;
3415 }
3416 
do_expr_implies_predicate(Scheme_Object * expr,Optimize_Info * info,int * _involves_k_cross,int fuel,Scheme_Hash_Tree * ignore_vars)3417 static Scheme_Object *do_expr_implies_predicate(Scheme_Object *expr, Optimize_Info *info,
3418                                                 int *_involves_k_cross, int fuel,
3419                                                 Scheme_Hash_Tree *ignore_vars)
3420 /* can be called by the JIT with info = NULL;
3421    in that case, beware that the validator must be
3422    able to reconstruct the result in a shallow way, so don't
3423    make the result of a function call depend on its arguments */
3424 {
3425   if (fuel <= 0)
3426     return NULL;
3427 
3428   switch (SCHEME_TYPE(expr)) {
3429   case scheme_ir_local_type:
3430     {
3431       if (scheme_eq_hash_tree_get(ignore_vars, expr))
3432         return NULL;
3433 
3434       if (!SCHEME_VAR(expr)->mutated) {
3435         Scheme_Object *p;
3436 
3437         if (info) {
3438           p = optimize_get_predicate(info, expr, 0);
3439           if (p)
3440             return p;
3441         }
3442 
3443         p = local_type_to_predicate(SCHEME_VAR(expr)->val_type);
3444         if (p) {
3445           if (_involves_k_cross
3446               && SCHEME_VAR(expr)->escapes_after_k_tick)
3447             *_involves_k_cross = 1;
3448           return p;
3449         }
3450 
3451         if ((SCHEME_VAR(expr)->mode == SCHEME_VAR_MODE_OPTIMIZE)
3452             && SCHEME_VAR(expr)->optimize.known_val)
3453           return do_expr_implies_predicate(SCHEME_VAR(expr)->optimize.known_val, info, _involves_k_cross,
3454                                            fuel-1, ignore_vars);
3455       }
3456     }
3457     break;
3458   case scheme_application2_type:
3459     {
3460       Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
3461 
3462       if (SCHEME_PRIMP(app->rator)
3463           && SCHEME_PRIM_PROC_OPT_FLAGS(app->rator) & SCHEME_PRIM_CLOSED_ON_REALS) {
3464         Scheme_Object *p;
3465         p = do_expr_implies_predicate(app->rand, info, NULL, fuel-1, ignore_vars);
3466         if (p && predicate_implies(p, scheme_real_p_proc))
3467           return scheme_real_p_proc;
3468       }
3469 
3470       if (SAME_OBJ(app->rator, scheme_cdr_proc)
3471           || SAME_OBJ(app->rator, scheme_unsafe_cdr_proc)) {
3472         Scheme_Object *p;
3473         p = do_expr_implies_predicate(app->rand, info, NULL, fuel-1, ignore_vars);
3474         if (predicate_implies(p, scheme_list_p_proc))
3475           return scheme_list_p_proc;
3476       }
3477 
3478       return rator_implies_predicate(app->rator, info, 1);
3479     }
3480     break;
3481   case scheme_application3_type:
3482     {
3483       Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
3484       if (SCHEME_PRIMP(app->rator)
3485           && (SCHEME_PRIM_PROC_OPT_FLAGS(app->rator) & SCHEME_PRIM_IS_BINARY_INLINED)
3486           && IS_NAMED_PRIM(app->rator, "bitwise-and")) {
3487          /* Assume that a fixnum argument to bitwise-and will never get lost,
3488             and so the validator will be able to confirm that a `bitwise-and`
3489             combination produces a fixnum if either argument is a literal,
3490             nonnegative fixnum. */
3491          if ((SCHEME_INTP(app->rand1)
3492               && (SCHEME_INT_VAL(app->rand1) >= 0)
3493               && IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand1)))
3494              || (SCHEME_INTP(app->rand2)
3495                  && (SCHEME_INT_VAL(app->rand2) >= 0)
3496                  && IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand2)))) {
3497            return scheme_fixnum_p_proc;
3498          }
3499       }
3500 
3501       if (SCHEME_PRIMP(app->rator)
3502           && SCHEME_PRIM_PROC_OPT_FLAGS(app->rator) & SCHEME_PRIM_CLOSED_ON_REALS) {
3503         Scheme_Object *p;
3504         p = do_expr_implies_predicate(app->rand1, info, NULL, fuel-1, ignore_vars);
3505         if (p && predicate_implies(p, scheme_real_p_proc)) {
3506           p = do_expr_implies_predicate(app->rand2, info, NULL, fuel-1, ignore_vars);
3507           if (p && predicate_implies(p, scheme_real_p_proc)) {
3508             return scheme_real_p_proc;
3509           }
3510         }
3511       }
3512 
3513       if (SAME_OBJ(app->rator, scheme_cons_proc)) {
3514         Scheme_Object *p;
3515         p = do_expr_implies_predicate(app->rand2, info, NULL, fuel-1, ignore_vars);
3516         if (SAME_OBJ(p, scheme_list_pair_p_proc)
3517             || SAME_OBJ(p, scheme_list_p_proc)
3518             || SAME_OBJ(p, scheme_null_p_proc))
3519           return scheme_list_pair_p_proc;
3520       }
3521 
3522       if (SCHEME_PRIMP(app->rator)
3523           && IS_NAMED_PRIM(app->rator, "append")) {
3524         Scheme_Object *p;
3525         p = do_expr_implies_predicate(app->rand2, info, NULL, fuel-1, ignore_vars);
3526         if (SAME_OBJ(p, scheme_list_pair_p_proc))
3527           return scheme_list_pair_p_proc;
3528         if (SAME_OBJ(p, scheme_list_p_proc)
3529             || SAME_OBJ(p, scheme_null_p_proc))
3530           return scheme_list_p_proc;
3531       }
3532 
3533       return rator_implies_predicate(app->rator, info, 2);
3534     }
3535     break;
3536   case scheme_application_type:
3537     {
3538       Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
3539 
3540       if (SCHEME_PRIMP(app->args[0])
3541           && SCHEME_PRIM_PROC_OPT_FLAGS(app->args[0]) & SCHEME_PRIM_CLOSED_ON_REALS) {
3542         Scheme_Object *p;
3543         int i;
3544         for (i = 0; i < app->num_args; i++) {
3545           p = do_expr_implies_predicate(app->args[i+1], info, NULL, fuel-1, ignore_vars);
3546           if (!p || !predicate_implies(p, scheme_real_p_proc))
3547             break;
3548         }
3549         if (i >= app->num_args)
3550           return scheme_real_p_proc;
3551       }
3552 
3553       if (SCHEME_PRIMP(app->args[0])
3554           && IS_NAMED_PRIM(app->args[0], "append")) {
3555         Scheme_Object *p;
3556         p = do_expr_implies_predicate(app->args[app->num_args], info, NULL, fuel-1, ignore_vars);
3557         if (SAME_OBJ(p, scheme_list_pair_p_proc))
3558           return scheme_list_pair_p_proc;
3559         if (SAME_OBJ(p, scheme_list_p_proc)
3560             || SAME_OBJ(p, scheme_null_p_proc))
3561           return scheme_list_p_proc;
3562       }
3563 
3564       return rator_implies_predicate(app->args[0], info, app->num_args);
3565     }
3566     break;
3567   case scheme_ir_lambda_type:
3568     return scheme_procedure_p_proc;
3569     break;
3570   case scheme_case_lambda_sequence_type:
3571     return scheme_procedure_p_proc;
3572     break;
3573   case scheme_branch_type:
3574     {
3575       Scheme_Object *l, *r;
3576       Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr;
3577       l = do_expr_implies_predicate(b->tbranch, info, _involves_k_cross, fuel-1, ignore_vars);
3578       if (l) {
3579         r = do_expr_implies_predicate(b->fbranch, info, _involves_k_cross, fuel-1, ignore_vars);
3580         if (predicate_implies(l, r))
3581           return r;
3582         else if (predicate_implies(r, l))
3583           return l;
3584         else
3585           return NULL;
3586       }
3587     }
3588     break;
3589   case scheme_sequence_type:
3590     {
3591       Scheme_Sequence *seq = (Scheme_Sequence *)expr;
3592 
3593       return do_expr_implies_predicate(seq->array[seq->count-1], info, _involves_k_cross, fuel-1, ignore_vars);
3594     }
3595   case scheme_with_cont_mark_type:
3596     {
3597       Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)expr;
3598 
3599       return do_expr_implies_predicate(wcm->body, info, _involves_k_cross, fuel-1, ignore_vars);
3600     }
3601   case scheme_ir_let_header_type:
3602     {
3603       Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)expr;
3604       Scheme_IR_Let_Value *irlv;
3605       int i, j;
3606       expr = lh->body;
3607       for (i = 0; i < lh->num_clauses; i++) {
3608         irlv = (Scheme_IR_Let_Value *)expr;
3609         for (j = 0; j < irlv->count; j++) {
3610           ignore_vars = scheme_hash_tree_set(ignore_vars, (Scheme_Object *)irlv->vars[j],
3611                                              scheme_true);
3612         }
3613         expr = irlv->body;
3614       }
3615       return do_expr_implies_predicate(expr, info, _involves_k_cross, fuel-1, ignore_vars);
3616     }
3617     break;
3618   case scheme_begin0_sequence_type:
3619     {
3620       Scheme_Sequence *seq = (Scheme_Sequence *)expr;
3621 
3622       return do_expr_implies_predicate(seq->array[0], info, _involves_k_cross, fuel-1, ignore_vars);
3623     }
3624   case scheme_vector_type:
3625     return scheme_vector_p_proc;
3626     break;
3627   case scheme_box_type:
3628     return scheme_box_p_proc;
3629     break;
3630   default:
3631     if (SCHEME_NUMBERP(expr)) {
3632       if (SCHEME_FLOATP(expr))
3633         return scheme_flonum_p_proc;
3634       if (SCHEME_LONG_DBLP(expr))
3635         return scheme_extflonum_p_proc;
3636       if (SCHEME_INTP(expr)
3637           && IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(expr)))
3638         return scheme_fixnum_p_proc;
3639       if (SCHEME_REALP(expr))
3640         return scheme_real_p_proc;
3641       return scheme_number_p_proc;
3642     }
3643 
3644     if (SCHEME_NULLP(expr))
3645       return scheme_null_p_proc;
3646     if (SCHEME_PAIRP(expr)) {
3647       if (scheme_is_list(expr))
3648         return scheme_list_pair_p_proc;
3649       return scheme_pair_p_proc;
3650     }
3651     if (SCHEME_MPAIRP(expr))
3652       return scheme_mpair_p_proc;
3653     if (SCHEME_CHAR_STRINGP(expr))
3654       return scheme_string_p_proc;
3655     if (SCHEME_BYTE_STRINGP(expr))
3656       return scheme_byte_string_p_proc;
3657     if (SCHEME_VOIDP(expr))
3658       return scheme_void_p_proc;
3659     if (SCHEME_EOFP(expr))
3660       return scheme_eof_object_p_proc;
3661     if (SCHEME_KEYWORDP(expr))
3662       return scheme_keyword_p_proc;
3663     if (SCHEME_SYMBOLP(expr))
3664       return scheme_symbol_p_proc;
3665     if (SCHEME_CHARP(expr)) {
3666       if (SCHEME_CHAR_VAL(expr) < 256)
3667         return scheme_interned_char_p_proc;
3668       return scheme_char_p_proc;
3669     }
3670     if (SAME_OBJ(expr, scheme_true))
3671       return scheme_true_object_p_proc;
3672     if (SCHEME_FALSEP(expr))
3673       return scheme_not_proc;
3674     if (SCHEME_PROCP(expr))
3675       return scheme_procedure_p_proc;
3676     if (SCHEME_LONG_DBLP(expr))
3677       return scheme_extflonum_p_proc;
3678   }
3679 
3680   /* This test is slower, so put it at the end */
3681   if (info
3682       && lookup_constant_proc(info, expr, -1)) {
3683     return scheme_procedure_p_proc;
3684   }
3685 
3686   return NULL;
3687 }
3688 
expr_implies_predicate(Scheme_Object * expr,Optimize_Info * info)3689 static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info *info)
3690 {
3691   return do_expr_implies_predicate(expr, info, NULL, 5, empty_eq_hash_tree);
3692 }
3693 
finish_optimize_app(Scheme_Object * o,Optimize_Info * info,int context)3694 static Scheme_Object *finish_optimize_app(Scheme_Object *o, Optimize_Info *info, int context)
3695 {
3696   switch(SCHEME_TYPE(o)) {
3697   case scheme_application_type:
3698     return finish_optimize_application((Scheme_App_Rec *)o, info, context);
3699   case scheme_application2_type:
3700     return finish_optimize_application2((Scheme_App2_Rec *)o, info, context);
3701   case scheme_application3_type:
3702     return finish_optimize_application3((Scheme_App3_Rec *)o, info, context);
3703   default:
3704     return o; /* may be a constant due to constant-folding */
3705   }
3706 }
3707 
direct_apply(Scheme_Object * expr,Scheme_Object * rator,Scheme_Object * last_rand,Optimize_Info * info)3708 static Scheme_Object *direct_apply(Scheme_Object *expr, Scheme_Object *rator, Scheme_Object *last_rand, Optimize_Info *info)
3709 /* Convert `(apply f arg1 ... (list arg2 ...))` to `(f arg1 ... arg2 ...)` */
3710 {
3711   if (SAME_OBJ(rator, scheme_apply_proc)) {
3712     switch(SCHEME_TYPE(last_rand)) {
3713     case scheme_application_type:
3714       rator = ((Scheme_App_Rec *)last_rand)->args[0];
3715       break;
3716     case scheme_application2_type:
3717       rator = ((Scheme_App2_Rec *)last_rand)->rator;
3718       break;
3719     case scheme_application3_type:
3720       rator = ((Scheme_App3_Rec *)last_rand)->rator;
3721       break;
3722     case scheme_pair_type:
3723       if (scheme_is_list(last_rand))
3724         rator = scheme_list_proc;
3725       else
3726         rator = NULL;
3727       break;
3728     case scheme_null_type:
3729       rator = scheme_list_proc;
3730       break;
3731     default:
3732       rator = NULL;
3733       break;
3734     }
3735 
3736     if (rator && SAME_OBJ(rator, scheme_list_proc)) {
3737       /* Convert (apply f arg1 ... (list arg2 ...))
3738          to (f arg1 ... arg2 ...) */
3739       Scheme_Object *l = scheme_null;
3740       int i;
3741 
3742       switch(SCHEME_TYPE(last_rand)) {
3743       case scheme_application_type:
3744         for (i = ((Scheme_App_Rec *)last_rand)->num_args; i--; ) {
3745           l = scheme_make_pair(((Scheme_App_Rec *)last_rand)->args[i+1], l);
3746         }
3747         break;
3748       case scheme_application2_type:
3749         l = scheme_make_pair(((Scheme_App2_Rec *)last_rand)->rand, l);
3750         break;
3751       case scheme_application3_type:
3752         l = scheme_make_pair(((Scheme_App3_Rec *)last_rand)->rand2, l);
3753         l = scheme_make_pair(((Scheme_App3_Rec *)last_rand)->rand1, l);
3754         break;
3755       case scheme_pair_type:
3756         l = last_rand;
3757         break;
3758       case scheme_null_type:
3759         l = scheme_null;
3760         break;
3761       }
3762 
3763       switch(SCHEME_TYPE(expr)) {
3764       case scheme_application_type:
3765         for (i = ((Scheme_App_Rec *)expr)->num_args - 1; i--; ) {
3766           l = scheme_make_pair(((Scheme_App_Rec *)expr)->args[i+1], l);
3767         }
3768         break;
3769       default:
3770       case scheme_application3_type:
3771         l = scheme_make_pair(((Scheme_App3_Rec *)expr)->rand1, l);
3772         break;
3773       }
3774 
3775       return scheme_make_application(l, info);
3776     }
3777   }
3778 
3779   return NULL;
3780 }
3781 
call_with_immed_mark(Scheme_Object * rator,Scheme_Object * rand1,Scheme_Object * rand2,Scheme_Object * rand3,Optimize_Info * info)3782 static Scheme_Object *call_with_immed_mark(Scheme_Object *rator,
3783                                            Scheme_Object *rand1,
3784                                            Scheme_Object *rand2,
3785                                            Scheme_Object *rand3,
3786                                            Optimize_Info *info)
3787 /* Convert `(call-with-immediate-continuation-mark (lambda (arg) M))`
3788    to the with-immediate-mark bytecode form. */
3789 {
3790   if (SAME_OBJ(rator, scheme_call_with_immed_mark_proc)
3791       && SAME_TYPE(SCHEME_TYPE(rand2), scheme_ir_lambda_type)
3792       && (((Scheme_Lambda *)rand2)->num_params == 1)
3793       && !(SCHEME_LAMBDA_FLAGS(((Scheme_Lambda *)rand2)) & LAMBDA_HAS_REST)) {
3794     Scheme_With_Continuation_Mark *wcm;
3795     Scheme_Object *e;
3796 
3797     wcm = MALLOC_ONE_TAGGED(Scheme_With_Continuation_Mark);
3798     wcm->so.type = scheme_with_immed_mark_type;
3799 
3800     wcm->key = rand1;
3801     wcm->val = (rand3 ? rand3 : scheme_false);
3802 
3803     e = (Scheme_Object *)((Scheme_Lambda *)rand2)->ir_info->vars[0];
3804     e = scheme_make_mutable_pair(e, ((Scheme_Lambda *)rand2)->body);
3805     wcm->body = e;
3806 
3807     return (Scheme_Object *)wcm;
3808   }
3809 
3810   return NULL;
3811 }
3812 
optimize_application(Scheme_Object * o,Optimize_Info * info,int context)3813 static Scheme_Object *optimize_application(Scheme_Object *o, Optimize_Info *info, int context)
3814 {
3815   Scheme_Object *le;
3816   Scheme_App_Rec *app;
3817   int i, n, rator_apply_escapes = 0, sub_context = 0;
3818   Optimize_Info_Sequence info_seq;
3819 
3820   app = (Scheme_App_Rec *)o;
3821 
3822   /* Check for (apply ... (list ...)) early: */
3823   le = direct_apply((Scheme_Object *)app, app->args[0], app->args[app->num_args], info);
3824   if (le)
3825     return optimize_expr(le, info, context);
3826 
3827   if (app->num_args == 3) {
3828     le = call_with_immed_mark(app->args[0], app->args[1], app->args[2], app->args[3], info);
3829     if (le)
3830       return optimize_expr(le, info, context);
3831   }
3832 
3833   le = check_app_let_rator(o, app->args[0], info, app->num_args, context);
3834   if (le)
3835     return le;
3836 
3837   n = app->num_args + 1;
3838 
3839   optimize_info_seq_init(info, &info_seq);
3840 
3841   for (i = 0; i < n; i++) {
3842     if (!i) {
3843       le = optimize_for_inline(info, app->args[i], n - 1, app, NULL, NULL, context, 0);
3844       if (le)
3845         return le;
3846     }
3847 
3848     sub_context = OPT_CONTEXT_SINGLED;
3849     if (i > 0) {
3850       int ty;
3851       ty = wants_local_type_arguments(app->args[0], i - 1);
3852       if (ty)
3853         sub_context |= (ty << OPT_CONTEXT_TYPE_SHIFT);
3854     }
3855 
3856     optimize_info_seq_step(info, &info_seq);
3857     le = optimize_expr(app->args[i], info, sub_context);
3858     app->args[i] = le;
3859     if (info->escapes) {
3860       int j;
3861       Scheme_Object *e, *l;
3862       optimize_info_seq_done(info, &info_seq);
3863 
3864       l = scheme_make_pair(app->args[i], scheme_null);
3865 
3866       for (j = i - 1; j >= 0; j--) {
3867         e = app->args[j];
3868         e = optimize_ignored(e, info, 1, 1, 5);
3869         if (e) {
3870           e = ensure_single_value(e, info);
3871           l = scheme_make_pair(e, l);
3872         }
3873       }
3874       return ensure_noncm(scheme_make_sequence_compilation(l, 1, 0), info);
3875     }
3876 
3877     if (!i) {
3878       /* Maybe found "((lambda" after optimizing; try again */
3879       le = optimize_for_inline(info, app->args[i], n - 1, app, NULL, NULL, context, 1);
3880       if (le)
3881         return le;
3882       if (SAME_OBJ(app->args[0], scheme_values_proc)
3883           || SAME_OBJ(app->args[0], scheme_apply_proc))
3884         info->maybe_values_argument = 1;
3885       rator_apply_escapes = info->escapes;
3886     }
3887   }
3888 
3889   optimize_info_seq_done(info, &info_seq);
3890 
3891   /* Check for (apply ... (list ...)) after some optimizations: */
3892   le = direct_apply((Scheme_Object *)app, app->args[0], app->args[app->num_args], info);
3893   if (le) return finish_optimize_app(le, info, context);
3894 
3895   /* Convert (hash-ref '#hash... key (lambda () literal))
3896      to (hash-ref '#hash... key literal) */
3897   if ((app->num_args == 3)
3898       && SAME_OBJ(scheme_hash_ref_proc, app->args[0])
3899       && SCHEME_HASHTRP(app->args[1])
3900       && SAME_TYPE(scheme_ir_lambda_type, SCHEME_TYPE(app->args[3]))
3901       && (((Scheme_Lambda *)(app->args[3]))->num_params == 0)
3902       && (SCHEME_TYPE(((Scheme_Lambda *)app->args[3])->body) > _scheme_ir_values_types_)
3903       && !SCHEME_PROCP(((Scheme_Lambda *)app->args[3])->body)) {
3904     app->args[3] = ((Scheme_Lambda *)app->args[3])->body;
3905   }
3906 
3907   if (rator_apply_escapes) {
3908    info->escapes = 1;
3909    SCHEME_APPN_FLAGS(app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
3910   }
3911 
3912   return finish_optimize_application(app, info, context);
3913 }
3914 
appn_flags(Scheme_Object * rator,Optimize_Info * info)3915 static int appn_flags(Scheme_Object *rator, Optimize_Info *info)
3916 /* Record some properties of an application that are useful to the SFS pass. */
3917 {
3918   if (SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_toplevel_type)) {
3919     rator = get_defn_shape(info, (Scheme_IR_Toplevel *)rator);
3920     rator = no_potential_size(rator);
3921     if (!rator) return 0;
3922     if (SAME_TYPE(SCHEME_TYPE(rator), scheme_proc_shape_type)) {
3923       return APPN_FLAG_SFS_TAIL;
3924     } else if (SAME_TYPE(SCHEME_TYPE(rator), scheme_struct_proc_shape_type)) {
3925       int ps = SCHEME_PROC_SHAPE_MODE(rator) & STRUCT_PROC_SHAPE_MASK;
3926       if ((ps == STRUCT_PROC_SHAPE_PRED)
3927           || (ps == STRUCT_PROC_SHAPE_GETTER)
3928           || (ps == STRUCT_PROC_SHAPE_SETTER)
3929           || (ps == STRUCT_PROC_SHAPE_CONSTR))
3930         return (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
3931       return 0;
3932     }
3933   }
3934 
3935   if (SCHEME_PRIMP(rator)) {
3936     int opt = (SCHEME_PRIM_PROC_FLAGS(rator) & SCHEME_PRIM_OPT_MASK);
3937     if (opt >= SCHEME_PRIM_OPT_IMMEDIATE)
3938       return (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
3939     return 0;
3940   }
3941 
3942   if (SCHEME_LAMBDAP(rator)
3943       || SAME_TYPE(scheme_noninline_proc_type, SCHEME_TYPE(rator)))
3944     return APPN_FLAG_SFS_TAIL;
3945 
3946   return 0;
3947 }
3948 
3949 #define CHECK_PRIM_AD_HOC_OPT_FLAGS 0
3950 
check_known_variant(Optimize_Info * info,Scheme_Object * app,Scheme_Object * rator,Scheme_Object * rand,const char * who,Scheme_Object * expect_pred,Scheme_Object * unsafe,int unsafe_mode,Scheme_Object * implies_pred)3951 static int check_known_variant(Optimize_Info *info, Scheme_Object *app,
3952                                Scheme_Object *rator, Scheme_Object *rand,
3953                                const char *who, Scheme_Object *expect_pred,
3954                                Scheme_Object *unsafe, int unsafe_mode,
3955                                Scheme_Object *implies_pred)
3956 /* Replace the rator with an unsafe version if we know that it's ok:
3957    if the argument is consistent with `expect_pred`; if `unsafe` is
3958    #t, then just mark the application as omittable. Alternatively, the
3959    rator implies a check, so add type information for subsequent
3960    expressions: the argument is consistent with `implies_pred` (which
3961    must be itself implied by `expected_pred`, but might be weaker). If
3962    the rand has already an incompatible type, mark that this will
3963    generate an error. If unsafe is NULL then rator has no unsafe
3964    version, so only check the type. */
3965 {
3966 #if CHECK_PRIM_AD_HOC_OPT_FLAGS
3967   if (who) {
3968     Scheme_Object *p;
3969     p = scheme_builtin_value(who);
3970     if (!p) {
3971       printf("bad primitive name: %s\n", who);
3972       abort();
3973     }
3974     if (!(SCHEME_PRIM_PROC_OPT_FLAGS(p) & SCHEME_PRIM_AD_HOC_OPT)) {
3975       printf("missing SCHEME_PRIM_AD_HOC_OPT: %s\n", who);
3976       abort();
3977     }
3978   }
3979 #endif
3980 
3981   MZ_ASSERT(SCHEME_PRIMP(rator));
3982   if (!who || IS_NAMED_PRIM(rator, who)) {
3983     Scheme_Object *pred;
3984 
3985     if (unsafe_mode)
3986       pred = expect_pred;
3987     else
3988       pred = expr_implies_predicate(rand, info);
3989 
3990     if (pred) {
3991       if (predicate_implies(pred, expect_pred)) {
3992         if (unsafe) {
3993           if (SAME_OBJ(unsafe, scheme_true))
3994             set_application_omittable(app, unsafe);
3995           else
3996             reset_rator(app, unsafe);
3997         }
3998         return 1;
3999       } else if (predicate_implies_not(pred, implies_pred)) {
4000         info->escapes = 1;
4001       }
4002     } else {
4003       if (SAME_TYPE(SCHEME_TYPE(rand), scheme_ir_local_type))
4004         add_type(info, rand, implies_pred);
4005     }
4006   }
4007 
4008   return 0;
4009 }
4010 
check_known(Optimize_Info * info,Scheme_Object * app,Scheme_Object * rator,Scheme_Object * rand,const char * who,Scheme_Object * expect_pred,Scheme_Object * unsafe,int unsafe_mode)4011 static void check_known(Optimize_Info *info, Scheme_Object *app,
4012                         Scheme_Object *rator, Scheme_Object *rand,
4013                         const char *who, Scheme_Object *expect_pred,
4014                         Scheme_Object *unsafe, int unsafe_mode)
4015 /* When the expected predicate for unsafe substitution is the same as the implied predicate. */
4016 {
4017   (void)check_known_variant(info, app, rator, rand, who, expect_pred, unsafe, unsafe_mode, expect_pred);
4018 }
4019 
check_known_rator(Optimize_Info * info,Scheme_Object * rator)4020 static void check_known_rator(Optimize_Info *info, Scheme_Object *rator)
4021 /* Check that rator is a procedure or add type information for subsequent expressions. */
4022 {
4023   Scheme_Object *pred;
4024 
4025   pred = expr_implies_predicate(rator, info);
4026   if (pred) {
4027     if (predicate_implies_not(pred, scheme_procedure_p_proc))
4028       info->escapes = 1;
4029   } else {
4030     if (SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_local_type))
4031       add_type(info, rator, scheme_procedure_p_proc);
4032   }
4033 }
4034 
check_known_both_try(Optimize_Info * info,Scheme_Object * app,Scheme_Object * rator,Scheme_Object * rand1,Scheme_Object * rand2,const char * who,Scheme_Object * expect_pred,Scheme_Object * unsafe,int unsafe_mode)4035 static void check_known_both_try(Optimize_Info *info, Scheme_Object *app,
4036                                  Scheme_Object *rator, Scheme_Object *rand1, Scheme_Object *rand2,
4037                                  const char *who, Scheme_Object *expect_pred,
4038                                  Scheme_Object *unsafe, int unsafe_mode)
4039 /* Replace the rator with an unsafe version if both rands have the right type.
4040    If not, don't save the type, nor mark this as an error */
4041 {
4042   MZ_ASSERT(SCHEME_PRIMP(rator));
4043   if (!who || IS_NAMED_PRIM(rator, who)) {
4044     Scheme_Object *pred1, *pred2;
4045 
4046     if (unsafe_mode) {
4047       reset_rator(app, unsafe);
4048     } else {
4049       pred1 = expr_implies_predicate(rand1, info);
4050       if (pred1 && predicate_implies(pred1, expect_pred)) {
4051         pred2 = expr_implies_predicate(rand2, info);
4052         if (pred2 && predicate_implies(pred2, expect_pred)) {
4053           reset_rator(app, unsafe);
4054         }
4055       }
4056     }
4057   }
4058 }
4059 
check_known_both_variant(Optimize_Info * info,Scheme_Object * app,Scheme_Object * rator,Scheme_Object * rand1,Scheme_Object * rand2,const char * who,Scheme_Object * expect_pred,Scheme_Object * unsafe,int unsafe_mode,Scheme_Object * implies_pred)4060 static void check_known_both_variant(Optimize_Info *info, Scheme_Object *app,
4061                                      Scheme_Object *rator, Scheme_Object *rand1, Scheme_Object *rand2,
4062                                      const char *who, Scheme_Object *expect_pred,
4063                                      Scheme_Object *unsafe, int unsafe_mode,
4064                                      Scheme_Object *implies_pred)
4065 {
4066   MZ_ASSERT(SCHEME_PRIMP(rator));
4067   if (!who || IS_NAMED_PRIM(rator, who)) {
4068     int ok1;
4069     ok1 = check_known_variant(info, app, rator, rand1, who, expect_pred, NULL, unsafe_mode, implies_pred);
4070     check_known_variant(info, app, rator, rand2, who, expect_pred, (ok1 ? unsafe : NULL), unsafe_mode, implies_pred);
4071   }
4072 }
4073 
check_known_both(Optimize_Info * info,Scheme_Object * app,Scheme_Object * rator,Scheme_Object * rand1,Scheme_Object * rand2,const char * who,Scheme_Object * expect_pred,Scheme_Object * unsafe,int unsafe_mode)4074 static void check_known_both(Optimize_Info *info, Scheme_Object *app,
4075                              Scheme_Object *rator, Scheme_Object *rand1, Scheme_Object *rand2,
4076                              const char *who, Scheme_Object *expect_pred,
4077                              Scheme_Object *unsafe, int unsafe_mode)
4078 {
4079   check_known_both_variant(info, app, rator, rand1, rand2, who, expect_pred, unsafe, unsafe_mode, expect_pred);
4080 }
4081 
4082 
check_known_all(Optimize_Info * info,Scheme_Object * _app,int skip_head,int skip_tail,const char * who,Scheme_Object * expect_pred,Scheme_Object * unsafe,int unsafe_mode)4083 static void check_known_all(Optimize_Info *info, Scheme_Object *_app, int skip_head, int skip_tail,
4084                             const char *who, Scheme_Object *expect_pred,
4085                             Scheme_Object *unsafe, int unsafe_mode)
4086 {
4087   Scheme_App_Rec *app = (Scheme_App_Rec *)_app;
4088   if (SCHEME_PRIMP(app->args[0]) && (!who || IS_NAMED_PRIM(app->args[0], who))) {
4089     int ok_so_far = 1, i;
4090 
4091     for (i = skip_head; i < app->num_args - skip_tail; i++) {
4092       if (!check_known_variant(info, _app, app->args[0], app->args[i+1], who, expect_pred,
4093                                NULL, unsafe_mode, expect_pred))
4094         ok_so_far = 0;
4095     }
4096 
4097     if (ok_so_far && unsafe) {
4098       if (SAME_OBJ(unsafe, scheme_true))
4099         set_application_omittable(_app, unsafe);
4100       else
4101         reset_rator(_app, unsafe);
4102     }
4103   }
4104 }
4105 
finish_optimize_any_application(Scheme_Object * app,Scheme_Object * rator,int argc,Optimize_Info * info,int context)4106 static Scheme_Object *finish_optimize_any_application(Scheme_Object *app, Scheme_Object *rator, int argc,
4107                                                       Optimize_Info *info, int context)
4108 {
4109   check_known_rator(info, rator);
4110 
4111   if ((context & OPT_CONTEXT_BOOLEAN) && !info->escapes) {
4112     Scheme_Object *pred;
4113     pred = rator_implies_predicate(rator, info, argc);
4114     if (pred && predicate_implies_not(pred, scheme_not_proc))
4115       return make_discarding_sequence(app, scheme_true, info);
4116     else if (pred && predicate_implies(pred, scheme_not_proc))
4117       return make_discarding_sequence(app, scheme_false, info);
4118   }
4119 
4120   if (SAME_OBJ(rator, scheme_void_proc))
4121     return make_discarding_sequence(app, scheme_void, info);
4122 
4123   if (is_always_escaping_primitive(rator)) {
4124     info->escapes = 1;
4125   }
4126 
4127   return app;
4128 }
4129 
increment_clock_counts_for_application(GC_CAN_IGNORE int * _vclock,GC_CAN_IGNORE int * _aclock,GC_CAN_IGNORE int * _kclock,GC_CAN_IGNORE int * _sclock,Scheme_Object * rator,int argc)4130 static void increment_clock_counts_for_application(GC_CAN_IGNORE int *_vclock,
4131                                                    GC_CAN_IGNORE int *_aclock,
4132                                                    GC_CAN_IGNORE int *_kclock,
4133                                                    GC_CAN_IGNORE int *_sclock,
4134                                                    Scheme_Object *rator,
4135                                                    int argc)
4136 {
4137   if (!is_nonmutating_nondependant_primitive(rator, argc))
4138     *_vclock += 1;
4139   else if (is_primitive_allocating(rator, argc))
4140     *_aclock += 1;
4141 
4142   if (!is_noncapturing_primitive(rator, argc))
4143     *_kclock += 1;
4144 
4145   if (!is_nonsaving_primitive(rator, argc))
4146     *_sclock += 1;
4147 }
4148 
increment_clocks_for_application(Optimize_Info * info,Scheme_Object * rator,int argc)4149 static void increment_clocks_for_application(Optimize_Info *info,
4150                                              Scheme_Object *rator,
4151                                              int argc)
4152 {
4153   int v, a, k, s;
4154 
4155   v = info->vclock;
4156   a = info->aclock;
4157   k = info->kclock;
4158   s = info->sclock;
4159 
4160   increment_clock_counts_for_application(&v, &a, &k, &s, rator, argc);
4161 
4162   info->vclock = v;
4163   info->aclock = a;
4164   info->kclock = k;
4165   info->sclock = s;
4166 }
4167 
finish_optimize_application(Scheme_App_Rec * app,Optimize_Info * info,int context)4168 static Scheme_Object *finish_optimize_application(Scheme_App_Rec *app, Optimize_Info *info, int context)
4169 {
4170   Scheme_Object *le;
4171   Scheme_Object *rator =  app->args[0];
4172   int all_vals = 1, i, flags, rator_flags;
4173 
4174   for (i = app->num_args; i--; ) {
4175     if (SCHEME_TYPE(app->args[i+1]) < _scheme_ir_values_types_)
4176       all_vals = 0;
4177   }
4178 
4179   info->size += 1;
4180   info->preserves_marks = 1;
4181   info->single_result = 1;
4182 
4183   if (all_vals) {
4184     le = try_optimize_fold(rator, NULL, (Scheme_Object *)app, info);
4185     if (le)
4186       return le;
4187   }
4188 
4189   if (!app->num_args && SCHEME_PRIMP(rator)) {
4190     if (SAME_OBJ(rator, scheme_list_proc))
4191       return scheme_null;
4192     if (SAME_OBJ(rator, scheme_append_proc))
4193       return scheme_null;
4194     if (SAME_OBJ(rator, scheme_hasheq_proc))
4195       return (Scheme_Object *)scheme_make_hash_tree(0);
4196     if (SAME_OBJ(rator, scheme_hash_proc))
4197       return (Scheme_Object *)scheme_make_hash_tree(1);
4198     if (SAME_OBJ(rator, scheme_hasheqv_proc))
4199       return (Scheme_Object *)scheme_make_hash_tree(2);
4200   }
4201 
4202   if (SCHEME_PRIMP(rator)
4203       && (app->num_args >= ((Scheme_Primitive_Proc *)rator)->mina)
4204       && (app->num_args <= ((Scheme_Primitive_Proc *)rator)->mu.maxa)) {
4205     Scheme_Object *app_o = (Scheme_Object *)app;
4206     Scheme_Object *rand1 = NULL, *rand2 = NULL, *rand3 = NULL;
4207 
4208     if (app->num_args >= 1)
4209       rand1 = app->args[1];
4210 
4211     if (app->num_args >= 2)
4212       rand2 = app->args[2];
4213 
4214     if (app->num_args >= 3)
4215       rand3 = app->args[3];
4216 
4217     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_AD_HOC_OPT) {
4218       check_known(info, app_o, rator, rand1, "vector-set!", scheme_vector_p_proc, NULL, info->unsafe_mode);
4219       check_known(info, app_o, rator, rand2, "vector-set!", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
4220       check_known(info, app_o, rator, rand1, "vector*-set!", scheme_vector_p_proc,
4221                   (info->unsafe_mode ? scheme_unsafe_vector_star_set_proc : NULL), info->unsafe_mode);
4222       check_known(info, app_o, rator, rand2, "vector*-set!", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
4223 
4224       check_known(info, app_o, rator, rand1, "procedure-arity-includes?", scheme_procedure_p_proc, NULL, info->unsafe_mode);
4225 
4226       check_known(info, app_o, rator, rand1, "map", scheme_procedure_p_proc, NULL, info->unsafe_mode);
4227       check_known(info, app_o, rator, rand1, "for-each", scheme_procedure_p_proc, NULL, info->unsafe_mode);
4228       check_known(info, app_o, rator, rand1, "andmap", scheme_procedure_p_proc, NULL, info->unsafe_mode);
4229       check_known(info, app_o, rator, rand1, "ormap", scheme_procedure_p_proc, NULL, info->unsafe_mode);
4230       check_known_all(info, app_o, 1, 0, "map", scheme_list_p_proc, NULL, info->unsafe_mode);
4231       check_known_all(info, app_o, 1, 0, "for-each", scheme_list_p_proc, NULL, info->unsafe_mode);
4232       check_known_all(info, app_o, 1, 0, "andmap", scheme_list_p_proc, NULL, info->unsafe_mode);
4233       check_known_all(info, app_o, 1, 0, "ormap", scheme_list_p_proc, NULL, info->unsafe_mode);
4234 
4235       check_known(info, app_o, rator, rand1, "string-set!", scheme_string_p_proc,
4236                   (info->unsafe_mode ? scheme_unsafe_string_set_proc : NULL), info->unsafe_mode);
4237       check_known(info, app_o, rator, rand2, "string-set!", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
4238       check_known(info, app_o, rator, rand3, "string-set!", scheme_char_p_proc, NULL, info->unsafe_mode);
4239       check_known(info, app_o, rator, rand1, "bytes-set!", scheme_byte_string_p_proc,
4240                   (info->unsafe_mode ? scheme_unsafe_bytes_set_proc : NULL), info->unsafe_mode);
4241       check_known(info, app_o, rator, rand2, "bytes-set!", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
4242       check_known(info, app_o, rator, rand3, "bytes-set!", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
4243 
4244       check_known_all(info, app_o, 0, 0, "string-append", scheme_string_p_proc, scheme_true, info->unsafe_mode);
4245       check_known_all(info, app_o, 0, 0, "bytes-append", scheme_byte_string_p_proc, scheme_true, info->unsafe_mode);
4246 
4247       check_known_all(info, app_o, 0, 1, "append", scheme_list_p_proc, scheme_true, info->unsafe_mode);
4248     }
4249 
4250     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_WANTS_REAL)
4251       check_known_all(info, app_o, 0, 0, NULL, scheme_real_p_proc,
4252                       (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_OMITTABLE_ON_GOOD_ARGS) ? scheme_true : NULL,
4253                       info->unsafe_mode);
4254     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_WANTS_NUMBER)
4255       check_known_all(info, app_o, 0, 0, NULL, scheme_number_p_proc,
4256                       (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_OMITTABLE_ON_GOOD_ARGS) ? scheme_true : NULL,
4257                       info->unsafe_mode);
4258 
4259     /* Some of these may have changed app->rator. */
4260     rator = app->args[0];
4261   }
4262 
4263   increment_clocks_for_application(info, rator, app->num_args);
4264 
4265   rator_flags = get_rator_flags(rator, app->num_args, info);
4266   info->preserves_marks = !!(rator_flags & LAMBDA_PRESERVES_MARKS);
4267   info->single_result = !!(rator_flags & LAMBDA_SINGLE_RESULT);
4268   if ((rator_flags & LAMBDA_STATUS_MASK) == LAMBDA_STATUS_RESULT_TENTATIVE) {
4269     info->preserves_marks = -info->preserves_marks;
4270     info->single_result = -info->single_result;
4271   }
4272 
4273   register_local_argument_types(app, NULL, NULL, info);
4274 
4275   flags = appn_flags(app->args[0], info);
4276   SCHEME_APPN_FLAGS(app) |= flags;
4277 
4278   return finish_optimize_any_application((Scheme_Object *)app, app->args[0], app->num_args,
4279                                          info, context);
4280 }
4281 
try_reduce_predicate(Scheme_Object * rator,Scheme_Object * rand,Optimize_Info * info)4282 static Scheme_Object *try_reduce_predicate(Scheme_Object *rator, Scheme_Object *rand,
4283                                            Optimize_Info *info)
4284 /* Change (pair? (list X complex-Y Z)) => (begin complex-Y #t), etc.
4285    It's especially nice to avoid the constructions. */
4286 {
4287   Scheme_Object *pred;
4288 
4289   if (!relevant_predicate(rator))
4290     return NULL;
4291 
4292   pred = expr_implies_predicate(rand, info);
4293 
4294   if (!pred)
4295     return NULL;
4296 
4297   if (predicate_implies(pred, rator))
4298     return make_discarding_sequence(rand, scheme_true, info);
4299   else if (predicate_implies_not(pred, rator))
4300     return make_discarding_sequence(rand, scheme_false, info);
4301 
4302   return NULL;
4303 }
4304 
check_ignored_call_cc(Scheme_Object * rator,Scheme_Object * rand,Optimize_Info * info,int context)4305 static Scheme_Object *check_ignored_call_cc(Scheme_Object *rator, Scheme_Object *rand,
4306                                             Optimize_Info *info, int context)
4307 /* Convert (call/cc (lambda (ignored) body ...)) to (begin body ...) */
4308 {
4309   if (SCHEME_PRIMP(rator)
4310       && (IS_NAMED_PRIM(rator, "call-with-current-continuation")
4311           || IS_NAMED_PRIM(rator, "call-with-composable-continuation")
4312           || IS_NAMED_PRIM(rator, "call-with-escape-continuation"))) {
4313       Scheme_Object *proc;
4314 
4315       proc = lookup_constant_proc(info, rand, 1);
4316 
4317       if (proc && SAME_TYPE(SCHEME_TYPE(proc), scheme_ir_lambda_type)) {
4318           Scheme_Lambda *lam = (Scheme_Lambda *)proc;
4319           if (lam->num_params == 1) {
4320               Scheme_IR_Lambda_Info *cl = lam->ir_info;
4321               if (!cl->vars[0]->use_count) {
4322                 Scheme_Object *expr;
4323                 info->vclock++;
4324                 expr = make_application_2(rand, scheme_void, info);
4325                 if (IS_NAMED_PRIM(rator, "call-with-escape-continuation")) {
4326                   Scheme_Sequence *seq;
4327 
4328                   seq = scheme_malloc_sequence(1);
4329                   seq->so.type = scheme_begin0_sequence_type;
4330                   seq->count = 1;
4331                   seq->array[0] = expr;
4332 
4333                   expr = (Scheme_Object *)seq;
4334                 }
4335                 return optimize_expr(expr, info, context);
4336               }
4337           }
4338       }
4339   }
4340   return NULL;
4341 }
4342 
make_optimize_prim_application2(Scheme_Object * prim,Scheme_Object * rand,Optimize_Info * info,int context)4343 static Scheme_Object *make_optimize_prim_application2(Scheme_Object *prim, Scheme_Object *rand,
4344                                                       Optimize_Info *info, int context)
4345 /* make (prim rand) and optimize it. rand must be already optimized */
4346 {
4347   Scheme_Object *alt;
4348   alt = make_application_2(prim, rand, info);
4349   /* scheme_make_application may use constant folding, check that alt is not a constant */
4350   if (SAME_TYPE(SCHEME_TYPE(alt), scheme_application2_type)) {
4351     return finish_optimize_application2((Scheme_App2_Rec *)alt, info, context);
4352   } else
4353     return alt;
4354 }
4355 
4356 
optimize_application2(Scheme_Object * o,Optimize_Info * info,int context)4357 static Scheme_Object *optimize_application2(Scheme_Object *o, Optimize_Info *info, int context)
4358 {
4359   Scheme_App2_Rec *app;
4360   Scheme_Object *le;
4361   int rator_apply_escapes, sub_context, ty;
4362   Optimize_Info_Sequence info_seq;
4363 
4364   app = (Scheme_App2_Rec *)o;
4365 
4366   le = check_app_let_rator(o, app->rator, info, 1, context);
4367   if (le)
4368     return le;
4369 
4370   le = check_ignored_call_cc(app->rator, app->rand, info, context);
4371   if (le)
4372     return le;
4373 
4374   le = optimize_for_inline(info, app->rator, 1, NULL, app, NULL, context, 0);
4375   if (le)
4376     return le;
4377 
4378   optimize_info_seq_init(info, &info_seq);
4379 
4380   sub_context = OPT_CONTEXT_SINGLED;
4381 
4382   le = optimize_expr(app->rator, info, sub_context);
4383   app->rator = le;
4384   if (info->escapes) {
4385     optimize_info_seq_done(info, &info_seq);
4386     return ensure_noncm(app->rator, info);
4387   }
4388 
4389   {
4390     /* Maybe found "((lambda" after optimizing; try again */
4391     le = optimize_for_inline(info, app->rator, 1, NULL, app, NULL, context, 1);
4392     if (le)
4393       return le;
4394     rator_apply_escapes = info->escapes;
4395   }
4396 
4397   if (SAME_PTR(scheme_not_proc, app->rator)){
4398     sub_context |= OPT_CONTEXT_BOOLEAN;
4399   } else {
4400     ty = wants_local_type_arguments(app->rator, 0);
4401     if (ty)
4402       sub_context |= (ty << OPT_CONTEXT_TYPE_SHIFT);
4403   }
4404 
4405   optimize_info_seq_step(info, &info_seq);
4406 
4407   le = optimize_expr(app->rand, info, sub_context);
4408   app->rand = le;
4409   optimize_info_seq_done(info, &info_seq);
4410   if (info->escapes) {
4411     info->size += 1;
4412     return ensure_noncm(make_discarding_first_sequence(app->rator, app->rand, info), info);
4413   }
4414 
4415   if (rator_apply_escapes) {
4416    info->escapes = 1;
4417    SCHEME_APPN_FLAGS(app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
4418   }
4419 
4420   return finish_optimize_application2(app, info, context);
4421 }
4422 
finish_optimize_application2(Scheme_App2_Rec * app,Optimize_Info * info,int context)4423 static Scheme_Object *finish_optimize_application2(Scheme_App2_Rec *app, Optimize_Info *info, int context)
4424 {
4425   int flags, rator_flags;
4426   Scheme_Object *rator =  app->rator;
4427   Scheme_Object *rand, *inside = NULL, *alt;
4428 
4429   info->size += 1;
4430   info->preserves_marks = 1;
4431   info->single_result = 1;
4432 
4433   /* Path for direct constant folding */
4434   if (SCHEME_TYPE(app->rand) > _scheme_ir_values_types_) {
4435     Scheme_Object *le;
4436     le = try_optimize_fold(rator, NULL, (Scheme_Object *)app, info);
4437     if (le)
4438       return le;
4439   }
4440 
4441   rand = app->rand;
4442 
4443   /* We can go inside a `begin' and a `let', which is useful in case
4444      the argument was a function call that has been inlined. */
4445   extract_tail_inside(&rand, &inside, 0);
4446 
4447   if (SCHEME_TYPE(rand) > _scheme_ir_values_types_) {
4448     Scheme_Object *le;
4449     le = try_optimize_fold(rator, scheme_make_pair(rand, scheme_null), NULL, info);
4450     if (le)
4451       return replace_tail_inside(le, inside, app->rand);
4452   }
4453 
4454   if (SAME_OBJ(scheme_values_proc, rator)
4455       || SAME_OBJ(scheme_list_star_proc, rator)
4456       || (SCHEME_PRIMP(rator) && IS_NAMED_PRIM(rator, "append"))) {
4457     SCHEME_APPN_FLAGS(app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
4458     if ((context & OPT_CONTEXT_SINGLED)
4459         || scheme_omittable_expr(rand, 1, -1, 0, info, info)
4460         || single_valued_noncm_expression(rand, info, 5)) {
4461       return replace_tail_inside(rand, inside, app->rand);
4462     }
4463     app->rator = scheme_values_proc;
4464     rator = scheme_values_proc;
4465   }
4466 
4467   if (SCHEME_PRIMP(rator)
4468       && (1 >= ((Scheme_Primitive_Proc *)rator)->mina)
4469       && (1 <= ((Scheme_Primitive_Proc *)rator)->mu.maxa)) {
4470     /* Check for things like (cXr (cons X Y)): */
4471     switch (SCHEME_TYPE(rand)) {
4472     case scheme_application2_type:
4473       {
4474         Scheme_App2_Rec *app2 = (Scheme_App2_Rec *)rand;
4475         if (IS_NAMED_PRIM(rator, "car")
4476             || IS_NAMED_PRIM(rator, "unsafe-car")) {
4477           if (SAME_OBJ(scheme_list_proc, app2->rator)) {
4478             /* (car (list X)) */
4479             alt = ensure_single_value_noncm(app2->rand, info);
4480             return replace_tail_inside(alt, inside, app->rand);
4481           }
4482         } else if (IS_NAMED_PRIM(rator, "cdr")
4483                    || IS_NAMED_PRIM(rator, "unsafe-cdr")) {
4484           if (SAME_OBJ(scheme_list_proc, app2->rator)) {
4485             /* (cdr (list X)) */
4486             alt = make_discarding_sequence(app2->rand, scheme_null, info);
4487             return replace_tail_inside(alt, inside, app->rand);
4488           }
4489         } else if (IS_NAMED_PRIM(rator, "unbox")
4490                    || IS_NAMED_PRIM(rator, "unsafe-unbox")
4491                    || IS_NAMED_PRIM(rator, "unsafe-unbox*")) {
4492           if (SAME_OBJ(scheme_box_proc, app2->rator)) {
4493             /* (unbox (box X)) */
4494             alt = ensure_single_value_noncm(app2->rand, info);
4495             return replace_tail_inside(alt, inside, app->rand);
4496           }
4497         }
4498         break;
4499       }
4500     case scheme_application3_type:
4501       {
4502         Scheme_App3_Rec *app3 = (Scheme_App3_Rec *)rand;
4503         if (IS_NAMED_PRIM(rator, "car")
4504             || IS_NAMED_PRIM(rator, "unsafe-car")) {
4505           if (SAME_OBJ(scheme_cons_proc, app3->rator)
4506               || SAME_OBJ(scheme_unsafe_cons_list_proc, app3->rator)
4507               || SAME_OBJ(scheme_list_proc, app3->rator)
4508               || SAME_OBJ(scheme_list_star_proc, app3->rator)) {
4509             /* (car ({cons|list|list*} X Y)) */
4510             alt = make_discarding_reverse_sequence(app3->rand2, app3->rand1, info);
4511             return replace_tail_inside(alt, inside, app->rand);
4512           }
4513         } else if (IS_NAMED_PRIM(rator, "cdr")
4514                    || IS_NAMED_PRIM(rator, "unsafe-cdr")) {
4515           if (SAME_OBJ(scheme_cons_proc, app3->rator)
4516               || SAME_OBJ(scheme_unsafe_cons_list_proc, app3->rator)
4517               || SAME_OBJ(scheme_list_star_proc, app3->rator)) {
4518             /* (cdr ({cons|list*} X Y)) */
4519             alt = make_discarding_sequence(app3->rand1, app3->rand2, info);
4520             return replace_tail_inside(alt, inside, app->rand);
4521           } else if (SAME_OBJ(scheme_list_proc, app3->rator)) {
4522             /* (cdr (list X Y)) */
4523             alt = make_application_2(scheme_list_proc, app3->rand2, info);
4524             SCHEME_APPN_FLAGS(((Scheme_App_Rec *)alt)) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
4525             alt = make_discarding_sequence(app3->rand1, alt, info);
4526             return replace_tail_inside(alt, inside, app->rand);
4527           }
4528         } else if (IS_NAMED_PRIM(rator, "cadr")) {
4529           if (SAME_OBJ(scheme_list_proc, app3->rator)) {
4530             /* (cadr (list X Y)) */
4531             alt = make_discarding_sequence(app3->rand1, app3->rand2, info);
4532             return replace_tail_inside(alt, inside, app->rand);
4533           }
4534         }
4535         break;
4536       }
4537     case scheme_application_type:
4538       {
4539         Scheme_App_Rec *appr = (Scheme_App_Rec *)rand;
4540         Scheme_Object *r = appr->args[0];
4541         if (IS_NAMED_PRIM(rator, "car")
4542             || IS_NAMED_PRIM(rator, "unsafe-car")) {
4543           if ((appr->args > 0)
4544               && (SAME_OBJ(scheme_list_proc, r)
4545                   || SAME_OBJ(scheme_list_star_proc, r))) {
4546             /* (car ({list|list*} X Y ...)) */
4547             alt = make_discarding_app_sequence(appr, 0, NULL, info);
4548             return replace_tail_inside(alt, inside, app->rand);
4549           }
4550         } else if (IS_NAMED_PRIM(rator, "cdr")
4551                    || IS_NAMED_PRIM(rator, "unsafe-cdr")) {
4552           /* (cdr ({list|list*} X Y ...)) */
4553           if ((appr->args > 0)
4554               && (SAME_OBJ(scheme_list_proc, r)
4555                   || SAME_OBJ(scheme_list_star_proc, r))) {
4556             Scheme_Object *al = scheme_null;
4557             int k;
4558             for (k = appr->num_args; k > 1; k--) {
4559               al = scheme_make_pair(appr->args[k], al);
4560             }
4561             al = scheme_make_pair(r, al);
4562             alt = scheme_make_application(al, info);
4563             SCHEME_APPN_FLAGS(((Scheme_App_Rec *)alt)) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
4564             alt = make_discarding_sequence(appr->args[1], alt, info);
4565             return replace_tail_inside(alt, inside, app->rand);
4566           }
4567         }
4568         break;
4569       }
4570     }
4571 
4572     if (IS_NAMED_PRIM(rator, "length")
4573         && SCHEME_LISTP(rand)) {
4574       alt = scheme_make_integer(scheme_list_length(rand));
4575       return replace_tail_inside(alt, inside, app->rand);
4576     }
4577 
4578     alt = try_reduce_predicate(rator, rand, info);
4579     if (alt)
4580       return replace_tail_inside(alt, inside, app->rand);
4581 
4582     if (SAME_OBJ(scheme_struct_type_p_proc, rator)) {
4583       Scheme_Object *c;
4584       c = get_struct_proc_shape(rand, info, 0);
4585       if (c && ((SCHEME_PROC_SHAPE_MODE(c) & STRUCT_PROC_SHAPE_MASK)
4586                 == STRUCT_PROC_SHAPE_STRUCT)) {
4587         return replace_tail_inside(scheme_true, inside, app->rand);
4588       }
4589     }
4590 
4591     if (SAME_OBJ(scheme_varref_const_p_proc, rator)
4592         && SAME_TYPE(SCHEME_TYPE(rand), scheme_varref_form_type)) {
4593       Scheme_Object *var = SCHEME_PTR1_VAL(rand);
4594       if (SAME_OBJ(var, scheme_true)) {
4595         return replace_tail_inside(scheme_true, inside, app->rand);
4596       } else if (SAME_OBJ(var, scheme_false)) {
4597         return replace_tail_inside(scheme_false, inside, app->rand);
4598       } else {
4599         if (var && ir_propagate_ok(var, info, 1, NULL)) {
4600           /* can propagate => is a constant */
4601           return replace_tail_inside(scheme_true, inside, app->rand);
4602         }
4603       }
4604     }
4605 
4606     /* We can resolve (variable-reference-from-unsafe (#%variable-reference))
4607        to a specific boolean result */
4608     if (SAME_OBJ(scheme_varref_unsafe_p_proc, rator)
4609         && SAME_TYPE(SCHEME_TYPE(rand), scheme_varref_form_type)) {
4610       Scheme_Object *result = (info->unsafe_mode ? scheme_true : scheme_false);
4611       return replace_tail_inside(result, inside, app->rand);
4612     }
4613 
4614     if (SCHEME_PRIMP(rator)
4615         && (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_PRODUCES_BOOL)
4616         && (IS_NAMED_PRIM(rator, "zero?")
4617             || IS_NAMED_PRIM(rator, "positive?")
4618             || IS_NAMED_PRIM(rator, "negative?"))) {
4619       Scheme_Object* pred;
4620       Scheme_App3_Rec *new;
4621 
4622       pred = expr_implies_predicate(rand, info);
4623       if (pred && SAME_OBJ(pred, scheme_fixnum_p_proc)) {
4624         Scheme_Object *cmp;
4625         if (IS_NAMED_PRIM(rator, "positive?"))
4626           cmp = scheme_unsafe_fx_gt_proc;
4627         else if (IS_NAMED_PRIM(rator, "negative?"))
4628           cmp = scheme_unsafe_fx_lt_proc;
4629         else
4630           cmp = scheme_unsafe_fx_eq_proc;
4631         new = (Scheme_App3_Rec *)make_application_3(cmp, app->rand, scheme_make_integer(0), info);
4632         SCHEME_APPN_FLAGS(new) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
4633         return finish_optimize_application3(new, info, context);
4634       }
4635     }
4636 
4637     if (SAME_OBJ(rator, scheme_system_type_proc)
4638         && SCHEME_SYMBOLP(rand)
4639         && !SCHEME_SYM_WEIRDP(rand)
4640         && !strcmp(SCHEME_SYM_VAL(rand), "vm")) {
4641       /* For the expander's benefit, optimize `(system-type 'vm)` to `'racket`
4642          to effectively select backend details statically. */
4643       return replace_tail_inside(scheme_intern_symbol("racket"), inside, app->rand);
4644     }
4645 
4646     {
4647       /* Try to check the argument's type, and use the unsafe versions if possible. */
4648       Scheme_Object *app_o = (Scheme_Object *)app;
4649 
4650       if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_AD_HOC_OPT) {
4651         check_known_variant(info, app_o, rator, rand, "bitwise-not", scheme_fixnum_p_proc, scheme_unsafe_fxnot_proc, 0, scheme_real_p_proc);
4652         check_known_variant(info, app_o, rator, rand, "fxnot", scheme_fixnum_p_proc, scheme_unsafe_fxnot_proc, info->unsafe_mode, scheme_real_p_proc);
4653 
4654         check_known(info, app_o, rator, rand, "car", scheme_pair_p_proc, scheme_unsafe_car_proc, info->unsafe_mode);
4655         check_known(info, app_o, rator, rand, "unsafe-car", scheme_pair_p_proc, NULL, info->unsafe_mode);
4656         check_known(info, app_o, rator, rand, "cdr", scheme_pair_p_proc, scheme_unsafe_cdr_proc, info->unsafe_mode);
4657         check_known(info, app_o, rator, rand, "unsafe-cdr", scheme_pair_p_proc, NULL, info->unsafe_mode);
4658         check_known(info, app_o, rator, rand, "mcar", scheme_mpair_p_proc, scheme_unsafe_mcar_proc, info->unsafe_mode);
4659         check_known(info, app_o, rator, rand, "unsafe-mcar", scheme_mpair_p_proc, NULL, info->unsafe_mode);
4660         check_known(info, app_o, rator, rand, "mcdr", scheme_mpair_p_proc, scheme_unsafe_mcdr_proc, info->unsafe_mode);
4661         check_known(info, app_o, rator, rand, "unsafe-mcdr", scheme_mpair_p_proc, NULL, info->unsafe_mode);
4662         check_known(info, app_o, rator, rand, "string-length", scheme_string_p_proc, scheme_unsafe_string_length_proc, info->unsafe_mode);
4663         check_known(info, app_o, rator, rand, "bytes-length", scheme_byte_string_p_proc, scheme_unsafe_byte_string_length_proc, info->unsafe_mode);
4664         /* It's not clear that these are useful, since a chaperone check is needed anyway: */
4665         check_known(info, app_o, rator, rand, "unbox", scheme_box_p_proc, scheme_unsafe_unbox_proc, info->unsafe_mode);
4666         check_known(info, app_o, rator, rand, "unbox*", scheme_box_p_proc,
4667                     (info->unsafe_mode ? scheme_unsafe_unbox_star_proc : NULL), info->unsafe_mode);
4668         check_known(info, app_o, rator, rand, "unsafe-unbox", scheme_box_p_proc, NULL, info->unsafe_mode);
4669         check_known(info, app_o, rator, rand, "unsafe-unbox*", scheme_box_p_proc, NULL, info->unsafe_mode);
4670         check_known(info, app_o, rator, rand, "vector-length", scheme_vector_p_proc, scheme_unsafe_vector_length_proc, info->unsafe_mode);
4671         check_known(info, app_o, rator, rand, "vector*-length", scheme_vector_p_proc,
4672                     (info->unsafe_mode ? scheme_unsafe_vector_star_length_proc : NULL), info->unsafe_mode);
4673 
4674         check_known(info, app_o, rator, rand, "length", scheme_list_p_proc, scheme_true, info->unsafe_mode);
4675 
4676         check_known(info, app_o, rator, rand, "string-append", scheme_string_p_proc, scheme_true, info->unsafe_mode);
4677         check_known(info, app_o, rator, rand, "string-append-immutable", scheme_string_p_proc, scheme_true, info->unsafe_mode);
4678         check_known(info, app_o, rator, rand, "bytes-append", scheme_byte_string_p_proc, scheme_true, info->unsafe_mode);
4679         check_known(info, app_o, rator, rand, "string->immutable-string", scheme_string_p_proc, scheme_true, info->unsafe_mode);
4680         check_known(info, app_o, rator, rand, "bytes->immutable-bytes", scheme_byte_string_p_proc, scheme_true, info->unsafe_mode);
4681 
4682         check_known(info, app_o, rator, rand, "string->symbol", scheme_string_p_proc, scheme_true, info->unsafe_mode);
4683         check_known(info, app_o, rator, rand, "symbol->string", scheme_symbol_p_proc, scheme_true, info->unsafe_mode);
4684         check_known(info, app_o, rator, rand, "symbol->string-immutable", scheme_symbol_p_proc, scheme_true, info->unsafe_mode);
4685         check_known(info, app_o, rator, rand, "string->keyword", scheme_string_p_proc, scheme_true, info->unsafe_mode);
4686         check_known(info, app_o, rator, rand, "keyword->string", scheme_keyword_p_proc, scheme_true, info->unsafe_mode);
4687 
4688         check_known(info, app_o, rator, rand, "char->integer", scheme_char_p_proc, scheme_unsafe_char_to_integer_proc, info->unsafe_mode);
4689 
4690         if (IS_NAMED_PRIM(rator, "real->double-flonum")
4691             || IS_NAMED_PRIM(rator, "exact->inexact")) {
4692           Scheme_Object *pred;
4693           pred = expr_implies_predicate(rand, info);
4694           if (predicate_implies(pred, scheme_flonum_p_proc))
4695             return replace_tail_inside(rand, inside, rand);
4696           else if (predicate_implies(pred, scheme_fixnum_p_proc))
4697             reset_rator(app_o, scheme_unsafe_fx_to_fl_proc);
4698         }
4699       }
4700 
4701       if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_WANTS_REAL)
4702         check_known(info, app_o, rator, rand, NULL, scheme_real_p_proc,
4703                     (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_OMITTABLE_ON_GOOD_ARGS) ? scheme_true : NULL,
4704                     info->unsafe_mode);
4705       if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_WANTS_NUMBER)
4706         check_known(info, app_o, rator, rand, NULL, scheme_number_p_proc,
4707                     (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_OMITTABLE_ON_GOOD_ARGS) ? scheme_true : NULL,
4708                     info->unsafe_mode);
4709 
4710       if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_AD_HOC_OPT) {
4711         /* These operation don't have an unsafe replacement. Check to record types and detect errors: */
4712         check_known(info, app_o, rator, rand, "caar", scheme_pair_p_proc, NULL, info->unsafe_mode);
4713         check_known(info, app_o, rator, rand, "cadr", scheme_pair_p_proc, NULL, info->unsafe_mode);
4714         check_known(info, app_o, rator, rand, "cdar", scheme_pair_p_proc, NULL, info->unsafe_mode);
4715         check_known(info, app_o, rator, rand, "cddr", scheme_pair_p_proc, NULL, info->unsafe_mode);
4716 
4717         check_known(info, app_o, rator, rand, "caddr", scheme_pair_p_proc, NULL, info->unsafe_mode);
4718         check_known(info, app_o, rator, rand, "cdddr", scheme_pair_p_proc, NULL, info->unsafe_mode);
4719         check_known(info, app_o, rator, rand, "cadddr", scheme_pair_p_proc, NULL, info->unsafe_mode);
4720         check_known(info, app_o, rator, rand, "cddddr", scheme_pair_p_proc, NULL, info->unsafe_mode);
4721 
4722         check_known(info, app_o, rator, rand, "list->vector", scheme_list_p_proc, scheme_true, info->unsafe_mode);
4723         check_known(info, app_o, rator, rand, "vector->list", scheme_vector_p_proc, NULL, info->unsafe_mode);
4724         check_known(info, app_o, rator, rand, "vector->values", scheme_vector_p_proc, NULL, info->unsafe_mode);
4725         check_known(info, app_o, rator, rand, "vector->immutable-vector", scheme_vector_p_proc, NULL, info->unsafe_mode);
4726         check_known(info, app_o, rator, rand, "make-vector", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
4727       }
4728 
4729       /* Some of these may have changed app->rator. */
4730       rator = app->rator;
4731     }
4732   }
4733 
4734   /* Using a struct getter or predicate? */
4735   alt = get_struct_proc_shape(rator, info, 0);
4736   if (alt) {
4737     int mode = (SCHEME_PROC_SHAPE_MODE(alt) & STRUCT_PROC_SHAPE_MASK);
4738 
4739     if ((mode == STRUCT_PROC_SHAPE_PRED)
4740         || (mode == STRUCT_PROC_SHAPE_GETTER)) {
4741       Scheme_Object *pred;
4742       int unsafe = 0;
4743 
4744       if (info->unsafe_mode && (mode == STRUCT_PROC_SHAPE_GETTER)) {
4745         pred = NULL;
4746         unsafe = 1;
4747       } else
4748         pred = expr_implies_predicate(rand, info);
4749 
4750       if (unsafe
4751           || (pred
4752               && SAME_TYPE(SCHEME_TYPE(pred), scheme_struct_proc_shape_type)
4753               && is_struct_identity_subtype(SCHEME_PROC_SHAPE_IDENTITY(pred),
4754                                             SCHEME_PROC_SHAPE_IDENTITY(alt)))) {
4755         if (mode == STRUCT_PROC_SHAPE_PRED) {
4756           /* We know that the predicate will succeed */
4757           return replace_tail_inside(make_discarding_sequence(rand, scheme_true, info),
4758                                      inside,
4759                                      app->rand);
4760         } else {
4761           /* Struct type matches, so use `unsafe-struct-ref` */
4762           Scheme_App3_Rec *new;
4763           new = (Scheme_App3_Rec *)make_application_3(((SCHEME_PROC_SHAPE_MODE(alt) & STRUCT_PROC_SHAPE_AUTHENTIC)
4764                                                        ? scheme_unsafe_struct_star_ref_proc
4765                                                        : scheme_unsafe_struct_ref_proc),
4766                                                       app->rand,
4767                                                       scheme_make_integer(SCHEME_PROC_SHAPE_MODE(alt) >> STRUCT_PROC_SHAPE_SHIFT),
4768                                                       info);
4769           SCHEME_APPN_FLAGS(new) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
4770           return finish_optimize_application3(new, info, context);
4771         }
4772       } else if ((mode == STRUCT_PROC_SHAPE_PRED) && pred && predicate_implies_not(pred, alt)) {
4773          /* We know that the predicate will fail */
4774         return replace_tail_inside(make_discarding_sequence(rand, scheme_false, info),
4775                                    inside,
4776                                    app->rand);
4777       }
4778 
4779       /* Register type based on getter succeeding: */
4780       if ((mode == STRUCT_PROC_SHAPE_GETTER)
4781           && !SCHEME_NULLP(SCHEME_PROC_SHAPE_IDENTITY(alt))
4782           && SAME_TYPE(SCHEME_TYPE(rand), scheme_ir_local_type))
4783         add_type(info, rand, scheme_make_struct_proc_shape(STRUCT_PROC_SHAPE_PRED,
4784                                                            SCHEME_PROC_SHAPE_IDENTITY(alt)));
4785     }
4786   }
4787 
4788   increment_clocks_for_application(info, rator, 1);
4789 
4790   rator_flags = get_rator_flags(rator, 1, info);
4791   info->preserves_marks = !!(rator_flags & LAMBDA_PRESERVES_MARKS);
4792   info->single_result = !!(rator_flags & LAMBDA_SINGLE_RESULT);
4793   if ((rator_flags & LAMBDA_STATUS_MASK) == LAMBDA_STATUS_RESULT_TENTATIVE) {
4794     info->preserves_marks = -info->preserves_marks;
4795     info->single_result = -info->single_result;
4796   }
4797 
4798   register_local_argument_types(NULL, app, NULL, info);
4799 
4800   flags = appn_flags(rator, info);
4801   SCHEME_APPN_FLAGS(app) |= flags;
4802 
4803   return finish_optimize_any_application((Scheme_Object *)app, rator, 1, info, context);
4804 }
4805 
optimize_application3(Scheme_Object * o,Optimize_Info * info,int context)4806 static Scheme_Object *optimize_application3(Scheme_Object *o, Optimize_Info *info, int context)
4807 {
4808   Scheme_App3_Rec *app;
4809   Scheme_Object *le;
4810   int rator_apply_escapes, sub_context, ty, flags;
4811   Optimize_Info_Sequence info_seq;
4812 
4813   app = (Scheme_App3_Rec *)o;
4814 
4815   if (SAME_OBJ(app->rator, scheme_check_not_undefined_proc)
4816       && SCHEME_SYMBOLP(app->rand2)) {
4817     if (scheme_log_level_p(info->logger, SCHEME_LOG_DEBUG))
4818       scheme_log(info->logger,
4819                  SCHEME_LOG_DEBUG,
4820                  0,
4821                  "warning%s: use-before-definition check inserted on variable: %S",
4822                  scheme_optimize_context_to_string(info->context),
4823                  app->rand2);
4824   }
4825 
4826   /* Check for (apply ... (list ...)) early: */
4827   le = direct_apply((Scheme_Object *)app, app->rator, app->rand2, info);
4828   if (le)
4829     return optimize_expr(le, info, context);
4830 
4831   le = call_with_immed_mark(app->rator, app->rand1, app->rand2, NULL, info);
4832   if (le)
4833     return optimize_expr(le, info, context);
4834 
4835   le = check_app_let_rator(o, app->rator, info, 2, context);
4836   if (le)
4837     return le;
4838 
4839   le = optimize_for_inline(info, app->rator, 2, NULL, NULL, app, context, 0);
4840   if (le)
4841     return le;
4842 
4843   optimize_info_seq_init(info, &info_seq);
4844 
4845   sub_context = OPT_CONTEXT_SINGLED;
4846 
4847   le = optimize_expr(app->rator, info, sub_context);
4848   app->rator = le;
4849   if (info->escapes) {
4850     optimize_info_seq_done(info, &info_seq);
4851     return ensure_noncm(app->rator, info);
4852   }
4853 
4854   {
4855     /* Maybe found "((lambda" after optimizing; try again */
4856     le = optimize_for_inline(info, app->rator, 2, NULL, NULL, app, context, 1);
4857     if (le)
4858       return le;
4859     rator_apply_escapes = info->escapes;
4860   }
4861 
4862   if (SAME_OBJ(app->rator, scheme_values_proc)
4863       || SAME_OBJ(app->rator, scheme_apply_proc))
4864     info->maybe_values_argument = 1;
4865 
4866   /* 1st arg */
4867 
4868   ty = wants_local_type_arguments(app->rator, 0);
4869   if (ty)
4870     sub_context |= (ty << OPT_CONTEXT_TYPE_SHIFT);
4871 
4872   optimize_info_seq_step(info, &info_seq);
4873 
4874   le = optimize_expr(app->rand1, info, sub_context);
4875   app->rand1 = le;
4876   if (info->escapes) {
4877     info->size += 1;
4878     return ensure_noncm(make_discarding_first_sequence(app->rator, app->rand1, info), info);
4879   }
4880 
4881   /* 2nd arg */
4882 
4883   ty = wants_local_type_arguments(app->rator, 1);
4884   if (ty)
4885     sub_context |= (ty << OPT_CONTEXT_TYPE_SHIFT);
4886   else
4887     sub_context &= ~OPT_CONTEXT_TYPE_MASK;
4888 
4889   optimize_info_seq_step(info, &info_seq);
4890 
4891   le = optimize_expr(app->rand2, info, sub_context);
4892   app->rand2 = le;
4893   optimize_info_seq_done(info, &info_seq);
4894   if (info->escapes) {
4895     info->size += 1;
4896     le = make_discarding_first_sequence(app->rator,
4897                                         make_discarding_first_sequence(app->rand1, app->rand2,
4898                                                                        info),
4899                                         info);
4900     return ensure_noncm(le, info);
4901   }
4902 
4903   /* Check for (apply ... (list ...)) after some optimizations: */
4904   le = direct_apply((Scheme_Object *)app, app->rator, app->rand2, info);
4905   if (le) return finish_optimize_app(le, info, context);
4906 
4907   flags = appn_flags(app->rator, info);
4908   SCHEME_APPN_FLAGS(app) |= flags;
4909 
4910   if (rator_apply_escapes) {
4911    info->escapes = 1;
4912    SCHEME_APPN_FLAGS(app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
4913   }
4914 
4915   return finish_optimize_application3(app, info, context);
4916 }
4917 
finish_optimize_application3(Scheme_App3_Rec * app,Optimize_Info * info,int context)4918 static Scheme_Object *finish_optimize_application3(Scheme_App3_Rec *app, Optimize_Info *info, int context)
4919 {
4920   int flags, rator_flags;
4921   Scheme_Object *le;
4922   int all_vals = 1;
4923 
4924   info->size += 1;
4925   info->preserves_marks = 1;
4926   info->single_result = 1;
4927 
4928   if (SCHEME_TYPE(app->rand1) < _scheme_ir_values_types_)
4929     all_vals = 0;
4930   if (SCHEME_TYPE(app->rand2) < _scheme_ir_values_types_)
4931     all_vals = 0;
4932 
4933 
4934   if (all_vals) {
4935     le = try_optimize_fold(app->rator, NULL, (Scheme_Object *)app, info);
4936     if (le)
4937       return le;
4938   }
4939 
4940   /* Check for (call-with-values (lambda () M) N): */
4941   if (SAME_OBJ(app->rator, scheme_call_with_values_proc)) {
4942     if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_ir_lambda_type)) {
4943       Scheme_Lambda *lam = (Scheme_Lambda *)app->rand1;
4944 
4945       if (!lam->num_params) {
4946         /* Convert to apply-values form: */
4947         return optimize_apply_values(app->rand2, lam->body, info,
4948                                      ((SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_SINGLE_RESULT)
4949                                       ? (((SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_STATUS_MASK) == LAMBDA_STATUS_RESULT_TENTATIVE)
4950                                          ? -1
4951                                          : 1)
4952                                        : 0),
4953                                      context);
4954       }
4955     }
4956   }
4957 
4958   if (SAME_OBJ(scheme_procedure_arity_includes_proc, app->rator)) {
4959     if (SCHEME_INTP(app->rand2) && SCHEME_INT_VAL(app->rand2) >= 0) {
4960       Scheme_Object *proc;
4961 
4962       proc = lookup_constant_proc(info, app->rand1, SCHEME_INT_VAL(app->rand2));
4963       if (proc) {
4964         info->preserves_marks = 1;
4965         info->single_result = 1;
4966         return make_discarding_sequence(app->rand1,
4967                                         SAME_OBJ(proc, scheme_true) ? scheme_false : scheme_true,
4968                                         info);
4969       }
4970     }
4971   }
4972 
4973   if (SAME_OBJ(app->rator, scheme_equal_proc)
4974        || SAME_OBJ(app->rator, scheme_eqv_proc)
4975        || SAME_OBJ(app->rator, scheme_eq_proc)) {
4976     if (equivalent_exprs(app->rand1, app->rand2, NULL, NULL, 0)) {
4977       return make_discarding_sequence_3(app->rand1, app->rand2, scheme_true, info);
4978     }
4979     {
4980       Scheme_Object *pred1, *pred2, *pred_new = NULL;
4981       int rel1=0, rel2=0, rel_max, eq_type=0;
4982 
4983       pred1 = expr_implies_predicate(app->rand1, info);
4984       pred2 = expr_implies_predicate(app->rand2, info);
4985       rel1 = relevant_predicate(pred1);
4986       rel2 = relevant_predicate(pred2);
4987       if ((pred1 && pred2)
4988           && (predicate_implies_not(pred1, pred2)
4989               || predicate_implies_not(pred2, pred1))) {
4990         info->preserves_marks = 1;
4991         info->single_result = 1;
4992         return make_discarding_sequence_3(app->rand1, app->rand2, scheme_false, info);
4993       }
4994 
4995       /* Try to transform it into a predicate */
4996       if (rel1 >= RLV_SINGLETON) {
4997         Scheme_Object *new_app;
4998         new_app = make_optimize_prim_application2(pred1, app->rand2, info, context);
4999         return make_discarding_sequence(app->rand1, new_app, info);
5000       }
5001       if (rel2 >= RLV_SINGLETON) {
5002         Scheme_Object *new_app;
5003         new_app = make_optimize_prim_application2(pred2, app->rand1, info, context);
5004         return make_discarding_reverse_sequence(app->rand2, new_app, info);
5005       }
5006 
5007       /* Optimize `equal?' or `eqv?' test on certain types
5008          to `eqv?` or `eq?'. This is especially helpful for the JIT. */
5009       if (SAME_OBJ(app->rator, scheme_eqv_proc))
5010         eq_type = RLV_EQV_TESTEABLE;
5011       if (SAME_OBJ(app->rator, scheme_eq_proc))
5012         eq_type = RLV_EQ_TESTEABLE;
5013 
5014       rel_max = (rel1 >= rel2) ? rel1 : rel2;
5015       if (rel_max >= RLV_EQ_TESTEABLE && eq_type < RLV_EQ_TESTEABLE)
5016         pred_new = scheme_eq_proc;
5017       else if (rel_max >= RLV_EQV_TESTEABLE && eq_type < RLV_EQV_TESTEABLE)
5018         pred_new = scheme_eqv_proc;
5019 
5020       if (pred_new) {
5021         app->rator = pred_new;
5022         SCHEME_APPN_FLAGS(app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
5023 
5024         /* eq? and eqv? are foldable */
5025         if (all_vals) {
5026           le = try_optimize_fold(app->rator, NULL, (Scheme_Object *)app, info);
5027           if (le)
5028             return le;
5029         }
5030       }
5031     }
5032   }
5033 
5034   /* Ad hoc optimization of (unsafe-+ <x> 0), etc. */
5035   if (SCHEME_PRIMP(app->rator)
5036       && (SCHEME_PRIM_PROC_OPT_FLAGS(app->rator) & SCHEME_PRIM_IS_UNSAFE_NONMUTATING)) {
5037     int z1, z2;
5038 
5039     z1 = SAME_OBJ(app->rand1, scheme_make_integer(0));
5040     z2 = SAME_OBJ(app->rand2, scheme_make_integer(0));
5041     if (IS_NAMED_PRIM(app->rator, "unsafe-fx+")) {
5042       if (z1)
5043         return ensure_single_value_noncm(app->rand2, info);
5044       else if (z2)
5045         return ensure_single_value_noncm(app->rand1, info);
5046     } else if (IS_NAMED_PRIM(app->rator, "unsafe-fx-")) {
5047       if (z2)
5048         return ensure_single_value_noncm(app->rand1, info);
5049     } else if (IS_NAMED_PRIM(app->rator, "unsafe-fx*")) {
5050       if (z1 || z2) {
5051         if (z1 && z2)
5052           return scheme_make_integer(0);
5053         else if (z2)
5054           return make_discarding_sequence(app->rand1, scheme_make_integer(0), info);
5055         else
5056           return make_discarding_sequence(app->rand2, scheme_make_integer(0), info);
5057       }
5058       if (SAME_OBJ(app->rand1, scheme_make_integer(1)))
5059         return ensure_single_value_noncm(app->rand2, info);
5060       if (SAME_OBJ(app->rand2, scheme_make_integer(1)))
5061         return ensure_single_value_noncm(app->rand1, info);
5062     } else if (IS_NAMED_PRIM(app->rator, "unsafe-fxquotient")) {
5063       if (z1)
5064         return make_discarding_sequence(app->rand2, scheme_make_integer(0), info);
5065       if (SAME_OBJ(app->rand2, scheme_make_integer(1)))
5066         return ensure_single_value_noncm(app->rand1, info);
5067     } else if (IS_NAMED_PRIM(app->rator, "unsafe-fxremainder")
5068                || IS_NAMED_PRIM(app->rator, "unsafe-fxmodulo")) {
5069       if (z1)
5070         return make_discarding_sequence(app->rand2, scheme_make_integer(0), info);
5071       if (SAME_OBJ(app->rand2, scheme_make_integer(1)))
5072         return make_discarding_sequence(app->rand1, scheme_make_integer(0), info);
5073     }
5074 
5075     z1 = (SCHEME_FLOATP(app->rand1) && (SCHEME_FLOAT_VAL(app->rand1) == 0.0));
5076     z2 = (SCHEME_FLOATP(app->rand2) && (SCHEME_FLOAT_VAL(app->rand2) == 0.0));
5077 
5078     if (IS_NAMED_PRIM(app->rator, "unsafe-fl+")) {
5079       if (z1)
5080         return ensure_single_value_noncm(app->rand2, info);
5081       else if (z2)
5082         return ensure_single_value_noncm(app->rand1, info);
5083     } else if (IS_NAMED_PRIM(app->rator, "unsafe-fl-")) {
5084       if (z2)
5085         return ensure_single_value_noncm(app->rand1, info);
5086     } else if (IS_NAMED_PRIM(app->rator, "unsafe-fl*")) {
5087       if (SCHEME_FLOATP(app->rand1) && (SCHEME_FLOAT_VAL(app->rand1) == 1.0))
5088         return ensure_single_value_noncm(app->rand2, info);
5089       if (SCHEME_FLOATP(app->rand2) && (SCHEME_FLOAT_VAL(app->rand2) == 1.0))
5090         return ensure_single_value_noncm(app->rand1, info);
5091     } else if (IS_NAMED_PRIM(app->rator, "unsafe-fl/")) {
5092       if (SCHEME_FLOATP(app->rand2) && (SCHEME_FLOAT_VAL(app->rand2) == 1.0))
5093         return ensure_single_value_noncm(app->rand1, info);
5094     }
5095 
5096     /* Possible improvement: detect 0 and 1 constants even when general
5097        extflonum operations are not supported. */
5098 #ifdef MZ_LONG_DOUBLE
5099     z1 = (SCHEME_LONG_DBLP(app->rand1) && long_double_is_zero(SCHEME_LONG_DBL_VAL(app->rand1)));
5100     z2 = (SCHEME_LONG_DBLP(app->rand2) && long_double_is_zero(SCHEME_LONG_DBL_VAL(app->rand2)));
5101 
5102     if (IS_NAMED_PRIM(app->rator, "unsafe-extfl+")) {
5103       if (z1)
5104         return ensure_single_value_noncm(app->rand2, info);
5105       else if (z2)
5106         return ensure_single_value_noncm(app->rand1, info);
5107     } else if (IS_NAMED_PRIM(app->rator, "unsafe-extfl-")) {
5108       if (z2)
5109         return ensure_single_value_noncm(app->rand1, info);
5110     } else if (IS_NAMED_PRIM(app->rator, "unsafe-extfl*")) {
5111       if (SCHEME_LONG_DBLP(app->rand1) && long_double_is_1(SCHEME_LONG_DBL_VAL(app->rand1)))
5112         return ensure_single_value_noncm(app->rand2, info);
5113       if (SCHEME_LONG_DBLP(app->rand2) && long_double_is_1(SCHEME_LONG_DBL_VAL(app->rand2)))
5114         return ensure_single_value_noncm(app->rand1, info);
5115     } else if (IS_NAMED_PRIM(app->rator, "unsafe-extfl/")) {
5116       if (SCHEME_LONG_DBLP(app->rand2) && long_double_is_1(SCHEME_LONG_DBL_VAL(app->rand2)))
5117         return ensure_single_value_noncm(app->rand1, info);
5118     }
5119 #endif
5120   } else if (SCHEME_PRIMP(app->rator)
5121              && (SCHEME_PRIM_PROC_OPT_FLAGS(app->rator) & SCHEME_PRIM_IS_BINARY_INLINED)) {
5122     if (IS_NAMED_PRIM(app->rator, "arithmetic-shift")) {
5123       if (SCHEME_INTP(app->rand2) && (SCHEME_INT_VAL(app->rand2) <= 0)
5124           && (is_local_type_expression(app->rand1, info) == SCHEME_LOCAL_TYPE_FIXNUM)) {
5125         app->rator = scheme_unsafe_fxrshift_proc;
5126         app->rand2 = scheme_make_integer(-(SCHEME_INT_VAL(app->rand2)));
5127       }
5128     } else if (IS_NAMED_PRIM(app->rator, "string=?")) {
5129       if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_char_string_type)
5130           && SAME_TYPE(SCHEME_TYPE(app->rand2), scheme_char_string_type)) {
5131         return scheme_string_eq_2(app->rand1, app->rand2);
5132       }
5133     } else if (IS_NAMED_PRIM(app->rator, "bytes=?")) {
5134       if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_byte_string_type)
5135           && SAME_TYPE(SCHEME_TYPE(app->rand2), scheme_byte_string_type)) {
5136         return scheme_byte_string_eq_2(app->rand1, app->rand2);
5137       }
5138     } else if (IS_NAMED_PRIM(app->rator, "char=?")) {
5139       if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_char_type)
5140           && SAME_TYPE(SCHEME_TYPE(app->rand2), scheme_char_type)) {
5141         return (SCHEME_CHAR_VAL(app->rand1) == SCHEME_CHAR_VAL(app->rand2)) ? scheme_true : scheme_false;
5142       }
5143     }
5144   }
5145 
5146   if (SCHEME_PRIMP(app->rator)
5147       && (2 >= ((Scheme_Primitive_Proc *)app->rator)->mina)
5148       && (2 <= ((Scheme_Primitive_Proc *)app->rator)->mu.maxa)) {
5149     Scheme_Object *app_o = (Scheme_Object *)app, *rator = app->rator, *rand1 = app->rand1, *rand2 = app->rand2;
5150 
5151     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_AD_HOC_OPT) {
5152       check_known_both_variant(info, app_o, rator, rand1, rand2, "bitwise-and", scheme_fixnum_p_proc,
5153                                scheme_unsafe_fxand_proc, 0, scheme_real_p_proc);
5154       check_known_both_variant(info, app_o, rator, rand1, rand2, "bitwise-ior", scheme_fixnum_p_proc,
5155                                scheme_unsafe_fxior_proc, 0, scheme_real_p_proc);
5156       check_known_both_variant(info, app_o, rator, rand1, rand2, "bitwise-xor", scheme_fixnum_p_proc,
5157                                scheme_unsafe_fxxor_proc, 0, scheme_real_p_proc);
5158 
5159       check_known_both_variant(info, app_o, rator, rand1, rand2, "fxand", scheme_fixnum_p_proc,
5160                                scheme_unsafe_fxand_proc, info->unsafe_mode, scheme_real_p_proc);
5161       check_known_both_variant(info, app_o, rator, rand1, rand2, "fxior", scheme_fixnum_p_proc,
5162                                scheme_unsafe_fxior_proc, info->unsafe_mode, scheme_real_p_proc);
5163       check_known_both_variant(info, app_o, rator, rand1, rand2, "fxxor", scheme_fixnum_p_proc,
5164                                scheme_unsafe_fxxor_proc, info->unsafe_mode, scheme_real_p_proc);
5165 
5166       check_known_both_try(info, app_o, rator, rand1, rand2, "=", scheme_fixnum_p_proc, scheme_unsafe_fx_eq_proc, 0);
5167       check_known_both_try(info, app_o, rator, rand1, rand2, "<", scheme_fixnum_p_proc, scheme_unsafe_fx_lt_proc, 0);
5168       check_known_both_try(info, app_o, rator, rand1, rand2, ">", scheme_fixnum_p_proc, scheme_unsafe_fx_gt_proc, 0);
5169       check_known_both_try(info, app_o, rator, rand1, rand2, "<=", scheme_fixnum_p_proc, scheme_unsafe_fx_lt_eq_proc, 0);
5170       check_known_both_try(info, app_o, rator, rand1, rand2, ">=", scheme_fixnum_p_proc, scheme_unsafe_fx_gt_eq_proc, 0);
5171       check_known_both_try(info, app_o, rator, rand1, rand2, "min", scheme_fixnum_p_proc, scheme_unsafe_fx_min_proc, 0);
5172       check_known_both_try(info, app_o, rator, rand1, rand2, "max", scheme_fixnum_p_proc, scheme_unsafe_fx_max_proc, 0);
5173 
5174       check_known_both_try(info, app_o, rator, rand1, rand2, "fx=", scheme_fixnum_p_proc, scheme_unsafe_fx_eq_proc, info->unsafe_mode);
5175       check_known_both_try(info, app_o, rator, rand1, rand2, "fx<", scheme_fixnum_p_proc, scheme_unsafe_fx_lt_proc, info->unsafe_mode);
5176       check_known_both_try(info, app_o, rator, rand1, rand2, "fx>", scheme_fixnum_p_proc, scheme_unsafe_fx_gt_proc, info->unsafe_mode);
5177       check_known_both_try(info, app_o, rator, rand1, rand2, "fx<=", scheme_fixnum_p_proc, scheme_unsafe_fx_lt_eq_proc, info->unsafe_mode);
5178       check_known_both_try(info, app_o, rator, rand1, rand2, "fx>=", scheme_fixnum_p_proc, scheme_unsafe_fx_gt_eq_proc, info->unsafe_mode);
5179       check_known_both_try(info, app_o, rator, rand1, rand2, "fxmin", scheme_fixnum_p_proc, scheme_unsafe_fx_min_proc, info->unsafe_mode);
5180       check_known_both_try(info, app_o, rator, rand1, rand2, "fxmax", scheme_fixnum_p_proc, scheme_unsafe_fx_max_proc, info->unsafe_mode);
5181 
5182       check_known_both_try(info, app_o, rator, rand1, rand2, "fx+", scheme_fixnum_p_proc, scheme_unsafe_fx_plus_proc, info->unsafe_mode);
5183       check_known_both_try(info, app_o, rator, rand1, rand2, "fx-", scheme_fixnum_p_proc, scheme_unsafe_fx_minus_proc, info->unsafe_mode);
5184       check_known_both_try(info, app_o, rator, rand1, rand2, "fx*", scheme_fixnum_p_proc, scheme_unsafe_fx_times_proc, info->unsafe_mode);
5185 
5186       check_known_both_try(info, app_o, rator, rand1, rand2, "char=?", scheme_char_p_proc, scheme_unsafe_char_eq_proc, info->unsafe_mode);
5187       check_known_both_try(info, app_o, rator, rand1, rand2, "char<?", scheme_char_p_proc, scheme_unsafe_char_lt_proc, info->unsafe_mode);
5188       check_known_both_try(info, app_o, rator, rand1, rand2, "char>?", scheme_char_p_proc, scheme_unsafe_char_gt_proc, info->unsafe_mode);
5189       check_known_both_try(info, app_o, rator, rand1, rand2, "char<=?", scheme_char_p_proc, scheme_unsafe_char_lt_eq_proc, info->unsafe_mode);
5190       check_known_both_try(info, app_o, rator, rand1, rand2, "char>=?", scheme_char_p_proc, scheme_unsafe_char_gt_eq_proc, info->unsafe_mode);
5191 
5192       rator = app->rator; /* in case it was updated */
5193 
5194       check_known_both(info, app_o, rator, rand1, rand2, "string-append", scheme_string_p_proc, scheme_true, info->unsafe_mode);
5195       check_known_both(info, app_o, rator, rand1, rand2, "bytes-append", scheme_byte_string_p_proc, scheme_true, info->unsafe_mode);
5196       check_known(info, app_o, rator, rand1, "string-ref", scheme_string_p_proc, NULL, info->unsafe_mode);
5197       check_known(info, app_o, rator, rand2, "string-ref", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
5198       check_known(info, app_o, rator, rand1, "bytes-ref", scheme_byte_string_p_proc,
5199                   (info->unsafe_mode ? scheme_unsafe_bytes_ref_proc : NULL), info->unsafe_mode);
5200       check_known(info, app_o, rator, rand2, "bytes-ref", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
5201 
5202       check_known(info, app_o, rator, rand1, "append", scheme_list_p_proc, scheme_true, info->unsafe_mode);
5203       check_known(info, app_o, rator, rand1, "list-ref", scheme_pair_p_proc, NULL, info->unsafe_mode);
5204       check_known(info, app_o, rator, rand2, "list-ref", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
5205     }
5206 
5207     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_WANTS_REAL)
5208       check_known_both(info, app_o, rator, rand1, rand2, NULL, scheme_real_p_proc,
5209                        (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_OMITTABLE_ON_GOOD_ARGS) ? scheme_true : NULL,
5210                        info->unsafe_mode);
5211     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_WANTS_NUMBER)
5212       check_known_both(info, app_o, rator, rand1, rand2, NULL, scheme_number_p_proc,
5213                        (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_OMITTABLE_ON_GOOD_ARGS) ? scheme_true : NULL,
5214                        info->unsafe_mode);
5215 
5216     if (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_AD_HOC_OPT) {
5217       check_known(info, app_o, rator, rand1, "vector-ref", scheme_vector_p_proc, NULL, info->unsafe_mode);
5218       check_known(info, app_o, rator, rand2, "vector-ref", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
5219       check_known(info, app_o, rator, rand1, "vector*-ref", scheme_vector_p_proc,
5220                   (info->unsafe_mode ? scheme_unsafe_vector_star_ref_proc: NULL), info->unsafe_mode);
5221       check_known(info, app_o, rator, rand2, "vector*-ref", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
5222       check_known(info, app_o, rator, rand1, "make-vector", scheme_fixnum_p_proc, NULL, info->unsafe_mode);
5223 
5224       check_known(info, app_o, rator, rand1, "set-box!", scheme_box_p_proc, NULL, info->unsafe_mode);
5225       check_known(info, app_o, rator, rand1, "set-box*!", scheme_box_p_proc,
5226                   (info->unsafe_mode ? scheme_unsafe_set_box_star_proc : NULL), info->unsafe_mode);
5227       check_known(info, app_o, rator, rand1, "unsafe-set-box!", scheme_box_p_proc, NULL, info->unsafe_mode);
5228       check_known(info, app_o, rator, rand1, "unsafe-set-box*!", scheme_box_p_proc, NULL, info->unsafe_mode);
5229 
5230       check_known(info, app_o, rator, rand1, "procedure-closure-contents-eq?", scheme_procedure_p_proc, NULL, info->unsafe_mode);
5231       check_known(info, app_o, rator, rand2, "procedure-closure-contents-eq?", scheme_procedure_p_proc, NULL, info->unsafe_mode);
5232       check_known(info, app_o, rator, rand1, "procedure-arity-includes?", scheme_procedure_p_proc, NULL, info->unsafe_mode);
5233 
5234       check_known(info, app_o, rator, rand1, "map", scheme_procedure_p_proc, NULL, info->unsafe_mode);
5235       check_known(info, app_o, rator, rand1, "for-each", scheme_procedure_p_proc, NULL, info->unsafe_mode);
5236       check_known(info, app_o, rator, rand1, "andmap", scheme_procedure_p_proc, NULL, info->unsafe_mode);
5237       check_known(info, app_o, rator, rand1, "ormap", scheme_procedure_p_proc, NULL, info->unsafe_mode);
5238       check_known(info, app_o, rator, rand2, "map", scheme_list_p_proc, NULL, info->unsafe_mode);
5239       check_known(info, app_o, rator, rand2, "for-each", scheme_list_p_proc, NULL, info->unsafe_mode);
5240       check_known(info, app_o, rator, rand2, "andmap", scheme_list_p_proc, NULL, info->unsafe_mode);
5241       check_known(info, app_o, rator, rand2, "ormap", scheme_list_p_proc, NULL, info->unsafe_mode);
5242     }
5243 
5244   }
5245 
5246   /* Using a struct mutator? */
5247   {
5248     Scheme_Object *alt;
5249     alt = get_struct_proc_shape(app->rator, info, 0);
5250     if (alt) {
5251       int mode = (SCHEME_PROC_SHAPE_MODE(alt) & STRUCT_PROC_SHAPE_MASK);
5252 
5253       if (mode == STRUCT_PROC_SHAPE_SETTER) {
5254         Scheme_Object *pred;
5255         int unsafe = 0;
5256 
5257         if (info->unsafe_mode) {
5258           pred = NULL;
5259           unsafe = 1;
5260         } else
5261           pred = expr_implies_predicate(app->rand1, info);
5262 
5263         if ((unsafe
5264              || (pred
5265                  && SAME_TYPE(SCHEME_TYPE(pred), scheme_struct_proc_shape_type)
5266                  && is_struct_identity_subtype(SCHEME_PROC_SHAPE_IDENTITY(pred),
5267                                                SCHEME_PROC_SHAPE_IDENTITY(alt))))
5268             /* Only if the field position is known: */
5269             && ((SCHEME_PROC_SHAPE_MODE(alt) >> STRUCT_PROC_SHAPE_SHIFT) != 0)) {
5270           /* Struct type matches, so use `unsafe-struct-set!` */
5271           Scheme_Object *l;
5272           Scheme_App_Rec *new_app;
5273           int pos = (SCHEME_PROC_SHAPE_MODE(alt) >> STRUCT_PROC_SHAPE_SHIFT) - 1;
5274           l = scheme_make_pair(scheme_make_integer(pos),
5275                                scheme_make_pair(app->rand2,
5276                                                 scheme_null));
5277           l = scheme_make_pair(app->rand1, l);
5278           l = scheme_make_pair(((SCHEME_PROC_SHAPE_MODE(alt) & STRUCT_PROC_SHAPE_AUTHENTIC)
5279                                 ? scheme_unsafe_struct_star_set_proc
5280                                 : scheme_unsafe_struct_set_proc),
5281                                l);
5282           new_app = (Scheme_App_Rec *)scheme_make_application(l, info);
5283           SCHEME_APPN_FLAGS(new_app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
5284           return finish_optimize_application(new_app, info, context);
5285         }
5286 
5287         /* Register type based on setter succeeding: */
5288         if (!SCHEME_NULLP(SCHEME_PROC_SHAPE_IDENTITY(alt))
5289             && SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_ir_local_type))
5290           add_type(info, app->rand1, scheme_make_struct_proc_shape(STRUCT_PROC_SHAPE_PRED,
5291                                                                    SCHEME_PROC_SHAPE_IDENTITY(alt)));
5292       }
5293     }
5294   }
5295 
5296   increment_clocks_for_application(info, app->rator, 2);
5297 
5298   rator_flags = get_rator_flags(app->rator, 2, info);
5299   info->preserves_marks = !!(rator_flags & LAMBDA_PRESERVES_MARKS);
5300   info->single_result = !!(rator_flags & LAMBDA_SINGLE_RESULT);
5301   if ((rator_flags & LAMBDA_STATUS_MASK) == LAMBDA_STATUS_RESULT_TENTATIVE) {
5302     info->preserves_marks = -info->preserves_marks;
5303     info->single_result = -info->single_result;
5304   }
5305 
5306   register_local_argument_types(NULL, NULL, app, info);
5307 
5308   flags = appn_flags(app->rator, info);
5309   SCHEME_APPN_FLAGS(app) |= flags;
5310 
5311   return finish_optimize_any_application((Scheme_Object *)app, app->rator, 2,
5312                                          info, context);
5313 }
5314 
5315 /*========================================================================*/
5316 /*                   the apply-values bytecode form                       */
5317 /*========================================================================*/
5318 
optimize_apply_values(Scheme_Object * f,Scheme_Object * e,Optimize_Info * info,int e_single_result,int context)5319 Scheme_Object *optimize_apply_values(Scheme_Object *f, Scheme_Object *e,
5320                                      Optimize_Info *info,
5321                                      int e_single_result,
5322                                      int context)
5323 /* f and e are already optimized */
5324 {
5325   Scheme_Object *o_f;
5326 
5327   info->preserves_marks = 0;
5328   info->single_result = 0;
5329 
5330   o_f = lookup_constant_proc(info, f, (e_single_result > 0) ? 1 : -1);
5331   if (o_f) {
5332     if (SAME_TYPE(SCHEME_TYPE(o_f), scheme_ir_lambda_type)) {
5333       Scheme_Lambda *lam = (Scheme_Lambda *)o_f;
5334       int flags = SCHEME_LAMBDA_FLAGS(lam);
5335       info->preserves_marks = !!(flags & LAMBDA_PRESERVES_MARKS);
5336       info->single_result = !!(flags & LAMBDA_SINGLE_RESULT);
5337       if ((flags & LAMBDA_STATUS_MASK) == LAMBDA_STATUS_RESULT_TENTATIVE) {
5338         info->preserves_marks = -info->preserves_marks;
5339         info->single_result = -info->single_result;
5340       }
5341     }
5342   }
5343 
5344   if (o_f && (e_single_result > 0)) {
5345     /* Just make it an application (N M): */
5346     Scheme_App2_Rec *app2;
5347     Scheme_Object *e_cloned, *f_cloned;
5348 
5349     app2 = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
5350     app2->iso.so.type = scheme_application2_type;
5351 
5352     /* Try to inline... */
5353 
5354     e_cloned = optimize_clone(1, e, info, empty_eq_hash_tree, 0);
5355     if (e_cloned) {
5356       if (SAME_TYPE(SCHEME_TYPE(f), scheme_ir_lambda_type))
5357         f_cloned = optimize_clone(1, f, info, empty_eq_hash_tree, 0);
5358       else {
5359         /* Otherwise, no clone is needed. */
5360         f_cloned = f;
5361       }
5362 
5363       if (f_cloned) {
5364         app2->rator = f_cloned;
5365         app2->rand = e_cloned;
5366         info->inline_fuel >>= 1; /* because we've already optimized the rand */
5367         return optimize_application2((Scheme_Object *)app2, info, context);
5368       }
5369     }
5370 
5371     app2->rator = f;
5372     app2->rand = e;
5373     return (Scheme_Object *)app2;
5374   }
5375 
5376   {
5377     Scheme_Object *av;
5378     av = scheme_alloc_object();
5379     av->type = scheme_apply_values_type;
5380     SCHEME_PTR1_VAL(av) = f;
5381     SCHEME_PTR2_VAL(av) = e;
5382     return av;
5383   }
5384 }
5385 
5386 /*========================================================================*/
5387 /*                             begin and begin0                           */
5388 /*========================================================================*/
5389 
5390 static Scheme_Object *optimize_sequence(Scheme_Object *o, Optimize_Info *info, int context, int sub_opt);
5391 
flatten_sequence(Scheme_Object * o,Optimize_Info * info,int context)5392 static Scheme_Object *flatten_sequence(Scheme_Object *o, Optimize_Info *info, int context)
5393 {
5394   Scheme_Sequence *s = (Scheme_Sequence *)o, *s2, *s3;
5395   Scheme_Object *o3;
5396   int i, j, k, count, extra = 0, split = 0, b0, new_count;
5397 
5398   if (!info->flatten_fuel)
5399     return o;
5400 
5401   b0 = SAME_TYPE(SCHEME_TYPE(o), scheme_begin0_sequence_type);
5402   count = s->count;
5403 
5404   /* exceptions: (begin ... (begin0 ...)) and (begin0 (begin ...) ...) */
5405   for (i = 0; i < count; i++) {
5406     o3 = s->array[i];
5407     if ((SAME_TYPE(SCHEME_TYPE(o3), scheme_sequence_type) && !(!i && b0))
5408         || (SAME_TYPE(SCHEME_TYPE(o3), scheme_begin0_sequence_type) && !(i == count - 1 && !b0))) {
5409       s3 = (Scheme_Sequence *)o3;
5410       extra += s3->count;
5411       split++;
5412     }
5413   }
5414 
5415   if (!split)
5416     return o;
5417 
5418   info->flatten_fuel--;
5419   info->size -= split;
5420 
5421   new_count = s->count + extra - split;
5422   if (new_count > 0) {
5423     s2 = scheme_malloc_sequence(new_count);
5424     s2->so.type = s->so.type;
5425     s2->count = new_count;
5426   } else
5427     s2 = NULL;
5428   k = 0;
5429 
5430   /* exceptions: (begin ... (begin0 ...)) and (begin0 (begin ...) ...) */
5431   for (i = 0; i < count; i++) {
5432     o3 = s->array[i];
5433     if ((SAME_TYPE(SCHEME_TYPE(o3), scheme_sequence_type) && !(!i && b0))
5434         || (SAME_TYPE(SCHEME_TYPE(o3), scheme_begin0_sequence_type) && !(i == count - 1 && !b0))) {
5435       s3 = (Scheme_Sequence *)o3;
5436       for (j = 0; j < s3->count; j++) {
5437         s2->array[k++] = s3->array[j];
5438       }
5439     } else {
5440       s2->array[k++] = o3;
5441     }
5442   }
5443 
5444   MZ_ASSERT(k == new_count);
5445 
5446   if (s2->count == 1)
5447     return s2->array[0];
5448 
5449   if (SAME_TYPE(SCHEME_TYPE(s2), scheme_sequence_type))
5450     return optimize_sequence((Scheme_Object *)s2, info, context, 0);
5451   else
5452     return (Scheme_Object *)s2;
5453 }
5454 
optimize_sequence(Scheme_Object * o,Optimize_Info * info,int context,int sub_opt)5455 static Scheme_Object *optimize_sequence(Scheme_Object *o, Optimize_Info *info, int context, int sub_opt)
5456 {
5457   Scheme_Sequence *s = (Scheme_Sequence *)o;
5458   Scheme_Object *le;
5459   int i, count, prev_size;
5460   int drop = 0, preserves_marks = 0, single_result = 0;
5461   Optimize_Info_Sequence info_seq;
5462 
5463   /* If !sub_opt, then just inspect already-optimized results. Note
5464      that `info` doesn't change in this mode, so we shouldn't try to
5465      check whether an expression escapes, for example. */
5466 
5467   if (sub_opt)
5468     optimize_info_seq_init(info, &info_seq);
5469   else
5470     memset(&info_seq, 0, sizeof(info_seq));
5471 
5472   count = s->count;
5473   for (i = 0; i < count; i++) {
5474     prev_size = info->size;
5475 
5476     if (sub_opt) {
5477       optimize_info_seq_step(info, &info_seq);
5478       le = optimize_expr(s->array[i], info,
5479                          ((i + 1 == count)
5480                           ? scheme_optimize_tail_context(context)
5481                           : 0));
5482     } else
5483       le = s->array[i];
5484 
5485     if (i + 1 == count) {
5486       single_result = info->single_result;
5487       preserves_marks = info->preserves_marks;
5488       s->array[i] = le;
5489     } else {
5490       if (!sub_opt || !info->escapes) {
5491         /* Inlining and constant propagation can expose omittable expressions. */
5492         le = optimize_ignored(le, info, -1, 1, 5);
5493         if (!le) {
5494           drop++;
5495           info->size = prev_size;
5496           s->array[i] = NULL;
5497         } else {
5498           s->array[i] = le;
5499         }
5500       } else {
5501         int j;
5502 
5503         single_result = info->single_result;
5504         preserves_marks = info->preserves_marks;
5505         /* Move to last position in case the begin form is dropped */
5506         s->array[count - 1] = le;
5507         for (j = i; j < count - 1; j++) {
5508           drop++;
5509           s->array[j] = NULL;
5510         }
5511         break;
5512       }
5513     }
5514   }
5515 
5516   if (sub_opt)
5517     optimize_info_seq_done(info, &info_seq);
5518 
5519   info->preserves_marks = preserves_marks;
5520   info->single_result = single_result;
5521 
5522   if (drop + 1 == s->count) {
5523     le = s->array[drop];
5524     if (info->escapes)
5525       le = ensure_noncm(le, info);
5526     return le;
5527   }
5528 
5529   if (drop) {
5530     Scheme_Sequence *s2;
5531     int j = 0;
5532 
5533     s2 = scheme_malloc_sequence(s->count - drop);
5534     s2->so.type = s->so.type;
5535     s2->count = s->count - drop;
5536 
5537     for (i = 0; i < s->count; i++) {
5538       if (s->array[i]) {
5539         s2->array[j++] = s->array[i];
5540       }
5541     }
5542 
5543     s = s2;
5544   }
5545 
5546   return flatten_sequence((Scheme_Object *)s, info, context);
5547 }
5548 
5549 /*========================================================================*/
5550 /*                      conditionals and types                            */
5551 /*========================================================================*/
5552 
collapse_local(Scheme_Object * var,Optimize_Info * info,int context)5553 static Scheme_Object *collapse_local(Scheme_Object *var, Optimize_Info *info, int context)
5554 /* Replace `var` in the given context with a constant, if possible based on its type  */
5555 {
5556   if (!SCHEME_VAR(var)->mutated) {
5557     Scheme_Object *pred;
5558 
5559     pred = expr_implies_predicate(var, info);
5560     if (pred) {
5561       if (predicate_implies(pred, scheme_not_proc))
5562         return scheme_false;
5563 
5564       if (context & OPT_CONTEXT_BOOLEAN) {
5565         if (predicate_implies_not(pred, scheme_not_proc))
5566           return scheme_true;
5567       }
5568 
5569       if (SAME_OBJ(pred, scheme_true_object_p_proc))
5570         return scheme_true;
5571       if (SAME_OBJ(pred, scheme_null_p_proc))
5572         return scheme_null;
5573       if (SAME_OBJ(pred, scheme_void_p_proc))
5574         return scheme_void;
5575       if (SAME_OBJ(pred, scheme_eof_object_p_proc))
5576         return scheme_eof;
5577     }
5578   }
5579   return NULL;
5580 }
5581 
5582 /* This function is used to reduce:
5583    (if <x> a b) => (begin <x> <result-a-or-b>)
5584    (if a b #f) => a , and similar
5585    (eq? a b) => (begin a b #t)
5586    The function considers only values and variable references, so <a> and <b> don't have side effects.
5587    But each reduction has a very different behavior for expressions with side effects. */
equivalent_exprs(Scheme_Object * a,Scheme_Object * b,Optimize_Info * a_info,Optimize_Info * b_info,int context)5588 static Scheme_Object *equivalent_exprs(Scheme_Object *a, Scheme_Object *b,
5589                                        Optimize_Info *a_info, Optimize_Info *b_info, int context)
5590 {
5591   if (SAME_OBJ(a, b))
5592     return a;
5593 
5594   if (SAME_TYPE(SCHEME_TYPE(a), scheme_ir_toplevel_type)
5595       && SAME_TYPE(SCHEME_TYPE(b), scheme_ir_toplevel_type)
5596       && (SCHEME_IR_TOPLEVEL_INSTANCE(a) == SCHEME_IR_TOPLEVEL_INSTANCE(b))
5597       && (SCHEME_IR_TOPLEVEL_POS(a) == SCHEME_IR_TOPLEVEL_POS(b)))
5598     return a;
5599 
5600   if (b_info
5601       && SAME_TYPE(SCHEME_TYPE(a), scheme_ir_local_type)
5602       && (SCHEME_TYPE(b) > _scheme_ir_values_types_)) {
5603     Scheme_Object *n;
5604     n = collapse_local(a, b_info, context);
5605     if (n && SAME_OBJ(n, b))
5606       return a;
5607   }
5608 
5609   if (a_info
5610       && SAME_TYPE(SCHEME_TYPE(b), scheme_ir_local_type)
5611       && (SCHEME_TYPE(a) > _scheme_ir_values_types_)) {
5612     Scheme_Object *n;
5613     n = collapse_local(b, a_info, context);
5614     if (n && SAME_OBJ(n, a))
5615       return b;
5616   }
5617 
5618   return NULL;
5619 }
5620 
add_type(Optimize_Info * info,Scheme_Object * var,Scheme_Object * pred)5621 static void add_type(Optimize_Info *info, Scheme_Object *var, Scheme_Object *pred)
5622 /* This is conceptually an intersection, but `Any` is represented by a
5623    missing entry, so the implementation looks like an union. */
5624 {
5625   Scheme_Hash_Tree *new_types = info->types;
5626   Scheme_Object *old_pred;
5627 
5628   if (SCHEME_VAR(var)->mutated)
5629     return;
5630 
5631   /* Don't add the type if something is already there, which may happen when no_types,
5632      as long as the existing predicate implies the new one. */
5633   if (SCHEME_VAR(var)->val_type) /* => more specific than other predicates */
5634     return;
5635   old_pred = optimize_get_predicate(info, var, 1);
5636   if (old_pred && predicate_implies(old_pred, pred))
5637     return;
5638 
5639   /* special case: list? and pair? => list-pair? */
5640   if (old_pred) {
5641     if ((SAME_OBJ(old_pred, scheme_list_p_proc)
5642          && (SAME_OBJ(pred, scheme_pair_p_proc)))
5643         || (SAME_OBJ(old_pred, scheme_pair_p_proc)
5644             && (SAME_OBJ(pred, scheme_list_p_proc)))) {
5645       pred = scheme_list_pair_p_proc;
5646     }
5647   }
5648 
5649   if (!new_types)
5650     new_types = scheme_make_hash_tree(SCHEME_hashtr_eq);
5651   new_types = scheme_hash_tree_set(new_types, var, pred);
5652   info->types = new_types;
5653 }
5654 
add_type_no(Optimize_Info * info,Scheme_Object * var,Scheme_Object * pred)5655 static void add_type_no(Optimize_Info *info, Scheme_Object *var, Scheme_Object *pred)
5656 /* Currently only check a few special cases for lists and booleans. */
5657 {
5658   Scheme_Object *old_pred;
5659 
5660   if (SCHEME_VAR(var)->mutated)
5661     return;
5662 
5663   old_pred = optimize_get_predicate(info, var, 1);
5664 
5665   if (old_pred && SAME_OBJ(old_pred, scheme_list_p_proc)) {
5666     /* list? but not null? => list-pair? */
5667     if (SAME_OBJ(pred, scheme_null_p_proc))
5668       add_type(info, var, scheme_list_pair_p_proc);
5669 
5670     /* list? but not pair? => null? */
5671     /* list? but not list-pair? => null? */
5672     if (SAME_OBJ(pred, scheme_pair_p_proc)
5673         ||SAME_OBJ(pred, scheme_list_pair_p_proc))
5674       add_type(info, var, scheme_null_p_proc);
5675   }
5676 
5677   if (old_pred && SAME_OBJ(old_pred, scheme_boolean_p_proc)) {
5678     /* boolean? but not `not` => true-object? */
5679     if (SAME_OBJ(pred, scheme_not_proc))
5680       add_type(info, var, scheme_true_object_p_proc);
5681 
5682     /* boolean? but not true-object? => `not` */
5683     if (SAME_OBJ(pred, scheme_true_object_p_proc))
5684       add_type(info, var, scheme_not_proc);
5685   }
5686 }
5687 
5688 
5689 
merge_types(Optimize_Info * src_info,Optimize_Info * info,Scheme_Hash_Tree * skip_vars)5690 static void merge_types(Optimize_Info *src_info, Optimize_Info *info, Scheme_Hash_Tree *skip_vars)
5691 {
5692   Scheme_Hash_Tree *types = src_info->types;
5693   Scheme_Object *var, *pred;
5694   intptr_t i;
5695 
5696   if (!types)
5697     return;
5698 
5699   if (skip_vars) {
5700     /* Remove variables from `types` that we're supposed to skip */
5701     i = scheme_hash_tree_next(skip_vars, -1);
5702     while (i != -1) {
5703       scheme_hash_tree_index(skip_vars, i, &var, NULL);
5704       types = scheme_hash_tree_set(types, var, NULL);
5705       i = scheme_hash_tree_next(skip_vars, i);
5706     }
5707   }
5708 
5709   if (!info->types || (types->count > info->types->count)) {
5710     /* It will be faster to merge the old table into the new one: */
5711     Scheme_Hash_Tree *old_types = info->types;
5712     info->types = types;
5713     if (!old_types)
5714       return;
5715     types = old_types;
5716   }
5717 
5718   i = scheme_hash_tree_next(types, -1);
5719   while (i != -1) {
5720     scheme_hash_tree_index(types, i, &var, &pred);
5721     add_type(info, var, pred);
5722     i = scheme_hash_tree_next(types, i);
5723   }
5724 }
5725 
merge_branchs_types(Optimize_Info * t_info,Optimize_Info * f_info,Optimize_Info * base_info)5726 static void merge_branchs_types(Optimize_Info *t_info, Optimize_Info *f_info,
5727                                       Optimize_Info *base_info)
5728 /* This is conceptually an union, but `Any` is represented by a
5729    missing entry, so the implementation looks like an intersection.
5730    This adds to base_info the "intersection" of the types of t_info and f_info */
5731 {
5732   Scheme_Hash_Tree *t_types = t_info->types, *f_types = f_info->types;
5733   Scheme_Object *var, *t_pred, *f_pred;
5734   intptr_t i;
5735 
5736   if (!t_types || !f_types)
5737     return;
5738 
5739   if (f_types->count > t_types->count) {
5740     Scheme_Hash_Tree *swap = f_types;
5741     f_types = t_types;
5742     t_types = swap;
5743   }
5744 
5745   i = scheme_hash_tree_next(f_types, -1);
5746   while (i != -1) {
5747     scheme_hash_tree_index(f_types, i, &var, &f_pred);
5748     t_pred = scheme_eq_hash_tree_get(t_types, var);
5749     if (t_pred) {
5750       if (predicate_implies(f_pred, t_pred))
5751         add_type(base_info, var, t_pred);
5752       else if (predicate_implies(t_pred, f_pred))
5753         add_type(base_info, var, f_pred);
5754       else {
5755         /* special case: null? or list-pair? => list? */
5756        if ((SAME_OBJ(t_pred, scheme_null_p_proc)
5757          && (SAME_OBJ(f_pred, scheme_list_pair_p_proc)))
5758         || (SAME_OBJ(t_pred, scheme_list_pair_p_proc)
5759             && (SAME_OBJ(f_pred, scheme_null_p_proc)))) {
5760         add_type(base_info, var, scheme_list_p_proc);
5761        }
5762         /* special case: true-object? or `not` => boolean? */
5763        if ((SAME_OBJ(t_pred, scheme_not_proc)
5764          && (SAME_OBJ(f_pred, scheme_true_object_p_proc)))
5765         || (SAME_OBJ(t_pred, scheme_true_object_p_proc)
5766             && (SAME_OBJ(f_pred, scheme_not_proc)))) {
5767         add_type(base_info, var, scheme_boolean_p_proc);
5768        }
5769       }
5770     }
5771     i = scheme_hash_tree_next(f_types, i);
5772   }
5773 }
5774 
relevant_predicate(Scheme_Object * pred)5775 static int relevant_predicate(Scheme_Object *pred)
5776 {
5777   /* Relevant predicates need to be disjoint for try_reduce_predicate(),
5778      finish_optimize_application3() and add_types_for_t_branch().
5779      The predicate_implies() and predicate_implies_not() functions must
5780      be kept in sync with this list. */
5781 
5782   if (SAME_OBJ(pred, scheme_pair_p_proc)
5783       || SAME_OBJ(pred, scheme_list_p_proc)
5784       || SAME_OBJ(pred, scheme_list_pair_p_proc)
5785       || SAME_OBJ(pred, scheme_mpair_p_proc)
5786       || SAME_OBJ(pred, scheme_box_p_proc)
5787       || SAME_OBJ(pred, scheme_string_p_proc)
5788       || SAME_OBJ(pred, scheme_byte_string_p_proc)
5789       || SAME_OBJ(pred, scheme_vector_p_proc)
5790       || SAME_OBJ(pred, scheme_procedure_p_proc)
5791       || SAME_OBJ(pred, scheme_syntax_p_proc))
5792     return RLV_IS_RELEVANT;
5793   if (SAME_OBJ(pred, scheme_char_p_proc)
5794       || SAME_OBJ(pred, scheme_flonum_p_proc)
5795       || SAME_OBJ(pred, scheme_number_p_proc)
5796       || SAME_OBJ(pred, scheme_real_p_proc)
5797       || SAME_OBJ(pred, scheme_extflonum_p_proc))
5798     return RLV_EQV_TESTEABLE;
5799   if (SAME_OBJ(pred, scheme_symbol_p_proc)
5800       || SAME_OBJ(pred, scheme_keyword_p_proc)
5801       || SAME_OBJ(pred, scheme_fixnum_p_proc)
5802       || SAME_OBJ(pred, scheme_interned_char_p_proc)
5803       || SAME_OBJ(pred, scheme_boolean_p_proc))
5804     return RLV_EQ_TESTEABLE;
5805   if (SAME_OBJ(pred, scheme_null_p_proc)
5806       || SAME_OBJ(pred, scheme_void_p_proc)
5807       || SAME_OBJ(pred, scheme_eof_object_p_proc)
5808       || SAME_OBJ(pred, scheme_true_object_p_proc)
5809       || SAME_OBJ(pred, scheme_not_proc))
5810     return RLV_SINGLETON;
5811 
5812   return 0;
5813 }
5814 
predicate_implies(Scheme_Object * pred1,Scheme_Object * pred2)5815 static int predicate_implies(Scheme_Object *pred1, Scheme_Object *pred2)
5816 {
5817   if (!pred1 || !pred2)
5818     return 0;
5819 
5820   /* P => P */
5821   if (SAME_OBJ(pred1, pred2))
5822     return 1;
5823 
5824   /* null? => list? */
5825   if (SAME_OBJ(pred2, scheme_list_p_proc)
5826       && SAME_OBJ(pred1, scheme_null_p_proc))
5827     return 1;
5828 
5829   /* list-pair? => list? */
5830   if (SAME_OBJ(pred2, scheme_list_p_proc)
5831       && SAME_OBJ(pred1, scheme_list_pair_p_proc))
5832     return 1;
5833 
5834   /* list-pair? => pair? */
5835   if (SAME_OBJ(pred2, scheme_pair_p_proc)
5836       && SAME_OBJ(pred1, scheme_list_pair_p_proc))
5837     return 1;
5838 
5839   /* interned-char? => char? */
5840   if (SAME_OBJ(pred2, scheme_char_p_proc)
5841       && SAME_OBJ(pred1, scheme_interned_char_p_proc))
5842     return 1;
5843 
5844   /* not, true-object? => boolean? */
5845   if (SAME_OBJ(pred2, scheme_boolean_p_proc)
5846       && (SAME_OBJ(pred1, scheme_not_proc)
5847           || SAME_OBJ(pred1, scheme_true_object_p_proc)))
5848     return 1;
5849 
5850   /* real?, fixnum?, or flonum? => number? */
5851   if (SAME_OBJ(pred2, scheme_number_p_proc)
5852       && (SAME_OBJ(pred1, scheme_real_p_proc)
5853           || SAME_OBJ(pred1, scheme_fixnum_p_proc)
5854           || SAME_OBJ(pred1, scheme_flonum_p_proc)))
5855     return 1;
5856 
5857   /* fixnum? or flonum? => real? */
5858   if (SAME_OBJ(pred2, scheme_real_p_proc)
5859       && (SAME_OBJ(pred1, scheme_fixnum_p_proc)
5860           || SAME_OBJ(pred1, scheme_flonum_p_proc)))
5861     return 1;
5862 
5863   /* structure subtype? */
5864   if (SAME_TYPE(SCHEME_TYPE(pred1), scheme_struct_proc_shape_type)
5865       && SAME_TYPE(SCHEME_TYPE(pred2), scheme_struct_proc_shape_type)
5866       && is_struct_identity_subtype(SCHEME_PROC_SHAPE_IDENTITY(pred1),
5867                                     SCHEME_PROC_SHAPE_IDENTITY(pred2)))
5868     return 1;
5869 
5870   return 0;
5871 }
5872 
predicate_implies_not(Scheme_Object * pred1,Scheme_Object * pred2)5873 static int predicate_implies_not(Scheme_Object *pred1, Scheme_Object *pred2)
5874 {
5875   if (SAME_OBJ(pred1, scheme_pair_p_proc) && SAME_OBJ(pred2, scheme_list_p_proc))
5876     return 0;
5877   if (SAME_OBJ(pred1, scheme_list_p_proc) && SAME_OBJ(pred2, scheme_pair_p_proc))
5878     return 0;
5879 
5880   /* we don't track structure-type identity precisely enough to know
5881      that structures don't rule out other structures; among the
5882      tracked predicates, only `procedure?` is compatible with
5883      structures */
5884   if ((SAME_TYPE(SCHEME_TYPE(pred1), scheme_struct_proc_shape_type)
5885        || SAME_OBJ(pred1, scheme_procedure_p_proc))
5886       && (SAME_TYPE(SCHEME_TYPE(pred2), scheme_struct_proc_shape_type)
5887           || SAME_OBJ(pred2, scheme_procedure_p_proc)))
5888     return 0;
5889 
5890   /* Otherwise, with our current set of predicates, overlapping matches happen
5891      only when one implies the other: */
5892   return (!predicate_implies(pred1, pred2) && !predicate_implies(pred2, pred1));
5893 }
5894 
add_types_for_t_branch(Scheme_Object * t,Optimize_Info * info,int fuel)5895 static void add_types_for_t_branch(Scheme_Object *t, Optimize_Info *info, int fuel)
5896 {
5897   if (fuel < 0)
5898     return;
5899 
5900   if (SAME_TYPE(SCHEME_TYPE(t), scheme_ir_local_type)) {
5901     add_type_no(info, t, scheme_not_proc);
5902   } else if (SAME_TYPE(SCHEME_TYPE(t), scheme_application2_type)) {
5903     Scheme_App2_Rec *app = (Scheme_App2_Rec *)t;
5904     if (SCHEME_PRIMP(app->rator)
5905         && SAME_TYPE(SCHEME_TYPE(app->rand), scheme_ir_local_type)
5906         && relevant_predicate(app->rator)) {
5907       /* Looks like a predicate on a local variable. Record that the
5908          predicate succeeded, which may allow conversion of safe
5909          operations to unsafe operations. */
5910       add_type(info, app->rand, app->rator);
5911     }
5912     if (SAME_OBJ(app->rator, scheme_not_proc)) {
5913       add_types_for_f_branch(app->rand, info, fuel-1);
5914     }
5915 
5916     if (SAME_TYPE(SCHEME_TYPE(app->rand), scheme_ir_local_type)) {
5917       Scheme_Object *shape;
5918       shape = get_struct_proc_shape(app->rator, info, 0);
5919       if (shape
5920           && ((SCHEME_PROC_SHAPE_MODE(shape) & STRUCT_PROC_SHAPE_MASK) == STRUCT_PROC_SHAPE_PRED)
5921           && !SCHEME_NULLP(SCHEME_PROC_SHAPE_IDENTITY(shape))) {
5922         add_type(info, app->rand, shape);
5923       }
5924     }
5925   } else if (SAME_TYPE(SCHEME_TYPE(t), scheme_application3_type)) {
5926     Scheme_App3_Rec *app = (Scheme_App3_Rec *)t;
5927     Scheme_Object *pred1, *pred2;
5928     if (SAME_OBJ(app->rator, scheme_eq_proc)
5929         || SAME_OBJ(app->rator, scheme_eqv_proc)
5930         || SAME_OBJ(app->rator, scheme_equal_proc)) {
5931       if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_ir_local_type)) {
5932         pred1 = expr_implies_predicate(app->rand1, info);
5933         if (!pred1) {
5934           pred2 = expr_implies_predicate(app->rand2, info);
5935           if (pred2)
5936             add_type(info, app->rand1, pred2);
5937         }
5938       }
5939       if (SAME_TYPE(SCHEME_TYPE(app->rand2), scheme_ir_local_type)) {
5940         pred2 = expr_implies_predicate(app->rand2, info);
5941         if (!pred2) {
5942           pred1 = expr_implies_predicate(app->rand1, info);
5943           if (pred1)
5944             add_type(info, app->rand2, pred1);
5945         }
5946       }
5947     }
5948 
5949   } else if (SAME_TYPE(SCHEME_TYPE(t), scheme_branch_type)) {
5950     Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)t;
5951     if (SCHEME_FALSEP(b->fbranch)) {
5952       add_types_for_t_branch(b->test, info, fuel-1);
5953       add_types_for_t_branch(b->tbranch, info, fuel-1);
5954     }
5955     if (SCHEME_FALSEP(b->tbranch)) {
5956       add_types_for_f_branch(b->test, info, fuel-1);
5957       add_types_for_t_branch(b->fbranch, info, fuel-1);
5958     }
5959   }
5960 }
5961 
add_types_for_f_branch(Scheme_Object * t,Optimize_Info * info,int fuel)5962 static void add_types_for_f_branch(Scheme_Object *t, Optimize_Info *info, int fuel)
5963 {
5964   if (fuel < 0)
5965     return;
5966 
5967   if (SAME_TYPE(SCHEME_TYPE(t), scheme_ir_local_type)) {
5968     add_type(info, t, scheme_not_proc);
5969 
5970   } else if (SAME_TYPE(SCHEME_TYPE(t), scheme_application2_type)) {
5971     Scheme_App2_Rec *app = (Scheme_App2_Rec *)t;
5972     if (SCHEME_PRIMP(app->rator)
5973         && SAME_TYPE(SCHEME_TYPE(app->rand), scheme_ir_local_type)
5974         && relevant_predicate(app->rator)) {
5975       /* Looks like a predicate on a local variable. Record that the
5976          predicate failed, this is currently useful only for lists. */
5977       add_type_no(info, app->rand, app->rator);
5978     }
5979 
5980   } else if (SAME_TYPE(SCHEME_TYPE(t), scheme_branch_type)) {
5981     Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)t;
5982     if (SAME_OBJ(b->fbranch, scheme_true)) {
5983       add_types_for_t_branch(b->test, info, fuel-1);
5984       add_types_for_f_branch(b->tbranch, info, fuel-1);
5985     }
5986     if (SAME_OBJ(b->tbranch, scheme_true)) {
5987       add_types_for_f_branch(b->test, info, fuel-1);
5988       add_types_for_f_branch(b->fbranch, info, fuel-1);
5989     }
5990   }
5991 }
5992 
or_tentative(int x,int y)5993 static int or_tentative(int x, int y)
5994 {
5995   if (x && y) {
5996     if ((x < 0) || (y < 0))
5997       return -1;
5998     else
5999       return 1;
6000   } else {
6001     return 0;
6002   }
6003 }
6004 
optimize_branch(Scheme_Object * o,Optimize_Info * info,int context)6005 static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int context)
6006 {
6007   Scheme_Branch_Rec *b;
6008   Scheme_Object *t, *tb, *fb;
6009   int init_vclock, init_aclock, init_kclock, init_sclock;
6010   Optimize_Info *then_info, *else_info;
6011   Optimize_Info *then_info_init, *else_info_init;
6012   Optimize_Info_Sequence info_seq;
6013 
6014   b = (Scheme_Branch_Rec *)o;
6015 
6016   t = b->test;
6017   tb = b->tbranch;
6018   fb = b->fbranch;
6019 
6020   /* Convert (if <id> expr <id>) to (if <id> expr #f) */
6021   if (equivalent_exprs(t, fb, NULL, NULL, 0)) {
6022     fb = scheme_false;
6023   }
6024 
6025   /* For test position, convert (if <id> <id> expr) to (if <id> #t expr) */
6026   if ((context & OPT_CONTEXT_BOOLEAN)
6027       && equivalent_exprs(t, tb, NULL, NULL, 0)) {
6028       tb = scheme_true;
6029   }
6030 
6031   optimize_info_seq_init(info, &info_seq);
6032 
6033   t = optimize_expr(t, info, OPT_CONTEXT_BOOLEAN | OPT_CONTEXT_SINGLED);
6034 
6035   if (info->escapes) {
6036     optimize_info_seq_done(info, &info_seq);
6037     return ensure_noncm(t, info);
6038   }
6039 
6040   /* Try to lift out `let`s and `begin`s around a test: */
6041   {
6042     Scheme_Object *inside = NULL, *t2 = t;
6043 
6044     while (1) {
6045       extract_tail_inside(&t2, &inside, 0);
6046 
6047       /* Try optimize: (if (not x) y z) => (if x z y) */
6048       if (SAME_TYPE(SCHEME_TYPE(t2), scheme_application2_type)) {
6049         Scheme_App2_Rec *app = (Scheme_App2_Rec *)t2;
6050 
6051         if (SAME_PTR(scheme_not_proc, app->rator)) {
6052           t2 = tb;
6053           tb = fb;
6054           fb = t2;
6055 
6056           t2 = app->rand;
6057           t = replace_tail_inside(t2, inside, t);
6058         } else
6059           break;
6060       } else
6061         break;
6062     }
6063 
6064     if (!(SCHEME_TYPE(t2) > _scheme_ir_values_types_)) {
6065       /* (if (let (...) (cons x y)) a b) => (if (begin (let (...) (begin x y #<void>)) #t/#f) a b)
6066          but don't expand (if (let (...) (begin x K)) a b) */
6067       Scheme_Object *pred;
6068 
6069       pred = expr_implies_predicate(t2, info);
6070       if (pred) {
6071         Scheme_Object *test_val = NULL;
6072 
6073         if (predicate_implies(pred, scheme_not_proc))
6074           test_val = scheme_false;
6075         else if (predicate_implies_not(pred, scheme_not_proc))
6076           test_val = scheme_true;
6077 
6078         if (test_val) {
6079           t2 = optimize_ignored(t2, info, 1, 0, 5);
6080           t = replace_tail_inside(t2, inside, t);
6081 
6082           t2 = test_val;
6083           if (scheme_omittable_expr(t, 1, 5, 0, info, NULL)) {
6084             t = test_val;
6085             inside = NULL;
6086           } else {
6087             t = make_sequence_2(t, test_val);
6088             inside = t;
6089           }
6090         }
6091       }
6092     }
6093 
6094     if (SCHEME_TYPE(t2) > _scheme_ir_values_types_) {
6095       /* Branch is statically known */
6096       Scheme_Object *xb;
6097 
6098       optimize_info_seq_done(info, &info_seq);
6099       info->size -= 1;
6100 
6101       if (SCHEME_FALSEP(t2))
6102         xb = optimize_expr(fb, info, scheme_optimize_tail_context(context));
6103       else
6104         xb = optimize_expr(tb, info, scheme_optimize_tail_context(context));
6105 
6106       optimize_info_seq_done(info, &info_seq);
6107       return replace_tail_inside(xb, inside, t);
6108     }
6109   }
6110 
6111   optimize_info_seq_step(info, &info_seq);
6112 
6113   info->vclock += 1; /* model branch as clock increment */
6114 
6115   init_vclock = info->vclock;
6116   init_aclock = info->aclock;
6117   init_kclock = info->kclock;
6118   init_sclock = info->sclock;
6119 
6120   then_info = optimize_info_add_frame(info, 0);
6121   add_types_for_t_branch(t, then_info, 5);
6122   then_info_init = optimize_info_add_frame(then_info, 0);
6123   tb = optimize_expr(tb, then_info, scheme_optimize_tail_context(context));
6124   optimize_info_done(then_info, NULL);
6125 
6126   info->escapes = 0;
6127   info->vclock = init_vclock;
6128   info->aclock = init_aclock;
6129   info->kclock = init_kclock;
6130   info->sclock = init_sclock;
6131 
6132   optimize_info_seq_step(info, &info_seq);
6133 
6134   else_info = optimize_info_add_frame(info, 0);
6135   add_types_for_f_branch(t, else_info, 5);
6136   else_info_init = optimize_info_add_frame(else_info, 0);
6137   fb = optimize_expr(fb, else_info, scheme_optimize_tail_context(context));
6138   optimize_info_done(else_info, NULL);
6139 
6140   if (then_info->escapes && else_info->escapes) {
6141     /* both branches escaped */
6142     info->preserves_marks = 1;
6143     info->single_result = 1;
6144     info->kclock = init_kclock;
6145 
6146   } else if (else_info->escapes) {
6147     info->preserves_marks = then_info->preserves_marks;
6148     info->single_result = then_info->single_result;
6149     info->kclock = then_info->kclock;
6150     merge_types(then_info, info, NULL);
6151     info->escapes = 0;
6152 
6153   } else if (then_info->escapes) {
6154     info->preserves_marks = else_info->preserves_marks;
6155     info->single_result = else_info->single_result;
6156     merge_types(else_info, info, NULL);
6157     info->escapes = 0;
6158 
6159   } else {
6160     int new_preserves_marks, new_single_result;
6161 
6162     new_preserves_marks = or_tentative(then_info->preserves_marks, else_info->preserves_marks);
6163     info->preserves_marks = new_preserves_marks;
6164     new_single_result = or_tentative(then_info->single_result, else_info->single_result);
6165     info->single_result = new_single_result;
6166     if (then_info->kclock > info->kclock)
6167       info->kclock = then_info->kclock;
6168     merge_branchs_types(then_info, else_info, info);
6169   }
6170 
6171   if (then_info->sclock > info->sclock)
6172     info->sclock = then_info->sclock;
6173   if (then_info->aclock > info->aclock)
6174     info->aclock = then_info->aclock;
6175 
6176   if ((init_vclock == then_info->vclock) && (init_vclock == info->vclock)) {
6177     /* we can rewind the vclock to just after the test, because the
6178        `if` as a whole has no effect */
6179     info->vclock--;
6180   }
6181 
6182   optimize_info_seq_done(info, &info_seq);
6183 
6184   /* Try optimize: (if x #f #t) => (not x) */
6185   if (SCHEME_FALSEP(tb)
6186       && SAME_OBJ(fb, scheme_true)) {
6187     info->size -= 2;
6188     return make_optimize_prim_application2(scheme_not_proc, t, info, context);
6189   }
6190 
6191   /* Convert (if <boolean> #t #f) to <boolean>
6192      and, for test position, convert (if <expr> #t #f) to <expr> */
6193   if (SAME_OBJ(tb, scheme_true) && SAME_OBJ(fb, scheme_false)) {
6194     Scheme_Object *pred;
6195 
6196     if (context & OPT_CONTEXT_BOOLEAN)
6197       /* In a boolean context, any expression can be extrated. */
6198       pred = scheme_boolean_p_proc;
6199     else
6200       pred = expr_implies_predicate(t, info);
6201 
6202     if (pred && predicate_implies(pred, scheme_boolean_p_proc)) {
6203       info->size -= 2;
6204       return ensure_single_value_noncm(t, info);
6205     }
6206   }
6207 
6208   /* Try optimize: (if <expr> v v) => (begin <expr> v) */
6209   {
6210     Scheme_Object *nb;
6211 
6212     nb = equivalent_exprs(tb, fb, then_info_init, else_info_init, context);
6213     if (nb) {
6214       info->size -= 1;
6215       return make_discarding_first_sequence(t, nb, info);
6216     }
6217   }
6218 
6219   /* Try optimize: (if x x #f) => x
6220      This pattern is included in the previous reduction,
6221      but this is still useful if x is mutable or a top level*/
6222   if (SCHEME_FALSEP(fb)
6223       && equivalent_exprs(t, tb, NULL, NULL, 0)) {
6224       info->size -= 2;
6225       return ensure_single_value(t, info);
6226   }
6227 
6228   /* Convert: expressions like
6229      (if (if M N #f) P K) => (if M (if N P K) K)
6230      for simple constants K. This is useful to expose simple
6231      tests to the JIT. */
6232   if (SAME_TYPE(SCHEME_TYPE(t), scheme_branch_type)) {
6233     Scheme_Branch_Rec *b2 = (Scheme_Branch_Rec *)t;
6234     Scheme_Object *ntb, *nfb, *nt2 = NULL;
6235     if (SCHEME_FALSEP(b2->fbranch)
6236         && scheme_ir_duplicate_ok(fb, 0)) {
6237       /* (if (if M N #f) P K) => (if M (if N P K) K) */
6238       ntb = (Scheme_Object *)b2;
6239       nfb = optimize_clone(0, fb, info, empty_eq_hash_tree, 0);
6240       nt2 = b2->tbranch;
6241     } else if (SCHEME_FALSEP(b2->tbranch)
6242                && scheme_ir_duplicate_ok(fb, 0)) {
6243       /* (if (if M #f N) P K) => (if M K (if N P K)) */
6244       ntb = optimize_clone(0, fb, info, empty_eq_hash_tree, 0);
6245       nfb = (Scheme_Object *)b2;
6246       nt2 = b2->fbranch;
6247     } else if (SAME_OBJ(b2->fbranch, scheme_true)
6248                && scheme_ir_duplicate_ok(tb, 0)) {
6249       /* (if (if M N #t) K P) => (if M (if N K P) K) */
6250       ntb = (Scheme_Object *)b2;
6251       nfb = optimize_clone(0, tb, info, empty_eq_hash_tree, 0);
6252       nt2 = b2->tbranch;
6253     } else if (SAME_OBJ(b2->tbranch, scheme_true)
6254                && scheme_ir_duplicate_ok(tb, 0)) {
6255       /* (if (if M #t N) K P) => (if M K (if N K P)) */
6256       ntb = optimize_clone(0, tb, info, empty_eq_hash_tree, 0);
6257       nfb = (Scheme_Object *)b2;
6258       nt2 = b2->fbranch;
6259     }
6260     if (nt2) {
6261       t = b2->test;
6262       b2->test = nt2;
6263       b2->tbranch = tb;
6264       b2->fbranch = fb;
6265       tb = ntb;
6266       fb = nfb;
6267     }
6268   }
6269 
6270   b->test = t;
6271   b->tbranch = tb;
6272   b->fbranch = fb;
6273 
6274   if (OPT_BRANCH_ADDS_NO_SIZE) {
6275     /* Seems to work better to not to increase the size
6276        specifically for `if' */
6277   } else {
6278     info->size += 1;
6279   }
6280 
6281   return o;
6282 }
6283 
6284 /*========================================================================*/
6285 /*                       with-continuation-marks                          */
6286 /*========================================================================*/
6287 
omittable_key(Scheme_Object * k,Optimize_Info * info)6288 static int omittable_key(Scheme_Object *k, Optimize_Info *info)
6289 {
6290   /* A key is not omittable if it might refer to a chaperoned/impersonated
6291      continuation mark key, so that's why we pass OMITTABLE_KEEP_VARS: */
6292   return scheme_omittable_expr(k, 1, 20, OMITTABLE_KEEP_VARS, info, info);
6293 }
6294 
optimize_wcm(Scheme_Object * o,Optimize_Info * info,int context)6295 static Scheme_Object *optimize_wcm(Scheme_Object *o, Optimize_Info *info, int context)
6296 {
6297   Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)o;
6298   Scheme_Object *k, *v, *b;
6299   int init_vclock, can_omit_key;
6300   Optimize_Info_Sequence info_seq;
6301 
6302   optimize_info_seq_init(info, &info_seq);
6303 
6304   k = optimize_expr(wcm->key, info, OPT_CONTEXT_SINGLED);
6305 
6306   if (info->escapes) {
6307     optimize_info_seq_done(info, &info_seq);
6308     return ensure_noncm(k, info);
6309   }
6310 
6311   optimize_info_seq_step(info, &info_seq);
6312 
6313   v = optimize_expr(wcm->val, info, OPT_CONTEXT_SINGLED);
6314 
6315   if (info->escapes) {
6316     optimize_info_seq_done(info, &info_seq);
6317     info->size += 1;
6318     return ensure_noncm(make_discarding_first_sequence(k, v, info), info);
6319   }
6320 
6321   /* The presence of a key can be detected by other expressions,
6322      to increment vclock to prevent expressions incorrectly
6323      moving under the mark: */
6324   info->vclock++;
6325   init_vclock = info->vclock;
6326 
6327   optimize_info_seq_step(info, &info_seq);
6328 
6329   b = optimize_expr(wcm->body, info, scheme_optimize_tail_context(context));
6330 
6331   if (init_vclock == info->vclock) {
6332     /* body has no effect itself, so we can rewind the clock */
6333     info->vclock--;
6334   }
6335 
6336   optimize_info_seq_done(info, &info_seq);
6337 
6338   /* If the body cannot inspect the continution, and if the key is not
6339      a chaperone, no need to add the mark: */
6340   can_omit_key = omittable_key(k, info);
6341   if (can_omit_key
6342       && scheme_omittable_expr(b, -1, 20, 0, info, info))
6343     return make_discarding_first_sequence(v, b, info);
6344 
6345   /* info->single_result is already set */
6346   info->preserves_marks = 0;
6347 
6348   wcm->key = k;
6349   wcm->val = v;
6350   wcm->body = b;
6351 
6352   info->size += 1;
6353 
6354   /* Simplify (with-continuation-mark <same-key> <val1>
6355                (with-continuation-mark <same-key> <val2>
6356                  <body>))
6357      to (begin
6358          <val1>
6359          (with-continuation-mark <same-key> <val2>
6360          <body>))
6361      as long as <val2> doesn't inspect the continuation. */
6362   if (can_omit_key
6363       && SAME_TYPE(SCHEME_TYPE(wcm->body), scheme_with_cont_mark_type)
6364       && equivalent_exprs(wcm->key, ((Scheme_With_Continuation_Mark *)wcm->body)->key, NULL, NULL, 0)
6365       && scheme_omittable_expr(((Scheme_With_Continuation_Mark *)wcm->body)->val, 1, 20, 0, info, info))
6366     return make_discarding_first_sequence(wcm->val, wcm->body, info);
6367 
6368   return (Scheme_Object *)wcm;
6369 }
6370 
6371 /*========================================================================*/
6372 /*                            other syntax                                */
6373 /*========================================================================*/
6374 
6375 static Scheme_Object *
define_values_optimize(Scheme_Object * data,Optimize_Info * info,int context)6376 define_values_optimize(Scheme_Object *data, Optimize_Info *info, int context)
6377 {
6378   Scheme_Object *val = SCHEME_DEFN_RHS(data);
6379 
6380   optimize_info_used_top(info);
6381   val = optimize_expr(val, info, 0);
6382 
6383   SCHEME_DEFN_RHS(data) = val;
6384 
6385   return data;
6386 }
6387 
6388 static Scheme_Object *
set_optimize(Scheme_Object * data,Optimize_Info * info,int context)6389 set_optimize(Scheme_Object *data, Optimize_Info *info, int context)
6390 {
6391   Scheme_Set_Bang *sb = (Scheme_Set_Bang *)data;
6392   Scheme_Object *var, *val;
6393 
6394   var = sb->var;
6395   val = sb->val;
6396 
6397   val = optimize_expr(val, info, OPT_CONTEXT_SINGLED);
6398 
6399   if (info->escapes)
6400     return ensure_noncm(val, info);
6401 
6402   info->preserves_marks = 1;
6403   info->single_result = 1;
6404 
6405   if (SAME_TYPE(SCHEME_TYPE(var), scheme_ir_local_type)) {
6406     register_use(SCHEME_VAR(var), info);
6407   } else {
6408     MZ_ASSERT(((Scheme_IR_Toplevel *)var)->instance_pos == -1);
6409     optimize_info_used_top(info);
6410   }
6411 
6412   info->vclock++;
6413 
6414   sb->var = var;
6415   sb->val = val;
6416 
6417   return (Scheme_Object *)sb;
6418 }
6419 
6420 static Scheme_Object *
set_clone(int single_use,Scheme_Object * data,Optimize_Info * info,Scheme_Hash_Tree * var_map)6421 set_clone(int single_use, Scheme_Object *data, Optimize_Info *info, Scheme_Hash_Tree *var_map)
6422 {
6423   Scheme_Set_Bang *sb = (Scheme_Set_Bang *)data, *naya;
6424   Scheme_Object *var, *val;
6425 
6426   naya = MALLOC_ONE_TAGGED(Scheme_Set_Bang);
6427   memcpy(naya, sb, sizeof(Scheme_Set_Bang));
6428 
6429   var = naya->var;
6430   val = naya->val;
6431 
6432   val = optimize_clone(single_use, val, info, var_map, 0);
6433   if (!val) return NULL;
6434   if (SAME_TYPE(SCHEME_TYPE(var), scheme_ir_local_type)) {
6435     var = optimize_clone(single_use, var, info, var_map, 0);
6436     if (!var) return NULL;
6437   }
6438 
6439   naya->var = var;
6440   naya->val = val;
6441 
6442   return (Scheme_Object *)naya;
6443 }
6444 
6445 static Scheme_Object *
ref_optimize(Scheme_Object * data,Optimize_Info * info,int context)6446 ref_optimize(Scheme_Object *data, Optimize_Info *info, int context)
6447 {
6448   Scheme_Object *v;
6449 
6450   optimize_info_used_top(info);
6451 
6452   v = SCHEME_PTR1_VAL(data);
6453   if (SAME_TYPE(SCHEME_TYPE(v), scheme_ir_local_type)) {
6454     SCHEME_PTR1_VAL(data) = (SCHEME_VAR(v)->mutated ? scheme_false : scheme_true);
6455   } else if (SAME_TYPE(SCHEME_TYPE(v), scheme_ir_toplevel_type)) {
6456     /* Knowing whether a top-level variable is fixed lets us optimize
6457        uses of `variable-reference-constant?` */
6458     if (get_defn_shape(info, (Scheme_IR_Toplevel *)v)
6459         || get_import_shape(info, (Scheme_IR_Toplevel *)v)) {
6460       v = scheme_ir_toplevel_to_flagged_toplevel(v, SCHEME_TOPLEVEL_FIXED);
6461       SCHEME_PTR1_VAL(data) = v;
6462     }
6463     register_import_used(info, (Scheme_IR_Toplevel *)v);
6464   }
6465 
6466   info->preserves_marks = 1;
6467   info->single_result = 1;
6468   info->size++;
6469 
6470   return data;
6471 }
6472 
6473 static Scheme_Object *
ref_clone(int single_use,Scheme_Object * data,Optimize_Info * info,Scheme_Hash_Tree * var_map)6474 ref_clone(int single_use, Scheme_Object *data, Optimize_Info *info, Scheme_Hash_Tree *var_map)
6475 {
6476   Scheme_Object *naya;
6477   Scheme_Object *a, *b;
6478 
6479   a = SCHEME_PTR1_VAL(data);
6480   a = optimize_clone(single_use, a, info, var_map, 0);
6481   if (!a) return NULL;
6482 
6483   b = SCHEME_PTR2_VAL(data);
6484   b = optimize_clone(single_use, b, info, var_map, 0);
6485   if (!b) return NULL;
6486 
6487   naya = scheme_alloc_object();
6488   naya->type = scheme_varref_form_type;
6489   SCHEME_PTR1_VAL(naya) = a;
6490   SCHEME_PTR2_VAL(naya) = b;
6491 
6492   return naya;
6493 }
6494 
6495 static Scheme_Object *
apply_values_optimize(Scheme_Object * data,Optimize_Info * info,int context)6496 apply_values_optimize(Scheme_Object *data, Optimize_Info *info, int context)
6497 {
6498   Scheme_Object *f, *e;
6499   Optimize_Info_Sequence info_seq;
6500 
6501   f = SCHEME_PTR1_VAL(data);
6502   e = SCHEME_PTR2_VAL(data);
6503 
6504   optimize_info_seq_init(info, &info_seq);
6505 
6506   f = optimize_expr(f, info, OPT_CONTEXT_SINGLED);
6507 
6508   if (info->escapes) {
6509     optimize_info_seq_done(info, &info_seq);
6510     return ensure_noncm(f, info);
6511   }
6512   optimize_info_seq_step(info, &info_seq);
6513 
6514   e = optimize_expr(e, info, 0);
6515 
6516   optimize_info_seq_done(info, &info_seq);
6517 
6518   if (info->escapes) {
6519     info->size += 1;
6520     return ensure_noncm(make_discarding_first_sequence(f, e, info), info);
6521   }
6522 
6523   info->size += 1;
6524   info->vclock += 1;
6525   info->kclock += 1;
6526   info->sclock += 1;
6527 
6528   return optimize_apply_values(f, e, info, info->single_result, context);
6529 }
6530 
6531 static Scheme_Object *
apply_values_clone(int single_use,Scheme_Object * data,Optimize_Info * info,Scheme_Hash_Tree * var_map)6532 apply_values_clone(int single_use, Scheme_Object *data, Optimize_Info *info, Scheme_Hash_Tree *var_map)
6533 {
6534   Scheme_Object *f, *e;
6535 
6536   f = SCHEME_PTR1_VAL(data);
6537   e = SCHEME_PTR2_VAL(data);
6538 
6539   f = optimize_clone(single_use, f, info, var_map, 0);
6540   if (!f) return NULL;
6541   e = optimize_clone(single_use, e, info, var_map, 0);
6542   if (!e) return NULL;
6543 
6544   data = scheme_alloc_object();
6545   data->type = scheme_apply_values_type;
6546   SCHEME_PTR1_VAL(data) = f;
6547   SCHEME_PTR2_VAL(data) = e;
6548 
6549   return data;
6550 }
6551 
6552 static Scheme_Object *
with_immed_mark_optimize(Scheme_Object * data,Optimize_Info * info,int context)6553 with_immed_mark_optimize(Scheme_Object *data, Optimize_Info *info, int context)
6554 {
6555   Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)data;
6556   Scheme_Object *key, *val, *body;
6557   Optimize_Info_Sequence info_seq;
6558   Optimize_Info *body_info;
6559   Scheme_IR_Local *var;
6560 
6561   optimize_info_seq_init(info, &info_seq);
6562 
6563   key = optimize_expr(wcm->key, info, OPT_CONTEXT_SINGLED);
6564   optimize_info_seq_step(info, &info_seq);
6565   if (info->escapes) {
6566     optimize_info_seq_done(info, &info_seq);
6567     return ensure_noncm(key, info);
6568   }
6569 
6570   val = optimize_expr(wcm->val, info, OPT_CONTEXT_SINGLED);
6571   optimize_info_seq_step(info, &info_seq);
6572   if (info->escapes) {
6573     optimize_info_seq_done(info, &info_seq);
6574     return ensure_noncm(make_discarding_first_sequence(key, val, info), info);
6575   }
6576 
6577   optimize_info_seq_done(info, &info_seq);
6578 
6579   body_info = optimize_info_add_frame(info, 0);
6580   var = SCHEME_VAR(SCHEME_CAR(wcm->body));
6581   set_optimize_mode(var);
6582   var->optimize.lambda_depth = body_info->lambda_depth;
6583   var->optimize_used = 0;
6584   var->optimize.init_kclock = info->kclock;
6585 
6586   body = optimize_expr(SCHEME_CDR(wcm->body), body_info, 0);
6587 
6588   optimize_info_done(body_info, NULL);
6589 
6590   wcm->key = key;
6591   wcm->val = val;
6592   SCHEME_CDR(wcm->body) = body;
6593 
6594   info->preserves_marks = 0;
6595 
6596   return data;
6597 }
6598 
6599 static Scheme_Object *
with_immed_mark_clone(int single_use,Scheme_Object * data,Optimize_Info * info,Scheme_Hash_Tree * var_map)6600 with_immed_mark_clone(int single_use, Scheme_Object *data, Optimize_Info *info, Scheme_Hash_Tree *var_map)
6601 {
6602   Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)data;
6603   Scheme_With_Continuation_Mark *wcm2;
6604   Scheme_Object *e;
6605   Scheme_IR_Local *var;
6606 
6607   wcm2 = MALLOC_ONE_TAGGED(Scheme_With_Continuation_Mark);
6608   wcm2->so.type = scheme_with_immed_mark_type;
6609 
6610   e = optimize_clone(single_use, wcm->key, info, var_map, 0);
6611   if (!e) return NULL;
6612   wcm2->key = e;
6613 
6614   e = optimize_clone(single_use, wcm->val, info, var_map, 0);
6615   if (!e) return NULL;
6616   wcm2->val = e;
6617 
6618   var = clone_variable(SCHEME_VAR(SCHEME_CAR(wcm->body)));
6619   var_map = scheme_hash_tree_set(var_map, SCHEME_CAR(wcm->body), (Scheme_Object *)var);
6620 
6621   e = optimize_clone(single_use, SCHEME_CDR(wcm->body), info, var_map, 0);
6622   if (!e) return NULL;
6623   e = scheme_make_mutable_pair((Scheme_Object *)var, e);
6624   wcm2->body = e;
6625 
6626   return (Scheme_Object *)wcm2;
6627 }
6628 
6629 static Scheme_Object *
case_lambda_optimize(Scheme_Object * expr,Optimize_Info * info,int context)6630 case_lambda_optimize(Scheme_Object *expr, Optimize_Info *info, int context)
6631 {
6632   Scheme_Object *le;
6633   int i;
6634   Scheme_Case_Lambda *seq = (Scheme_Case_Lambda *)expr;
6635 
6636   for (i = 0; i < seq->count; i++) {
6637     le = seq->array[i];
6638     le = optimize_expr(le, info, 0);
6639     seq->array[i] = le;
6640   }
6641 
6642   info->preserves_marks = 1;
6643   info->single_result = 1;
6644   info->size += 1;
6645 
6646   return expr;
6647 }
6648 
6649 static Scheme_Object *
case_lambda_clone(int single_use,Scheme_Object * data,Optimize_Info * info,Scheme_Hash_Tree * var_map)6650 case_lambda_clone(int single_use, Scheme_Object *data, Optimize_Info *info, Scheme_Hash_Tree *var_map)
6651 {
6652   Scheme_Object *le;
6653   int i, sz;
6654   Scheme_Case_Lambda *seq = (Scheme_Case_Lambda *)data;
6655   Scheme_Case_Lambda *seq2;
6656 
6657   sz = sizeof(Scheme_Case_Lambda) + ((seq->count - mzFLEX_DELTA) * sizeof(Scheme_Object*));
6658   seq2 = (Scheme_Case_Lambda *)scheme_malloc_tagged(sz);
6659   memcpy(seq2, seq, sz);
6660 
6661   for (i = 0; i < seq->count; i++) {
6662     le = seq->array[i];
6663     le = optimize_clone(single_use, le, info, var_map, 0);
6664     if (!le) return NULL;
6665     seq2->array[i] = le;
6666   }
6667 
6668   return (Scheme_Object *)seq2;
6669 }
6670 
begin0_optimize(Scheme_Object * obj,Optimize_Info * info,int context)6671 static Scheme_Object *begin0_optimize(Scheme_Object *obj, Optimize_Info *info, int context)
6672 {
6673   int i, count, drop = 0, prev_size, single_result = 0, preserves_marks = 0, kclock = 0, sclock = 0;
6674   Scheme_Sequence *s = (Scheme_Sequence *)obj;
6675   Scheme_Object *inside = NULL, *expr, *orig_first;
6676   Scheme_Object *le;
6677   Optimize_Info_Sequence info_seq;
6678 
6679   count = s->count;
6680   optimize_info_seq_init(info, &info_seq);
6681 
6682   for (i = 0; i < count; i++) {
6683     prev_size = info->size;
6684 
6685     optimize_info_seq_step(info, &info_seq);
6686 
6687     le = optimize_expr(s->array[i],
6688                        info,
6689                        (!i
6690                         ? scheme_optimize_result_context(context)
6691                         : 0));
6692 
6693     if (!i) {
6694       single_result = info->single_result;
6695       preserves_marks = info->preserves_marks;
6696       kclock = info->kclock;
6697       sclock = info->sclock;
6698       s->array[0] = le;
6699     } else {
6700       /* Inlining and constant propagation can expose omittable expressions: */
6701       le = optimize_ignored(le, info, -1, 1, 5);
6702       if (!le) {
6703         drop++;
6704         info->size = prev_size;
6705         s->array[i] = NULL;
6706       } else {
6707         s->array[i] = le;
6708       }
6709     }
6710 
6711     if (info->escapes) {
6712       int j;
6713       single_result = info->single_result;
6714       preserves_marks = info->preserves_marks;
6715       for (j = i + 1; j < count; j++) {
6716         drop++;
6717         s->array[j] = NULL;
6718       }
6719       break;
6720     }
6721   }
6722 
6723   optimize_info_seq_done(info, &info_seq);
6724 
6725   if (info->escapes) {
6726     /* In case of an error, optimize (begin0 ... <error> ...) => (begin ... <error>) */
6727     Scheme_Sequence *s2;
6728     int j = 0;
6729 
6730     info->single_result = 1;
6731     info->preserves_marks = 1;
6732 
6733     if (i != 0) {
6734       /* We will ignore the first expression too */
6735       le = optimize_ignored(s->array[0], info, -1, 1, 5);
6736       if (!le) {
6737         drop++;
6738         info->size = prev_size;
6739         s->array[0] = NULL;
6740       } else {
6741         s->array[0] = le;
6742       }
6743     }
6744 
6745     if ((count - drop) == 1) {
6746       /* If it's only one expression we can drop the begin0 */
6747       return ensure_noncm(s->array[i], info);
6748     }
6749 
6750     s2 = scheme_malloc_sequence(count - drop);
6751     s2->so.type = scheme_sequence_type;
6752     s2->count = count - drop;
6753 
6754     for (i = 0; i < count; i++) {
6755       if (s->array[i]) {
6756         s2->array[j++] = s->array[i];
6757       }
6758     }
6759     return flatten_sequence((Scheme_Object *)s2, info, context);
6760   }
6761 
6762   info->preserves_marks = 1;
6763   info->single_result = single_result;
6764 
6765   if ((s->count - drop) == 1 && (preserves_marks == 1)) {
6766     /* If the first expression preserves marks we can drop the begin0 */
6767     return s->array[0];
6768   }
6769 
6770   expr = s->array[0];
6771   orig_first = s->array[0];
6772   extract_tail_inside(&expr, &inside, 0);
6773 
6774   /* Try optimize (begin0 <movable> ...) => (begin ... <movable>) */
6775   if (movable_expression(expr, info, 0, kclock != info->kclock,
6776                          sclock != info->sclock, 0, 50)) {
6777     if ((s->count - drop) == 1) {
6778       /* drop the begin0 */
6779       info->size -= 1;
6780       /* expr = expr */
6781     } else {
6782       Scheme_Sequence *s2;
6783       int j = 0;
6784 
6785       s2 = scheme_malloc_sequence(s->count - drop);
6786       s2->so.type = scheme_sequence_type;
6787       s2->count = s->count - drop;
6788 
6789       for (i = 1; i < s->count; i++) {
6790         if (s->array[i]) {
6791           s2->array[j++] = s->array[i];
6792         }
6793       }
6794       s2->array[j++] = expr;
6795 
6796       expr = (Scheme_Object *)s2;
6797     }
6798   } else {
6799     if (drop) {
6800       Scheme_Sequence *s2;
6801       int j = 0;
6802 
6803       s2 = scheme_malloc_sequence(s->count - drop);
6804       s2->so.type = s->so.type;
6805       s2->count = s->count - drop;
6806 
6807       s2->array[j++] = expr;
6808       for (i = 1; i < s->count; i++) {
6809         if (s->array[i]) {
6810           s2->array[j++] = s->array[i];
6811         }
6812       }
6813 
6814       expr = (Scheme_Object *)s2;
6815     } else {
6816       s->array[0] = expr;
6817       expr = (Scheme_Object *)s;
6818     }
6819   }
6820 
6821   info->size += 1;
6822   expr = flatten_sequence(expr, info, context);
6823   return replace_tail_inside(expr, inside, orig_first);
6824 }
6825 
6826 /*========================================================================*/
6827 /*                    let, let-values, letrec, etc.                       */
6828 /*========================================================================*/
6829 
is_liftable_prim(Scheme_Object * v,int or_escape)6830 static int is_liftable_prim(Scheme_Object *v, int or_escape)
6831 /* Can we lift a call to `v` out of a `letrec` to a wrapping `let`? */
6832 {
6833   if (SCHEME_PRIMP(v)) {
6834     int opt = (((Scheme_Primitive_Proc *)v)->pp.flags & SCHEME_PRIM_OPT_MASK);
6835     if (opt >= SCHEME_PRIM_OPT_IMMEDIATE)
6836       return 1;
6837     if (or_escape && (opt >= SCHEME_PRIM_OPT_NONCM)) {
6838       if (SCHEME_PRIM_PROC_OPT_FLAGS(v) & SCHEME_PRIM_ALWAYS_ESCAPES)
6839         return 1;
6840     }
6841   }
6842 
6843   if (SAME_OBJ(v, scheme_values_proc))
6844     return 1;
6845 
6846   return 0;
6847 }
6848 
scheme_is_liftable(Scheme_Object * o,Scheme_Hash_Tree * exclude_vars,int fuel,int as_rator,int or_escape)6849 int scheme_is_liftable(Scheme_Object *o, Scheme_Hash_Tree *exclude_vars, int fuel, int as_rator, int or_escape)
6850   /* Can we lift `o` out of a `letrec` to a wrapping `let`? Refences
6851      to `exclude_vars` are not allowed, since those are the LHS. */
6852 {
6853   Scheme_Type t = SCHEME_TYPE(o);
6854 
6855   if (!fuel) return 0;
6856 
6857   switch (t) {
6858   case scheme_ir_lambda_type:
6859     return !as_rator;
6860   case scheme_case_lambda_sequence_type:
6861     return !as_rator;
6862   case scheme_ir_toplevel_type:
6863     return 1;
6864   case scheme_ir_local_type:
6865     if (!scheme_eq_hash_tree_get(exclude_vars, o))
6866       return 1;
6867     break;
6868   case scheme_branch_type:
6869     {
6870       Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)o;
6871       if (scheme_is_liftable(b->test, exclude_vars, fuel - 1, 0, or_escape)
6872 	  && scheme_is_liftable(b->tbranch, exclude_vars, fuel - 1, as_rator, or_escape)
6873 	  && scheme_is_liftable(b->fbranch, exclude_vars, fuel - 1, as_rator, or_escape))
6874 	return 1;
6875     }
6876     break;
6877   case scheme_application_type:
6878     {
6879       Scheme_App_Rec *app = (Scheme_App_Rec *)o;
6880       int i;
6881       if (!is_liftable_prim(app->args[0], or_escape))
6882         return 0;
6883       for (i = app->num_args + 1; i--; ) {
6884 	if (!scheme_is_liftable(app->args[i], exclude_vars, fuel - 1, 1, or_escape))
6885 	  return 0;
6886       }
6887       return 1;
6888     }
6889     break;
6890   case scheme_application2_type:
6891     {
6892       Scheme_App2_Rec *app = (Scheme_App2_Rec *)o;
6893       if (!is_liftable_prim(app->rator, or_escape))
6894         return 0;
6895       if (scheme_is_liftable(app->rator, exclude_vars, fuel - 1, 1, or_escape)
6896 	  && scheme_is_liftable(app->rand, exclude_vars, fuel - 1, 1, or_escape))
6897 	return 1;
6898     }
6899     break;
6900   case scheme_application3_type:
6901     {
6902       Scheme_App3_Rec *app = (Scheme_App3_Rec *)o;
6903       if (!is_liftable_prim(app->rator, or_escape))
6904         return 0;
6905       if (scheme_is_liftable(app->rator, exclude_vars, fuel - 1, 1, or_escape)
6906 	  && scheme_is_liftable(app->rand1, exclude_vars, fuel - 1, 1, or_escape)
6907 	  && scheme_is_liftable(app->rand2, exclude_vars, fuel - 1, 1, or_escape))
6908 	return 1;
6909     }
6910     break;
6911   case scheme_ir_let_header_type:
6912     {
6913       Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)o;
6914       int i;
6915 
6916       o = lh->body;
6917       for (i = lh->num_clauses; i--; ) {
6918         if (!scheme_is_liftable(((Scheme_IR_Let_Value *)o)->value, exclude_vars, fuel - 1, as_rator, or_escape))
6919           return 0;
6920         o = ((Scheme_IR_Let_Value *)o)->body;
6921       }
6922       if (scheme_is_liftable(o, exclude_vars, fuel - 1, as_rator, or_escape))
6923         return 1;
6924       break;
6925     }
6926   default:
6927     if (t > _scheme_ir_values_types_)
6928       return 1;
6929   }
6930 
6931   return 0;
6932 }
6933 
ir_propagate_ok(Scheme_Object * value,Optimize_Info * info,int used_once,Scheme_IR_Local * once_var)6934 int ir_propagate_ok(Scheme_Object *value, Optimize_Info *info, int used_once, Scheme_IR_Local *once_var)
6935 /* Can we constant-propagate the expression `value`?
6936    If `used_once` is true, the value is known to be used once,
6937    but if `once_var` is provided, record when the result
6938    relies on that once-usedness. */
6939 {
6940   if (SAME_TYPE(SCHEME_TYPE(value), scheme_ir_lambda_type)) {
6941     int sz;
6942     sz = lambda_body_size_plus_info((Scheme_Lambda *)value, 1, info, NULL);
6943     if ((sz >= 0) && (sz <= MAX_PROC_INLINE_SIZE))
6944       return 1;
6945     else if (used_once) {
6946       if (once_var) {
6947         /* Mark the variable as having a known value only as long as it's used just
6948            once. In case the one reference is duplicated --- perhaps because it is
6949            used in a non-application position in a function that is itself inlined
6950            --- then the known value should be cleared. */
6951         once_var->optimize.clear_known_on_multi_use = 1;
6952       }
6953       return 1;
6954     } else {
6955       if (scheme_log_level_p(info->logger, SCHEME_LOG_DEBUG)) {
6956         Scheme_Lambda *lam = (Scheme_Lambda *)value;
6957         if (sz < 0)
6958           scheme_log(info->logger,
6959                      SCHEME_LOG_DEBUG,
6960                      0,
6961                      /* contains non-copyable body elements that prevent inlining */
6962                      "non-copyable %s size: %d threshold: %d#<separator>%s",
6963                      scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL),
6964                      sz,
6965                      0, /* no sensible threshold here */
6966                      scheme_optimize_context_to_string(info->context));
6967         else
6968           scheme_log(info->logger,
6969                      SCHEME_LOG_DEBUG,
6970                      0,
6971                      /* too large to be an inlining candidate */
6972                      "too-large %s size: %d threshold: %d#<separator>%s",
6973                      scheme_write_to_string(lam->name ? lam->name : scheme_false, NULL),
6974                      sz,
6975                      0, /* no sensible threshold here */
6976                      scheme_optimize_context_to_string(info->context));
6977       }
6978       return 0;
6979     }
6980   }
6981 
6982   if (SAME_TYPE(scheme_case_lambda_sequence_type, SCHEME_TYPE(value))) {
6983     Scheme_Case_Lambda *cl = (Scheme_Case_Lambda *)value;
6984     int i;
6985     for (i = cl->count; i--; ) {
6986       if (!ir_propagate_ok(cl->array[i], info, used_once, once_var))
6987         return 0;
6988     }
6989     return 1;
6990   }
6991 
6992   if (SAME_TYPE(SCHEME_TYPE(value), scheme_ir_toplevel_type)) {
6993     if ((SCHEME_IR_TOPLEVEL_FLAGS((Scheme_IR_Toplevel *)value) & SCHEME_TOPLEVEL_FLAGS_MASK) >= SCHEME_TOPLEVEL_FIXED)
6994       return 1;
6995     if (get_import_shape(info, (Scheme_IR_Toplevel *)value))
6996       return 1;
6997 
6998     value = get_defn_shape(info, (Scheme_IR_Toplevel *)value);
6999     value = no_potential_size(value);
7000     if (SAME_OBJ(value, scheme_constant_key)
7001         || (value && SAME_TYPE(SCHEME_TYPE(value), scheme_struct_proc_shape_type)))
7002       return 0;
7003     else if (value)
7004       return 1;
7005     else
7006       return 0;
7007   }
7008 
7009   /* Test this after the specific cases,
7010      because it recognizes locals and toplevels. */
7011   if (scheme_ir_duplicate_ok(value, 0))
7012     return 1;
7013 
7014   return 0;
7015 }
7016 
scheme_is_statically_proc(Scheme_Object * value,Optimize_Info * info,int flags)7017 int scheme_is_statically_proc(Scheme_Object *value, Optimize_Info *info, int flags)
7018 /* Does `value` definitely produce a procedure of a specific shape?
7019    This function can be used on resolved (and SFS) forms, too, and it
7020    must be consistent with (i.e., as least as accepting as)
7021    optimization-time decisions. The `flags` argument is for
7022    scheme_omittable_expr(). */
7023 {
7024   while (1) {
7025     if (SCHEME_LAMBDAP(value)
7026         || SCHEME_PROCP(value)
7027         || SAME_TYPE(SCHEME_TYPE(value), scheme_lambda_type)
7028         || SAME_TYPE(SCHEME_TYPE(value), scheme_case_lambda_sequence_type)
7029         || SAME_TYPE(SCHEME_TYPE(value), scheme_inline_variant_type))
7030       return 1;
7031     else if (SAME_TYPE(SCHEME_TYPE(value), scheme_ir_let_header_type)) {
7032       /* Look for (let ([x <omittable>]) <proc>), which is generated for optional arguments. */
7033       Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)value;
7034       if (lh->num_clauses == 1) {
7035         Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)lh->body;
7036         if (scheme_omittable_expr(lv->value, lv->count, 20, flags, info, NULL)) {
7037           value = lv->body;
7038         } else
7039           break;
7040       } else
7041         break;
7042     } else if (SAME_TYPE(SCHEME_TYPE(value), scheme_let_one_type)) {
7043       Scheme_Let_One *lo = (Scheme_Let_One *)value;
7044       if (scheme_omittable_expr(lo->value, 1, 20, flags, info, NULL)) {
7045         value = lo->body;
7046       } else
7047         break;
7048     } else if (SAME_TYPE(SCHEME_TYPE(value), scheme_boxenv_type)) {
7049       value = SCHEME_PTR2_VAL(value);
7050     } else if (SAME_TYPE(SCHEME_TYPE(value), scheme_sequence_type)
7051                /* Handle a sequence for resolved mode, because it might
7052                   be for safe-for-space clears around a procedure */
7053                && (flags & OMITTABLE_RESOLVED)) {
7054       Scheme_Sequence *seq = (Scheme_Sequence *)value;
7055       int i;
7056       for (i = 0; i < seq->count-1; i++) {
7057         if (!scheme_omittable_expr(seq->array[i], 1, 5, flags, info, NULL))
7058           break;
7059       }
7060       if (i == seq->count-1) {
7061         value = seq->array[i];
7062       } else
7063         break;
7064     } else
7065       break;
7066   }
7067 
7068   return 0;
7069 }
7070 
scheme_make_noninline_proc(Scheme_Object * e)7071 Scheme_Object *scheme_make_noninline_proc(Scheme_Object *e)
7072 /* Make a record that presents a procedure of a known shape, but
7073    that should not be inlined. */
7074 {
7075   Scheme_Object *ni;
7076 
7077   while (SAME_TYPE(SCHEME_TYPE(e), scheme_ir_let_header_type)) {
7078     /* This must be (let ([x <omittable>]) <proc>); see scheme_is_statically_proc() */
7079     Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)e;
7080     Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)lh->body;
7081     MZ_ASSERT(lh->num_clauses == 1);
7082     e = lv->body;
7083   }
7084 
7085   ni = scheme_alloc_small_object();
7086   ni->type = scheme_noninline_proc_type;
7087   SCHEME_PTR_VAL(ni) = e;
7088 
7089   return ni;
7090 }
7091 
is_values_apply(Scheme_Object * e,int n,Optimize_Info * info,Scheme_Hash_Tree * except_vars,int fuel)7092 static int is_values_apply(Scheme_Object *e, int n, Optimize_Info *info, Scheme_Hash_Tree *except_vars, int fuel)
7093 /* Is `e` a `(values ...)` form --- or, in the case of `if`, can be be
7094    converted to one, so that we can split apart the results
7095    statically? */
7096 {
7097   if (SAME_TYPE(SCHEME_TYPE(e), scheme_application_type)) {
7098     Scheme_App_Rec *app = (Scheme_App_Rec *)e;
7099     if (n != app->num_args) return 0;
7100     return SAME_OBJ(scheme_values_proc, app->args[0]);
7101   } else if ((n == 1) && SAME_TYPE(SCHEME_TYPE(e), scheme_application2_type)) {
7102     Scheme_App2_Rec *app = (Scheme_App2_Rec *)e;
7103     return SAME_OBJ(scheme_values_proc, app->rator);
7104   } else if ((n == 2) && SAME_TYPE(SCHEME_TYPE(e), scheme_application3_type)) {
7105     Scheme_App3_Rec *app = (Scheme_App3_Rec *)e;
7106     return SAME_OBJ(scheme_values_proc, app->rator);
7107   } else if (fuel && SAME_TYPE(SCHEME_TYPE(e), scheme_branch_type)) {
7108     Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)e;
7109     if (SAME_TYPE(SCHEME_TYPE(b->test), scheme_ir_local_type)
7110         && !scheme_eq_hash_tree_get(except_vars, b->test)
7111         && !SCHEME_VAR(b->test)->mutated) {
7112       return (is_values_apply(b->tbranch, n, info, except_vars, 0)
7113               && is_values_apply(b->fbranch, n, info, except_vars, 0));
7114     }
7115   }
7116 
7117   return 0;
7118 }
7119 
no_mutable_bindings(Scheme_IR_Let_Value * irlv)7120 static int no_mutable_bindings(Scheme_IR_Let_Value *irlv)
7121 /* Check whether a `let` clause has any mutable bindings */
7122 {
7123   int i;
7124 
7125   for (i = irlv->count; i--; ) {
7126     if (irlv->vars[i]->mutated)
7127       return 0;
7128   }
7129 
7130   return 1;
7131 }
7132 
update_rhs_value(Scheme_IR_Let_Value * naya,Scheme_Object * e,Optimize_Info * info,Scheme_IR_Local * tst)7133 static void update_rhs_value(Scheme_IR_Let_Value *naya, Scheme_Object *e,
7134                              Optimize_Info *info, Scheme_IR_Local *tst)
7135 /* Install an expression from a split `(values ...)` */
7136 {
7137   if (tst) {
7138     Scheme_Object *n;
7139 
7140     n = equivalent_exprs(naya->value, e, NULL, NULL, 0);
7141     if (!n) {
7142       Scheme_Branch_Rec *b;
7143 
7144       /* We're duplicating the test */
7145       increment_use_count(tst, 0);
7146 
7147       b = MALLOC_ONE_TAGGED(Scheme_Branch_Rec);
7148       b->so.type = scheme_branch_type;
7149       b->test = (Scheme_Object *)tst;
7150       b->tbranch = naya->value;
7151       b->fbranch = e;
7152 
7153       naya->value = (Scheme_Object *)b;
7154     } else
7155       naya->value = n;
7156   } else
7157     naya->value = e;
7158 }
7159 
unpack_values_application(Scheme_Object * e,Scheme_IR_Let_Value * naya,Optimize_Info * info,Scheme_IR_Local * branch_test)7160 static void unpack_values_application(Scheme_Object *e, Scheme_IR_Let_Value *naya,
7161                                       Optimize_Info *info, Scheme_IR_Local *branch_test)
7162 /* Install the expressions from a split `values` form into new `let` clauses */
7163 {
7164   if (SAME_TYPE(SCHEME_TYPE(e), scheme_application_type)) {
7165     Scheme_App_Rec *app = (Scheme_App_Rec *)e;
7166     int i;
7167     for (i = 0; i < app->num_args; i++) {
7168       update_rhs_value(naya, app->args[i + 1], info, branch_test);
7169       naya = (Scheme_IR_Let_Value *)naya->body;
7170     }
7171   } else if (SAME_TYPE(SCHEME_TYPE(e), scheme_application2_type)) {
7172     Scheme_App2_Rec *app = (Scheme_App2_Rec *)e;
7173     update_rhs_value(naya, app->rand, info, branch_test);
7174   } else if (SAME_TYPE(SCHEME_TYPE(e), scheme_application3_type)) {
7175     Scheme_App3_Rec *app = (Scheme_App3_Rec *)e;
7176     update_rhs_value(naya, app->rand1, info, branch_test);
7177     naya = (Scheme_IR_Let_Value *)naya->body;
7178     update_rhs_value(naya, app->rand2, info, branch_test);
7179   } else if (SAME_TYPE(SCHEME_TYPE(e), scheme_branch_type)) {
7180     Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)e;
7181 
7182     MZ_ASSERT(SAME_TYPE(SCHEME_TYPE(b->test), scheme_ir_local_type));
7183 
7184     unpack_values_application(b->tbranch, naya, info, NULL);
7185     unpack_values_application(b->fbranch, naya, info, SCHEME_VAR(b->test));
7186   }
7187 }
7188 
make_clones(Scheme_IR_Let_Value * retry_start,Scheme_IR_Let_Value * pre_body,Optimize_Info * body_info)7189 static Scheme_Object *make_clones(Scheme_IR_Let_Value *retry_start,
7190                                   Scheme_IR_Let_Value *pre_body,
7191                                   Optimize_Info *body_info)
7192 /* Clone `lambda`s for re-optimization and for a fixpoint computation of
7193    procedure properties */
7194 {
7195   Scheme_IR_Let_Value *irlv;
7196   Scheme_Object *value, *clone, *pr;
7197   Scheme_Object *last = NULL, *first = NULL;
7198 
7199   irlv = retry_start;
7200   while (1) {
7201     value = irlv->value;
7202     if (SCHEME_LAMBDAP(value)) {
7203       clone = optimize_clone(1, value, body_info, empty_eq_hash_tree, 0);
7204       if (clone) {
7205         pr = scheme_make_raw_pair(scheme_make_raw_pair(value, clone), NULL);
7206       } else
7207         pr = scheme_make_raw_pair(NULL, NULL);
7208       if (last)
7209         SCHEME_CDR(last) = pr;
7210       else
7211         first = pr;
7212       last = pr;
7213     }
7214     if (irlv == pre_body)
7215       break;
7216     irlv = (Scheme_IR_Let_Value *)irlv->body;
7217   }
7218 
7219   return first;
7220 }
7221 
set_one_code_flags(Scheme_Object * value,int flags,Scheme_Object * first,Scheme_Object * second,int set_flags,int mask_flags,int just_tentative,int merge_local_typed)7222 static int set_one_code_flags(Scheme_Object *value, int flags,
7223                               Scheme_Object *first, Scheme_Object *second,
7224                               int set_flags, int mask_flags, int just_tentative,
7225                               int merge_local_typed)
7226 /* Set, record, or merge procedure-property flags */
7227 {
7228   Scheme_Case_Lambda *cl, *cl2, *cl3;
7229   Scheme_Lambda *lam, *lam2, *lam3;
7230   int i, count;
7231 
7232   if (SAME_TYPE(scheme_ir_lambda_type, SCHEME_TYPE(value))) {
7233     count = 1;
7234     cl = NULL;
7235     cl2 = NULL;
7236     cl3 = NULL;
7237   } else {
7238     cl = (Scheme_Case_Lambda *)value;
7239     cl2 = (Scheme_Case_Lambda *)first;
7240     cl3 = (Scheme_Case_Lambda *)second;
7241     count = cl->count;
7242   }
7243 
7244   for (i = 0; i < count; i++) {
7245     if (cl) {
7246       lam = (Scheme_Lambda *)cl->array[i];
7247       lam2 = (Scheme_Lambda *)cl2->array[i];
7248       lam3 = (Scheme_Lambda *)cl3->array[i];
7249     } else {
7250       lam = (Scheme_Lambda *)value;
7251       lam2 = (Scheme_Lambda *)first;
7252       lam3 = (Scheme_Lambda *)second;
7253     }
7254 
7255     if (merge_local_typed) {
7256       merge_lambda_arg_types(lam, lam2);
7257       merge_lambda_arg_types(lam, lam3);
7258       merge_lambda_arg_types(lam, lam2);
7259     }
7260 
7261     if (!just_tentative || ((SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_STATUS_MASK) == LAMBDA_STATUS_RESULT_TENTATIVE)) {
7262       flags = (flags & SCHEME_LAMBDA_FLAGS(lam));
7263       SCHEME_LAMBDA_FLAGS(lam2) = set_flags | (SCHEME_LAMBDA_FLAGS(lam2) & mask_flags);
7264       SCHEME_LAMBDA_FLAGS(lam3) = set_flags | (SCHEME_LAMBDA_FLAGS(lam3) & mask_flags);
7265     }
7266   }
7267 
7268   return flags;
7269 }
7270 
set_code_flags(Scheme_IR_Let_Value * retry_start,Scheme_IR_Let_Value * pre_body,Scheme_Object * clones,int set_flags,int mask_flags,int just_tentative,int merge_local_typed)7271 static int set_code_flags(Scheme_IR_Let_Value *retry_start,
7272                           Scheme_IR_Let_Value *pre_body,
7273                           Scheme_Object *clones,
7274                           int set_flags, int mask_flags, int just_tentative,
7275                           int merge_local_typed)
7276 /* Set, record, or merge procedure-property flags */
7277 {
7278   Scheme_IR_Let_Value *irlv;
7279   Scheme_Object *value, *first;
7280   int flags = LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS;
7281 
7282   /* The first in a clone pair is the one that is consulted for
7283      references. The second one is the clone, and it's the one whose
7284      flags are updated by optimization. So consult the clone, and set
7285      flags in both. */
7286 
7287   irlv = retry_start;
7288   while (clones) {
7289     value = irlv->value;
7290     if (SCHEME_LAMBDAP(value)) {
7291       first = SCHEME_CAR(clones);
7292 
7293       if (first)
7294         flags = set_one_code_flags(value, flags,
7295                                    SCHEME_CAR(first), SCHEME_CDR(first),
7296                                    set_flags, mask_flags, just_tentative,
7297                                    merge_local_typed);
7298 
7299       clones = SCHEME_CDR(clones);
7300     }
7301 
7302     if (irlv == pre_body)
7303       break;
7304     irlv = (Scheme_IR_Let_Value *)irlv->body;
7305   }
7306 
7307   return flags;
7308 }
7309 
lambda_body_size(Scheme_Object * o,int less_args)7310 static int lambda_body_size(Scheme_Object *o, int less_args)
7311 {
7312   int bsz;
7313 
7314   if (SAME_TYPE(SCHEME_TYPE(o), scheme_ir_lambda_type)) {
7315     bsz = lambda_body_size_plus_info((Scheme_Lambda *)o, 0, NULL, NULL);
7316     if (less_args) bsz -= ((Scheme_Lambda *)o)->num_params;
7317     return bsz;
7318   } else if (SAME_TYPE(SCHEME_TYPE(o), scheme_case_lambda_sequence_type)) {
7319     Scheme_Case_Lambda *cl = (Scheme_Case_Lambda *)o;
7320     int i, sz = 0;
7321     for (i = cl->count; i--; ) {
7322       bsz = lambda_body_size_plus_info((Scheme_Lambda *)cl->array[i], 0, NULL, NULL);
7323       if (less_args) {
7324         bsz -= ((Scheme_Lambda *)cl->array[i])->num_params;
7325         if (bsz > sz) sz = bsz;
7326       } else
7327         sz += bsz;
7328     }
7329     return sz;
7330   } else
7331     return 0;
7332 }
7333 
expr_size(Scheme_Object * o)7334 static int expr_size(Scheme_Object *o)
7335 {
7336   return lambda_body_size(o, 0) + 1;
7337 }
7338 
scheme_might_invoke_call_cc(Scheme_Object * value)7339 int scheme_might_invoke_call_cc(Scheme_Object *value)
7340 {
7341   return !scheme_is_liftable(value, empty_eq_hash_tree, 10, 0, 1);
7342 }
7343 
7344 #define ADVANCE_CLOCKS_INIT_FUEL 3
7345 
advance_clocks_for_optimized(Scheme_Object * o,GC_CAN_IGNORE int * _vclock,GC_CAN_IGNORE int * _aclock,GC_CAN_IGNORE int * _kclock,GC_CAN_IGNORE int * _sclock,Optimize_Info * info,int fuel)7346 void advance_clocks_for_optimized(Scheme_Object *o,
7347                                   GC_CAN_IGNORE int *_vclock,
7348                                   GC_CAN_IGNORE int *_aclock,
7349                                   GC_CAN_IGNORE int *_kclock,
7350                                   GC_CAN_IGNORE int *_sclock,
7351                                   Optimize_Info *info,
7352                                   int fuel)
7353 /* It's ok for this function to advance clocks *less* than
7354    accurately, but not more than accurately */
7355 {
7356   Scheme_Object *rator = NULL;
7357   int argc = 0;
7358 
7359   if (!fuel) return;
7360 
7361   switch (SCHEME_TYPE(o)) {
7362   case scheme_application_type:
7363     {
7364       Scheme_App_Rec *app = (Scheme_App_Rec *)o;
7365       int i;
7366       for (i = 0; i < app->num_args; i++) {
7367         advance_clocks_for_optimized(app->args[i+1],
7368                                      _vclock, _aclock, _kclock, _sclock,
7369                                      info, fuel - 1);
7370       }
7371       rator = app->args[0];
7372       argc = app->num_args;
7373     }
7374     break;
7375   case scheme_application2_type:
7376     {
7377       Scheme_App2_Rec *app = (Scheme_App2_Rec *)o;
7378       advance_clocks_for_optimized(app->rand,
7379                                    _vclock, _aclock, _kclock, _sclock,
7380                                    info, fuel - 1);
7381       rator = app->rator;
7382       argc = 1;
7383       break;
7384     }
7385   case scheme_application3_type:
7386     {
7387       Scheme_App3_Rec *app = (Scheme_App3_Rec *)o;
7388       advance_clocks_for_optimized(app->rand1,
7389                                    _vclock, _aclock, _kclock, _sclock,
7390                                    info, fuel - 1);
7391       advance_clocks_for_optimized(app->rand2,
7392                                    _vclock, _aclock, _kclock, _sclock,
7393                                    info, fuel - 1);
7394       rator = app->rator;
7395       argc = 2;
7396     }
7397     break;
7398   default:
7399     break;
7400   }
7401 
7402   if (rator)
7403     increment_clock_counts_for_application(_vclock, _aclock, _kclock, _sclock, rator, argc);
7404 
7405   if ((*_vclock > info->vclock)
7406       || (*_aclock > info->aclock)
7407       || (*_kclock > info->kclock)
7408       || (*_sclock > info->sclock))
7409     scheme_signal_error("internal error: optimizer clock tracking has gone wrong");
7410 }
7411 
set_application_types(Scheme_Object * o,Optimize_Info * info,int fuel)7412 static void set_application_types(Scheme_Object *o, Optimize_Info *info, int fuel)
7413 /* Peek ahead in an expression to set readily apparent type information
7414    for function calls. This information is useful for type-invariant loop
7415    arguments, for example. */
7416 {
7417   if (!fuel) return;
7418 
7419   switch (SCHEME_TYPE(o)) {
7420   case scheme_application_type:
7421     {
7422       Scheme_App_Rec *app = (Scheme_App_Rec *)o;
7423       int i;
7424       register_local_argument_types(app, NULL, NULL, info);
7425       for (i = 0; i < app->num_args+1; i++) {
7426         set_application_types(app->args[i], info, fuel - 1);
7427       }
7428     }
7429     break;
7430   case scheme_application2_type:
7431     {
7432       Scheme_App2_Rec *app = (Scheme_App2_Rec *)o;
7433       register_local_argument_types(NULL, app, NULL, info);
7434       set_application_types(app->rator, info, fuel - 1);
7435       set_application_types(app->rand, info, fuel - 1);
7436       break;
7437     }
7438   case scheme_application3_type:
7439     {
7440       Scheme_App3_Rec *app = (Scheme_App3_Rec *)o;
7441       register_local_argument_types(NULL, NULL, app, info);
7442       set_application_types(app->rator, info, fuel - 1);
7443       set_application_types(app->rand1, info, fuel - 1);
7444       set_application_types(app->rand2, info, fuel - 1);
7445     }
7446     break;
7447   case scheme_sequence_type:
7448   case scheme_begin0_sequence_type:
7449     {
7450       Scheme_Sequence *seq = (Scheme_Sequence *)o;
7451       int i;
7452 
7453       for (i = 0; i < seq->count; i++) {
7454         set_application_types(seq->array[i], info, fuel - 1);
7455       }
7456     }
7457     break;
7458   case scheme_branch_type:
7459     {
7460       Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)o;
7461       set_application_types(b->test, info, fuel - 1);
7462       set_application_types(b->tbranch, info, fuel - 1);
7463       set_application_types(b->fbranch, info, fuel - 1);
7464     }
7465     break;
7466   default:
7467     break;
7468   }
7469 }
7470 
flip_transitive(Scheme_Hash_Table * ht,int on)7471 static void flip_transitive(Scheme_Hash_Table *ht, int on)
7472 /* Adjust usage flags based on recorded tentative uses */
7473 {
7474   Scheme_IR_Local *tvar;
7475   int j;
7476   Scheme_Object *to_remove = scheme_null;
7477 
7478   for (j = 0; j < ht->size; j++) {
7479     if (ht->vals[j]) {
7480       tvar = SCHEME_VAR(ht->keys[j]);
7481       if (on) {
7482         if (tvar->optimize_used) {
7483           /* use of `tvar` is no longer dependent on another variable */
7484           to_remove = scheme_make_pair((Scheme_Object *)tvar,
7485                                        to_remove);
7486         } else
7487           tvar->optimize_used = 1;
7488       } else {
7489         /* It's possible that `tvar->optimize_used` is already 0; a variable
7490            is sometimes tenatively marked as used, and then unmarked */
7491         tvar->optimize_used = 0;
7492       }
7493     }
7494   }
7495 
7496   while (!SCHEME_NULLP(to_remove)) {
7497     scheme_hash_set(ht, SCHEME_CAR(to_remove), NULL);
7498     to_remove = SCHEME_CDR(to_remove);
7499   }
7500 }
7501 
start_transitive_use_record(Optimize_Info * to_info,Optimize_Info * info,Scheme_IR_Local * var)7502 static void start_transitive_use_record(Optimize_Info *to_info, Optimize_Info *info, Scheme_IR_Local *var)
7503 /* Start recording uses as tentative. Uses in a `lambda` as the RHS of
7504    the binding of `var` will only be used in the end of `var` itself
7505    is used. */
7506 {
7507   if (var->optimize_used)
7508     return;
7509 
7510   info->transitive_use_var = var;
7511 
7512   /* Restore use flags, if any, saved from before: */
7513   if (var->optimize.transitive_uses)
7514     flip_transitive(var->optimize.transitive_uses, 1);
7515 }
7516 
end_transitive_use_record(Optimize_Info * info)7517 static void end_transitive_use_record(Optimize_Info *info)
7518 /* Stop recording uses as tentative. */
7519 {
7520   Scheme_IR_Local *var = info->transitive_use_var;
7521 
7522   if (var != info->next->transitive_use_var) {
7523     info->transitive_use_var = info->next->transitive_use_var;
7524 
7525     if (var->optimize.transitive_uses)
7526       flip_transitive(var->optimize.transitive_uses, 0);
7527   }
7528 }
7529 
7530 /* Convert up to `c` clauses for `let-values` into a `begin`, where
7531    the converted clauses have zero bindings. The `head` argument will
7532    be non-NULL if there's a possibility of remaining clauses. */
convert_leading_zero_bindings_to_begin(Scheme_IR_Let_Header * head,Scheme_Object * start_body,int c)7533 static Scheme_Object *convert_leading_zero_bindings_to_begin(Scheme_IR_Let_Header *head,
7534                                                              Scheme_Object *start_body,
7535                                                              int c)
7536 {
7537   Scheme_Object *body;
7538   Scheme_IR_Let_Value *irlv;
7539   Scheme_Sequence *seq;
7540   int i, n = 0;
7541 
7542   body = start_body;
7543   for (i = 0; i < c; i++) {
7544     irlv = (Scheme_IR_Let_Value *)body;
7545     if (irlv->count)
7546       break;
7547     n++;
7548     body = irlv->body;
7549   }
7550 
7551   seq = scheme_malloc_sequence(n + 1);
7552   seq->so.type = scheme_sequence_type;
7553   seq->count = n + 1;
7554   body = start_body;
7555   for (i = 0; i < n; i++) {
7556     irlv = (Scheme_IR_Let_Value *)body;
7557     seq->array[i] = irlv->value;
7558     body = irlv->body;
7559   }
7560 
7561   if (n < c) {
7562     head->num_clauses -= n;
7563     head->body = body;
7564     seq->array[n] = (Scheme_Object *)head;
7565   } else
7566     seq->array[n] = body;
7567 
7568   return (Scheme_Object *)seq;
7569 }
7570 
optimize_lets(Scheme_Object * form,Optimize_Info * info,int context)7571 static Scheme_Object *optimize_lets(Scheme_Object *form, Optimize_Info *info, int context)
7572 /* This is the main entry point for optimizing a `let[rec]-values` form. */
7573 {
7574   Optimize_Info *body_info, *rhs_info;
7575   Optimize_Info_Sequence info_seq;
7576   Scheme_IR_Let_Header *head = (Scheme_IR_Let_Header *)form;
7577   Scheme_IR_Let_Value *irlv, *pre_body, *retry_start, *prev_body;
7578   Scheme_Object *body, *value, *ready_pairs = NULL, *rp_last = NULL, *ready_pairs_start;
7579   Scheme_Object *escape_body = scheme_false;
7580   Scheme_Once_Used *once_used;
7581   Scheme_Hash_Tree *merge_skip_vars;
7582   int i, j, is_rec, not_simply_let_star = 0, undiscourage, skip_opts = 0;
7583   int did_set_value, found_escapes;
7584   int remove_last_one = 0, inline_fuel;
7585   int pre_vclock, pre_aclock, pre_kclock, pre_sclock, increments_kclock = 0;
7586   int once_vclock, once_aclock, once_kclock, once_sclock, once_increments_kclock = 0;
7587 
7588   /* Special case: (let ([x M]) (if x x N)), where x is not in N,
7589      to (if M #t N), when the expression is in a test position
7590      or the result of M is a boolean?. */
7591   if (!(SCHEME_LET_FLAGS(head) & SCHEME_LET_RECURSIVE)
7592       && (head->count == 1)
7593       && (head->num_clauses == 1)) {
7594     irlv = (Scheme_IR_Let_Value *)head->body;
7595     if (SAME_TYPE(SCHEME_TYPE(irlv->body), scheme_branch_type)
7596         && (irlv->vars[0]->use_count == 2)) {
7597       Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)irlv->body;
7598       if (SAME_OBJ(b->test, (Scheme_Object *)irlv->vars[0])
7599           && SAME_OBJ(b->tbranch, (Scheme_Object *)irlv->vars[0])) {
7600         Scheme_Object *pred;
7601 
7602         if (context & OPT_CONTEXT_BOOLEAN)
7603           /* In a boolean context, any expression can be moved. */
7604           pred = scheme_boolean_p_proc;
7605         else
7606           pred = expr_implies_predicate(irlv->value, info);
7607 
7608         if (pred && predicate_implies(pred, scheme_boolean_p_proc)) {
7609           Scheme_Branch_Rec *b3;
7610 
7611           b3 = MALLOC_ONE_TAGGED(Scheme_Branch_Rec);
7612           b3->so.type = scheme_branch_type;
7613           b3->test = irlv->value;
7614           b3->tbranch = scheme_true;
7615           b3->fbranch = b->fbranch;
7616 
7617           form = optimize_expr((Scheme_Object *)b3, info, context);
7618 
7619           return form;
7620         }
7621       }
7622     }
7623   }
7624 
7625   is_rec = (SCHEME_LET_FLAGS(head) & SCHEME_LET_RECURSIVE);
7626 
7627   /* Special case: (let ([x E]) x) => E or (values E) */
7628   if (!is_rec
7629       && (head->count == 1)
7630       && (head->num_clauses == 1)) {
7631     irlv = (Scheme_IR_Let_Value *)head->body;
7632     if (SAME_OBJ((Scheme_Object *)irlv->vars[0], irlv->body)) {
7633       body = irlv->value;
7634       body = ensure_single_value_noncm(body, info);
7635       return optimize_expr(body, info, context);
7636     }
7637   }
7638 
7639   /* Zero leading bindings in unsafe mode => convert to `begin`, since
7640      we can unsafely drop the check on the number of results */
7641   if (!is_rec && info->unsafe_mode && head->num_clauses
7642       && !((Scheme_IR_Let_Value *)head->body)->count) {
7643     body = convert_leading_zero_bindings_to_begin(head, head->body, head->num_clauses);
7644     return optimize_expr(body, info, context);
7645   }
7646 
7647   if (!is_rec) {
7648     int try_again;
7649     do {
7650       try_again = 0;
7651       /* (let ([x (let ([y M]) N)]) P) => (let ([y M]) (let ([x N]) P))
7652          or (let ([x (begin M ... N)]) P) => (begin M ... (let ([x N]) P)) */
7653       if (head->num_clauses) {
7654         irlv = (Scheme_IR_Let_Value *)head->body; /* ([x ...]) */
7655         if (SAME_TYPE(SCHEME_TYPE(irlv->value), scheme_ir_let_header_type)) {
7656           Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)irlv->value; /* (let ([y ...]) ...) */
7657 
7658           if (!lh->num_clauses) {
7659             irlv->value = lh->body;
7660             lh->body = (Scheme_Object *)head;
7661           } else {
7662             body = lh->body;
7663             for (i = lh->num_clauses - 1; i--; ) {
7664               body = ((Scheme_IR_Let_Value *)body)->body;
7665             }
7666             irlv->value = ((Scheme_IR_Let_Value *)body)->body; /* N */
7667             ((Scheme_IR_Let_Value *)body)->body = (Scheme_Object *)head;
7668           }
7669 
7670           head = lh;
7671           form = (Scheme_Object *)head;
7672           is_rec = (SCHEME_LET_FLAGS(head) & SCHEME_LET_RECURSIVE);
7673           try_again = !is_rec;
7674         } else if (SAME_TYPE(SCHEME_TYPE(irlv->value), scheme_sequence_type)) {
7675           Scheme_Sequence *seq = (Scheme_Sequence *)irlv->value; /* (begin M ... N) */
7676 
7677           irlv->value = seq->array[seq->count - 1];
7678           seq->array[seq->count - 1] = (Scheme_Object *)head;
7679 
7680           return optimize_expr((Scheme_Object *)seq, info, context);
7681         }
7682       }
7683     } while (try_again);
7684   }
7685 
7686   body_info = optimize_info_add_frame(info, 0);
7687   rhs_info = body_info;
7688 
7689   merge_skip_vars = scheme_make_hash_tree(SCHEME_hashtr_eq);
7690   body = head->body;
7691   for (i = head->num_clauses; i--; ) {
7692     pre_body = (Scheme_IR_Let_Value *)body;
7693     for (j = pre_body->count; j--; ) {
7694       merge_skip_vars = scheme_hash_tree_set(merge_skip_vars, (Scheme_Object *)pre_body->vars[j], scheme_true);
7695       set_optimize_mode(pre_body->vars[j]);
7696       pre_body->vars[j]->optimize.lambda_depth = body_info->lambda_depth;
7697       pre_body->vars[j]->optimize_used = 0;
7698       pre_body->vars[j]->optimize_outside_binding = 0;
7699       if (!pre_body->vars[j]->mutated && is_rec) {
7700         /* Indicate that it's not yet ready, so it cannot be inlined: */
7701         Scheme_Object *rp;
7702         pre_body->vars[j]->optimize_unready = 1;
7703         rp = scheme_make_raw_pair((Scheme_Object *)pre_body->vars[j], NULL);
7704         if (rp_last)
7705           SCHEME_CDR(rp_last) = rp;
7706         else
7707           ready_pairs = rp;
7708         rp_last = rp;
7709       }
7710     }
7711     body = pre_body->body;
7712   }
7713 
7714   if (OPT_ESTIMATE_FUTURE_SIZES) {
7715     if (is_rec && !body_info->letrec_not_twice) {
7716       /* For each identifier bound to a procedure, register an initial
7717          size estimate, which is used to discourage early loop unrolling
7718          at the expense of later inlining. */
7719       body = head->body;
7720       pre_body = NULL;
7721       for (i = head->num_clauses; i--; ) {
7722         pre_body = (Scheme_IR_Let_Value *)body;
7723 
7724         if ((pre_body->count == 1)
7725             && SCHEME_LAMBDAP(pre_body->value)
7726             && !pre_body->vars[0]->mutated) {
7727           Scheme_Object *sz;
7728           sz = estimate_closure_size(pre_body->value);
7729           pre_body->vars[0]->optimize.known_val = sz;
7730         }
7731 
7732         body = pre_body->body;
7733       }
7734       rhs_info->use_psize = 1;
7735     }
7736   }
7737 
7738   optimize_info_seq_init(rhs_info, &info_seq);
7739 
7740   prev_body = NULL;
7741   body = head->body;
7742   pre_body = NULL;
7743   retry_start = NULL;
7744   ready_pairs_start = NULL;
7745   did_set_value = 0;
7746   found_escapes = 0;
7747   for (i = head->num_clauses; i--; ) {
7748     pre_body = (Scheme_IR_Let_Value *)body;
7749 
7750     if ((pre_body->count == 1)
7751         && SCHEME_LAMBDAP(pre_body->value)
7752         && !pre_body->vars[0]->optimize_used)
7753       start_transitive_use_record(body_info, rhs_info, pre_body->vars[0]);
7754 
7755     if (is_rec && OPT_DISCOURAGE_EARLY_INLINE && !rhs_info->letrec_not_twice
7756         && SCHEME_LAMBDAP(pre_body->value)) {
7757       inline_fuel = rhs_info->inline_fuel;
7758       if (inline_fuel > 2)
7759         rhs_info->inline_fuel = 2;
7760       rhs_info->letrec_not_twice++;
7761       undiscourage = 1;
7762     } else {
7763       inline_fuel = 0;
7764       undiscourage = 0;
7765     }
7766 
7767     if (!skip_opts) {
7768       pre_vclock = rhs_info->vclock;
7769       pre_aclock = rhs_info->aclock;
7770       pre_kclock = rhs_info->kclock;
7771       pre_sclock = rhs_info->sclock;
7772       if (!found_escapes) {
7773         optimize_info_seq_step(rhs_info, &info_seq);
7774         value = optimize_expr(pre_body->value, rhs_info,
7775                               (((pre_body->count == 1)
7776                                 ? OPT_CONTEXT_SINGLED
7777                                 : 0)
7778                                | (((pre_body->count == 1)
7779                                    && !pre_body->vars[0]->non_app_count)
7780                                   ? (pre_body->vars[0]->use_count << OPT_CONTEXT_APP_COUNT_SHIFT)
7781                                   : 0)));
7782         pre_body->value = value;
7783         if (rhs_info->escapes)
7784           found_escapes = 1;
7785       } else {
7786         optimize_info_seq_step(rhs_info, &info_seq);
7787         value = scheme_false;
7788         pre_body->value = value;
7789         body_info->single_result = 1;
7790         body_info->preserves_marks = 1;
7791         body_info->escapes = 1;
7792         body_info->size++;
7793       }
7794       once_vclock = rhs_info->vclock;
7795       once_aclock = rhs_info->aclock;
7796       once_kclock = rhs_info->kclock;
7797       once_sclock = rhs_info->sclock;
7798       increments_kclock = (once_kclock > pre_kclock);
7799       once_increments_kclock = increments_kclock;
7800     } else {
7801       value = pre_body->value;
7802       --skip_opts;
7803       if (skip_opts) {
7804         /* when a `values` group is split, we've lost track of the
7805            clock values for points between the `values` arguments;
7806            we can conservatively assume the clock before the whole group
7807            for the purpose of registering once-used variables,
7808            but we can also conservatively advance the clock: */
7809         if (!found_escapes)
7810           advance_clocks_for_optimized(value,
7811                                        &pre_vclock, &pre_aclock, &pre_kclock, &pre_sclock,
7812                                        rhs_info,
7813                                        ADVANCE_CLOCKS_INIT_FUEL);
7814         once_vclock = pre_vclock;
7815         once_aclock = pre_aclock;
7816         once_kclock = pre_kclock;
7817         once_sclock = pre_sclock;
7818       } else {
7819         /* end of split group, so rhs_info clock is right */
7820         once_vclock = rhs_info->vclock;
7821         once_aclock = rhs_info->aclock;
7822         once_kclock = rhs_info->kclock;
7823         once_sclock = rhs_info->sclock;
7824       }
7825       if (increments_kclock) {
7826         /* note that we conservatively assume that a member of a split
7827            advance the kclock, unless we can easily show otherwise */
7828         once_increments_kclock = 1;
7829       }
7830     }
7831 
7832     if (undiscourage) {
7833       rhs_info->inline_fuel = inline_fuel;
7834       --rhs_info->letrec_not_twice;
7835     }
7836 
7837     end_transitive_use_record(rhs_info);
7838 
7839     if (is_rec && !not_simply_let_star) {
7840       /* Keep track of whether we can simplify to let*: */
7841       if (scheme_might_invoke_call_cc(value)
7842           || optimize_any_uses(body_info, pre_body, i+1))
7843         not_simply_let_star = 1;
7844     }
7845 
7846     /* Change (let-values ([(id ...) (values e ...)]) body)
7847        to (let-values ([id e] ...) body) for simple e.
7848        The is_values_apply() and related functions also handle
7849        (if id (values e1 ...) (values e2 ...)) to effectively convert to
7850        (values (if id e1 e2) ...) and then split the values call, since
7851        duplicating the id use and test is likely to pay off. */
7852     if ((pre_body->count != 1)
7853         && ((!is_rec && found_escapes)
7854             || (is_values_apply(value, pre_body->count, rhs_info, merge_skip_vars, 1)
7855                 && ((!is_rec && no_mutable_bindings(pre_body))
7856                     /* If the right-hand side is omittable, then there are
7857                        no side effects, so mutation and recursiveness are ok */
7858                     || scheme_omittable_expr(value, pre_body->count, -1, 0, rhs_info, info))))) {
7859       if (!pre_body->count && !i) {
7860         /* We want to drop the clause entirely, but doing it
7861            here messes up the loop for letrec. So wait and
7862            remove it at the end. */
7863         remove_last_one = 1;
7864         /* If `found_escapes`, either this expression is the
7865            one that escaped, or `value` should have been simplified
7866            to `#f`. So, if it's not `#f`, we'll need to keep
7867            the expression part */
7868         if (!found_escapes)
7869           value = scheme_false;
7870         pre_body->value = value;
7871       } else {
7872         Scheme_IR_Let_Value *naya;
7873         Scheme_Object *rest = pre_body->body;
7874         int j;
7875 
7876         for (j = pre_body->count; j--; ) {
7877           Scheme_IR_Local **new_vars;
7878           naya = MALLOC_ONE_TAGGED(Scheme_IR_Let_Value);
7879           naya->iso.so.type = scheme_ir_let_value_type;
7880           naya->body = rest;
7881           naya->count = 1;
7882           new_vars = MALLOC_N(Scheme_IR_Local *, 1);
7883           new_vars[0] = pre_body->vars[j];
7884           naya->vars = new_vars;
7885           rest = (Scheme_Object *)naya;
7886         }
7887 
7888         naya = (Scheme_IR_Let_Value *)rest;
7889         if (!found_escapes) {
7890           unpack_values_application(value, naya, rhs_info, NULL);
7891         } else {
7892           Scheme_IR_Let_Value *naya2 = naya;
7893           for (j = 0; j < pre_body->count; j++) {
7894             if (!j)
7895               naya2->value = value;
7896             else
7897               naya2->value = scheme_false;
7898             naya2 = (Scheme_IR_Let_Value *)naya2->body;
7899           }
7900 
7901           if (!pre_body->count && !SCHEME_FALSEP(value)) {
7902             /* Since `value` is not false, this clause must be the one
7903                that is escaping. We'll end up dropping the remaining
7904                clauses and the original body, but we need to keep the
7905                erroring expression. */
7906             escape_body = value;
7907           }
7908         }
7909 
7910         if (prev_body)
7911           prev_body->body = (Scheme_Object *)naya;
7912         else
7913           head->body = (Scheme_Object *)naya;
7914         head->num_clauses += (pre_body->count - 1);
7915         i += (pre_body->count - 1);
7916         if (pre_body->count) {
7917           /* We're backing up. Since the RHSs have been optimized
7918              already, don't re-optimize. */
7919           skip_opts = pre_body->count - 1;
7920           pre_body = naya;
7921           body = (Scheme_Object *)naya;
7922           value = pre_body->value;
7923 
7924           if (skip_opts) {
7925             /* Use "pre" clocks: */
7926             if (!found_escapes)
7927               advance_clocks_for_optimized(value,
7928                                            &pre_vclock, &pre_aclock, &pre_kclock, &pre_sclock,
7929                                            rhs_info,
7930                                            ADVANCE_CLOCKS_INIT_FUEL);
7931             once_vclock = pre_vclock;
7932             once_aclock = pre_aclock;
7933             once_kclock = pre_kclock;
7934             once_sclock = pre_sclock;
7935           }
7936         } else {
7937           /* We've dropped this clause entirely. */
7938           i++;
7939           if (i > 0) {
7940             body = (Scheme_Object *)naya;
7941             continue;
7942           } else
7943             break;
7944         }
7945       }
7946     }
7947 
7948     if ((pre_body->count == 1) && !pre_body->vars[0]->mutated) {
7949       int indirect = 0, indirect_binding = 0;
7950 
7951       /* extract_tail_inside with `for_immediate_body` as true needs
7952          to be consistent with this peek inside, in case a single-use
7953          variable extracted as the binding for a single-use
7954          variable */
7955       while (indirect < 10) {
7956         if (SAME_TYPE(SCHEME_TYPE(value), scheme_sequence_type)) {
7957           Scheme_Sequence *seq = (Scheme_Sequence *)value;
7958           value = seq->array[seq->count - 1];
7959           indirect++;
7960         } else if (SAME_TYPE(SCHEME_TYPE(value), scheme_with_cont_mark_type)) {
7961           Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)value;
7962           value = wcm->body;
7963           indirect++;
7964         } else if (SAME_TYPE(SCHEME_TYPE(value), scheme_ir_let_header_type)) {
7965           Scheme_IR_Let_Header *head2 = (Scheme_IR_Let_Header *)value;
7966           int i;
7967 
7968           if (head2->num_clauses < 10) {
7969             value = head2->body;
7970             for (i = head2->num_clauses; i--; ) {
7971               value = ((Scheme_IR_Let_Value *)value)->body;
7972             }
7973           }
7974           indirect++;
7975           if (head2->count)
7976             indirect_binding = 1;
7977         } else
7978           break;
7979       }
7980 
7981       if (indirect_binding) {
7982         /* only allow constants */
7983         if (SCHEME_TYPE(value) < _scheme_ir_values_types_)
7984           value = NULL;
7985       }
7986 
7987       if (value && SAME_TYPE(SCHEME_TYPE(value), scheme_ir_local_type)) {
7988         /* Don't optimize reference to a local that's mutable; also,
7989            double-check that the value is ready, because we might be
7990            nested in the RHS of a `letrec': */
7991         if (SCHEME_VAR(value)->mutated || SCHEME_VAR(value)->optimize_unready)
7992           value = NULL;
7993       }
7994 
7995       if (value)
7996         value = extract_specialized_proc(value, value);
7997 
7998       if (value && ir_propagate_ok(value,
7999                                    body_info,
8000                                    (!indirect && (pre_body->vars[0]->use_count == 1)),
8001                                    pre_body->vars[0])) {
8002         pre_body->vars[0]->optimize.known_val = value;
8003         did_set_value = 1;
8004       } else if (value && !is_rec) {
8005         int cnt, ct, involves_k_cross;
8006         Scheme_Object *pred;
8007 
8008         ct = scheme_expr_produces_local_type(value, &involves_k_cross);
8009         if (ct) {
8010           SCHEME_VAR(pre_body->vars[0])->val_type = ct;
8011           if (involves_k_cross) {
8012             /* Although this variable's uses do not necessarily cross
8013                a continuation capture, the inference of its type
8014                depends on that crossing, so we treat as having a crossing.
8015                This is an accommodation to the bytecode format and
8016                validator, which has no way to distinguish between
8017                a known type and unboxing capability for that type. */
8018             SCHEME_VAR(pre_body->vars[0])->escapes_after_k_tick = 1;
8019           }
8020         }
8021 
8022         pred = expr_implies_predicate(value, rhs_info);
8023 
8024         if (pred)
8025           add_type(body_info, (Scheme_Object *)pre_body->vars[0], pred);
8026 
8027         if (!indirect) {
8028           cnt = pre_body->vars[0]->use_count;
8029           if (cnt == 1) {
8030             /* used only once; we may be able to shift the expression to the use
8031                site, instead of binding to a temporary */
8032             once_used = make_once_used(value, pre_body->vars[0],
8033                                        once_vclock, once_aclock, once_kclock, once_sclock,
8034                                        once_increments_kclock);
8035             pre_body->vars[0]->optimize.known_val = (Scheme_Object *)once_used;
8036             pre_body->vars[0]->optimize.clear_known_on_multi_use = 1;
8037           }
8038         }
8039       }
8040     }
8041 
8042     if (!retry_start) {
8043       retry_start = pre_body;
8044       ready_pairs_start = ready_pairs;
8045     }
8046 
8047     /* Re-optimize to inline letrec bindings? */
8048     if (is_rec
8049 	&& !body_info->letrec_not_twice
8050 	&& ((i < 1)
8051 	    || (!scheme_is_ir_lambda(((Scheme_IR_Let_Value *)pre_body->body)->value, 1, 1)
8052 		&& !scheme_is_liftable(((Scheme_IR_Let_Value *)pre_body->body)->value, merge_skip_vars, 5, 1, 0)))) {
8053       Scheme_Object *prop_later = NULL;
8054 
8055       if (did_set_value) {
8056         /* Next RHS ends a reorderable sequence.
8057            Re-optimize from retry_start to pre_body, inclusive.
8058            For procedures, assume LAMBDA_SINGLE_RESULT and LAMBDA_PRESERVES_MARKS for all,
8059            but then assume not for all if any turn out not (i.e., approximate fix point). */
8060         int flags;
8061         Scheme_Object *clones, *cl, *cl_first;
8062 
8063         /* If this is the last binding, peek ahead in the body to
8064            check for easy type info in function calls */
8065         if (!i)
8066           set_application_types(pre_body->body, body_info, 5);
8067 
8068         /* Reset "unready" flags: */
8069         for (rp_last = ready_pairs_start; !SAME_OBJ(rp_last, ready_pairs); rp_last = SCHEME_CDR(rp_last)) {
8070           SCHEME_VAR(SCHEME_CAR(rp_last))->optimize_unready = 1;
8071         }
8072         /* Set-flags loop: */
8073         clones = make_clones(retry_start, pre_body, rhs_info);
8074         (void)set_code_flags(retry_start, pre_body, clones,
8075                              LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS | LAMBDA_STATUS_RESULT_TENTATIVE,
8076                              0xFFFF,
8077                              0,
8078                              0);
8079         /* Re-optimize loop: */
8080         irlv = retry_start;
8081         cl = clones;
8082         while (1) {
8083          value = irlv->value;
8084           if (cl) {
8085             cl_first = SCHEME_CAR(cl);
8086             if (!cl_first)
8087               cl = SCHEME_CDR(cl);
8088           } else
8089             cl_first = NULL;
8090           if (cl_first && SAME_OBJ(value, SCHEME_CAR(cl_first))) {
8091             /* Try optimization. */
8092             Scheme_Object *self_value;
8093             int sz;
8094             char use_psize;
8095 
8096             if ((irlv->count == 1)
8097                 && !irlv->vars[0]->optimize_used)
8098               start_transitive_use_record(body_info, rhs_info, irlv->vars[0]);
8099 
8100             cl = SCHEME_CDR(cl);
8101             self_value = SCHEME_CDR(cl_first);
8102 
8103             /* Drop old size, and remove old inline fuel: */
8104             sz = lambda_body_size(value, 0);
8105             rhs_info->size -= (sz + 1);
8106 
8107             /* Setting letrec_not_twice prevents inlinining
8108                of letrec bindings in this RHS. There's a small
8109                chance that we miss some optimizations, but we
8110                avoid the possibility of N^2 behavior. */
8111             if (!OPT_DISCOURAGE_EARLY_INLINE)
8112               rhs_info->letrec_not_twice++;
8113             inline_fuel = rhs_info->inline_fuel;
8114             rhs_info->inline_fuel >>= 1;
8115             use_psize = rhs_info->use_psize;
8116             rhs_info->use_psize = info->use_psize;
8117 
8118             optimize_info_seq_step(rhs_info, &info_seq);
8119             value = optimize_expr(self_value, rhs_info,
8120                                   (((irlv->count == 1)
8121                                     ? OPT_CONTEXT_SINGLED
8122                                     : 0)
8123                                    | (((irlv->count == 1)
8124                                        && !irlv->vars[0]->non_app_count)
8125                                       ? (irlv->vars[0]->use_count << OPT_CONTEXT_APP_COUNT_SHIFT)
8126                                       : 0)));
8127 
8128             if (!OPT_DISCOURAGE_EARLY_INLINE)
8129               --rhs_info->letrec_not_twice;
8130             rhs_info->inline_fuel = inline_fuel;
8131             rhs_info->use_psize = use_psize;
8132 
8133             irlv->value = value;
8134 
8135             if (!irlv->vars[0]->mutated) {
8136               if (ir_propagate_ok(value, rhs_info, irlv->vars[0]->use_count == 1, irlv->vars[0])) {
8137                 /* Register re-optimized as the value for the binding, but
8138                    maybe only if it didn't grow too much: */
8139                 int new_sz;
8140                 if (OPT_DELAY_GROUP_PROPAGATE || OPT_LIMIT_FUNCTION_RESIZE)
8141                   new_sz = lambda_body_size(value, 0);
8142                 else
8143                   new_sz = 0;
8144                 if (new_sz <= sz) {
8145                   irlv->vars[0]->optimize.known_val = value;
8146                 }
8147                 else if (!OPT_LIMIT_FUNCTION_RESIZE
8148                          || (new_sz < 4 * sz))
8149                   prop_later = scheme_make_raw_pair(scheme_make_pair((Scheme_Object *)irlv->vars[0],
8150                                                                      value),
8151                                                     prop_later);
8152               }
8153             }
8154 
8155             end_transitive_use_record(rhs_info);
8156 	  }
8157 	  if (irlv == pre_body)
8158 	    break;
8159           {
8160             /* Since letrec is really letrec*, the variables
8161                for this binding are now ready: */
8162             int i;
8163             for (i = irlv->count; i--; ) {
8164               if (!irlv->vars[i]->mutated) {
8165                 SCHEME_VAR(SCHEME_CAR(ready_pairs_start))->optimize_unready = 0;
8166                 ready_pairs_start = SCHEME_CDR(ready_pairs_start);
8167               }
8168             }
8169           }
8170 	  irlv = (Scheme_IR_Let_Value *)irlv->body;
8171 	}
8172         /* Check flags loop: */
8173         flags = set_code_flags(retry_start, pre_body, clones, 0, 0xFFFF, 0, 0);
8174         /* Reset-flags loop: */
8175         (void)set_code_flags(retry_start, pre_body, clones,
8176                              (flags & (LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS)),
8177                              ~(LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS | LAMBDA_STATUS_MASK),
8178                              1,
8179                              1);
8180       }
8181       retry_start = NULL;
8182       ready_pairs_start = NULL;
8183       did_set_value = 0;
8184 
8185       while (prop_later) {
8186         value = SCHEME_CAR(prop_later);
8187         SCHEME_VAR(SCHEME_CAR(value))->optimize.known_val = SCHEME_CDR(value);
8188         prop_later = SCHEME_CDR(prop_later);
8189       }
8190     }
8191 
8192     if (is_rec) {
8193       /* Since letrec is really letrec*, the variables
8194          for this binding are now ready: */
8195       int i;
8196       for (i = pre_body->count; i--; ) {
8197         pre_body->vars[i]->optimize.init_kclock = rhs_info->kclock;
8198         if (!pre_body->vars[i]->mutated) {
8199           SCHEME_VAR(SCHEME_CAR(ready_pairs))->optimize_unready = 0;
8200           ready_pairs = SCHEME_CDR(ready_pairs);
8201         }
8202       }
8203     }
8204 
8205     if (remove_last_one) {
8206       head->num_clauses -= 1;
8207       body = (Scheme_Object *)pre_body->body;
8208 
8209       if (found_escapes && !SCHEME_FALSEP(pre_body->value)) {
8210         /* Since `pre_body->value` wasn't simplified to #f,
8211            keep this as the new body */
8212         escape_body = pre_body->value;
8213       }
8214 
8215       if (prev_body) {
8216         prev_body->body = body;
8217         pre_body = prev_body;
8218       } else {
8219         head->body = body;
8220         pre_body = NULL;
8221       }
8222       break;
8223     }
8224 
8225     prev_body = pre_body;
8226     body = pre_body->body;
8227   }
8228 
8229   if (!is_rec) {
8230     /* All `let`-bound variables are now allocated: */
8231     body = head->body;
8232     for (i = head->num_clauses; i--; ) {
8233       pre_body = (Scheme_IR_Let_Value *)body;
8234       for (j = pre_body->count; j--; ) {
8235         pre_body->vars[j]->optimize.init_kclock = body_info->kclock;
8236       }
8237       body = pre_body->body;
8238     }
8239   }
8240 
8241   optimize_info_seq_done(body_info, &info_seq);
8242 
8243   if (!found_escapes) {
8244     body = optimize_expr(body, body_info, scheme_optimize_tail_context(context));
8245   } else {
8246     body = ensure_noncm(escape_body, body_info);
8247     body_info->single_result = 1;
8248     body_info->preserves_marks = 1;
8249     body_info->escapes = 1;
8250     body_info->size++;
8251   }
8252   if (head->num_clauses)
8253     pre_body->body = body;
8254   else
8255     head->body = body;
8256 
8257   /* Propagate any use from formerly tentative uses: */
8258   while (1) {
8259     int changed = 0;
8260     body = head->body;
8261     for (i = head->num_clauses; i--; ) {
8262       pre_body = (Scheme_IR_Let_Value *)body;
8263       for (j = pre_body->count; j--; ) {
8264         if (pre_body->vars[j]->optimize_used
8265             && pre_body->vars[j]->optimize.transitive_uses) {
8266           register_transitive_uses(pre_body->vars[j], body_info);
8267           changed = 1;
8268           pre_body->vars[j]->optimize.transitive_uses = NULL;
8269         }
8270       }
8271       body = pre_body->body;
8272     }
8273     if (!changed)
8274       break;
8275   }
8276 
8277   info->single_result = body_info->single_result;
8278   info->preserves_marks = body_info->preserves_marks;
8279   info->vclock = body_info->vclock;
8280   info->aclock = body_info->aclock;
8281   info->kclock = body_info->kclock;
8282   info->sclock = body_info->sclock;
8283 
8284   /* Clear used flags where possible, clear once-used references, etc. */
8285   body = head->body;
8286   prev_body = NULL;
8287   for (i = head->num_clauses; i--; ) {
8288     int used = 0, j;
8289 
8290     pre_body = (Scheme_IR_Let_Value *)body;
8291 
8292     if (pre_body->count == 1) {
8293       /* If the right-hand side is a function, make sure all use sites
8294          are accounted for toward type inference of arguments. */
8295       if (pre_body->vars[0]->optimize.known_val
8296           && SAME_TYPE(SCHEME_TYPE(pre_body->vars[0]->optimize.known_val), scheme_lambda_type)) {
8297         check_lambda_arg_types_registered((Scheme_Lambda *)pre_body->vars[0]->optimize.known_val,
8298                                           pre_body->vars[0]->use_count);
8299       }
8300     }
8301 
8302     for (j = pre_body->count; j--; ) {
8303       if (pre_body->vars[j]->optimize_used) {
8304         used = 1;
8305         break;
8306       }
8307     }
8308 
8309     /* once-used moved implies not optimize_used: */
8310     MZ_ASSERT(!(used
8311                 && (pre_body->count == 1)
8312                 && pre_body->vars[0]->optimize.known_val
8313                 && SAME_TYPE(scheme_once_used_type, SCHEME_TYPE(pre_body->vars[0]->optimize.known_val))
8314                 && ((Scheme_Once_Used *)pre_body->vars[0]->optimize.known_val)->moved));
8315 
8316     if (!used
8317         && (scheme_omittable_expr(pre_body->value, pre_body->count, -1, 0, info, info)
8318             || ((pre_body->count == 1)
8319                 && pre_body->vars[0]->optimize.known_val
8320                 && SAME_TYPE(scheme_once_used_type, SCHEME_TYPE(pre_body->vars[0]->optimize.known_val))
8321                 && ((Scheme_Once_Used *)pre_body->vars[0]->optimize.known_val)->moved))) {
8322       /* Drop the binding(s) */
8323       for (j = pre_body->count; j--; ) {
8324         pre_body->vars[j]->mode = SCHEME_VAR_MODE_NONE;
8325       }
8326       head->num_clauses -= 1;
8327       head->count -= pre_body->count;
8328       if (prev_body)
8329         prev_body->body = pre_body->body;
8330       else
8331         head->body = pre_body->body;
8332       /* Deduct from size to aid further inlining. */
8333       {
8334         int sz;
8335         sz = expr_size(pre_body->value);
8336         body_info->size -= sz;
8337       }
8338     } else {
8339       if (!used && (pre_body->count == 1)) {
8340         /* The whole binding is not omittable, but maybe the tail is omittable: */
8341         Scheme_Object *v2 = pre_body->value, *inside;
8342         extract_tail_inside(&v2, &inside, 1);
8343         if (scheme_omittable_expr(v2, pre_body->count, -1, 0, info, info)) {
8344           replace_tail_inside(scheme_false, inside, pre_body->value);
8345         }
8346       }
8347 
8348       for (j = pre_body->count; j--; ) {
8349         int ct;
8350 
8351         pre_body->vars[j]->optimize_outside_binding = 1;
8352         if (pre_body->vars[j]->optimize.known_val
8353             && SAME_TYPE(scheme_once_used_type, SCHEME_TYPE(pre_body->vars[j]->optimize.known_val))) {
8354           /* We're keeping this clause here, so don't allow movement of the once-used
8355              value when peeking under bindings via extract_tail_inside(): */
8356           pre_body->vars[j]->optimize.known_val = NULL;
8357         }
8358 
8359         ct = pre_body->vars[j]->arg_type;
8360         if (ct) {
8361           if (ALWAYS_PREFER_UNBOX_TYPE(ct)
8362               || !pre_body->vars[j]->escapes_after_k_tick)
8363             pre_body->vars[j]->arg_type = ct;
8364         }
8365       }
8366       info->size += 1;
8367       prev_body = pre_body;
8368     }
8369     body = pre_body->body;
8370   }
8371 
8372   optimize_info_done(body_info, NULL);
8373   merge_types(body_info, info, merge_skip_vars);
8374 
8375   if (is_rec && !not_simply_let_star) {
8376     /* We can simplify letrec to let* */
8377     SCHEME_LET_FLAGS(head) -= SCHEME_LET_RECURSIVE;
8378     is_rec = 0;
8379     optimize_uses_of_mutable_imply_early_alloc((Scheme_IR_Let_Value *)head->body, head->num_clauses);
8380   }
8381 
8382   /* Optimized away all clauses? */
8383   if (!head->num_clauses) {
8384     return body;
8385   }
8386 
8387   if (!is_rec
8388       && ((SCHEME_TYPE(body) > _scheme_ir_values_types_)
8389           || SAME_TYPE(SCHEME_TYPE(body), scheme_ir_toplevel_type)
8390           || SAME_TYPE(SCHEME_TYPE(body), scheme_ir_local_type))) {
8391     /* If the body is a constant, toplevel or another local, the last binding
8392        is unused, so reduce (let ([x <expr>]) K) => (begin <expr> K).
8393        As a special case, include a second check for (let ([x E]) x) => E or (values E). */
8394     Scheme_Object *inside;
8395 
8396     inside = (Scheme_Object *)head;
8397     pre_body = (Scheme_IR_Let_Value *)head->body;
8398     for (i = head->num_clauses - 1; i--; ) {
8399       inside = (Scheme_Object *)pre_body;
8400       pre_body = (Scheme_IR_Let_Value *)pre_body->body;
8401     }
8402 
8403     if (pre_body->count == 1) {
8404       if (!SAME_OBJ((Scheme_Object *)pre_body->vars[0], body)
8405           && !found_escapes) {
8406         body = make_discarding_sequence(pre_body->value, body, info);
8407       } else {
8408         /* Special case for (let ([x E]) x) and (let ([x <error>]) #f) */
8409         body = pre_body->value;
8410         body = ensure_single_value_noncm(body, info);
8411         if (found_escapes) {
8412           found_escapes = 0; /* Perhaps the error is moved to the body. */
8413           body = ensure_noncm(body, info);
8414         }
8415       }
8416 
8417       if (head->num_clauses == 1)
8418         return body;
8419 
8420       (void)replace_tail_inside(body, inside, NULL);
8421       head->count--;
8422       head->num_clauses--;
8423     }
8424   }
8425 
8426   if (!is_rec) {
8427     /* One last pass to peel off unused bindings */
8428     Scheme_Object *prev = NULL, *rhs;
8429 
8430     body = head->body;
8431     for (i = head->num_clauses; i--; ) {
8432       pre_body = (Scheme_IR_Let_Value *)body;
8433       if ((pre_body->count == 1)
8434           && !pre_body->vars[0]->optimize_used) {
8435         Scheme_Sequence *seq;
8436         Scheme_Object *new_body;
8437 
8438         pre_body->vars[0]->mode = SCHEME_VAR_MODE_NONE;
8439 
8440         seq = scheme_malloc_sequence(2);
8441         seq->so.type = scheme_sequence_type;
8442         seq->count = 2;
8443 
8444         rhs = pre_body->value;
8445         rhs = ensure_single_value_noncm(rhs, info);
8446         seq->array[0] = rhs;
8447 
8448         head->count--;
8449         head->num_clauses--;
8450         head->body = pre_body->body;
8451 
8452         new_body = (Scheme_Object *)seq;
8453 
8454         if (head->num_clauses)
8455           seq->array[1] = (Scheme_Object *)head;
8456         else if (found_escapes && SCHEME_FALSEP(head->body)) {
8457           /* don't need the `#f` for the body, because some RHS escapes */
8458           new_body = ensure_noncm(rhs, info);
8459         } else
8460           seq->array[1] = head->body;
8461 
8462         if (prev)
8463           (void)replace_tail_inside(new_body, prev, NULL);
8464         else
8465           form = new_body;
8466         prev = new_body;
8467 
8468         body = pre_body->body;
8469       } else
8470         break;
8471     }
8472 
8473     if (prev && SAME_TYPE(SCHEME_TYPE(prev), scheme_sequence_type))
8474       form = optimize_sequence(form, info, context, 0);
8475   }
8476 
8477   if (!is_rec && info->unsafe_mode) {
8478     /* Peel zero-binding clauses off the end in unsafe mode? */
8479     if (SAME_TYPE(SCHEME_TYPE(form), scheme_ir_let_header_type)) {
8480       int i, c, n;
8481       head = (Scheme_IR_Let_Header *)form;
8482       c = head->num_clauses;
8483       n = head->count;
8484       prev_body = NULL;
8485       body = head->body;
8486       for (i = 0; i < c; i++) {
8487         if (!n) {
8488           /* We've seen as many bindings as exist, to the rest
8489              must be clauses with zero bindings */
8490           body = convert_leading_zero_bindings_to_begin(NULL, body, c - i);
8491           if (prev_body) {
8492             prev_body->body = body;
8493             head->num_clauses = i;
8494           } else
8495             form = body;
8496           break;
8497         } else {
8498           irlv = (Scheme_IR_Let_Value *)body;
8499           n -= irlv->count;
8500           prev_body = irlv;
8501           body = irlv->body;
8502         }
8503       }
8504     }
8505   }
8506 
8507   return form;
8508 }
8509 
8510 /*========================================================================*/
8511 /*                               lambda                                   */
8512 /*========================================================================*/
8513 
8514 static Scheme_Object *
optimize_lambda(Scheme_Object * _lam,Optimize_Info * info,int context)8515 optimize_lambda(Scheme_Object *_lam, Optimize_Info *info, int context)
8516 {
8517   Scheme_Lambda *lam;
8518   Scheme_Object *code, *ctx, *to_remove;
8519   Scheme_IR_Lambda_Info *cl;
8520   int i, init_vclock, init_aclock, init_kclock, init_sclock;
8521   Scheme_Hash_Table *ht;
8522   int app_count = OPT_CONTEXT_APP_COUNT(context);
8523 
8524   lam = (Scheme_Lambda *)_lam;
8525 
8526   info->single_result = 1;
8527   info->preserves_marks = 1;
8528 
8529   info = optimize_info_add_frame(info, SCHEME_LAMBDA_FRAME);
8530 
8531   ht = scheme_make_hash_table(SCHEME_hash_ptr);
8532   info->uses = ht;
8533 
8534   init_vclock = info->vclock;
8535   init_aclock = info->aclock;
8536   init_kclock = info->kclock;
8537   init_sclock = info->sclock;
8538 
8539   info->vclock += 1; /* model delayed evaluation as vclock increment */
8540   info->kclock += 1;
8541   info->sclock += 1;
8542 
8543   /* For reporting warnings: */
8544   if (info->context && SCHEME_PAIRP(info->context))
8545     ctx = scheme_make_pair((Scheme_Object *)lam,
8546                            SCHEME_CDR(info->context));
8547   else if (info->context)
8548     ctx = scheme_make_pair((Scheme_Object *)lam, info->context);
8549   else
8550     ctx = (Scheme_Object *)lam;
8551   info->context = ctx;
8552 
8553   cl = lam->ir_info;
8554   for (i = 0; i < lam->num_params; i++) {
8555     set_optimize_mode(cl->vars[i]);
8556     cl->vars[i]->optimize.lambda_depth = info->lambda_depth;
8557     cl->vars[i]->optimize_used = 0;
8558     cl->vars[i]->optimize.init_kclock = info->kclock;
8559     if (app_count
8560         && (app_count < SCHEME_USE_COUNT_INF)
8561         && cl->arg_types
8562         && cl->arg_types[i]
8563         && (cl->arg_type_contributors[i] == ((1 << app_count) - 1))) {
8564       /* All uses accounted for, so we can rely on type info */
8565       add_type(info, (Scheme_Object *)cl->vars[i], cl->arg_types[i]);
8566     }
8567   }
8568 
8569   code = optimize_expr(lam->body, info, 0);
8570 
8571   propagate_used_variables(info);
8572 
8573   if (info->single_result)
8574     SCHEME_LAMBDA_FLAGS(lam) |= LAMBDA_SINGLE_RESULT;
8575   else if (SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_SINGLE_RESULT)
8576     SCHEME_LAMBDA_FLAGS(lam) -= LAMBDA_SINGLE_RESULT;
8577 
8578   if (info->preserves_marks)
8579     SCHEME_LAMBDA_FLAGS(lam) |= LAMBDA_PRESERVES_MARKS;
8580   else if (SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_PRESERVES_MARKS)
8581     SCHEME_LAMBDA_FLAGS(lam) -= LAMBDA_PRESERVES_MARKS;
8582 
8583   if ((info->single_result > 0) && (info->preserves_marks > 0)
8584       && ((SCHEME_LAMBDA_FLAGS(lam) & LAMBDA_STATUS_MASK) == LAMBDA_STATUS_RESULT_TENTATIVE))
8585     SCHEME_LAMBDA_FLAGS(lam) -= LAMBDA_STATUS_RESULT_TENTATIVE;
8586 
8587   lam->body = code;
8588 
8589   /* Double check that variables registered for the closure are marked
8590      as used. Although the resolve pass double-checks use flags, we
8591      need to remove any variable that was tentaively marked as used,
8592      because it's non-use may turn a `letrec` into a `let`, and the
8593      `let`-bound variable may be later used after all --- after the
8594      `letrec`->`let` conversion is decided. In other words, ensure
8595      that the closure's free variables are consistent with any
8596      `letrec`->`let` decisions when the `lambda` appear on the
8597      right-hand side of a binding. */
8598   to_remove = scheme_null;
8599   for (i = 0; i < ht->size; i++) {
8600     if (ht->vals[i]) {
8601       Scheme_IR_Local *var = SCHEME_VAR(ht->keys[i]);
8602       if (!var->optimize_used) {
8603         /* Must have been tentively used, but not used after all. */
8604         to_remove = scheme_make_pair((Scheme_Object *)var, to_remove);
8605       }
8606     }
8607   }
8608   while (SCHEME_PAIRP(to_remove)) {
8609     scheme_hash_set(ht, SCHEME_CAR(to_remove), NULL);
8610     to_remove = SCHEME_CDR(to_remove);
8611   }
8612 
8613   /* Remembers positions of used vars (and unsets usage for this level) */
8614   cl->base_closure = info->uses;
8615   if (env_uses_toplevel(info))
8616     cl->has_tl = 1;
8617   else
8618     cl->has_tl = 0;
8619   cl->body_size = info->size;
8620   cl->body_psize = info->psize;
8621   cl->has_nonleaf = info->has_nonleaf;
8622 
8623   /* closure itself is not an effect */
8624   info->vclock = init_vclock;
8625   info->aclock = init_aclock;
8626   info->kclock = init_kclock;
8627   info->sclock = init_sclock;
8628   info->escapes = 0;
8629 
8630   info->size++;
8631 
8632   lam->closure_size = (cl->base_closure->count
8633                        + (cl->has_tl ? 1 : 0));
8634 
8635   optimize_info_done(info, NULL);
8636 
8637   return (Scheme_Object *)lam;
8638 }
8639 
merge_lambda_arg_types(Scheme_Lambda * lam1,Scheme_Lambda * lam2)8640 static void merge_lambda_arg_types(Scheme_Lambda *lam1, Scheme_Lambda *lam2)
8641 {
8642   Scheme_IR_Lambda_Info *cl1 = lam1->ir_info;
8643   Scheme_IR_Lambda_Info *cl2 = lam2->ir_info;
8644   int i;
8645 
8646   if (!cl1->arg_types) {
8647     if (cl2->arg_types) {
8648       cl1->arg_types = cl2->arg_types;
8649       cl1->arg_type_contributors = cl2->arg_type_contributors;
8650     }
8651   } else {
8652     if (cl2->arg_types) {
8653       for (i = lam1->num_params; i--; ) {
8654         if (!cl1->arg_type_contributors[i]) {
8655           cl1->arg_types[i] = cl2->arg_types[i];
8656           cl1->arg_type_contributors[i] = cl2->arg_type_contributors[i];
8657         } else if (cl2->arg_type_contributors[i]) {
8658           if (!cl2->arg_types[i])
8659             cl1->arg_types[i] = NULL;
8660           else if (predicate_implies(cl1->arg_types[i], cl2->arg_types[i]))
8661             cl1->arg_types[i] = cl2->arg_types[i];
8662           else if (!predicate_implies(cl2->arg_types[i], cl1->arg_types[i])) {
8663             cl1->arg_types[i] = NULL;
8664             cl1->arg_type_contributors[i] |= (1 << (SCHEME_USE_COUNT_INF-1));
8665           }
8666           cl1->arg_type_contributors[i] |= cl2->arg_type_contributors[i];
8667         }
8668       }
8669     }
8670 
8671     cl2->arg_types = cl1->arg_types;
8672     cl2->arg_type_contributors = cl1->arg_type_contributors;
8673   }
8674 }
8675 
check_lambda_arg_types_registered(Scheme_Lambda * lam,int app_count)8676 static void check_lambda_arg_types_registered(Scheme_Lambda *lam, int app_count)
8677 {
8678   if (lam->ir_info->arg_types) {
8679     int i;
8680     for (i = lam->num_params; i--; ) {
8681       if (lam->ir_info->arg_types[i]) {
8682         if ((lam->ir_info->arg_type_contributors[i] & (1 << (SCHEME_USE_COUNT_INF-1)))
8683             || (lam->ir_info->arg_type_contributors[i] < ((1 << app_count) - 1))) {
8684           /* someone caller didn't weigh in with a type,
8685              of an anonymous caller had no type to record */
8686           lam->ir_info->arg_types[i] = NULL;
8687         }
8688       }
8689     }
8690   }
8691 }
8692 
clone_variable(Scheme_IR_Local * var)8693 static Scheme_IR_Local *clone_variable(Scheme_IR_Local *var)
8694 {
8695   Scheme_IR_Local *var2;
8696   MZ_ASSERT(SAME_TYPE(var->so.type, scheme_ir_local_type));
8697   var2 = MALLOC_ONE_TAGGED(Scheme_IR_Local);
8698   memcpy(var2, var, sizeof(Scheme_IR_Local));
8699   scheme_set_distinct_eq_hash((Scheme_Object *)var2);
8700   return var2;
8701 }
8702 
clone_variable_array(Scheme_IR_Local ** vars,int sz,Scheme_Hash_Tree ** _var_map)8703 static Scheme_IR_Local **clone_variable_array(Scheme_IR_Local **vars,
8704                                               int sz,
8705                                               Scheme_Hash_Tree **_var_map)
8706 {
8707   Scheme_IR_Local **new_vars, *var;
8708   Scheme_Hash_Tree *var_map = *_var_map;
8709   int j;
8710 
8711   new_vars = MALLOC_N(Scheme_IR_Local*, sz);
8712   for (j = sz; j--; ) {
8713     var = clone_variable(vars[j]);
8714     var->mode = SCHEME_VAR_MODE_NONE;
8715     new_vars[j] = var;
8716     var_map = scheme_hash_tree_set(var_map, (Scheme_Object *)vars[j], (Scheme_Object *)new_vars[j]);
8717   }
8718 
8719   *_var_map = var_map;
8720   return new_vars;
8721 }
8722 
clone_lambda(int single_use,Scheme_Object * _lam,Optimize_Info * info,Scheme_Hash_Tree * var_map)8723 static Scheme_Object *clone_lambda(int single_use, Scheme_Object *_lam, Optimize_Info *info, Scheme_Hash_Tree *var_map)
8724 {
8725   Scheme_Lambda *lam, *lam2;
8726   Scheme_Object *body, *var;
8727   Scheme_Hash_Table *ht;
8728   Scheme_IR_Lambda_Info *cl;
8729   Scheme_IR_Local **vars;
8730   int sz;
8731   Scheme_Object **arg_types;
8732   short *arg_type_contributors;
8733 
8734   lam = (Scheme_Lambda *)_lam;
8735 
8736   lam2 = MALLOC_ONE_TAGGED(Scheme_Lambda);
8737   memcpy(lam2, lam, sizeof(Scheme_Lambda));
8738 
8739   cl = MALLOC_ONE_RT(Scheme_IR_Lambda_Info);
8740   memcpy(cl, lam->ir_info, sizeof(Scheme_IR_Lambda_Info));
8741   lam2->ir_info = cl;
8742 
8743   vars = clone_variable_array(cl->vars, lam2->num_params, &var_map);
8744   cl->vars = vars;
8745 
8746   cl->is_dup |= !single_use;
8747 
8748   body = optimize_clone(single_use, lam->body, info, var_map, 0);
8749   if (!body) return NULL;
8750 
8751   lam2->body = body;
8752 
8753   if (cl->arg_types) {
8754     sz = lam2->num_params;
8755     arg_types = MALLOC_N(Scheme_Object*, sz);
8756     arg_type_contributors = MALLOC_N_ATOMIC(short, sz);
8757     memcpy(arg_types, cl->arg_types, sz * sizeof(Scheme_Object*));
8758     memcpy(arg_type_contributors, cl->arg_type_contributors, sz * sizeof(short));
8759     cl->arg_types = arg_types;
8760     cl->arg_type_contributors = arg_type_contributors;
8761   }
8762 
8763   if (cl->base_closure && var_map->count) {
8764     int i;
8765     ht = scheme_make_hash_table(SCHEME_hash_ptr);
8766     for (i = 0; i < cl->base_closure->size; i++) {
8767       if (cl->base_closure->vals[i]) {
8768         var = scheme_eq_hash_tree_get(var_map, cl->base_closure->keys[i]);
8769         scheme_hash_set(ht,
8770                         (var
8771                          ? var
8772                          : cl->base_closure->keys[i]),
8773                         cl->base_closure->vals[i]);
8774       }
8775     }
8776     cl->base_closure = ht;
8777   }
8778 
8779   return (Scheme_Object *)lam2;
8780 }
8781 
lambda_body_size_plus_info(Scheme_Lambda * lam,int check_assign,Optimize_Info * info,int * is_leaf)8782 static int lambda_body_size_plus_info(Scheme_Lambda *lam, int check_assign,
8783                                       Optimize_Info *info, int *is_leaf)
8784 {
8785   int i;
8786   Scheme_IR_Lambda_Info *cl;
8787 
8788   cl = lam->ir_info;
8789 
8790   if (check_assign) {
8791     /* Don't try to inline if any arguments are mutated: */
8792     for (i = lam->num_params; i--; ) {
8793       if (cl->vars[i]->mutated)
8794 	return -1;
8795     }
8796   }
8797 
8798   if (is_leaf)
8799     *is_leaf = !cl->has_nonleaf;
8800 
8801   return cl->body_size + ((info && info->use_psize) ? cl->body_psize : 0);
8802 }
8803 
lambda_has_top_level(Scheme_Lambda * lam)8804 static int lambda_has_top_level(Scheme_Lambda *lam)
8805 {
8806   return lam->ir_info->has_tl;
8807 }
8808 
8809 /*========================================================================*/
8810 /*                              linklets                                   */
8811 /*========================================================================*/
8812 
set_code_closure_flags(Scheme_Object * clones,int set_flags,int mask_flags,int just_tentative)8813 static int set_code_closure_flags(Scheme_Object *clones,
8814                                   int set_flags, int mask_flags,
8815                                   int just_tentative)
8816 {
8817   Scheme_Object *clone, *orig, *first;
8818   int flags = LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS;
8819 
8820   /* The first in a clone pair is the one that is consulted for
8821      references. The second one is the original, and its the one whose
8822      flags are updated by optimization. So consult the original, and set
8823      flags in both. */
8824 
8825   while (clones) {
8826     first = SCHEME_CAR(clones);
8827     clone = SCHEME_CAR(first);
8828     orig = SCHEME_CDR(first);
8829 
8830     flags = set_one_code_flags(orig, flags,
8831                                orig, clone,
8832                                set_flags, mask_flags, just_tentative,
8833                                0);
8834 
8835     clones = SCHEME_CDR(clones);
8836   }
8837 
8838   return flags;
8839 }
8840 
is_cross_linklet_inline_candidiate(Scheme_Object * e,Optimize_Info * info,int size_override)8841 static Scheme_Object *is_cross_linklet_inline_candidiate(Scheme_Object *e, Optimize_Info *info,
8842                                                         int size_override)
8843 {
8844   if (SCHEME_LAMBDAP(e)) {
8845     if (size_override || (lambda_body_size(e, 1) < CROSS_LINKLET_INLINE_SIZE))
8846       return optimize_clone(0, e, info, empty_eq_hash_tree, 0);
8847   }
8848 
8849   return NULL;
8850 }
8851 
is_general_lambda(Scheme_Object * e,Optimize_Info * info)8852 static int is_general_lambda(Scheme_Object *e, Optimize_Info *info)
8853 {
8854   /* recognize (begin <omitable>* <proc>) */
8855   if (SCHEME_TYPE(e) == scheme_sequence_type) {
8856     Scheme_Sequence *seq = (Scheme_Sequence *)e;
8857     if (seq->count > 0) {
8858       int i;
8859       for (i = seq->count - 1; i--; ) {
8860         if (!scheme_omittable_expr(seq->array[i], -1, 20, 0, info, NULL))
8861           return 0;
8862       }
8863     }
8864     e = seq->array[seq->count - 1];
8865   }
8866 
8867   /* recognize (let ([x <proc>]) x) */
8868   if (SCHEME_TYPE(e) == scheme_ir_let_header_type) {
8869     Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)e;
8870     if (!(SCHEME_LET_FLAGS(lh) & SCHEME_LET_RECURSIVE)
8871         && (lh->count == 1)
8872         && (lh->num_clauses == 1)
8873         && SAME_TYPE(SCHEME_TYPE(lh->body), scheme_ir_let_value_type)) {
8874       Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)lh->body;
8875       if (SCHEME_LAMBDAP(lv->value))
8876         return SAME_OBJ(lv->body, (Scheme_Object *)lv->vars[0]);
8877     }
8878   }
8879 
8880   if (SCHEME_LAMBDAP(e))
8881     return 1;
8882 
8883   return 0;
8884 }
8885 
install_definition(Scheme_Object * bodies,int pos,Scheme_Object * old_defn,int name_pos,Scheme_Object * rhs)8886 void install_definition(Scheme_Object *bodies, int pos, Scheme_Object *old_defn, int name_pos, Scheme_Object *rhs)
8887 {
8888   Scheme_Object *def;
8889 
8890   def = scheme_make_vector(2, NULL);
8891   SCHEME_DEFN_RHS(def) = rhs;
8892   SCHEME_DEFN_VAR_(def, 0) = SCHEME_DEFN_VAR_(old_defn, name_pos);
8893   def->type = scheme_define_values_type;
8894 
8895   SCHEME_VEC_ELS(bodies)[pos] = def;
8896 }
8897 
split_define_values(Scheme_Object * defn,int n,Scheme_Object * bodies,int offset)8898 int split_define_values(Scheme_Object *defn, int n, Scheme_Object *bodies, int offset)
8899 {
8900   Scheme_Object *e = SCHEME_DEFN_RHS(defn);
8901 
8902   if (SAME_TYPE(SCHEME_TYPE(e), scheme_ir_let_header_type)) {
8903     /* This is a tedious case to recognize the pattern
8904          (let ([x rhs] ...) (values x ...))
8905        which might be the result of expansion that involved a local
8906        macro to define the `x's */
8907     Scheme_IR_Let_Header *lh = (Scheme_IR_Let_Header *)e;
8908     if ((lh->count == n) && (lh->num_clauses == n)
8909         && !(SCHEME_LET_FLAGS(lh) & SCHEME_LET_RECURSIVE)) {
8910       Scheme_Object *body = lh->body;
8911       int i;
8912       for (i = 0; i < n; i++) {
8913         if (SAME_TYPE(SCHEME_TYPE(body), scheme_ir_let_value_type)) {
8914           Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)body;
8915           if (lv->count == 1) {
8916             if (!scheme_omittable_expr(lv->value, 1, 5, 0, NULL, NULL))
8917               return 0;
8918             body = lv->body;
8919           } else
8920             return 0;
8921         } else
8922           return 0;
8923       }
8924       if ((n == 2) && SAME_TYPE(SCHEME_TYPE(body), scheme_application3_type)) {
8925         Scheme_App3_Rec *app = (Scheme_App3_Rec *)body;
8926         Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)lh->body;
8927         if (SAME_OBJ(app->rator, scheme_values_proc)
8928             && SAME_OBJ(app->rand1, (Scheme_Object *)lv->vars[0])
8929             && SAME_OBJ(app->rand2, (Scheme_Object *)((Scheme_IR_Let_Value *)lv->body)->vars[0])) {
8930           if (bodies) {
8931             install_definition(bodies, offset, defn, 0, lv->value);
8932             lv = (Scheme_IR_Let_Value *)lv->body;
8933             install_definition(bodies, offset+1, defn, 1, lv->value);
8934           }
8935           return 1;
8936         }
8937       } else if (SAME_TYPE(SCHEME_TYPE(body), scheme_application_type)
8938                  && ((Scheme_App_Rec *)body)->num_args == n) {
8939         Scheme_App_Rec *app = (Scheme_App_Rec *)body;
8940         Scheme_IR_Let_Value *lv = (Scheme_IR_Let_Value *)lh->body;
8941         if (SAME_OBJ(app->args[0], scheme_values_proc)) {
8942           for (i = 0; i < n; i++) {
8943             if (!SAME_TYPE(SCHEME_TYPE(app->args[i+1]), scheme_ir_local_type)
8944                 || !SAME_OBJ((Scheme_Object *)lv->vars[0], app->args[i+1]))
8945               return 0;
8946             lv = (Scheme_IR_Let_Value *)lv->body;
8947           }
8948           if (bodies) {
8949             body = lh->body;
8950             for (i = 0; i < n; i++) {
8951               Scheme_IR_Let_Value *lv2 = (Scheme_IR_Let_Value *)body;
8952               install_definition(bodies, offset+i, defn, i, lv2->value);
8953               body = lv2->body;
8954             }
8955           }
8956           return 1;
8957         }
8958       }
8959     }
8960   } else if ((n == 2) && SAME_TYPE(SCHEME_TYPE(e), scheme_application3_type)) {
8961     Scheme_App3_Rec *app = (Scheme_App3_Rec *)e;
8962     if (SAME_OBJ(app->rator, scheme_values_proc)
8963         && scheme_omittable_expr(app->rand1, 1, 5, 0, NULL, NULL)
8964         && scheme_omittable_expr(app->rand2, 1, 5, 0, NULL, NULL)) {
8965       if (bodies) {
8966         install_definition(bodies, offset, defn, 0, app->rand1);
8967         install_definition(bodies, offset+1, defn, 1, app->rand2);
8968       }
8969       return 1;
8970     }
8971   } else if (SAME_TYPE(SCHEME_TYPE(e), scheme_application_type)
8972              && ((Scheme_App_Rec *)e)->num_args == n) {
8973     Scheme_App_Rec *app = (Scheme_App_Rec *)e;
8974     if (SAME_OBJ(app->args[0], scheme_values_proc)) {
8975       int i;
8976       for (i = 0; i < n; i++) {
8977         if (!scheme_omittable_expr(app->args[i+1], 1, 5, 0, NULL, NULL))
8978           return 0;
8979       }
8980       if (bodies) {
8981         for (i = 0; i < n; i++) {
8982           install_definition(bodies, offset+i, defn, i, app->args[i+1]);
8983         }
8984       }
8985       return 1;
8986     }
8987   }
8988 
8989   return 0;
8990 }
8991 
set_as_fixed(Scheme_Hash_Table * fixed_table,Optimize_Info * info,int pos)8992 static Scheme_Hash_Table *set_as_fixed(Scheme_Hash_Table *fixed_table, Optimize_Info *info, int pos)
8993 {
8994   if (!fixed_table) {
8995     fixed_table = scheme_make_hash_table(SCHEME_hash_ptr);
8996     if (!info->top_level_consts) {
8997       Scheme_Hash_Table *consts;
8998       consts = scheme_make_hash_table(SCHEME_hash_ptr);
8999       info->top_level_consts = consts;
9000     }
9001     scheme_hash_set(info->top_level_consts, scheme_false, (Scheme_Object *)fixed_table);
9002   }
9003 
9004   scheme_hash_set(fixed_table, scheme_make_integer(pos), scheme_true);
9005 
9006   return fixed_table;
9007 }
9008 
scheme_optimize_linklet(Scheme_Linklet * linklet,int enforce_const,int can_inline,int unsafe_mode,Scheme_Object ** _import_keys,Scheme_Object * get_import)9009 Scheme_Linklet *scheme_optimize_linklet(Scheme_Linklet *linklet,
9010                                         int enforce_const, int can_inline, int unsafe_mode,
9011                                         Scheme_Object **_import_keys, Scheme_Object *get_import)
9012 {
9013   Scheme_Object *e;
9014   int start_simultaneous = 0, i_m, cnt;
9015   Scheme_Object *cl_first = NULL, *cl_last = NULL;
9016   Scheme_Hash_Table *consts = NULL, *fixed_table = NULL, *re_consts = NULL;
9017   Scheme_Hash_Table *originals = NULL;
9018   int cont, inline_fuel, is_proc_def, any_defns = 0;
9019   Optimize_Info *info;
9020   Optimize_Info *limited_info;
9021   Optimize_Info_Sequence info_seq;
9022   Scheme_Hash_Tree **iu;
9023   /* For now, treat unsafe mode as a hint that cooperation with the validator
9024      is not needed. We may eventually give up on the validator completely. */
9025   int support_validation = !unsafe_mode;
9026 
9027   info = optimize_info_create(linklet, enforce_const, can_inline, unsafe_mode);
9028   info->context = (Scheme_Object *)linklet;
9029 
9030   /* Less inlining for a large module: */
9031   if (SCHEME_VEC_SIZE(linklet->bodies) > 128)
9032     info->inline_fuel >>= 1;
9033 
9034   if (_import_keys) {
9035     Cross_Linklet_Info *cross;
9036     Scheme_Hash_Tree *ht;
9037     int i;
9038 
9039     iu = MALLOC_N(Scheme_Hash_Tree*, 1);
9040     *iu = empty_eq_hash_tree;
9041     info->imports_used = iu;
9042 
9043     cross = (Cross_Linklet_Info *)scheme_malloc(sizeof(Cross_Linklet_Info));
9044     info->cross = cross;
9045 
9046     cross->get_import = get_import;
9047 
9048     cross->import_keys = empty_eq_hash_tree;
9049     cross->rev_import_keys = empty_eq_hash_tree;
9050     for (i = 0; i < SCHEME_VEC_SIZE(*_import_keys); i++) {
9051       ht = scheme_hash_tree_set(cross->import_keys,
9052                                 scheme_make_integer(i),
9053                                 SCHEME_VEC_ELS(*_import_keys)[i]);
9054       cross->import_keys = ht;
9055       ht = scheme_hash_tree_set(cross->rev_import_keys,
9056                                 SCHEME_VEC_ELS(*_import_keys)[i],
9057                                 scheme_make_integer(i));
9058       cross->rev_import_keys = ht;
9059     }
9060     cross->linklets = empty_eq_hash_tree;
9061     cross->import_next_keys = empty_eq_hash_tree;
9062     cross->inline_variants = empty_eq_hash_tree;
9063     cross->import_syms = empty_eq_hash_tree;
9064   }
9065 
9066   optimize_info_seq_init(info, &info_seq);
9067 
9068   cnt = SCHEME_VEC_SIZE(linklet->bodies);
9069 
9070   /* First, flatten `(define-values (x ...) (values e ...))'
9071      to `(define (x) e) ...' when possible. */
9072   {
9073     int inc = 0;
9074     for (i_m = 0; i_m < cnt; i_m++) {
9075       e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9076       if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type))  {
9077         int n;
9078         n = SCHEME_DEFN_VAR_COUNT(e);
9079         if (n > 1) {
9080           if (split_define_values(e, n, NULL, 0))
9081             inc += (n - 1);
9082         }
9083         any_defns = 1;
9084       }
9085     }
9086 
9087     if (inc > 0) {
9088       Scheme_Object *new_bodies;
9089       int j = 0;
9090       new_bodies = scheme_make_vector(cnt+inc, scheme_false);
9091       for (i_m = 0; i_m < cnt; i_m++) {
9092         e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9093         if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type)) {
9094           int n;
9095           n = SCHEME_DEFN_VAR_COUNT(e);
9096           if (n > 1) {
9097             if (split_define_values(e, n, new_bodies, j)) {
9098               j += n;
9099             } else
9100               SCHEME_VEC_ELS(new_bodies)[j++] = e;
9101           } else
9102             SCHEME_VEC_ELS(new_bodies)[j++] = e;
9103         } else
9104           SCHEME_VEC_ELS(new_bodies)[j++] = e;
9105       }
9106       cnt += inc;
9107       linklet->bodies = new_bodies;
9108     }
9109   }
9110 
9111   if (any_defns) {
9112     /* Use `limited_info` for optimization decisions that need to be
9113        rediscovered by the validator. The validator knows shape
9114        information for imported variables, and it knows about structure
9115        bindings for later forms. */
9116     limited_info = MALLOC_ONE_RT(Optimize_Info);
9117 #ifdef MZTAG_REQUIRED
9118     limited_info->type = scheme_rt_optimize_info;
9119 #endif
9120     limited_info->linklet = info->linklet;
9121   } else
9122     limited_info = NULL;
9123 
9124   if (OPT_ESTIMATE_FUTURE_SIZES && any_defns) {
9125     if (info->enforce_const) {
9126       /* For each identifier bound to a procedure, register an initial
9127          size estimate, which is used to discourage early loop unrolling
9128          at the expense of later inlining. */
9129       for (i_m = 0; i_m < cnt; i_m++) {
9130         e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9131         if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type))  {
9132           int n;
9133 
9134           n = SCHEME_DEFN_VAR_COUNT(e);
9135           if ((n == 1) && SCHEME_LAMBDAP(SCHEME_DEFN_RHS(e)))  {
9136             Scheme_IR_Toplevel *var = SCHEME_DEFN_VAR(e, 0);
9137 
9138             if (!(SCHEME_IR_TOPLEVEL_FLAGS(var) & SCHEME_IR_TOPLEVEL_MUTATED)) {
9139               if (!consts)
9140                 consts = scheme_make_hash_table(SCHEME_hash_ptr);
9141               scheme_hash_set(consts, scheme_make_integer(var->variable_pos), estimate_closure_size(e));
9142             }
9143           }
9144         }
9145       }
9146 
9147       if (consts) {
9148         info->top_level_consts = consts;
9149         consts = NULL;
9150       }
9151     }
9152   }
9153 
9154   for (i_m = 0; i_m < cnt; i_m++) {
9155     /* Optimize this expression: */
9156     e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9157 
9158     is_proc_def = 0;
9159     if (OPT_DISCOURAGE_EARLY_INLINE && info->enforce_const) {
9160       if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type)) {
9161         Scheme_Object *e2;
9162         e2 = SCHEME_DEFN_RHS(e);
9163         if (is_general_lambda(e2, info))
9164           is_proc_def = 1;
9165       }
9166     }
9167 
9168     inline_fuel = info->inline_fuel;
9169     if (is_proc_def && OPT_DISCOURAGE_EARLY_INLINE) {
9170       info->use_psize = 1;
9171       if (inline_fuel > 2)
9172         info->inline_fuel = 2;
9173     }
9174     optimize_info_seq_step(info, &info_seq);
9175     e = optimize_expr(e, info, 0);
9176     if (is_proc_def && OPT_DISCOURAGE_EARLY_INLINE) {
9177       info->use_psize = 0;
9178     }
9179     info->inline_fuel = inline_fuel;
9180     SCHEME_VEC_ELS(linklet->bodies)[i_m] = e;
9181 
9182     if (info->enforce_const) {
9183       /* If this expression/definition can't have any side effect
9184 	 (including raising an exception), then continue the group of
9185 	 simultaneous definitions: */
9186       if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type)) {
9187 	int n, cnst = 0, sproc = 0, sprop = 0, has_guard = 0;
9188         Scheme_Object *sstruct = NULL, *parent_identity = NULL;
9189         Simple_Struct_Type_Info stinfo;
9190         Scheme_Object *defn = e;
9191 
9192         n = SCHEME_DEFN_VAR_COUNT(defn);
9193 	e = SCHEME_DEFN_RHS(defn);
9194 
9195         if (support_validation)
9196           limited_info->cross = info->cross;
9197 	cont = scheme_omittable_expr(e, n, -1,
9198                                      (support_validation
9199                                       /* ignore APPN_FLAG_OMITTABLE, because the
9200                                          validator won't be able to reconstruct it
9201                                          in general; also, don't recognize struct-type
9202                                          functions, since they weren't recognized
9203                                          as immediate calls */
9204                                       ? (OMITTABLE_IGNORE_APPN_OMIT
9205                                          | OMITTABLE_IGNORE_MAKE_STRUCT_TYPE)
9206                                       : 0),
9207                                      /* similarly, use `limited_info` instead of `info'
9208                                         here, because the decision
9209                                         of omittable should not depend on
9210                                         information that's only available at
9211                                         optimization time: */
9212                                      (support_validation ? limited_info : info),
9213                                      info);
9214         if (support_validation)
9215           info->cross = limited_info->cross;
9216 
9217         if (n == 1) {
9218           if (ir_propagate_ok(e, info, 0, NULL))
9219             cnst = 1;
9220           else if (scheme_is_statically_proc(e, info, OMITTABLE_IGNORE_APPN_OMIT)) {
9221             cnst = 1;
9222             sproc = 1;
9223           }
9224         } else if (scheme_is_simple_make_struct_type(e, n, 0, NULL,
9225                                                      &stinfo, &parent_identity,
9226                                                      info,
9227                                                      NULL, NULL, 0, NULL,
9228                                                      &sstruct,
9229                                                      5)) {
9230           sstruct = scheme_make_pair(sstruct, parent_identity);
9231           cnst = 1;
9232         } else if (scheme_is_simple_make_struct_type_property(e, n, 0,
9233                                                               &has_guard,
9234                                                               info,
9235                                                               NULL, NULL, 0, NULL,
9236                                                               5)) {
9237           sprop = 1;
9238           cnst = 1;
9239         } else
9240           sstruct = NULL;
9241 
9242         if (support_validation && (sstruct || sprop) && !cont) {
9243           /* Since the `make-struct-type` or `make-struct-tye-property` form is immediate
9244              enough that the validator can see it, re-check whether we can continue
9245              a group of simultaneously defined variables. */
9246           cont = scheme_omittable_expr(e, n, 5, OMITTABLE_IGNORE_APPN_OMIT, limited_info, NULL);
9247         }
9248 
9249         if (cont) {
9250           /* Record for the resolve pass's pruning that definition is omittable */
9251           SCHEME_SET_DEFN_CAN_OMIT(defn);
9252         }
9253 
9254 	if (cnst) {
9255 	  Scheme_IR_Toplevel *var;
9256           int i;
9257           for (i = 0; i < n; i++) {
9258             var = SCHEME_DEFN_VAR(defn, i);
9259 
9260             if (!(SCHEME_IR_TOPLEVEL_FLAGS(var) & SCHEME_IR_TOPLEVEL_MUTATED)) {
9261               Scheme_Object *e2;
9262 
9263               if (sstruct) {
9264                 e2 = scheme_make_struct_proc_shape(scheme_get_struct_proc_shape(i, &stinfo),
9265                                                    sstruct);
9266               } else if (sprop) {
9267                 e2 = scheme_make_struct_property_proc_shape(scheme_get_struct_property_proc_shape(i, has_guard));
9268               } else if (sproc) {
9269                 e2 = scheme_make_noninline_proc(e);
9270               } else if (SCHEME_LAMBDAP(e)) {
9271                 e2 = optimize_clone(1, e, info, empty_eq_hash_tree, 0);
9272                 if (e2) {
9273                   Scheme_Object *pr;
9274                   pr = scheme_make_raw_pair(scheme_make_raw_pair(e2, e), NULL);
9275                   if (cl_last)
9276                     SCHEME_CDR(cl_last) = pr;
9277                   else
9278                     cl_first = pr;
9279                   cl_last = pr;
9280                 } else
9281                   e2 = scheme_make_noninline_proc(e);
9282               } else {
9283                 e2 = e;
9284               }
9285 
9286               if (e2) {
9287                 consts = info->top_level_consts;
9288                 if (!consts) {
9289                   consts = scheme_make_hash_table(SCHEME_hash_ptr);
9290                   info->top_level_consts = consts;
9291                 }
9292                 scheme_hash_set(consts, scheme_make_integer(var->variable_pos), e2);
9293 
9294                 if (sstruct || sprop) {
9295                   /* include in `limited_info` */
9296                   Scheme_Hash_Table *limited_consts = limited_info->top_level_consts;
9297                   if (!limited_consts) {
9298                     limited_consts = scheme_make_hash_table(SCHEME_hash_ptr);
9299                     limited_info->top_level_consts = limited_consts;
9300                   }
9301                   scheme_hash_set(limited_consts, scheme_make_integer(var->variable_pos), e2);
9302                 }
9303 
9304                 if (sstruct || (SCHEME_TYPE(e2) > _scheme_ir_values_types_)) {
9305                   /* No use re-optimizing */
9306                 } else {
9307                   if (!re_consts)
9308                     re_consts = scheme_make_hash_table(SCHEME_hash_ptr);
9309                   scheme_hash_set(re_consts, scheme_make_integer(i_m), scheme_make_integer(var->variable_pos));
9310                 }
9311               } else {
9312                 /* At least mark it as fixed */
9313                 fixed_table = set_as_fixed(fixed_table, info, SCHEME_IR_TOPLEVEL_POS(var));
9314               }
9315             }
9316           }
9317 	} else if (cont) {
9318 	  /* The binding is not inlinable/propagatable, but unless it's
9319 	     set!ed, it is constant after evaluating the definition. We
9320 	     map the top-level position to indicate constantness --- immediately
9321              if `cont`, and later if not. */
9322           int i, n = SCHEME_DEFN_VAR_COUNT(defn);
9323           Scheme_IR_Toplevel *var;
9324 
9325           for (i = 0; i < n; i++) {
9326             var = SCHEME_DEFN_VAR(defn, i);
9327 
9328             /* Test for set!: */
9329             if (!(SCHEME_IR_TOPLEVEL_FLAGS(var) & SCHEME_IR_TOPLEVEL_MUTATED)) {
9330               if (!info->top_level_consts
9331                   || !scheme_hash_get(info->top_level_consts, (Scheme_Object *)var)) {
9332                 fixed_table = set_as_fixed(fixed_table, info, var->variable_pos);
9333               }
9334             }
9335           }
9336         }
9337       } else {
9338         if (i_m + 1 == cnt)
9339           cont = 0;
9340         else
9341           cont = scheme_omittable_expr(e, -1, -1, 0, NULL, NULL);
9342       }
9343     } else {
9344       cont = 1;
9345     }
9346 
9347     if (!cont || (i_m + 1 == cnt)) {
9348       Scheme_Object *prop_later = NULL;
9349       /* If we have new constants, re-optimize to inline: */
9350       if (consts) {
9351         int flags;
9352 
9353         /* Same as in letrec: assume LAMBDA_SINGLE_RESULT and
9354            LAMBDA_PRESERVES_MARKS for all, but then assume not for all
9355            if any turn out not (i.e., approximate fix point). */
9356         (void)set_code_closure_flags(cl_first,
9357                                      LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS | LAMBDA_STATUS_RESULT_TENTATIVE,
9358                                      0xFFFF,
9359                                      0);
9360 
9361 	while (1) {
9362 	  /* Re-optimize this expression. */
9363           int old_sz, new_sz, orig_fuel;
9364 
9365           e = SCHEME_VEC_ELS(linklet->bodies)[start_simultaneous];
9366 
9367           if (OPT_DELAY_GROUP_PROPAGATE || OPT_LIMIT_FUNCTION_RESIZE) {
9368             if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type)) {
9369               Scheme_Object *sub_e;
9370               sub_e = SCHEME_DEFN_RHS(e);
9371               old_sz = lambda_body_size(sub_e, 0);
9372             } else
9373               old_sz = 0;
9374           } else
9375             old_sz = 0;
9376 
9377           optimize_info_seq_step(info, &info_seq);
9378           orig_fuel = info->inline_fuel;
9379           e = optimize_expr(e, info, 0);
9380           info->inline_fuel = orig_fuel;
9381 	  SCHEME_VEC_ELS(linklet->bodies)[start_simultaneous] = e;
9382 
9383           if (re_consts) {
9384             /* Install optimized closures into constant table ---
9385                unless, maybe, they grow too much: */
9386             Scheme_Object *rpos;
9387             rpos = scheme_hash_get(re_consts, scheme_make_integer(start_simultaneous));
9388             if (rpos) {
9389               Scheme_Object *old_e;
9390 
9391               e = SCHEME_DEFN_RHS(e);
9392 
9393               old_e = scheme_hash_get(info->top_level_consts, rpos);
9394               if (old_e && SCHEME_LAMBDAP(old_e) && OPT_PRE_OPTIMIZE_FOR_CROSS_LINKLET(1)) {
9395                 if (!originals)
9396                   originals = scheme_make_hash_table(SCHEME_hash_ptr);
9397                 scheme_hash_set(originals, scheme_make_integer(start_simultaneous), old_e);
9398               }
9399 
9400               if (!ir_propagate_ok(e, info, 0, NULL)
9401                   && scheme_is_statically_proc(e, info, 0)) {
9402                 /* If we previously installed a procedure for inlining,
9403                    don't replace that with a worse approximation. */
9404                 if (SCHEME_LAMBDAP(old_e))
9405                   e = NULL;
9406                 else
9407                   e = scheme_make_noninline_proc(e);
9408               }
9409 
9410               if (e) {
9411                 if (OPT_DELAY_GROUP_PROPAGATE || OPT_LIMIT_FUNCTION_RESIZE)
9412                   new_sz = lambda_body_size(e, 0);
9413                 else
9414                   new_sz = 0;
9415 
9416                 if (!old_sz
9417                     || (new_sz <= old_sz)
9418                     || (!OPT_DELAY_GROUP_PROPAGATE && !OPT_LIMIT_FUNCTION_RESIZE))
9419                   scheme_hash_set(info->top_level_consts, rpos, e);
9420                 else if (!OPT_LIMIT_FUNCTION_RESIZE
9421                          || (new_sz < 4 * old_sz))
9422                   prop_later = scheme_make_raw_pair(scheme_make_pair(rpos, e), prop_later);
9423               }
9424             }
9425           }
9426 
9427 	  if (start_simultaneous == i_m)
9428 	    break;
9429           start_simultaneous++;
9430 	}
9431 
9432         flags = set_code_closure_flags(cl_first, 0, 0xFFFF, 0);
9433         (void)set_code_closure_flags(cl_first,
9434                                      (flags & (LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS)),
9435                                      ~(LAMBDA_SINGLE_RESULT | LAMBDA_PRESERVES_MARKS | LAMBDA_STATUS_MASK),
9436                                      1);
9437       }
9438 
9439       cl_last = cl_first = NULL;
9440       consts = NULL;
9441       re_consts = NULL;
9442       start_simultaneous = i_m + 1;
9443 
9444       while (prop_later) {
9445         e = SCHEME_CAR(prop_later);
9446         scheme_hash_set(info->top_level_consts, SCHEME_CAR(e), SCHEME_CDR(e));
9447         prop_later = SCHEME_CDR(prop_later);
9448       }
9449     }
9450 
9451     if (!cont) {
9452       /* Now that the definition is evaluated, its variables are
9453          certainly fixed if they're not `set!`ed. */
9454       e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9455       if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type)) {
9456         int i, n = SCHEME_DEFN_VAR_COUNT(e);
9457         Scheme_IR_Toplevel *var;
9458 
9459         for (i = 0; i < n; i++) {
9460           var = SCHEME_DEFN_VAR(e, i);
9461 
9462           /* Test for set!: */
9463           if (!(SCHEME_IR_TOPLEVEL_FLAGS(var) & SCHEME_IR_TOPLEVEL_MUTATED)) {
9464             if (!info->top_level_consts
9465                 || !scheme_hash_get(info->top_level_consts, (Scheme_Object *)var)) {
9466               fixed_table = set_as_fixed(fixed_table, info, var->variable_pos);
9467             }
9468           }
9469         }
9470       }
9471     }
9472   }
9473 
9474   /* For functions that are potentially inlineable, perhaps
9475      before optimization, insert inline_variant records: */
9476   if (info->enforce_const) {
9477     for (i_m = 0; i_m < cnt; i_m++) {
9478       /* Optimize this expression: */
9479       e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9480       if (SAME_TYPE(SCHEME_TYPE(e), scheme_define_values_type)) {
9481         int size_override;
9482         size_override = SCHEME_DEFN_ALWAYS_INLINEP(e);
9483         if (SCHEME_DEFN_VAR_COUNT(e) == 1) {
9484           Scheme_Object *sub_e, *alt_e;
9485           sub_e = SCHEME_DEFN_RHS(e);
9486           alt_e = is_cross_linklet_inline_candidiate(sub_e, info, 0);
9487           if (!alt_e && originals && OPT_PRE_OPTIMIZE_FOR_CROSS_LINKLET(size_override)) {
9488             alt_e = scheme_hash_get(originals, scheme_make_integer(i_m));
9489             if (SAME_OBJ(alt_e, sub_e) && !size_override)
9490               alt_e = NULL;
9491             else if (alt_e)
9492               alt_e = is_cross_linklet_inline_candidiate(alt_e, info, size_override);
9493           }
9494           if (alt_e) {
9495             Scheme_Object *iv;
9496             iv = scheme_make_vector(3, scheme_false);
9497             iv->type = scheme_inline_variant_type;
9498             SCHEME_VEC_ELS(iv)[0] = sub_e;
9499             SCHEME_VEC_ELS(iv)[1] = alt_e;
9500             SCHEME_DEFN_RHS(e) = iv;
9501           }
9502         }
9503       }
9504     }
9505   }
9506 
9507   /* Check one more time for expressions that we can omit: */
9508   {
9509     int can_omit = 0;
9510     for (i_m = 0; i_m < cnt; i_m++) {
9511       /* Optimize this expression: */
9512       e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9513       if ((i_m < (cnt - 1)) && scheme_omittable_expr(e, -1, -1, 0, info, NULL)) {
9514         can_omit++;
9515       }
9516     }
9517     if (can_omit) {
9518       Scheme_Object *new_bodies;
9519       int j = 0;
9520       new_bodies = scheme_make_vector(cnt - can_omit, scheme_false);
9521       for (i_m = 0; i_m < cnt; i_m++) {
9522         /* Optimize this expression: */
9523         e = SCHEME_VEC_ELS(linklet->bodies)[i_m];
9524         if ((i_m == (cnt-1)) || !scheme_omittable_expr(e, -1, -1, 0, info, NULL)) {
9525           SCHEME_VEC_ELS(new_bodies)[j++] = e;
9526         }
9527       }
9528       linklet->bodies = new_bodies;
9529     }
9530     cnt -= can_omit;
9531   }
9532 
9533   /* Record shapes, if any, of imports as used for optimization; also
9534      reflect import usage, so that the resolve pass can remove unused
9535      imports */
9536   record_optimize_shapes(info, linklet, _import_keys);
9537 
9538   return linklet;
9539 }
9540 
9541 /*========================================================================*/
9542 /*                            expressions                                 */
9543 /*========================================================================*/
9544 
optimize_k(void)9545 static Scheme_Object *optimize_k(void)
9546 {
9547   Scheme_Thread *p = scheme_current_thread;
9548   Scheme_Object *expr = (Scheme_Object *)p->ku.k.p1;
9549   Optimize_Info *info = (Optimize_Info *)p->ku.k.p2;
9550   int context = p->ku.k.i1;
9551 
9552   p->ku.k.p1 = NULL;
9553   p->ku.k.p2 = NULL;
9554 
9555   return optimize_expr(expr, info, context);
9556 }
9557 
optimize_expr(Scheme_Object * expr,Optimize_Info * info,int context)9558 Scheme_Object *optimize_expr(Scheme_Object *expr, Optimize_Info *info, int context)
9559 {
9560   Scheme_Type type = SCHEME_TYPE(expr);
9561 
9562 #ifdef DO_STACK_CHECK
9563 # include "mzstkchk.h"
9564   {
9565     Scheme_Thread *p = scheme_current_thread;
9566 
9567     p->ku.k.p1 = (void *)expr;
9568     p->ku.k.p2 = (void *)info;
9569     p->ku.k.i1 = context;
9570 
9571     return scheme_handle_stack_overflow(optimize_k);
9572   }
9573 #endif
9574 
9575   info->preserves_marks = 1;
9576   info->single_result = 1;
9577   info->escapes = 0;
9578 
9579   switch (type) {
9580   case scheme_ir_local_type:
9581     {
9582       Scheme_Object *val;
9583 
9584       info->size += 1;
9585 
9586       if (SCHEME_VAR(expr)->mutated) {
9587         info->vclock += 1;
9588         register_use(SCHEME_VAR(expr), info);
9589         return expr;
9590       }
9591 
9592       val = optimize_info_propagate_local(expr);
9593       if (val) {
9594         info->size -= 1;
9595         return optimize_expr(val, info, context);
9596       }
9597 
9598       val = collapse_local(expr, info, context);
9599       if (val)
9600         return val;
9601 
9602       if (!(context & OPT_CONTEXT_NO_SINGLE)) {
9603         val = SCHEME_VAR(expr)->optimize.known_val;
9604 
9605         if (val && SAME_TYPE(SCHEME_TYPE(val), scheme_once_used_type)) {
9606           Scheme_Once_Used *o = (Scheme_Once_Used *)val;
9607 
9608           MZ_ASSERT(!o->moved);
9609           MZ_ASSERT(!SCHEME_VAR(expr)->optimize_outside_binding);
9610 
9611           /* In case this variable was tentatively used before: */
9612           SCHEME_VAR(expr)->optimize_used = 0;
9613 
9614           if (((o->vclock == info->vclock)
9615                && ((o->aclock == info->aclock)
9616                    || !o->spans_k)
9617                && ((context & OPT_CONTEXT_SINGLED)
9618                    || single_valued_noncm_expression(o->expr, info, 5)))
9619               || movable_expression(o->expr, info,
9620                                     o->var->optimize.lambda_depth != info->lambda_depth,
9621                                     o->kclock != info->kclock,
9622                                     o->sclock != info->sclock,
9623                                     0, 5)) {
9624             int save_fuel = info->inline_fuel, save_no_types = info->no_types;
9625             int save_vclock, save_aclock, save_kclock, save_sclock;
9626             info->size -= 1;
9627             info->inline_fuel = -1; /* no more inlining; o->expr was already optimized */
9628             info->no_types = 1; /* cannot used inferred types, in case `val' inferred them */
9629             save_vclock = info->vclock; /* allowed to move => no change to clocks */
9630             save_aclock = info->aclock;
9631             save_kclock = info->kclock;
9632             save_sclock = info->sclock;
9633 
9634             o->moved = 1;
9635 
9636             val = optimize_expr(o->expr, info, context);
9637 
9638             if (info->maybe_values_argument) {
9639               /* Although `val` could be counted as taking 0 time, we advance
9640                  the clock conservatively to be consistent with `values`
9641                  splitting. */
9642               advance_clocks_for_optimized(val,
9643                                            &save_vclock, &save_aclock, &save_kclock, &save_sclock,
9644                                            info,
9645                                            ADVANCE_CLOCKS_INIT_FUEL);
9646             }
9647 
9648             info->inline_fuel = save_fuel;
9649             info->no_types = save_no_types;
9650             info->vclock = save_vclock;
9651             info->aclock = save_aclock;
9652             info->kclock = save_kclock;
9653             info->sclock = save_sclock;
9654             return val;
9655           }
9656         }
9657       }
9658 
9659       /* If everything fails, mark it as used. */
9660       if (OPT_CONTEXT_TYPE(context))
9661         SCHEME_VAR(expr)->arg_type = OPT_CONTEXT_TYPE(context);
9662       if (info->kclock > SCHEME_VAR(expr)->optimize.init_kclock)
9663         SCHEME_VAR(expr)->escapes_after_k_tick = 1;
9664       register_use(SCHEME_VAR(expr), info);
9665       return expr;
9666     }
9667   case scheme_application_type:
9668     return optimize_application(expr, info, context);
9669   case scheme_application2_type:
9670     return optimize_application2(expr, info, context);
9671   case scheme_application3_type:
9672     return optimize_application3(expr, info, context);
9673   case scheme_sequence_type:
9674     return optimize_sequence(expr, info, context, 1);
9675   case scheme_branch_type:
9676     return optimize_branch(expr, info, context);
9677   case scheme_with_cont_mark_type:
9678     return optimize_wcm(expr, info, context);
9679   case scheme_ir_lambda_type:
9680     if (context & OPT_CONTEXT_BOOLEAN)
9681       return scheme_true;
9682     else
9683       return optimize_lambda(expr, info, context);
9684   case scheme_ir_let_header_type:
9685     return optimize_lets(expr, info, context);
9686   case scheme_ir_toplevel_type:
9687     info->size += 1;
9688     {
9689       Scheme_Object *c;
9690 
9691       while (1) {
9692         c = get_import_inline(info, (Scheme_IR_Toplevel *)expr, -1, 0);
9693         if (!c)
9694           c = get_defn_shape(info, (Scheme_IR_Toplevel *)expr);
9695         c = no_potential_size(c);
9696         if (c && SAME_TYPE(SCHEME_TYPE(c), scheme_ir_toplevel_type))
9697           expr = c;
9698         else
9699           break;
9700       }
9701 
9702       if (c) {
9703         if (SAME_OBJ(c, scheme_constant_key)) {
9704           /* can't copy, but constant across instantiations */
9705           expr = scheme_ir_toplevel_to_flagged_toplevel(expr, SCHEME_TOPLEVEL_CONST);
9706           if (context & OPT_CONTEXT_BOOLEAN)
9707             c = scheme_true;
9708           else
9709             c = NULL;
9710         } else if (SAME_OBJ(c, scheme_fixed_key)) {
9711           /* not constant across instantiations, but at least fixed */
9712           expr = scheme_ir_toplevel_to_flagged_toplevel(expr, SCHEME_TOPLEVEL_FIXED);
9713           c = NULL;
9714         }
9715       } else
9716         info->vclock += 1;
9717 
9718       if (c) {
9719         if (context & OPT_CONTEXT_BOOLEAN)
9720           return (SCHEME_FALSEP(c) ? scheme_false : scheme_true);
9721 
9722 	if (scheme_ir_duplicate_ok(c, 0))
9723 	  return c;
9724 
9725 	/* We can't inline, but mark the top level as a constant,
9726 	   so we can direct-jump and avoid null checks in JITed code: */
9727         expr = scheme_ir_toplevel_to_flagged_toplevel(expr, SCHEME_TOPLEVEL_CONST);
9728       }
9729     }
9730     optimize_info_used_top(info);
9731     register_import_used(info, (Scheme_IR_Toplevel *)expr);
9732     return expr;
9733   case scheme_variable_type:
9734     scheme_signal_error("got toplevel in wrong place");
9735     return 0;
9736   case scheme_define_values_type:
9737     return define_values_optimize(expr, info, context);
9738   case scheme_varref_form_type:
9739     return ref_optimize(expr, info, context);
9740   case scheme_set_bang_type:
9741     return set_optimize(expr, info, context);
9742   case scheme_case_lambda_sequence_type:
9743     if (context & OPT_CONTEXT_BOOLEAN)
9744       return scheme_true;
9745     else
9746       return case_lambda_optimize(expr, info, context);
9747   case scheme_begin0_sequence_type:
9748     return begin0_optimize(expr, info, context);
9749   case scheme_apply_values_type:
9750     return apply_values_optimize(expr, info, context);
9751   case scheme_with_immed_mark_type:
9752     return with_immed_mark_optimize(expr, info, context);
9753   default:
9754     info->size += 1;
9755     if ((context & OPT_CONTEXT_BOOLEAN)
9756         && (SCHEME_TYPE(expr) > _scheme_ir_values_types_)
9757         && SCHEME_TRUEP(expr))
9758       return scheme_true;
9759     else
9760       return expr;
9761   }
9762 }
9763 
increment_use_count(Scheme_IR_Local * var,int as_rator)9764 static void increment_use_count(Scheme_IR_Local *var, int as_rator)
9765 {
9766   if (var->use_count < SCHEME_USE_COUNT_INF)
9767     var->use_count++;
9768   if (!as_rator && (var->non_app_count < SCHEME_USE_COUNT_INF))
9769     var->non_app_count++;
9770 
9771   if (var->optimize.known_val
9772       && var->optimize.clear_known_on_multi_use)
9773     var->optimize.known_val = NULL;
9774 }
9775 
optimize_clone_k(void)9776 static Scheme_Object *optimize_clone_k(void)
9777 {
9778   Scheme_Thread *p = scheme_current_thread;
9779   Scheme_Object *expr = (Scheme_Object *)p->ku.k.p1;
9780   Optimize_Info *info = (Optimize_Info *)p->ku.k.p2;
9781   Scheme_Hash_Tree *var_map = (Scheme_Hash_Tree *)p->ku.k.p3;
9782   int single_use = p->ku.k.i1;
9783   int as_rator = p->ku.k.i2;
9784 
9785   p->ku.k.p1 = NULL;
9786   p->ku.k.p2 = NULL;
9787   p->ku.k.p3 = NULL;
9788 
9789   return optimize_clone(single_use, expr, info, var_map, as_rator);
9790 }
9791 
optimize_clone(int single_use,Scheme_Object * expr,Optimize_Info * info,Scheme_Hash_Tree * var_map,int as_rator)9792 Scheme_Object *optimize_clone(int single_use, Scheme_Object *expr, Optimize_Info *info, Scheme_Hash_Tree *var_map, int as_rator)
9793 /* If single_use is 1, then the old copy will be dropped --- so it's ok to "duplicate"
9794    any constant, and local-variable use counts should not be incremented. */
9795 {
9796   int t;
9797 
9798 #ifdef DO_STACK_CHECK
9799 # include "mzstkchk.h"
9800   {
9801     Scheme_Thread *p = scheme_current_thread;
9802 
9803     p->ku.k.i1 = single_use;
9804     p->ku.k.p1 = (void *)expr;
9805     p->ku.k.p2 = (void *)info;
9806     p->ku.k.p3 = (void *)var_map;
9807     p->ku.k.i2 = as_rator;
9808 
9809     return scheme_handle_stack_overflow(optimize_clone_k);
9810   }
9811 #endif
9812 
9813   t = SCHEME_TYPE(expr);
9814 
9815   switch(t) {
9816   case scheme_ir_local_type:
9817     {
9818       Scheme_Object *v;
9819       v = scheme_eq_hash_tree_get(var_map, expr);
9820       if (v)
9821         return v;
9822       else if (!single_use)
9823         increment_use_count(SCHEME_VAR(expr), as_rator);
9824       return expr;
9825     }
9826   case scheme_application2_type:
9827     {
9828       Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr, *app2;
9829 
9830       app2 = MALLOC_ONE_TAGGED(Scheme_App2_Rec);
9831       app2->iso.so.type = scheme_application2_type;
9832 
9833       expr = optimize_clone(single_use, app->rator, info, var_map, 1);
9834       if (!expr) return NULL;
9835       app2->rator = expr;
9836 
9837       expr = optimize_clone(single_use, app->rand, info, var_map, 0);
9838       if (!expr) return NULL;
9839       app2->rand = expr;
9840 
9841       SCHEME_APPN_FLAGS(app2) |= (SCHEME_APPN_FLAGS(app) & APPN_FLAG_MASK);
9842       if (single_use)
9843         SCHEME_APPN_FLAGS(app2) |= (SCHEME_APPN_FLAGS(app) & APPN_POSITION_MASK);
9844 
9845       return (Scheme_Object *)app2;
9846     }
9847   case scheme_application_type:
9848     {
9849       Scheme_App_Rec *app = (Scheme_App_Rec *)expr, *app2;
9850       int i;
9851 
9852       app2 = scheme_malloc_application(app->num_args + 1);
9853 
9854       for (i = app->num_args + 1; i--; ) {
9855 	expr = optimize_clone(single_use, app->args[i], info, var_map, !i);
9856 	if (!expr) return NULL;
9857 	app2->args[i] = expr;
9858       }
9859 
9860       SCHEME_APPN_FLAGS(app2) |= (SCHEME_APPN_FLAGS(app) & APPN_FLAG_MASK);
9861       if (single_use)
9862         SCHEME_APPN_FLAGS(app2) |= (SCHEME_APPN_FLAGS(app) & APPN_POSITION_MASK);
9863 
9864       return (Scheme_Object *)app2;
9865     }
9866   case scheme_application3_type:
9867     {
9868       Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr, *app2;
9869 
9870       app2 = MALLOC_ONE_TAGGED(Scheme_App3_Rec);
9871       app2->iso.so.type = scheme_application3_type;
9872 
9873       expr = optimize_clone(single_use, app->rator, info, var_map, 1);
9874       if (!expr) return NULL;
9875       app2->rator = expr;
9876 
9877       expr = optimize_clone(single_use, app->rand1, info, var_map, 0);
9878       if (!expr) return NULL;
9879       app2->rand1 = expr;
9880 
9881       expr = optimize_clone(single_use, app->rand2, info, var_map, 0);
9882       if (!expr) return NULL;
9883       app2->rand2 = expr;
9884 
9885       SCHEME_APPN_FLAGS(app2) |= (SCHEME_APPN_FLAGS(app) & APPN_FLAG_MASK);
9886       if (single_use)
9887         SCHEME_APPN_FLAGS(app2) |= (SCHEME_APPN_FLAGS(app) & APPN_POSITION_MASK);
9888 
9889       return (Scheme_Object *)app2;
9890     }
9891   case scheme_ir_let_header_type:
9892     {
9893       Scheme_IR_Let_Header *head = (Scheme_IR_Let_Header *)expr, *head2;
9894       Scheme_Object *body;
9895       Scheme_IR_Let_Value *lv, *lv2, *prev = NULL;
9896       Scheme_IR_Local **vars;
9897       int i;
9898 
9899       head2 = MALLOC_ONE_TAGGED(Scheme_IR_Let_Header);
9900       head2->iso.so.type = scheme_ir_let_header_type;
9901       head2->count = head->count;
9902       head2->num_clauses = head->num_clauses;
9903       SCHEME_LET_FLAGS(head2) = SCHEME_LET_FLAGS(head);
9904 
9905       /* Build let-value change: */
9906       body = head->body;
9907       for (i = head->num_clauses; i--; ) {
9908 	lv = (Scheme_IR_Let_Value *)body;
9909 
9910         vars = clone_variable_array(lv->vars, lv->count, &var_map);
9911 
9912 	lv2 = MALLOC_ONE_TAGGED(Scheme_IR_Let_Value);
9913         SCHEME_IRLV_FLAGS(lv2) |= (SCHEME_IRLV_FLAGS(lv) & 0x1);
9914 	lv2->iso.so.type = scheme_ir_let_value_type;
9915 	lv2->count = lv->count;
9916 	lv2->vars = vars;
9917         lv2->value = lv->value;
9918 
9919 	if (prev)
9920 	  prev->body = (Scheme_Object *)lv2;
9921 	else
9922 	  head2->body = (Scheme_Object *)lv2;
9923 	prev = lv2;
9924 
9925 	body = lv->body;
9926       }
9927       if (prev)
9928 	prev->body = body;
9929       else
9930 	head2->body = body;
9931 
9932       body = head2->body;
9933       for (i = head->num_clauses; i--; ) {
9934 	lv2 = (Scheme_IR_Let_Value *)body;
9935 
9936         expr = optimize_clone(single_use, lv2->value, info, var_map, 0);
9937 	if (!expr) return NULL;
9938 	lv2->value = expr;
9939 
9940 	body = lv2->body;
9941       }
9942 
9943       expr = optimize_clone(single_use, body, info, var_map, 0);
9944       if (!expr) return NULL;
9945 
9946       if (prev)
9947 	prev->body = expr;
9948       else
9949 	head2->body = expr;
9950 
9951       return (Scheme_Object *)head2;
9952     }
9953   case scheme_sequence_type:
9954   case scheme_begin0_sequence_type:
9955     {
9956       Scheme_Sequence *seq = (Scheme_Sequence *)expr, *seq2;
9957       int i;
9958 
9959       seq2 = scheme_malloc_sequence(seq->count);
9960       seq2->so.type = seq->so.type;
9961       seq2->count = seq->count;
9962 
9963       for (i = seq->count; i--; ) {
9964 	expr = optimize_clone(single_use, seq->array[i], info, var_map, 0);
9965 	if (!expr) return NULL;
9966 	seq2->array[i] = expr;
9967       }
9968 
9969       return (Scheme_Object *)seq2;
9970     }
9971   case scheme_branch_type:
9972     {
9973       Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr, *b2;
9974 
9975       b2 = MALLOC_ONE_TAGGED(Scheme_Branch_Rec);
9976       b2->so.type = scheme_branch_type;
9977 
9978       expr = optimize_clone(single_use, b->test, info, var_map, 0);
9979       if (!expr) return NULL;
9980       b2->test = expr;
9981 
9982       expr = optimize_clone(single_use, b->tbranch, info, var_map, 0);
9983       if (!expr) return NULL;
9984       b2->tbranch = expr;
9985 
9986       expr = optimize_clone(single_use, b->fbranch, info, var_map, 0);
9987       if (!expr) return NULL;
9988       b2->fbranch = expr;
9989 
9990       return (Scheme_Object *)b2;
9991     }
9992   case scheme_with_cont_mark_type:
9993     {
9994       Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)expr, *wcm2;
9995 
9996       wcm2 = MALLOC_ONE_TAGGED(Scheme_With_Continuation_Mark);
9997       wcm2->so.type = scheme_with_cont_mark_type;
9998 
9999       expr = optimize_clone(single_use, wcm->key, info, var_map, 0);
10000       if (!expr) return NULL;
10001       wcm2->key = expr;
10002 
10003       expr = optimize_clone(single_use, wcm->val, info, var_map, 0);
10004       if (!expr) return NULL;
10005       wcm2->val = expr;
10006 
10007       expr = optimize_clone(single_use, wcm->body, info, var_map, 0);
10008       if (!expr) return NULL;
10009       wcm2->body = expr;
10010 
10011       return (Scheme_Object *)wcm2;
10012     }
10013   case scheme_ir_lambda_type:
10014     return clone_lambda(single_use, expr, info, var_map);
10015   case scheme_ir_toplevel_type:
10016     return expr;
10017   case scheme_define_values_type:
10018   case scheme_boxenv_type:
10019     return NULL;
10020   case scheme_varref_form_type:
10021     return ref_clone(single_use, expr, info, var_map);
10022   case scheme_set_bang_type:
10023     return set_clone(single_use, expr, info, var_map);
10024   case scheme_apply_values_type:
10025     return apply_values_clone(single_use, expr, info, var_map);
10026   case scheme_with_immed_mark_type:
10027     return with_immed_mark_clone(single_use, expr, info, var_map);
10028   case scheme_case_lambda_sequence_type:
10029     return case_lambda_clone(single_use, expr, info, var_map);
10030   default:
10031     if (t > _scheme_ir_values_types_) {
10032       if (single_use || scheme_ir_duplicate_ok(expr, 0))
10033 	return expr;
10034     }
10035   }
10036 
10037   return NULL;
10038 }
10039 
10040 /*========================================================================*/
10041 /*                 compile-time env for optimization                      */
10042 /*========================================================================*/
10043 
optimize_info_allocate(Scheme_Linklet * linklet,int enforce_const,int can_inline,int unsafe_mode)10044 static Optimize_Info *optimize_info_allocate(Scheme_Linklet *linklet,
10045                                              int enforce_const, int can_inline, int unsafe_mode)
10046 {
10047   Optimize_Info *info;
10048 
10049   info = MALLOC_ONE_RT(Optimize_Info);
10050 #ifdef MZTAG_REQUIRED
10051   info->type = scheme_rt_optimize_info;
10052 #endif
10053   info->inline_fuel = INITIAL_INLINING_FUEL;
10054   info->flatten_fuel = INITIAL_FLATTENING_FUEL;
10055   info->linklet = linklet;
10056 
10057   info->enforce_const = enforce_const;
10058   if (!can_inline)
10059     info->inline_fuel = -1;
10060   info->unsafe_mode = unsafe_mode;
10061 
10062   return info;
10063 }
10064 
optimize_info_create(Scheme_Linklet * linklet,int enforce_const,int can_inline,int unsafe_mode)10065 static Optimize_Info *optimize_info_create(Scheme_Linklet *linklet,
10066                                            int enforce_const, int can_inline, int unsafe_mode)
10067 {
10068   Optimize_Info *info;
10069   Scheme_Logger *logger;
10070 
10071   info = optimize_info_allocate(linklet, enforce_const, can_inline, unsafe_mode);
10072 
10073   logger = (Scheme_Logger *)scheme_get_param(scheme_current_config(), MZCONFIG_LOGGER);
10074   logger = scheme_make_logger(logger, scheme_intern_symbol("optimizer"));
10075   info->logger = logger;
10076 
10077   return info;
10078 }
10079 
optimize_info_seq_init(Optimize_Info * info,Optimize_Info_Sequence * info_seq)10080 static void optimize_info_seq_init(Optimize_Info *info, Optimize_Info_Sequence *info_seq)
10081 {
10082   info_seq->init_flatten_fuel = info->flatten_fuel;
10083   info_seq->min_flatten_fuel = info->flatten_fuel;
10084 }
10085 
optimize_info_seq_step(Optimize_Info * info,Optimize_Info_Sequence * info_seq)10086 static void optimize_info_seq_step(Optimize_Info *info, Optimize_Info_Sequence *info_seq)
10087 {
10088   if (info->flatten_fuel < info_seq->min_flatten_fuel)
10089     info_seq->min_flatten_fuel = info->flatten_fuel;
10090   info->flatten_fuel = info_seq->init_flatten_fuel;
10091 }
10092 
optimize_info_seq_done(Optimize_Info * info,Optimize_Info_Sequence * info_seq)10093 static void optimize_info_seq_done(Optimize_Info *info, Optimize_Info_Sequence *info_seq)
10094 {
10095   if (info->flatten_fuel > info_seq->min_flatten_fuel)
10096     info->flatten_fuel = info_seq->min_flatten_fuel;
10097 }
10098 
propagate_used_variables(Optimize_Info * info)10099 static void propagate_used_variables(Optimize_Info *info)
10100 {
10101   Scheme_Hash_Table *ht;
10102   Scheme_IR_Local *tvar;
10103   int j;
10104 
10105   if (info->next->uses) {
10106     ht = info->uses;
10107     for (j = 0; j < ht->size; j++) {
10108       if (ht->vals[j]) {
10109         tvar = SCHEME_VAR(ht->keys[j]);
10110         if (tvar->optimize.lambda_depth < info->next->lambda_depth)
10111           scheme_hash_set(info->next->uses, (Scheme_Object *)tvar, scheme_true);
10112       }
10113     }
10114   }
10115 }
10116 
env_uses_toplevel(Optimize_Info * frame)10117 static int env_uses_toplevel(Optimize_Info *frame)
10118 {
10119   int used;
10120 
10121   used = frame->used_toplevel;
10122 
10123   if (used) {
10124     /* Propagate use to an enclosing lambda, if any: */
10125     frame = frame->next;
10126     while (frame) {
10127       if (frame->flags & SCHEME_LAMBDA_FRAME) {
10128 	frame->used_toplevel = 1;
10129 	break;
10130       }
10131       frame = frame->next;
10132     }
10133   }
10134 
10135   return used;
10136 }
10137 
optimize_info_used_top(Optimize_Info * info)10138 static void optimize_info_used_top(Optimize_Info *info)
10139 {
10140   while (info) {
10141     if (info->flags & SCHEME_LAMBDA_FRAME) {
10142       info->used_toplevel = 1;
10143       break;
10144     }
10145     info = info->next;
10146   }
10147 }
10148 
make_once_used(Scheme_Object * val,Scheme_IR_Local * var,int vclock,int aclock,int kclock,int sclock,int spans_k)10149 static Scheme_Once_Used *make_once_used(Scheme_Object *val, Scheme_IR_Local *var,
10150                                         int vclock, int aclock, int kclock, int sclock, int spans_k)
10151 {
10152   Scheme_Once_Used *o;
10153 
10154   /* Procedures should be handled more specifically, because there are
10155      issues with transitive delayed-use registration to handle
10156      `letrec`, where a value that has already been moved can be
10157      marked later as used. */
10158   MZ_ASSERT(!SCHEME_LAMBDAP(val));
10159 
10160   o = MALLOC_ONE_TAGGED(Scheme_Once_Used);
10161   o->so.type = scheme_once_used_type;
10162 
10163   o->expr = val;
10164   o->var = var;
10165   o->vclock = vclock;
10166   o->aclock = aclock;
10167   o->kclock = kclock;
10168   o->sclock = sclock;
10169   o->spans_k = spans_k;
10170 
10171   return o;
10172 }
10173 
optimize_any_uses(Optimize_Info * info,Scheme_IR_Let_Value * at_irlv,int n)10174 static int optimize_any_uses(Optimize_Info *info, Scheme_IR_Let_Value *at_irlv, int n)
10175 {
10176   int i, j;
10177   Scheme_IR_Let_Value *irlv = at_irlv;
10178 
10179   while (n--) {
10180     MZ_ASSERT(SAME_TYPE(irlv->iso.so.type, scheme_ir_let_value_type));
10181     for (i = irlv->count; i--; ) {
10182       if (irlv->vars[i]->optimize_used)
10183         return 1;
10184       for (j = at_irlv->count; j--; ) {
10185         if (at_irlv->vars[j]->optimize.transitive_uses) {
10186           if (scheme_hash_get(at_irlv->vars[j]->optimize.transitive_uses,
10187                               (Scheme_Object *)irlv->vars[i]))
10188             return 1;
10189         }
10190       }
10191     }
10192     irlv = (Scheme_IR_Let_Value *)irlv->body;
10193   }
10194 
10195   return 0;
10196 }
10197 
optimize_uses_of_mutable_imply_early_alloc(Scheme_IR_Let_Value * at_irlv,int n)10198 static void optimize_uses_of_mutable_imply_early_alloc(Scheme_IR_Let_Value *at_irlv, int n)
10199 {
10200   int i, j;
10201   Scheme_IR_Let_Value *irlv = at_irlv;
10202 
10203   /* We we're reinterpreting a `letrec` as `let*`, and when it really
10204      must be `let*` instead of `let`, and when a mutable variable is
10205      involved, then we need to tell the `resolve` pass that the
10206      mutable varaiable's value must be boxed immediately, instead of
10207      delaying to the body of the `let*`. */
10208 
10209   while (n--) {
10210     for (i = irlv->count; i--; ) {
10211       if (irlv->vars[i]->mutated) {
10212         int used = 0;
10213         if (irlv->vars[i]->optimize_used)
10214           used = 1;
10215         else {
10216           for (j = at_irlv->count; j--; ) {
10217             if (at_irlv->vars[j]->optimize.transitive_uses) {
10218               if (scheme_hash_get(at_irlv->vars[j]->optimize.transitive_uses,
10219                                   (Scheme_Object *)irlv->vars[i]))
10220                 used = 1;
10221             }
10222           }
10223         }
10224         if (used)
10225           irlv->vars[i]->must_allocate_immediately = 1;
10226       }
10227     }
10228     irlv = (Scheme_IR_Let_Value *)irlv->body;
10229   }
10230 }
10231 
register_use(Scheme_IR_Local * var,Optimize_Info * info)10232 static void register_use(Scheme_IR_Local *var, Optimize_Info *info)
10233 {
10234   MZ_ASSERT(SCHEME_VAR(var)->mode == SCHEME_VAR_MODE_OPTIMIZE);
10235   MZ_ASSERT(SCHEME_VAR(var)->use_count);
10236 
10237   if (var->optimize.lambda_depth < info->lambda_depth)
10238     scheme_hash_set(info->uses, (Scheme_Object *)var, scheme_true);
10239 
10240   if (!var->optimize_used) {
10241     var->optimize_used = 1;
10242 
10243     if (info->transitive_use_var
10244         && (var->optimize.lambda_depth
10245             <= info->transitive_use_var->optimize.lambda_depth)) {
10246       Scheme_Hash_Table *ht = info->transitive_use_var->optimize.transitive_uses;
10247 
10248       if (!ht) {
10249         ht = scheme_make_hash_table(SCHEME_hash_ptr);
10250         info->transitive_use_var->optimize.transitive_uses = ht;
10251       }
10252       scheme_hash_set(ht, (Scheme_Object *)var, scheme_true);
10253     }
10254   }
10255 }
10256 
register_transitive_uses(Scheme_IR_Local * var,Optimize_Info * info)10257 static void register_transitive_uses(Scheme_IR_Local *var, Optimize_Info *info)
10258 {
10259   Scheme_Hash_Table *ht;
10260   Scheme_IR_Local *tvar;
10261   int j;
10262 
10263   ht = var->optimize.transitive_uses;
10264 
10265   for (j = 0; j < ht->size; j++) {
10266     if (ht->vals[j]) {
10267       tvar = SCHEME_VAR(ht->keys[j]);
10268 
10269       if (tvar->optimize.known_val
10270           && SAME_TYPE(SCHEME_TYPE(tvar->optimize.known_val), scheme_once_used_type)
10271           && ((Scheme_Once_Used *)tvar->optimize.known_val)->moved) {
10272         /* variable no longer used, and any transitive uses were
10273            covered by re-optimizing in its use context */
10274         MZ_ASSERT(!tvar->optimize_used);
10275       } else
10276         register_use(tvar, info);
10277     }
10278   }
10279 }
10280 
optimize_info_lookup(Scheme_Object * var)10281 static Scheme_Object *optimize_info_lookup(Scheme_Object *var)
10282 {
10283   MZ_ASSERT(SCHEME_VAR(var)->mode == SCHEME_VAR_MODE_OPTIMIZE);
10284   MZ_ASSERT(SCHEME_VAR(var)->use_count);
10285 
10286   return SCHEME_VAR(var)->optimize.known_val;
10287 }
10288 
optimize_info_propagate_local(Scheme_Object * var)10289 static Scheme_Object *optimize_info_propagate_local(Scheme_Object *var)
10290 {
10291   Scheme_Object *last, *val = var;
10292 
10293   last = val; /* Avoid compiler warning */
10294 
10295   while (val && SAME_TYPE(SCHEME_TYPE(val), scheme_ir_local_type)) {
10296     MZ_ASSERT(SCHEME_VAR(val)->mode == SCHEME_VAR_MODE_OPTIMIZE);
10297     MZ_ASSERT(SCHEME_VAR(val)->use_count);
10298     last = val;
10299     val = SCHEME_VAR(val)->optimize.known_val;
10300   }
10301 
10302   if (!val
10303       || SCHEME_WILL_BE_LAMBDAP(val)
10304       || SCHEME_LAMBDAP(val)
10305       || SAME_TYPE(SCHEME_TYPE(val), scheme_once_used_type)) {
10306     if (SAME_OBJ(last, var))
10307       return NULL;
10308 
10309     if (SCHEME_VAR(var)->use_count != 1)
10310       increment_use_count(SCHEME_VAR(last), 0);
10311 
10312     return last;
10313   }
10314 
10315   return val;
10316 }
10317 
optimize_get_predicate(Optimize_Info * info,Scheme_Object * var,int ignore_no_types)10318 Scheme_Object *optimize_get_predicate(Optimize_Info *info, Scheme_Object *var, int ignore_no_types)
10319 {
10320   Scheme_Object *pred;
10321 
10322   if (info->no_types && !ignore_no_types) return NULL;
10323 
10324   while (info) {
10325     if (info->types) {
10326       pred = scheme_eq_hash_tree_get(info->types, var);
10327       if (pred)
10328         return pred;
10329     }
10330     info = info->next;
10331   }
10332 
10333   return NULL;
10334 }
10335 
optimize_info_add_frame(Optimize_Info * info,int flags)10336 static Optimize_Info *optimize_info_add_frame(Optimize_Info *info, int flags)
10337 {
10338   Optimize_Info *naya;
10339 
10340   naya = optimize_info_allocate(info->linklet, 0, 0, 0);
10341   naya->flags = (short)flags;
10342   naya->next = info;
10343   naya->inline_fuel = info->inline_fuel;
10344   naya->flatten_fuel = info->flatten_fuel;
10345   naya->letrec_not_twice = info->letrec_not_twice;
10346   naya->enforce_const = info->enforce_const;
10347   naya->unsafe_mode = info->unsafe_mode;
10348   naya->top_level_consts = info->top_level_consts;
10349   naya->context = info->context;
10350   naya->vclock = info->vclock;
10351   naya->aclock = info->aclock;
10352   naya->kclock = info->kclock;
10353   naya->sclock = info->sclock;
10354   naya->escapes = info->escapes;
10355   naya->init_kclock = info->kclock;
10356   naya->maybe_values_argument = info->maybe_values_argument;
10357   naya->use_psize = info->use_psize;
10358   naya->logger = info->logger;
10359   naya->no_types = info->no_types;
10360   naya->lambda_depth = info->lambda_depth + ((flags & SCHEME_LAMBDA_FRAME) ? 1 : 0);
10361   naya->uses = info->uses;
10362   naya->transitive_use_var = info->transitive_use_var;
10363   naya->cross = info->cross;
10364   naya->imports_used = info->imports_used;
10365 
10366   return naya;
10367 }
10368 
optimize_info_done(Optimize_Info * info,Optimize_Info * parent)10369 static void optimize_info_done(Optimize_Info *info, Optimize_Info *parent)
10370 {
10371   if (!parent) parent = info->next;
10372 
10373   parent->size += info->size;
10374   parent->vclock = info->vclock;
10375   parent->aclock = info->aclock;
10376   parent->kclock = info->kclock;
10377   parent->sclock = info->sclock;
10378   parent->escapes = info->escapes;
10379   parent->psize += info->psize;
10380   parent->flatten_fuel = info->flatten_fuel;
10381   if (info->has_nonleaf)
10382     parent->has_nonleaf = 1;
10383 }
10384 
10385 
10386 /*========================================================================*/
10387 /*                      shapes from linklet imports                       */
10388 /*========================================================================*/
10389 
is_procedure_expression(Scheme_Object * e)10390 static int is_procedure_expression(Scheme_Object *e)
10391 {
10392   Scheme_Type t;
10393 
10394   if (SCHEME_PROCP(e))
10395     return 1;
10396 
10397   t = SCHEME_TYPE(e);
10398 
10399   return ((t == scheme_lambda_type)
10400           || (t == scheme_case_lambda_sequence_type));
10401 }
10402 
linklet_setup_constants(Scheme_Linklet * linklet)10403 static void linklet_setup_constants(Scheme_Linklet *linklet)
10404 {
10405   int i, cnt, k, defns_start;
10406   Scheme_Object *form, *tl;
10407   Scheme_Hash_Table *ht;
10408 
10409   if (linklet->constants)
10410     return;
10411 
10412   /* find constants: */
10413   ht = scheme_make_hash_table(SCHEME_hash_ptr);
10414   linklet->constants = ht;
10415 
10416   defns_start = 1 + linklet->num_total_imports;
10417 
10418   cnt = SCHEME_VEC_SIZE(linklet->bodies);
10419   for (i = 0; i < cnt; i++) {
10420     form = SCHEME_VEC_ELS(linklet->bodies)[i];
10421 
10422     if (SAME_TYPE(SCHEME_TYPE(form), scheme_define_values_type)) {
10423       int checked_st = 0, is_st_prop = 0, has_guard = 0;
10424       Scheme_Object *is_st = NULL;
10425       Simple_Struct_Type_Info stinfo;
10426       Scheme_Object *parent_identity;
10427 
10428       for (k = SCHEME_DEFN_VAR_COUNT(form); k--; ) {
10429         tl = (Scheme_Object *)SCHEME_DEFN_VAR(form, k);
10430         if (SCHEME_TOPLEVEL_FLAGS(tl) & SCHEME_TOPLEVEL_SEAL) {
10431           int pos = SCHEME_TOPLEVEL_POS(tl) - defns_start;
10432 
10433           if (pos < linklet->num_exports) {
10434             Scheme_Object *v;
10435 
10436             if (SCHEME_DEFN_VAR_COUNT(form) == 1) {
10437               if (scheme_ir_duplicate_ok(SCHEME_DEFN_RHS(form), 1)) {
10438                 /* record simple constant for cross-linklet propagation: */
10439                 v = SCHEME_DEFN_RHS(form);
10440               } else if (SAME_TYPE(SCHEME_TYPE(SCHEME_DEFN_RHS(form)), scheme_inline_variant_type)) {
10441                 /* record a potentially inlineable function */
10442                 v = SCHEME_DEFN_RHS(form);
10443               } else if (is_procedure_expression(SCHEME_VEC_ELS(form)[0])) {
10444                 /* record that it's a procedure: */
10445                 v = scheme_make_vector(2, scheme_false);
10446                 SCHEME_VEC_ELS(v)[0] = SCHEME_DEFN_RHS(form);
10447               } else {
10448                 /* record that it's fixed for any given instantiation: */
10449                 v = scheme_fixed_key;
10450               }
10451             } else {
10452               if (!checked_st) {
10453                 if (scheme_is_simple_make_struct_type(SCHEME_DEFN_RHS(form),
10454                                                       SCHEME_DEFN_VAR_COUNT(form),
10455                                                       CHECK_STRUCT_TYPE_RESOLVED,
10456                                                       NULL, &stinfo, &parent_identity,
10457                                                       NULL, NULL, NULL, 0, linklet,
10458                                                       &is_st,
10459                                                       5)) {
10460                   is_st = scheme_make_pair(is_st, parent_identity);
10461                 } else {
10462                   is_st = NULL;
10463                   if (scheme_is_simple_make_struct_type_property(SCHEME_VEC_ELS(form)[0],
10464                                                                  SCHEME_VEC_SIZE(form)-1,
10465                                                                  CHECK_STRUCT_TYPE_RESOLVED,
10466                                                                  &has_guard,
10467                                                                  NULL, NULL, NULL, 0, linklet,
10468                                                                  5))
10469                     is_st_prop = 1;
10470                 }
10471                 checked_st = 1;
10472               }
10473               if (is_st) {
10474                 intptr_t shape;
10475                 shape = scheme_get_struct_proc_shape(k, &stinfo);
10476                 /* Vector of size 3 => struct shape */
10477                 v = scheme_make_vector(3, scheme_false);
10478                 SCHEME_VEC_ELS(v)[1] = scheme_make_integer(shape);
10479                 SCHEME_VEC_ELS(v)[2] = is_st;
10480               } else if (is_st_prop) {
10481                 intptr_t shape;
10482                 shape = scheme_get_struct_property_proc_shape(k, has_guard);
10483                 /* Vector of size 4 => struct property shape */
10484                 v = scheme_make_vector(4, scheme_false);
10485                 SCHEME_VEC_ELS(v)[1] = scheme_make_integer(shape);
10486               } else
10487                 v = NULL;
10488             }
10489             if (v)
10490               scheme_hash_set(ht, SCHEME_VEC_ELS(linklet->defns)[pos], v);
10491           }
10492         }
10493       }
10494     }
10495   }
10496 }
10497 
get_linklet_or_instance_for_import_key(Optimize_Info * info,Scheme_Object * key)10498 static Scheme_Object *get_linklet_or_instance_for_import_key(Optimize_Info *info, Scheme_Object *key)
10499 {
10500   Scheme_Object *v, *next_keys, *a[1];
10501   Cross_Linklet_Info *cross = info->cross;
10502   Scheme_Hash_Tree *ht;
10503 
10504   if (!cross || !cross->get_import)
10505     return NULL;
10506 
10507   v = scheme_eq_hash_tree_get(cross->linklets, key);
10508   if (!v) {
10509     a[0] = key;
10510     v = scheme_apply_multi(cross->get_import, 1, a);
10511     if (SAME_OBJ(v, SCHEME_MULTIPLE_VALUES)
10512         && (scheme_current_thread->ku.multiple.count == 2)) {
10513       v = scheme_current_thread->ku.multiple.array[0];
10514       next_keys = scheme_current_thread->ku.multiple.array[1];
10515     } else {
10516       scheme_wrong_return_arity("compile-linklet",
10517                                 2,
10518                                 (SAME_OBJ(v, SCHEME_MULTIPLE_VALUES)
10519                                  ? scheme_current_thread->ku.multiple.count
10520                                  : 1),
10521                                 (SAME_OBJ(v, SCHEME_MULTIPLE_VALUES)
10522                                  ? (Scheme_Object **)v
10523                                  : scheme_current_thread->ku.multiple.array),
10524                                 "");
10525       return NULL;
10526     }
10527 
10528     ht = scheme_hash_tree_set(cross->linklets, key, v);
10529     cross->linklets = ht;
10530 
10531     if (!SCHEME_FALSEP(v)) {
10532       if (!SAME_TYPE(SCHEME_TYPE(v), scheme_linklet_type)
10533           && !SAME_TYPE(SCHEME_TYPE(v), scheme_instance_type))
10534         scheme_wrong_contract("compile-linklet", "(or/c linklet? instance? #f)", -1, 0, &v);
10535 
10536       if (!SCHEME_FALSEP(next_keys)
10537           && (!SCHEME_VECTORP(next_keys)
10538               || !SAME_TYPE(SCHEME_TYPE(v), scheme_linklet_type)
10539               || SCHEME_VEC_SIZE(next_keys) != SCHEME_VEC_SIZE(((Scheme_Linklet *)v)->importss)))
10540         scheme_contract_error("compile-linklet",
10541                               "result is not #f or a vector of keys that match the result linklet's import count",
10542                               (SAME_TYPE(SCHEME_TYPE(v), scheme_linklet_type) ? "linklet" : "instance"), 1, v,
10543                               "import count", 1, scheme_make_integer(SCHEME_VEC_SIZE(((Scheme_Linklet *)v)->importss)),
10544                               "invalid as vector of keys", 1, next_keys,
10545                               NULL);
10546 
10547       if (SCHEME_TRUEP(next_keys)) {
10548         ht = scheme_hash_tree_set(cross->import_next_keys, key, next_keys);
10549         cross->import_next_keys = ht;
10550       }
10551     }
10552   }
10553 
10554   if (SCHEME_FALSEP(v))
10555     return NULL;
10556 
10557   return v;
10558 }
10559 
get_import_inline_or_shape(Optimize_Info * info,Scheme_IR_Toplevel * var,int argc,int want_shape,int for_props)10560 static Scheme_Object *get_import_inline_or_shape(Optimize_Info *info, Scheme_IR_Toplevel *var,
10561                                                  int argc, int want_shape, int for_props)
10562 /* Returns either a procedure shape, a value to inline, or (when `for_props`)
10563    a function to be used just for its properties. The
10564    special values scheme_constant_key and scheme_fixed_key may be
10565    returned. If `argc` is less than 0, then scheme_constant_key is
10566    returned for procedures. If `want_shape` or `argc` is less than 0
10567    and a non-NULL value is returned, then `info` records the fact that
10568    shape information is used. */
10569 {
10570   Scheme_Object *key, *v, *name, *l_or_i;
10571   Scheme_Hash_Table *iv_ht;
10572   Scheme_Linklet *linklet;
10573 
10574   if (!info->cross || (var->instance_pos < 0))
10575     return NULL;
10576 
10577   key = scheme_eq_hash_tree_get(info->cross->import_keys, scheme_make_integer(var->instance_pos));
10578   if (!key)
10579     return NULL;
10580 
10581   l_or_i = get_linklet_or_instance_for_import_key(info, key);
10582 
10583   if (!l_or_i)
10584     return NULL;
10585 
10586   if ((var->instance_pos < SCHEME_VEC_SIZE(info->linklet->importss))
10587       && (var->variable_pos < SCHEME_VEC_SIZE(SCHEME_VEC_ELS(info->linklet->importss)[var->instance_pos])))
10588     name = SCHEME_VEC_ELS(SCHEME_VEC_ELS(info->linklet->importss)[var->instance_pos])[var->variable_pos];
10589   else {
10590     Scheme_Hash_Tree *ht;
10591     ht = (Scheme_Hash_Tree *)scheme_eq_hash_tree_get(info->cross->import_syms,
10592                                                      scheme_make_integer(var->instance_pos));
10593     MZ_ASSERT(ht);
10594     name = scheme_eq_hash_tree_get(ht, scheme_make_integer(var->variable_pos));
10595   }
10596   MZ_ASSERT(name);
10597   MZ_ASSERT(SCHEME_SYMBOLP(name));
10598 
10599   if (SAME_TYPE(SCHEME_TYPE(l_or_i), scheme_linklet_type)) {
10600     linklet = (Scheme_Linklet *)l_or_i;
10601 
10602     if (!linklet->constants)
10603       linklet_setup_constants(linklet);
10604 
10605     if (!want_shape && !for_props && (argc >= 0)) {
10606       /* check for previously unresolved for this linklet: */
10607       iv_ht = (Scheme_Hash_Table *)scheme_eq_hash_tree_get(info->cross->inline_variants, key);
10608       if (iv_ht) {
10609         v = scheme_hash_get(iv_ht, name);
10610         if (v) {
10611           /* We have previously unresolved to `v` */
10612           if (SCHEME_HASHTP(v)) {
10613             /* It's a `case-lambda`, so try to get the right clause */
10614             v = scheme_hash_get((Scheme_Hash_Table *)v, scheme_make_integer(argc));
10615             if (v)
10616               return v;
10617             /* Try to unresolve the right arity */
10618           } else if (SCHEME_FALSEP(v)) {
10619             /* previous unresove attempt failed */
10620             return NULL;
10621           } else
10622             return v;
10623         }
10624       }
10625       /* Otherwise, not yet unresolved (maybe because it doesn't need to be) */
10626     } else
10627       iv_ht = NULL;
10628 
10629     v = scheme_hash_get(linklet->constants, name);
10630 
10631     if (!v)
10632       return NULL;
10633 
10634     if (SCHEME_VECTORP(v) && (SCHEME_VEC_SIZE(v) == 2)) {
10635       /* a procedure */
10636       if (want_shape)
10637         v = scheme_get_or_check_procedure_shape(SCHEME_VEC_ELS(v)[0], NULL, 0);
10638       else if (for_props)
10639         return SCHEME_VEC_ELS(v)[0];
10640       else if (argc < 0)
10641         v = scheme_constant_key;
10642       else
10643         v = NULL;
10644     } else if (SAME_TYPE(SCHEME_TYPE(v), scheme_inline_variant_type)) {
10645       /* a procedure that can be inlined (if unresolve succeeds) */
10646       if (for_props) {
10647         return SCHEME_VEC_ELS(v)[0];
10648       } else if (want_shape) {
10649         v = scheme_get_or_check_procedure_shape(v, NULL, 0);
10650         if (v)
10651           info->cross->used_import_shape = 1;
10652       } else if (argc >= 0) {
10653         int has_cases = 0;
10654 
10655         v = scheme_unresolve(v, argc, &has_cases, linklet, key, info);
10656 
10657         if (!iv_ht) {
10658           Scheme_Hash_Tree *ht;
10659           iv_ht = scheme_make_hash_table(SCHEME_hash_ptr);
10660           ht = scheme_hash_tree_set(info->cross->inline_variants, key, (Scheme_Object *)iv_ht);
10661           info->cross->inline_variants = ht;
10662         }
10663 
10664         /* Save unresolved */
10665         if (has_cases) {
10666           Scheme_Hash_Table *cl_ht;
10667           cl_ht = (Scheme_Hash_Table *)scheme_hash_get(iv_ht, name);
10668           if (!cl_ht) {
10669             cl_ht = scheme_make_hash_table(SCHEME_hash_ptr);
10670             scheme_hash_set(iv_ht, name, (Scheme_Object *)cl_ht);
10671           }
10672           scheme_hash_set(cl_ht, scheme_make_integer(argc), v);
10673         } else if (v)
10674           scheme_hash_set(iv_ht, name, v);
10675         else
10676           scheme_hash_set(iv_ht, name, scheme_false); /* record that it won't work */
10677       } else
10678         v = scheme_constant_key;
10679     } else if (SCHEME_VECTORP(v) && (SCHEME_VEC_SIZE(v) == 3)) {
10680       if (want_shape)
10681         v = scheme_make_struct_proc_shape(SCHEME_INT_VAL(SCHEME_VEC_ELS(v)[1]),
10682                                           SCHEME_VEC_ELS(v)[2]);
10683       else if ((argc < 0) || for_props)
10684         v = scheme_constant_key;
10685       else
10686         v = NULL;
10687     } else if (SCHEME_VECTORP(v) && (SCHEME_VEC_SIZE(v) == 4)) {
10688       if (want_shape)
10689         v = scheme_make_struct_property_proc_shape(SCHEME_INT_VAL(SCHEME_VEC_ELS(v)[1]));
10690       else if ((argc < 0) || for_props)
10691         v = scheme_constant_key;
10692       else
10693         v = NULL;
10694     }
10695   } else {
10696     Scheme_Bucket *b;
10697     int imprecise = SCHEME_INSTANCE_FLAGS((Scheme_Instance *)l_or_i) & SCHEME_INSTANCE_USE_IMPRECISE;
10698     b = scheme_instance_variable_bucket_or_null(name, (Scheme_Instance *)l_or_i);
10699     if (b && b->val && (((Scheme_Bucket_With_Flags *)b)->flags & GLOB_IS_CONSISTENT)) {
10700       v = b->val;
10701       if (want_shape)
10702         v = get_value_shape(v, imprecise);
10703       else if (argc < 0)
10704         v = scheme_constant_key;
10705       else
10706         v = NULL;
10707     } else
10708       v = NULL;
10709   }
10710 
10711   if (v && (want_shape || (argc < 0)))
10712     info->cross->used_import_shape = 1;
10713 
10714   return v;
10715 }
10716 
scheme_optimize_add_import_variable(Optimize_Info * info,Scheme_Object * linklet_key,Scheme_Object * symbol)10717 Scheme_Object *scheme_optimize_add_import_variable(Optimize_Info *info, Scheme_Object *linklet_key, Scheme_Object *symbol)
10718 /* Called from unresolver (for cross-linklet inlining) to find or add
10719    an imported variable from an existing instance import */
10720 {
10721   Scheme_Object *pos, *var_pos, *vec;
10722   Scheme_Hash_Tree *syms, *ht;
10723   int i;
10724 
10725   if (SCHEME_FALSEP(linklet_key))
10726     return NULL;
10727 
10728   pos = scheme_eq_hash_tree_get(info->cross->rev_import_keys, linklet_key);
10729   MZ_ASSERT(pos);
10730 
10731   syms = (Scheme_Hash_Tree *)scheme_eq_hash_tree_get(info->cross->import_syms, pos);
10732   if (!syms) {
10733     syms = empty_eq_hash_tree;
10734     if (SCHEME_INT_VAL(pos) < SCHEME_VEC_SIZE(info->linklet->importss)) {
10735       /* initialize from the linklet that we're optimizing */
10736       vec = SCHEME_VEC_ELS(info->linklet->importss)[SCHEME_INT_VAL(pos)];
10737       for (i = SCHEME_VEC_SIZE(vec); i--; ) {
10738         syms = scheme_hash_tree_set(syms, SCHEME_VEC_ELS(vec)[i], scheme_make_integer(i));
10739         syms = scheme_hash_tree_set(syms, scheme_make_integer(i), SCHEME_VEC_ELS(vec)[i]);
10740       }
10741     } else {
10742       /* must not have imported anything, yet, so the empty table is correct */
10743     }
10744     ht = scheme_hash_tree_set(info->cross->import_syms, pos, (Scheme_Object *)syms);
10745     info->cross->import_syms = ht;
10746   }
10747 
10748   var_pos = scheme_eq_hash_tree_get(syms, symbol);
10749   if (!var_pos) {
10750     var_pos = scheme_make_integer(syms->count >> 1);
10751     syms = scheme_hash_tree_set(syms, symbol, var_pos);
10752     syms = scheme_hash_tree_set(syms, var_pos, symbol);
10753     ht = scheme_hash_tree_set(info->cross->import_syms, pos, (Scheme_Object *)syms);
10754     info->cross->import_syms = ht;
10755   }
10756 
10757   /* SCHEME_TOPLEVEL_READY is conservative; optimizer can compute a refinement later */
10758   return (Scheme_Object *)scheme_make_ir_toplevel(SCHEME_INT_VAL(pos), SCHEME_INT_VAL(var_pos), SCHEME_TOPLEVEL_READY);
10759 }
10760 
scheme_optimize_get_import_key(Optimize_Info * info,Scheme_Object * linklet_key,int instance_pos)10761 Scheme_Object *scheme_optimize_get_import_key(Optimize_Info *info, Scheme_Object *linklet_key, int instance_pos)
10762 /* Called from unresolver (for cross-linklet inlining) to find or add
10763    an imported instance */
10764 {
10765   Scheme_Object *next_keys, *key, *pos;
10766   Scheme_Hash_Tree *ht;
10767 
10768   next_keys = scheme_eq_hash_tree_get(info->cross->import_next_keys, linklet_key);
10769   if (!next_keys) {
10770     /* chaining is not supported by the compilation client */
10771     return NULL;
10772   }
10773 
10774   MZ_ASSERT(instance_pos < SCHEME_VEC_SIZE(next_keys));
10775 
10776   key = SCHEME_VEC_ELS(next_keys)[instance_pos];
10777   pos = scheme_eq_hash_tree_get(info->cross->rev_import_keys, key);
10778   if (!pos) {
10779     /* Add this linklet as an import */
10780     pos = scheme_make_integer(info->cross->import_keys->count);
10781 
10782     ht = scheme_hash_tree_set(info->cross->import_keys, pos, key);
10783     info->cross->import_keys = ht;
10784 
10785     ht = scheme_hash_tree_set(info->cross->rev_import_keys, key, pos);
10786     info->cross->rev_import_keys = ht;
10787   }
10788 
10789   return key;
10790 }
10791 
get_import_shape(Optimize_Info * info,Scheme_IR_Toplevel * var)10792 static Scheme_Object *get_import_shape(Optimize_Info *info, Scheme_IR_Toplevel *var)
10793 {
10794   return get_import_inline_or_shape(info, var, -1, 1, 0);
10795 }
10796 
get_import_inline(Optimize_Info * info,Scheme_IR_Toplevel * var,int argc,int for_props)10797 static Scheme_Object *get_import_inline(Optimize_Info *info, Scheme_IR_Toplevel *var, int argc, int for_props)
10798 /* argc < 0 => scheme_constant_key for non-copyable procedures */
10799 {
10800   return get_import_inline_or_shape(info, var, argc, 0, for_props);
10801 }
10802 
register_import_used(Optimize_Info * info,Scheme_IR_Toplevel * var)10803 static void register_import_used(Optimize_Info *info, Scheme_IR_Toplevel *var)
10804 {
10805   if ((var->instance_pos >= 0) && info->imports_used) {
10806     /* Record that the import is used. The resolve pass can
10807        drop references that have been optimized away. */
10808     Scheme_Hash_Tree *ht;
10809     ht = (Scheme_Hash_Tree *)scheme_eq_hash_tree_get(*info->imports_used, scheme_make_integer(var->instance_pos));
10810     if (!ht)
10811       ht = empty_eq_hash_tree;
10812     if (!scheme_eq_hash_tree_get(ht, scheme_make_integer(var->variable_pos))) {
10813       ht = scheme_hash_tree_set(ht, scheme_make_integer(var->variable_pos), scheme_true);
10814       ht = scheme_hash_tree_set(*info->imports_used, scheme_make_integer(var->instance_pos), (Scheme_Object *)ht);
10815       (*info->imports_used) = ht;
10816     }
10817   }
10818 }
10819 
record_optimize_shapes(Optimize_Info * info,Scheme_Linklet * linklet,Scheme_Object ** _import_keys)10820 static void record_optimize_shapes(Optimize_Info *info, Scheme_Linklet *linklet, Scheme_Object **_import_keys)
10821 {
10822   int i, j, k, used, total, added_imports = 0, dropped_imports = 0, total_used;
10823   Scheme_Object *shapes, *v, *name;
10824   Scheme_Linklet *in_linklet;
10825   Scheme_Instance *in_instance;
10826   Scheme_Hash_Tree *ht;
10827   Scheme_Bucket *b;
10828 
10829   if (info->cross) {
10830     /* Add new imported instances */
10831     if (info->cross->import_keys->count > SCHEME_VEC_SIZE(linklet->importss)) {
10832       added_imports = SCHEME_VEC_SIZE(linklet->importss) - info->cross->import_keys->count;
10833       v = scheme_make_vector(info->cross->import_keys->count, scheme_make_vector(0, NULL));
10834       for (i = 0; i < SCHEME_VEC_SIZE(linklet->importss); i++) {
10835         SCHEME_VEC_ELS(v)[i] = SCHEME_VEC_ELS(linklet->importss)[i];
10836       }
10837       linklet->importss = v;
10838     }
10839 
10840     /* Add imported variables for each instance */
10841     for (i = 0; i < SCHEME_VEC_SIZE(linklet->importss); i++) {
10842       ht = (Scheme_Hash_Tree *)scheme_eq_hash_tree_get(info->cross->import_syms, scheme_make_integer(i));
10843       if (ht && ((ht->count >> 1) > SCHEME_VEC_SIZE(SCHEME_VEC_ELS(linklet->importss)[i]))) {
10844         Scheme_Object *sym;
10845         v = scheme_make_vector((ht->count >> 1), NULL);
10846         SCHEME_VEC_ELS(linklet->importss)[i] = v;
10847 
10848         for (j = ht->count >> 1; j--; ) {
10849           sym = scheme_eq_hash_tree_get(ht, scheme_make_integer(j));
10850           MZ_ASSERT(sym);
10851           SCHEME_VEC_ELS(v)[j] = sym;
10852         }
10853       }
10854     }
10855   }
10856 
10857   /* Prune unused imports (or, more precisely, tell the resolver how to prune) */
10858   total_used = 0;
10859   total = 0;
10860   for (i = 0; i < SCHEME_VEC_SIZE(linklet->importss); i++) {
10861     used = 0;
10862     k = SCHEME_VEC_SIZE(SCHEME_VEC_ELS(linklet->importss)[i]);
10863     total += k;
10864     if (info->imports_used) {
10865       ht = (Scheme_Hash_Tree *)scheme_eq_hash_tree_get(*info->imports_used, scheme_make_integer(i));
10866       if (!ht) ht = empty_eq_hash_tree;
10867       for (j = 0; j < k; j++) {
10868         if (!scheme_eq_hash_tree_get(ht, scheme_make_integer(j))) {
10869           /* Set symbol to #f to communicate non-use to the resolve pass: */
10870           SCHEME_VEC_ELS(SCHEME_VEC_ELS(linklet->importss)[i])[j] = scheme_false;
10871         } else
10872           used++;
10873       }
10874     } else
10875       used += k;
10876     total_used += used;
10877     if (!used && _import_keys
10878         /* When a key is #f or an instance, then dropping is not allowed */
10879         && ((i >= SCHEME_VEC_SIZE(*_import_keys))
10880             || (SCHEME_TRUEP(SCHEME_VEC_ELS(*_import_keys)[i])
10881                 && !SAME_TYPE(scheme_instance_type, SCHEME_TYPE(SCHEME_VEC_ELS(*_import_keys)[i]))))) {
10882       dropped_imports++;
10883       /* A number commuicates to the resolve pass that the import
10884          instance had that many variables, but we can drop it
10885          entirely */
10886       SCHEME_VEC_ELS(linklet->importss)[i] = scheme_make_integer(k);
10887     }
10888   }
10889   linklet->num_total_imports = total;
10890 
10891   if (dropped_imports || added_imports) {
10892     /* Report a revised set of imports back to the client */
10893     v = scheme_make_vector(SCHEME_VEC_SIZE(linklet->importss) - dropped_imports, NULL);
10894     *_import_keys = v;
10895     used = 0;
10896     for (i = 0; i < SCHEME_VEC_SIZE(linklet->importss); i++) {
10897       if (!SCHEME_INTP(SCHEME_VEC_ELS(linklet->importss)[i])) {
10898         v = scheme_eq_hash_tree_get(info->cross->import_keys, scheme_make_integer(i));
10899         MZ_ASSERT(v);
10900         SCHEME_VEC_ELS((*_import_keys))[used++] = v;
10901       }
10902     }
10903     MZ_ASSERT(used == (SCHEME_VEC_SIZE(linklet->importss) - dropped_imports));
10904   }
10905 
10906   if (info->cross && info->cross->used_import_shape) {
10907     /* The import-shapes vector needs only the imports that will be kept */
10908     shapes = scheme_make_vector(total_used, scheme_false);
10909     linklet->import_shapes = shapes;
10910     k = 0;
10911     for (i = 0; i < SCHEME_VEC_SIZE(linklet->importss); i++) {
10912       if (!SCHEME_INTP(SCHEME_VEC_ELS(linklet->importss)[i])) {
10913         v = scheme_eq_hash_tree_get(info->cross->import_keys, scheme_make_integer(i));
10914         if (v)
10915           v = scheme_eq_hash_tree_get(info->cross->linklets, v);
10916         in_linklet = ((v && SAME_TYPE(SCHEME_TYPE(v), scheme_linklet_type)) ? (Scheme_Linklet *)v : NULL);
10917         in_instance = ((v && SAME_TYPE(SCHEME_TYPE(v), scheme_instance_type)) ? (Scheme_Instance *)v : NULL);
10918         MZ_ASSERT(!in_linklet || SAME_TYPE(in_linklet->so.type, scheme_linklet_type));
10919         MZ_ASSERT(!in_instance || SAME_TYPE(in_instance->iso.so.type, scheme_instance_type));
10920         for (j = 0; j < SCHEME_VEC_SIZE(SCHEME_VEC_ELS(linklet->importss)[i]); j++) {
10921           name = SCHEME_VEC_ELS(SCHEME_VEC_ELS(linklet->importss)[i])[j];
10922           if (SCHEME_TRUEP(name)) {
10923             if (in_linklet && in_linklet->constants) {
10924               v = scheme_hash_get(in_linklet->constants, name);
10925               if (v) {
10926                 if (SCHEME_VECTORP(v) && (SCHEME_VEC_SIZE(v) == 3)) {
10927                   v = scheme_intern_struct_proc_shape(SCHEME_INT_VAL(SCHEME_VEC_ELS(v)[1]));
10928                   SCHEME_VEC_ELS(shapes)[k] = v;
10929                 } else if (SCHEME_VECTORP(v) && (SCHEME_VEC_SIZE(v) == 4)) {
10930                   v = scheme_intern_struct_prop_proc_shape(SCHEME_INT_VAL(SCHEME_VEC_ELS(v)[1]));
10931                   SCHEME_VEC_ELS(shapes)[k] = v;
10932                 } else if (SCHEME_VECTORP(v)) {
10933                   MZ_ASSERT(SCHEME_VEC_SIZE(v) == 2);
10934                   v = scheme_get_or_check_procedure_shape(SCHEME_VEC_ELS(v)[0], NULL, 0);
10935                   SCHEME_VEC_ELS(shapes)[k] = v;
10936                 } else if (SAME_TYPE(SCHEME_TYPE(v), scheme_inline_variant_type)) {
10937                   v = scheme_get_or_check_procedure_shape(v, NULL, 0);
10938                   SCHEME_VEC_ELS(shapes)[k] = v;
10939                 } else if (SAME_OBJ(v, scheme_fixed_key)) {
10940                   SCHEME_VEC_ELS(shapes)[k] = scheme_void;
10941                 } else {
10942                   /* anything else is constant-propagated or irrelevant */
10943                 }
10944               }
10945             } else if (in_instance) {
10946               b = scheme_instance_variable_bucket_or_null(name, in_instance);
10947               if (b && b->val && (((Scheme_Bucket_With_Flags *)b)->flags & GLOB_IS_CONSISTENT)) {
10948                 int imprecise = SCHEME_INSTANCE_FLAGS(in_instance) & SCHEME_INSTANCE_USE_IMPRECISE;
10949                 v = get_value_shape(b->val, imprecise);
10950                 if (v) {
10951                   if (SAME_TYPE(SCHEME_TYPE(v), scheme_struct_proc_shape_type))
10952                     v = scheme_intern_struct_proc_shape(SCHEME_PROC_SHAPE_MODE(v));
10953                   else if (SAME_TYPE(SCHEME_TYPE(v), scheme_struct_prop_proc_shape_type))
10954                     v = scheme_intern_struct_prop_proc_shape(SCHEME_PROP_PROC_SHAPE_MODE(v));
10955                   SCHEME_VEC_ELS(shapes)[k] = v;
10956                 } else
10957                   SCHEME_VEC_ELS(shapes)[k] = scheme_void;
10958               }
10959             }
10960             k++;
10961           }
10962         }
10963       }
10964     }
10965     MZ_ASSERT(k == total_used);
10966   }
10967 }
10968 
get_value_shape(Scheme_Object * v,int imprecise)10969 static Scheme_Object *get_value_shape(Scheme_Object *v, int imprecise)
10970 {
10971   intptr_t s;
10972   Scheme_Object *identity;
10973 
10974   s = scheme_get_or_check_structure_shape(v, NULL);
10975   if (s != -1) {
10976     if (SCHEME_STRUCT_TYPEP(v))
10977       identity = v;
10978     else
10979       identity = SCHEME_PRIM_CLOSURE_ELS(v)[0];
10980     return scheme_make_struct_proc_shape(s, identity);
10981   }
10982 
10983   s = scheme_get_or_check_structure_property_shape(v, NULL);
10984   if (s != -1)
10985     return scheme_make_struct_property_proc_shape(s);
10986 
10987   return scheme_get_or_check_procedure_shape(v, NULL, imprecise);
10988 }
10989 
10990 /*========================================================================*/
10991 /*                         precise GC traversers                          */
10992 /*========================================================================*/
10993 
10994 #ifdef MZ_PRECISE_GC
10995 
10996 START_XFORM_SKIP;
10997 
10998 #include "mzmark_optimize.inc"
10999 
register_traversers(void)11000 static void register_traversers(void)
11001 {
11002   GC_REG_TRAV(scheme_once_used_type, mark_once_used);
11003   GC_REG_TRAV(scheme_rt_optimize_info, mark_optimize_info);
11004 }
11005 
11006 END_XFORM_SKIP;
11007 
11008 #endif
11009