* 2022-01-18 [ci skip]
[ruby-80x24.org.git] / compile.c
blob8fd1fd65f888322aed9245fe900933dc2cbb509d
1 /**********************************************************************
3 compile.c - ruby node tree -> VM instruction sequence
5 $Author$
6 created at: 04/01/01 03:42:15 JST
8 Copyright (C) 2004-2007 Koichi Sasada
10 **********************************************************************/
12 #include "ruby/internal/config.h"
13 #include <math.h>
15 #ifdef HAVE_DLADDR
16 # include <dlfcn.h>
17 #endif
19 #include "encindex.h"
20 #include "gc.h"
21 #include "id_table.h"
22 #include "internal.h"
23 #include "internal/array.h"
24 #include "internal/compile.h"
25 #include "internal/complex.h"
26 #include "internal/encoding.h"
27 #include "internal/error.h"
28 #include "internal/hash.h"
29 #include "internal/numeric.h"
30 #include "internal/object.h"
31 #include "internal/rational.h"
32 #include "internal/re.h"
33 #include "internal/symbol.h"
34 #include "internal/thread.h"
35 #include "internal/variable.h"
36 #include "iseq.h"
37 #include "ruby/re.h"
38 #include "ruby/util.h"
39 #include "vm_core.h"
40 #include "vm_callinfo.h"
41 #include "vm_debug.h"
43 #include "builtin.h"
44 #include "insns.inc"
45 #include "insns_info.inc"
47 #undef RUBY_UNTYPED_DATA_WARNING
48 #define RUBY_UNTYPED_DATA_WARNING 0
50 #define FIXNUM_INC(n, i) ((n)+(INT2FIX(i)&~FIXNUM_FLAG))
51 #define FIXNUM_OR(n, i) ((n)|INT2FIX(i))
53 typedef struct iseq_link_element {
54 enum {
55 ISEQ_ELEMENT_ANCHOR,
56 ISEQ_ELEMENT_LABEL,
57 ISEQ_ELEMENT_INSN,
58 ISEQ_ELEMENT_ADJUST,
59 ISEQ_ELEMENT_TRACE,
60 } type;
61 struct iseq_link_element *next;
62 struct iseq_link_element *prev;
63 } LINK_ELEMENT;
65 typedef struct iseq_link_anchor {
66 LINK_ELEMENT anchor;
67 LINK_ELEMENT *last;
68 } LINK_ANCHOR;
70 typedef enum {
71 LABEL_RESCUE_NONE,
72 LABEL_RESCUE_BEG,
73 LABEL_RESCUE_END,
74 LABEL_RESCUE_TYPE_MAX
75 } LABEL_RESCUE_TYPE;
77 typedef struct iseq_label_data {
78 LINK_ELEMENT link;
79 int label_no;
80 int position;
81 int sc_state;
82 int sp;
83 int refcnt;
84 unsigned int set: 1;
85 unsigned int rescued: 2;
86 unsigned int unremovable: 1;
87 } LABEL;
89 typedef struct iseq_insn_data {
90 LINK_ELEMENT link;
91 enum ruby_vminsn_type insn_id;
92 int operand_size;
93 int sc_state;
94 VALUE *operands;
95 struct {
96 int line_no;
97 int node_id;
98 rb_event_flag_t events;
99 } insn_info;
100 } INSN;
102 typedef struct iseq_adjust_data {
103 LINK_ELEMENT link;
104 LABEL *label;
105 int line_no;
106 } ADJUST;
108 typedef struct iseq_trace_data {
109 LINK_ELEMENT link;
110 rb_event_flag_t event;
111 long data;
112 } TRACE;
114 struct ensure_range {
115 LABEL *begin;
116 LABEL *end;
117 struct ensure_range *next;
120 struct iseq_compile_data_ensure_node_stack {
121 const NODE *ensure_node;
122 struct iseq_compile_data_ensure_node_stack *prev;
123 struct ensure_range *erange;
126 const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO};
129 * debug function(macro) interface depend on CPDEBUG
130 * if it is less than 0, runtime option is in effect.
132 * debug level:
133 * 0: no debug output
134 * 1: show node type
135 * 2: show node important parameters
136 * ...
137 * 5: show other parameters
138 * 10: show every AST array
141 #ifndef CPDEBUG
142 #define CPDEBUG 0
143 #endif
145 #if CPDEBUG >= 0
146 #define compile_debug CPDEBUG
147 #else
148 #define compile_debug ISEQ_COMPILE_DATA(iseq)->option->debug_level
149 #endif
151 #if CPDEBUG
153 #define compile_debug_print_indent(level) \
154 ruby_debug_print_indent((level), compile_debug, gl_node_level * 2)
156 #define debugp(header, value) (void) \
157 (compile_debug_print_indent(1) && \
158 ruby_debug_print_value(1, compile_debug, (header), (value)))
160 #define debugi(header, id) (void) \
161 (compile_debug_print_indent(1) && \
162 ruby_debug_print_id(1, compile_debug, (header), (id)))
164 #define debugp_param(header, value) (void) \
165 (compile_debug_print_indent(1) && \
166 ruby_debug_print_value(1, compile_debug, (header), (value)))
168 #define debugp_verbose(header, value) (void) \
169 (compile_debug_print_indent(2) && \
170 ruby_debug_print_value(2, compile_debug, (header), (value)))
172 #define debugp_verbose_node(header, value) (void) \
173 (compile_debug_print_indent(10) && \
174 ruby_debug_print_value(10, compile_debug, (header), (value)))
176 #define debug_node_start(node) ((void) \
177 (compile_debug_print_indent(1) && \
178 (ruby_debug_print_node(1, CPDEBUG, "", (const NODE *)(node)), gl_node_level)), \
179 gl_node_level++)
181 #define debug_node_end() gl_node_level --
183 #else
185 #define debugi(header, id) ((void)0)
186 #define debugp(header, value) ((void)0)
187 #define debugp_verbose(header, value) ((void)0)
188 #define debugp_verbose_node(header, value) ((void)0)
189 #define debugp_param(header, value) ((void)0)
190 #define debug_node_start(node) ((void)0)
191 #define debug_node_end() ((void)0)
192 #endif
194 #if CPDEBUG > 1 || CPDEBUG < 0
195 #undef printf
196 #define printf ruby_debug_printf
197 #define debugs if (compile_debug_print_indent(1)) ruby_debug_printf
198 #define debug_compile(msg, v) ((void)(compile_debug_print_indent(1) && fputs((msg), stderr)), (v))
199 #else
200 #define debugs if(0)printf
201 #define debug_compile(msg, v) (v)
202 #endif
204 #define LVAR_ERRINFO (1)
206 /* create new label */
207 #define NEW_LABEL(l) new_label_body(iseq, (l))
208 #define LABEL_FORMAT "<L%03d>"
210 #define NEW_ISEQ(node, name, type, line_no) \
211 new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
213 #define NEW_CHILD_ISEQ(node, name, type, line_no) \
214 new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
216 /* add instructions */
217 #define ADD_SEQ(seq1, seq2) \
218 APPEND_LIST((seq1), (seq2))
220 /* add an instruction */
221 #define ADD_INSN(seq, line_node, insn) \
222 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0))
224 /* insert an instruction before next */
225 #define INSERT_BEFORE_INSN(next, line_node, insn) \
226 ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0))
228 /* insert an instruction after prev */
229 #define INSERT_AFTER_INSN(prev, line_node, insn) \
230 ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0))
232 /* add an instruction with some operands (1, 2, 3, 5) */
233 #define ADD_INSN1(seq, line_node, insn, op1) \
234 ADD_ELEM((seq), (LINK_ELEMENT *) \
235 new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1)))
237 /* insert an instruction with some operands (1, 2, 3, 5) before next */
238 #define INSERT_BEFORE_INSN1(next, line_node, insn, op1) \
239 ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) \
240 new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1)))
242 /* insert an instruction with some operands (1, 2, 3, 5) after prev */
243 #define INSERT_AFTER_INSN1(prev, line_node, insn, op1) \
244 ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) \
245 new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1)))
247 #define LABEL_REF(label) ((label)->refcnt++)
249 /* add an instruction with label operand (alias of ADD_INSN1) */
250 #define ADD_INSNL(seq, line_node, insn, label) (ADD_INSN1(seq, line_node, insn, label), LABEL_REF(label))
252 #define ADD_INSN2(seq, line_node, insn, op1, op2) \
253 ADD_ELEM((seq), (LINK_ELEMENT *) \
254 new_insn_body(iseq, (line_node), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
256 #define ADD_INSN3(seq, line_node, insn, op1, op2, op3) \
257 ADD_ELEM((seq), (LINK_ELEMENT *) \
258 new_insn_body(iseq, (line_node), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
260 /* Specific Insn factory */
261 #define ADD_SEND(seq, line_node, id, argc) \
262 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
264 #define ADD_SEND_WITH_FLAG(seq, line_node, id, argc, flag) \
265 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)(flag), NULL)
267 #define ADD_SEND_WITH_BLOCK(seq, line_node, id, argc, block) \
268 ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
270 #define ADD_CALL_RECEIVER(seq, line_node) \
271 ADD_INSN((seq), (line_node), putself)
273 #define ADD_CALL(seq, line_node, id, argc) \
274 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
276 #define ADD_CALL_WITH_BLOCK(seq, line_node, id, argc, block) \
277 ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
279 #define ADD_SEND_R(seq, line_node, id, argc, block, flag, keywords) \
280 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (line_node), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
282 #define ADD_TRACE(seq, event) \
283 ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), 0))
284 #define ADD_TRACE_WITH_DATA(seq, event, data) \
285 ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), (data)))
287 static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level);
288 static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level);
290 #define ADD_GETLOCAL(seq, line_node, idx, level) iseq_add_getlocal(iseq, (seq), (line_node), (idx), (level))
291 #define ADD_SETLOCAL(seq, line_node, idx, level) iseq_add_setlocal(iseq, (seq), (line_node), (idx), (level))
293 /* add label */
294 #define ADD_LABEL(seq, label) \
295 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
297 #define APPEND_LABEL(seq, before, label) \
298 APPEND_ELEM((seq), (before), (LINK_ELEMENT *) (label))
300 #define ADD_ADJUST(seq, line_node, label) \
301 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), nd_line(line_node)))
303 #define ADD_ADJUST_RESTORE(seq, label) \
304 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
306 #define LABEL_UNREMOVABLE(label) \
307 ((label) ? (LABEL_REF(label), (label)->unremovable=1) : 0)
308 #define ADD_CATCH_ENTRY(type, ls, le, iseqv, lc) do { \
309 VALUE _e = rb_ary_new3(5, (type), \
310 (VALUE)(ls) | 1, (VALUE)(le) | 1, \
311 (VALUE)(iseqv), (VALUE)(lc) | 1); \
312 LABEL_UNREMOVABLE(ls); \
313 LABEL_REF(le); \
314 LABEL_REF(lc); \
315 if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) \
316 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, rb_ary_tmp_new(3)); \
317 rb_ary_push(ISEQ_COMPILE_DATA(iseq)->catch_table_ary, freeze_hide_obj(_e)); \
318 } while (0)
320 /* compile node */
321 #define COMPILE(anchor, desc, node) \
322 (debug_compile("== " desc "\n", \
323 iseq_compile_each(iseq, (anchor), (node), 0)))
325 /* compile node, this node's value will be popped */
326 #define COMPILE_POPPED(anchor, desc, node) \
327 (debug_compile("== " desc "\n", \
328 iseq_compile_each(iseq, (anchor), (node), 1)))
330 /* compile node, which is popped when 'popped' is true */
331 #define COMPILE_(anchor, desc, node, popped) \
332 (debug_compile("== " desc "\n", \
333 iseq_compile_each(iseq, (anchor), (node), (popped))))
335 #define COMPILE_RECV(anchor, desc, node) \
336 (private_recv_p(node) ? \
337 (ADD_INSN(anchor, node, putself), VM_CALL_FCALL) : \
338 COMPILE(anchor, desc, node->nd_recv) ? 0 : -1)
340 #define OPERAND_AT(insn, idx) \
341 (((INSN*)(insn))->operands[(idx)])
343 #define INSN_OF(insn) \
344 (((INSN*)(insn))->insn_id)
346 #define IS_INSN(link) ((link)->type == ISEQ_ELEMENT_INSN)
347 #define IS_LABEL(link) ((link)->type == ISEQ_ELEMENT_LABEL)
348 #define IS_ADJUST(link) ((link)->type == ISEQ_ELEMENT_ADJUST)
349 #define IS_TRACE(link) ((link)->type == ISEQ_ELEMENT_TRACE)
350 #define IS_INSN_ID(iobj, insn) (INSN_OF(iobj) == BIN(insn))
351 #define IS_NEXT_INSN_ID(link, insn) \
352 ((link)->next && IS_INSN((link)->next) && IS_INSN_ID((link)->next, insn))
354 /* error */
355 #if CPDEBUG > 0
356 RBIMPL_ATTR_NORETURN()
357 #endif
358 RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
359 static void
360 append_compile_error(const rb_iseq_t *iseq, int line, const char *fmt, ...)
362 VALUE err_info = ISEQ_COMPILE_DATA(iseq)->err_info;
363 VALUE file = rb_iseq_path(iseq);
364 VALUE err = err_info == Qtrue ? Qfalse : err_info;
365 va_list args;
367 va_start(args, fmt);
368 err = rb_syntax_error_append(err, file, line, -1, NULL, fmt, args);
369 va_end(args);
370 if (NIL_P(err_info)) {
371 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err);
372 rb_set_errinfo(err);
374 else if (!err_info) {
375 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, Qtrue);
377 if (compile_debug) {
378 if (SPECIAL_CONST_P(err)) err = rb_eSyntaxError;
379 rb_exc_fatal(err);
383 #if 0
384 static void
385 compile_bug(rb_iseq_t *iseq, int line, const char *fmt, ...)
387 va_list args;
388 va_start(args, fmt);
389 rb_report_bug_valist(rb_iseq_path(iseq), line, fmt, args);
390 va_end(args);
391 abort();
393 #endif
395 #define COMPILE_ERROR append_compile_error
397 #define ERROR_ARGS_AT(n) iseq, nd_line(n),
398 #define ERROR_ARGS ERROR_ARGS_AT(node)
400 #define EXPECT_NODE(prefix, node, ndtype, errval) \
401 do { \
402 const NODE *error_node = (node); \
403 enum node_type error_type = nd_type(error_node); \
404 if (error_type != (ndtype)) { \
405 COMPILE_ERROR(ERROR_ARGS_AT(error_node) \
406 prefix ": " #ndtype " is expected, but %s", \
407 ruby_node_name(error_type)); \
408 return errval; \
410 } while (0)
412 #define EXPECT_NODE_NONULL(prefix, parent, ndtype, errval) \
413 do { \
414 COMPILE_ERROR(ERROR_ARGS_AT(parent) \
415 prefix ": must be " #ndtype ", but 0"); \
416 return errval; \
417 } while (0)
419 #define UNKNOWN_NODE(prefix, node, errval) \
420 do { \
421 const NODE *error_node = (node); \
422 COMPILE_ERROR(ERROR_ARGS_AT(error_node) prefix ": unknown node (%s)", \
423 ruby_node_name(nd_type(error_node))); \
424 return errval; \
425 } while (0)
427 #define COMPILE_OK 1
428 #define COMPILE_NG 0
430 #define CHECK(sub) if (!(sub)) {BEFORE_RETURN;return COMPILE_NG;}
431 #define NO_CHECK(sub) (void)(sub)
432 #define BEFORE_RETURN
434 /* leave name uninitialized so that compiler warn if INIT_ANCHOR is
435 * missing */
436 #define DECL_ANCHOR(name) \
437 LINK_ANCHOR name[1] = {{{ISEQ_ELEMENT_ANCHOR,},}}
438 #define INIT_ANCHOR(name) \
439 (name->last = &name->anchor)
441 static inline VALUE
442 freeze_hide_obj(VALUE obj)
444 OBJ_FREEZE(obj);
445 RBASIC_CLEAR_CLASS(obj);
446 return obj;
449 #include "optinsn.inc"
450 #if OPT_INSTRUCTIONS_UNIFICATION
451 #include "optunifs.inc"
452 #endif
454 /* for debug */
455 #if CPDEBUG < 0
456 #define ISEQ_ARG iseq,
457 #define ISEQ_ARG_DECLARE rb_iseq_t *iseq,
458 #else
459 #define ISEQ_ARG
460 #define ISEQ_ARG_DECLARE
461 #endif
463 #if CPDEBUG
464 #define gl_node_level ISEQ_COMPILE_DATA(iseq)->node_level
465 #endif
467 static void dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest);
468 static void dump_disasm_list(const LINK_ELEMENT *elem);
470 static int insn_data_length(INSN *iobj);
471 static int calc_sp_depth(int depth, INSN *iobj);
473 static INSN *new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_type insn_id, int argc, ...);
474 static LABEL *new_label_body(rb_iseq_t *iseq, long line);
475 static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line);
476 static TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data);
479 static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, const NODE *n, int);
480 static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
481 static int iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
482 static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
483 static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
485 static int iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl);
486 static int iseq_set_exception_local_table(rb_iseq_t *iseq);
487 static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const NODE *const node);
489 static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
490 static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
491 static int iseq_set_exception_table(rb_iseq_t *iseq);
492 static int iseq_set_optargs_table(rb_iseq_t *iseq);
494 static int compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr);
495 static int compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped);
498 * To make Array to LinkedList, use link_anchor
501 static void
502 verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *const anchor)
504 #if CPDEBUG
505 int flag = 0;
506 LINK_ELEMENT *list, *plist;
508 if (!compile_debug) return;
510 list = anchor->anchor.next;
511 plist = &anchor->anchor;
512 while (list) {
513 if (plist != list->prev) {
514 flag += 1;
516 plist = list;
517 list = list->next;
520 if (anchor->last != plist && anchor->last != 0) {
521 flag |= 0x70000;
524 if (flag != 0) {
525 rb_bug("list verify error: %08x (%s)", flag, info);
527 #endif
529 #if CPDEBUG < 0
530 #define verify_list(info, anchor) verify_list(iseq, (info), (anchor))
531 #endif
533 static void
534 verify_call_cache(rb_iseq_t *iseq)
536 #if CPDEBUG
537 // fprintf(stderr, "ci_size:%d\t", iseq->body->ci_size); rp(iseq);
539 VALUE *original = rb_iseq_original_iseq(iseq);
540 size_t i = 0;
541 while (i < iseq->body->iseq_size) {
542 VALUE insn = original[i];
543 const char *types = insn_op_types(insn);
545 for (int j=0; types[j]; j++) {
546 if (types[j] == TS_CALLDATA) {
547 struct rb_call_data *cd = (struct rb_call_data *)original[i+j+1];
548 const struct rb_callinfo *ci = cd->ci;
549 const struct rb_callcache *cc = cd->cc;
550 if (cc != vm_cc_empty()) {
551 vm_ci_dump(ci);
552 rb_bug("call cache is not initialized by vm_cc_empty()");
556 i += insn_len(insn);
559 for (unsigned int i=0; i<iseq->body->ci_size; i++) {
560 struct rb_call_data *cd = &iseq->body->call_data[i];
561 const struct rb_callinfo *ci = cd->ci;
562 const struct rb_callcache *cc = cd->cc;
563 if (cc != NULL && cc != vm_cc_empty()) {
564 vm_ci_dump(ci);
565 rb_bug("call cache is not initialized by vm_cc_empty()");
568 #endif
572 * elem1, elem2 => elem1, elem2, elem
574 static void
575 ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *elem)
577 elem->prev = anchor->last;
578 anchor->last->next = elem;
579 anchor->last = elem;
580 verify_list("add", anchor);
584 * elem1, before, elem2 => elem1, before, elem, elem2
586 static void
587 APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *before, LINK_ELEMENT *elem)
589 elem->prev = before;
590 elem->next = before->next;
591 elem->next->prev = elem;
592 before->next = elem;
593 if (before == anchor->last) anchor->last = elem;
594 verify_list("add", anchor);
596 #if CPDEBUG < 0
597 #define ADD_ELEM(anchor, elem) ADD_ELEM(iseq, (anchor), (elem))
598 #define APPEND_ELEM(anchor, before, elem) APPEND_ELEM(iseq, (anchor), (before), (elem))
599 #endif
601 static int
602 branch_coverage_valid_p(rb_iseq_t *iseq, int first_line)
604 if (!ISEQ_COVERAGE(iseq)) return 0;
605 if (!ISEQ_BRANCH_COVERAGE(iseq)) return 0;
606 if (first_line <= 0) return 0;
607 return 1;
610 static VALUE
611 decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type)
613 const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node);
614 const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node);
616 if (!branch_coverage_valid_p(iseq, first_lineno)) return Qundef;
619 * if !structure[node]
620 * structure[node] = [type, first_lineno, first_column, last_lineno, last_column, branches = {}]
621 * else
622 * branches = structure[node][5]
623 * end
626 VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0);
627 VALUE key = (VALUE)node | 1; // FIXNUM for hash key
628 VALUE branch_base = rb_hash_aref(structure, key);
629 VALUE branches;
631 if (NIL_P(branch_base)) {
632 branch_base = rb_ary_tmp_new(6);
633 rb_hash_aset(structure, key, branch_base);
634 rb_ary_push(branch_base, ID2SYM(rb_intern(type)));
635 rb_ary_push(branch_base, INT2FIX(first_lineno));
636 rb_ary_push(branch_base, INT2FIX(first_column));
637 rb_ary_push(branch_base, INT2FIX(last_lineno));
638 rb_ary_push(branch_base, INT2FIX(last_column));
639 branches = rb_hash_new();
640 rb_obj_hide(branches);
641 rb_ary_push(branch_base, branches);
643 else {
644 branches = RARRAY_AREF(branch_base, 5);
647 return branches;
650 static NODE
651 generate_dummy_line_node(int lineno, int node_id)
653 NODE dummy = { 0 };
654 nd_set_line(&dummy, lineno);
655 nd_set_node_id(&dummy, node_id);
656 return dummy;
659 static void
660 add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *node, int branch_id, const char *type, VALUE branches)
662 const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node);
663 const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node);
665 if (!branch_coverage_valid_p(iseq, first_lineno)) return;
668 * if !branches[branch_id]
669 * branches[branch_id] = [type, first_lineno, first_column, last_lineno, last_column, counter_idx]
670 * else
671 * counter_idx= branches[branch_id][5]
672 * end
675 VALUE key = INT2FIX(branch_id);
676 VALUE branch = rb_hash_aref(branches, key);
677 long counter_idx;
679 if (NIL_P(branch)) {
680 branch = rb_ary_tmp_new(6);
681 rb_hash_aset(branches, key, branch);
682 rb_ary_push(branch, ID2SYM(rb_intern(type)));
683 rb_ary_push(branch, INT2FIX(first_lineno));
684 rb_ary_push(branch, INT2FIX(first_column));
685 rb_ary_push(branch, INT2FIX(last_lineno));
686 rb_ary_push(branch, INT2FIX(last_column));
687 VALUE counters = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 1);
688 counter_idx = RARRAY_LEN(counters);
689 rb_ary_push(branch, LONG2FIX(counter_idx));
690 rb_ary_push(counters, INT2FIX(0));
692 else {
693 counter_idx = FIX2LONG(RARRAY_AREF(branch, 5));
696 ADD_TRACE_WITH_DATA(seq, RUBY_EVENT_COVERAGE_BRANCH, counter_idx);
698 NODE dummy_line_node = generate_dummy_line_node(last_lineno, nd_node_id(node));
699 ADD_INSN(seq, &dummy_line_node, nop);
702 #define ISEQ_LAST_LINE(iseq) (ISEQ_COMPILE_DATA(iseq)->last_line)
704 static int
705 validate_label(st_data_t name, st_data_t label, st_data_t arg)
707 rb_iseq_t *iseq = (rb_iseq_t *)arg;
708 LABEL *lobj = (LABEL *)label;
709 if (!lobj->link.next) {
710 do {
711 COMPILE_ERROR(iseq, lobj->position,
712 "%"PRIsVALUE": undefined label",
713 rb_sym2str((VALUE)name));
714 } while (0);
716 return ST_CONTINUE;
719 static void
720 validate_labels(rb_iseq_t *iseq, st_table *labels_table)
722 st_foreach(labels_table, validate_label, (st_data_t)iseq);
723 st_free_table(labels_table);
726 VALUE
727 rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func * ifunc)
729 DECL_ANCHOR(ret);
730 INIT_ANCHOR(ret);
732 (*ifunc->func)(iseq, ret, ifunc->data);
734 NODE dummy_line_node = generate_dummy_line_node(ISEQ_COMPILE_DATA(iseq)->last_line, -1);
735 ADD_INSN(ret, &dummy_line_node, leave);
737 CHECK(iseq_setup_insn(iseq, ret));
738 return iseq_setup(iseq, ret);
741 VALUE
742 rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
744 DECL_ANCHOR(ret);
745 INIT_ANCHOR(ret);
747 if (IMEMO_TYPE_P(node, imemo_ifunc)) {
748 rb_raise(rb_eArgError, "unexpected imemo_ifunc");
751 if (node == 0) {
752 NO_CHECK(COMPILE(ret, "nil", node));
753 iseq_set_local_table(iseq, 0);
755 /* assume node is T_NODE */
756 else if (nd_type_p(node, NODE_SCOPE)) {
757 /* iseq type of top, method, class, block */
758 iseq_set_local_table(iseq, node->nd_tbl);
759 iseq_set_arguments(iseq, ret, node->nd_args);
761 switch (iseq->body->type) {
762 case ISEQ_TYPE_BLOCK:
764 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
765 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
767 start->rescued = LABEL_RESCUE_BEG;
768 end->rescued = LABEL_RESCUE_END;
770 ADD_TRACE(ret, RUBY_EVENT_B_CALL);
771 NODE dummy_line_node = generate_dummy_line_node(FIX2INT(iseq->body->location.first_lineno), -1);
772 ADD_INSN (ret, &dummy_line_node, nop);
773 ADD_LABEL(ret, start);
774 CHECK(COMPILE(ret, "block body", node->nd_body));
775 ADD_LABEL(ret, end);
776 ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
777 ISEQ_COMPILE_DATA(iseq)->last_line = iseq->body->location.code_location.end_pos.lineno;
779 /* wide range catch handler must put at last */
780 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
781 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
782 break;
784 case ISEQ_TYPE_CLASS:
786 ADD_TRACE(ret, RUBY_EVENT_CLASS);
787 CHECK(COMPILE(ret, "scoped node", node->nd_body));
788 ADD_TRACE(ret, RUBY_EVENT_END);
789 ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
790 break;
792 case ISEQ_TYPE_METHOD:
794 ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
795 ADD_TRACE(ret, RUBY_EVENT_CALL);
796 CHECK(COMPILE(ret, "scoped node", node->nd_body));
797 ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
798 ADD_TRACE(ret, RUBY_EVENT_RETURN);
799 ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
800 break;
802 default: {
803 CHECK(COMPILE(ret, "scoped node", node->nd_body));
804 break;
808 else {
809 const char *m;
810 #define INVALID_ISEQ_TYPE(type) \
811 ISEQ_TYPE_##type: m = #type; goto invalid_iseq_type
812 switch (iseq->body->type) {
813 case INVALID_ISEQ_TYPE(METHOD);
814 case INVALID_ISEQ_TYPE(CLASS);
815 case INVALID_ISEQ_TYPE(BLOCK);
816 case INVALID_ISEQ_TYPE(EVAL);
817 case INVALID_ISEQ_TYPE(MAIN);
818 case INVALID_ISEQ_TYPE(TOP);
819 #undef INVALID_ISEQ_TYPE /* invalid iseq types end */
820 case ISEQ_TYPE_RESCUE:
821 iseq_set_exception_local_table(iseq);
822 CHECK(COMPILE(ret, "rescue", node));
823 break;
824 case ISEQ_TYPE_ENSURE:
825 iseq_set_exception_local_table(iseq);
826 CHECK(COMPILE_POPPED(ret, "ensure", node));
827 break;
828 case ISEQ_TYPE_PLAIN:
829 CHECK(COMPILE(ret, "ensure", node));
830 break;
831 default:
832 COMPILE_ERROR(ERROR_ARGS "unknown scope: %d", iseq->body->type);
833 return COMPILE_NG;
834 invalid_iseq_type:
835 COMPILE_ERROR(ERROR_ARGS "compile/ISEQ_TYPE_%s should not be reached", m);
836 return COMPILE_NG;
840 if (iseq->body->type == ISEQ_TYPE_RESCUE || iseq->body->type == ISEQ_TYPE_ENSURE) {
841 NODE dummy_line_node = generate_dummy_line_node(0, -1);
842 ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0);
843 ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ );
845 else {
846 NODE dummy_line_node = generate_dummy_line_node(ISEQ_COMPILE_DATA(iseq)->last_line, -1);
847 ADD_INSN(ret, &dummy_line_node, leave);
850 #if OPT_SUPPORT_JOKE
851 if (ISEQ_COMPILE_DATA(iseq)->labels_table) {
852 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
853 ISEQ_COMPILE_DATA(iseq)->labels_table = 0;
854 validate_labels(iseq, labels_table);
856 #endif
857 CHECK(iseq_setup_insn(iseq, ret));
858 return iseq_setup(iseq, ret);
861 static int
862 rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
864 #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
865 const void * const *table = rb_vm_get_insns_address_table();
866 unsigned int i;
867 VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
869 for (i = 0; i < iseq->body->iseq_size; /* */ ) {
870 int insn = (int)iseq->body->iseq_encoded[i];
871 int len = insn_len(insn);
872 encoded[i] = (VALUE)table[insn];
873 i += len;
875 FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
876 #endif
877 return COMPILE_OK;
880 VALUE *
881 rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */
883 VALUE *original_code;
885 if (ISEQ_ORIGINAL_ISEQ(iseq)) return ISEQ_ORIGINAL_ISEQ(iseq);
886 original_code = ISEQ_ORIGINAL_ISEQ_ALLOC(iseq, iseq->body->iseq_size);
887 MEMCPY(original_code, iseq->body->iseq_encoded, VALUE, iseq->body->iseq_size);
889 #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
891 unsigned int i;
893 for (i = 0; i < iseq->body->iseq_size; /* */ ) {
894 const void *addr = (const void *)original_code[i];
895 const int insn = rb_vm_insn_addr2insn(addr);
897 original_code[i] = insn;
898 i += insn_len(insn);
901 #endif
902 return original_code;
905 /*********************************************/
906 /* definition of data structure for compiler */
907 /*********************************************/
910 * On 32-bit SPARC, GCC by default generates SPARC V7 code that may require
911 * 8-byte word alignment. On the other hand, Oracle Solaris Studio seems to
912 * generate SPARCV8PLUS code with unaligned memory access instructions.
913 * That is why the STRICT_ALIGNMENT is defined only with GCC.
915 #if defined(__sparc) && SIZEOF_VOIDP == 4 && defined(__GNUC__)
916 #define STRICT_ALIGNMENT
917 #endif
920 * Some OpenBSD platforms (including sparc64) require strict alignment.
922 #if defined(__OpenBSD__)
923 #include <sys/endian.h>
924 #ifdef __STRICT_ALIGNMENT
925 #define STRICT_ALIGNMENT
926 #endif
927 #endif
929 #ifdef STRICT_ALIGNMENT
930 #if defined(HAVE_TRUE_LONG_LONG) && SIZEOF_LONG_LONG > SIZEOF_VALUE
931 #define ALIGNMENT_SIZE SIZEOF_LONG_LONG
932 #else
933 #define ALIGNMENT_SIZE SIZEOF_VALUE
934 #endif
935 #define PADDING_SIZE_MAX ((size_t)((ALIGNMENT_SIZE) - 1))
936 #define ALIGNMENT_SIZE_MASK PADDING_SIZE_MAX
937 /* Note: ALIGNMENT_SIZE == (2 ** N) is expected. */
938 #else
939 #define PADDING_SIZE_MAX 0
940 #endif /* STRICT_ALIGNMENT */
942 #ifdef STRICT_ALIGNMENT
943 /* calculate padding size for aligned memory access */
944 static size_t
945 calc_padding(void *ptr, size_t size)
947 size_t mis;
948 size_t padding = 0;
950 mis = (size_t)ptr & ALIGNMENT_SIZE_MASK;
951 if (mis > 0) {
952 padding = ALIGNMENT_SIZE - mis;
955 * On 32-bit sparc or equivalents, when a single VALUE is requested
956 * and padding == sizeof(VALUE), it is clear that no padding is needed.
958 #if ALIGNMENT_SIZE > SIZEOF_VALUE
959 if (size == sizeof(VALUE) && padding == sizeof(VALUE)) {
960 padding = 0;
962 #endif
964 return padding;
966 #endif /* STRICT_ALIGNMENT */
968 static void *
969 compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t size)
971 void *ptr = 0;
972 struct iseq_compile_data_storage *storage = *arena;
973 #ifdef STRICT_ALIGNMENT
974 size_t padding = calc_padding((void *)&storage->buff[storage->pos], size);
975 #else
976 const size_t padding = 0; /* expected to be optimized by compiler */
977 #endif /* STRICT_ALIGNMENT */
979 if (size >= INT_MAX - padding) rb_memerror();
980 if (storage->pos + size + padding > storage->size) {
981 unsigned int alloc_size = storage->size;
983 while (alloc_size < size + PADDING_SIZE_MAX) {
984 if (alloc_size >= INT_MAX / 2) rb_memerror();
985 alloc_size *= 2;
987 storage->next = (void *)ALLOC_N(char, alloc_size +
988 offsetof(struct iseq_compile_data_storage, buff));
989 storage = *arena = storage->next;
990 storage->next = 0;
991 storage->pos = 0;
992 storage->size = alloc_size;
993 #ifdef STRICT_ALIGNMENT
994 padding = calc_padding((void *)&storage->buff[storage->pos], size);
995 #endif /* STRICT_ALIGNMENT */
998 #ifdef STRICT_ALIGNMENT
999 storage->pos += (int)padding;
1000 #endif /* STRICT_ALIGNMENT */
1002 ptr = (void *)&storage->buff[storage->pos];
1003 storage->pos += (int)size;
1004 return ptr;
1007 static void *
1008 compile_data_alloc(rb_iseq_t *iseq, size_t size)
1010 struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->node.storage_current;
1011 return compile_data_alloc_with_arena(arena, size);
1014 static inline void *
1015 compile_data_alloc2(rb_iseq_t *iseq, size_t x, size_t y)
1017 size_t size = rb_size_mul_or_raise(x, y, rb_eRuntimeError);
1018 return compile_data_alloc(iseq, size);
1021 static inline void *
1022 compile_data_calloc2(rb_iseq_t *iseq, size_t x, size_t y)
1024 size_t size = rb_size_mul_or_raise(x, y, rb_eRuntimeError);
1025 void *p = compile_data_alloc(iseq, size);
1026 memset(p, 0, size);
1027 return p;
1030 static INSN *
1031 compile_data_alloc_insn(rb_iseq_t *iseq)
1033 struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->insn.storage_current;
1034 return (INSN *)compile_data_alloc_with_arena(arena, sizeof(INSN));
1037 static LABEL *
1038 compile_data_alloc_label(rb_iseq_t *iseq)
1040 return (LABEL *)compile_data_alloc(iseq, sizeof(LABEL));
1043 static ADJUST *
1044 compile_data_alloc_adjust(rb_iseq_t *iseq)
1046 return (ADJUST *)compile_data_alloc(iseq, sizeof(ADJUST));
1049 static TRACE *
1050 compile_data_alloc_trace(rb_iseq_t *iseq)
1052 return (TRACE *)compile_data_alloc(iseq, sizeof(TRACE));
1056 * elem1, elemX => elem1, elem2, elemX
1058 static void
1059 ELEM_INSERT_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1061 elem2->next = elem1->next;
1062 elem2->prev = elem1;
1063 elem1->next = elem2;
1064 if (elem2->next) {
1065 elem2->next->prev = elem2;
1070 * elem1, elemX => elemX, elem2, elem1
1072 static void
1073 ELEM_INSERT_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1075 elem2->prev = elem1->prev;
1076 elem2->next = elem1;
1077 elem1->prev = elem2;
1078 if (elem2->prev) {
1079 elem2->prev->next = elem2;
1084 * elemX, elem1, elemY => elemX, elem2, elemY
1086 static void
1087 ELEM_REPLACE(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1089 elem2->prev = elem1->prev;
1090 elem2->next = elem1->next;
1091 if (elem1->prev) {
1092 elem1->prev->next = elem2;
1094 if (elem1->next) {
1095 elem1->next->prev = elem2;
1099 static void
1100 ELEM_REMOVE(LINK_ELEMENT *elem)
1102 elem->prev->next = elem->next;
1103 if (elem->next) {
1104 elem->next->prev = elem->prev;
1108 static LINK_ELEMENT *
1109 FIRST_ELEMENT(const LINK_ANCHOR *const anchor)
1111 return anchor->anchor.next;
1114 static LINK_ELEMENT *
1115 LAST_ELEMENT(LINK_ANCHOR *const anchor)
1117 return anchor->last;
1120 static LINK_ELEMENT *
1121 ELEM_FIRST_INSN(LINK_ELEMENT *elem)
1123 while (elem) {
1124 switch (elem->type) {
1125 case ISEQ_ELEMENT_INSN:
1126 case ISEQ_ELEMENT_ADJUST:
1127 return elem;
1128 default:
1129 elem = elem->next;
1132 return NULL;
1135 static int
1136 LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor)
1138 LINK_ELEMENT *first_insn = ELEM_FIRST_INSN(FIRST_ELEMENT(anchor));
1139 if (first_insn != NULL &&
1140 ELEM_FIRST_INSN(first_insn->next) == NULL) {
1141 return TRUE;
1143 else {
1144 return FALSE;
1148 static int
1149 LIST_INSN_SIZE_ZERO(const LINK_ANCHOR *const anchor)
1151 if (ELEM_FIRST_INSN(FIRST_ELEMENT(anchor)) == NULL) {
1152 return TRUE;
1154 else {
1155 return FALSE;
1160 * anc1: e1, e2, e3
1161 * anc2: e4, e5
1162 *#=>
1163 * anc1: e1, e2, e3, e4, e5
1164 * anc2: e4, e5 (broken)
1166 static void
1167 APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *const anc1, LINK_ANCHOR *const anc2)
1169 if (anc2->anchor.next) {
1170 anc1->last->next = anc2->anchor.next;
1171 anc2->anchor.next->prev = anc1->last;
1172 anc1->last = anc2->last;
1174 verify_list("append", anc1);
1176 #if CPDEBUG < 0
1177 #define APPEND_LIST(anc1, anc2) APPEND_LIST(iseq, (anc1), (anc2))
1178 #endif
1180 #if CPDEBUG && 0
1181 static void
1182 debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *cur)
1184 LINK_ELEMENT *list = FIRST_ELEMENT(anchor);
1185 printf("----\n");
1186 printf("anch: %p, frst: %p, last: %p\n", (void *)&anchor->anchor,
1187 (void *)anchor->anchor.next, (void *)anchor->last);
1188 while (list) {
1189 printf("curr: %p, next: %p, prev: %p, type: %d\n", (void *)list, (void *)list->next,
1190 (void *)list->prev, (int)list->type);
1191 list = list->next;
1193 printf("----\n");
1195 dump_disasm_list_with_cursor(anchor->anchor.next, cur, 0);
1196 verify_list("debug list", anchor);
1198 #if CPDEBUG < 0
1199 #define debug_list(anc, cur) debug_list(iseq, (anc), (cur))
1200 #endif
1201 #else
1202 #define debug_list(anc, cur) ((void)0)
1203 #endif
1205 static TRACE *
1206 new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data)
1208 TRACE *trace = compile_data_alloc_trace(iseq);
1210 trace->link.type = ISEQ_ELEMENT_TRACE;
1211 trace->link.next = NULL;
1212 trace->event = event;
1213 trace->data = data;
1215 return trace;
1218 static LABEL *
1219 new_label_body(rb_iseq_t *iseq, long line)
1221 LABEL *labelobj = compile_data_alloc_label(iseq);
1223 labelobj->link.type = ISEQ_ELEMENT_LABEL;
1224 labelobj->link.next = 0;
1226 labelobj->label_no = ISEQ_COMPILE_DATA(iseq)->label_no++;
1227 labelobj->sc_state = 0;
1228 labelobj->sp = -1;
1229 labelobj->refcnt = 0;
1230 labelobj->set = 0;
1231 labelobj->rescued = LABEL_RESCUE_NONE;
1232 labelobj->unremovable = 0;
1233 return labelobj;
1236 static ADJUST *
1237 new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
1239 ADJUST *adjust = compile_data_alloc_adjust(iseq);
1240 adjust->link.type = ISEQ_ELEMENT_ADJUST;
1241 adjust->link.next = 0;
1242 adjust->label = label;
1243 adjust->line_no = line;
1244 LABEL_UNREMOVABLE(label);
1245 return adjust;
1248 static INSN *
1249 new_insn_core(rb_iseq_t *iseq, const NODE *line_node,
1250 int insn_id, int argc, VALUE *argv)
1252 INSN *iobj = compile_data_alloc_insn(iseq);
1254 /* printf("insn_id: %d, line: %d\n", insn_id, nd_line(line_node)); */
1256 iobj->link.type = ISEQ_ELEMENT_INSN;
1257 iobj->link.next = 0;
1258 iobj->insn_id = insn_id;
1259 iobj->insn_info.line_no = nd_line(line_node);
1260 iobj->insn_info.node_id = nd_node_id(line_node);
1261 iobj->insn_info.events = 0;
1262 iobj->operands = argv;
1263 iobj->operand_size = argc;
1264 iobj->sc_state = 0;
1265 return iobj;
1268 static INSN *
1269 new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_type insn_id, int argc, ...)
1271 VALUE *operands = 0;
1272 va_list argv;
1273 if (argc > 0) {
1274 int i;
1275 va_start(argv, argc);
1276 operands = compile_data_alloc2(iseq, sizeof(VALUE), argc);
1277 for (i = 0; i < argc; i++) {
1278 VALUE v = va_arg(argv, VALUE);
1279 operands[i] = v;
1281 va_end(argv);
1283 return new_insn_core(iseq, line_node, insn_id, argc, operands);
1286 static const struct rb_callinfo *
1287 new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_callinfo_kwarg *kw_arg, int has_blockiseq)
1289 VM_ASSERT(argc >= 0);
1291 if (!(flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT)) &&
1292 kw_arg == NULL && !has_blockiseq) {
1293 flag |= VM_CALL_ARGS_SIMPLE;
1296 if (kw_arg) {
1297 flag |= VM_CALL_KWARG;
1298 argc += kw_arg->keyword_len;
1301 // fprintf(stderr, "[%d] id:%s\t", (int)iseq->body->ci_size, rb_id2name(mid)); rp(iseq);
1302 iseq->body->ci_size++;
1303 const struct rb_callinfo *ci = vm_ci_new(mid, flag, argc, kw_arg);
1304 RB_OBJ_WRITTEN(iseq, Qundef, ci);
1305 return ci;
1308 static INSN *
1309 new_insn_send(rb_iseq_t *iseq, const NODE *const line_node, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords)
1311 VALUE *operands = compile_data_calloc2(iseq, sizeof(VALUE), 2);
1312 VALUE ci = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL);
1313 operands[0] = ci;
1314 operands[1] = (VALUE)blockiseq;
1315 if (blockiseq) {
1316 RB_OBJ_WRITTEN(iseq, Qundef, blockiseq);
1318 INSN *insn = new_insn_core(iseq, line_node, BIN(send), 2, operands);
1319 RB_OBJ_WRITTEN(iseq, Qundef, ci);
1320 RB_GC_GUARD(ci);
1321 return insn;
1324 static rb_iseq_t *
1325 new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
1326 VALUE name, const rb_iseq_t *parent, enum iseq_type type, int line_no)
1328 rb_iseq_t *ret_iseq;
1329 rb_ast_body_t ast;
1331 ast.root = node;
1332 ast.compile_option = 0;
1333 ast.script_lines = iseq->body->variable.script_lines;
1335 debugs("[new_child_iseq]> ---------------------------------------\n");
1336 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1337 ret_iseq = rb_iseq_new_with_opt(&ast, name,
1338 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1339 INT2FIX(line_no), parent,
1340 isolated_depth ? isolated_depth + 1 : 0,
1341 type, ISEQ_COMPILE_DATA(iseq)->option);
1342 debugs("[new_child_iseq]< ---------------------------------------\n");
1343 return ret_iseq;
1346 static rb_iseq_t *
1347 new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func *ifunc,
1348 VALUE name, const rb_iseq_t *parent, enum iseq_type type, int line_no)
1350 rb_iseq_t *ret_iseq;
1352 debugs("[new_child_iseq_with_callback]> ---------------------------------------\n");
1353 ret_iseq = rb_iseq_new_with_callback(ifunc, name,
1354 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1355 INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option);
1356 debugs("[new_child_iseq_with_callback]< ---------------------------------------\n");
1357 return ret_iseq;
1360 static void
1361 set_catch_except_p(struct rb_iseq_constant_body *body)
1363 body->catch_except_p = TRUE;
1364 if (body->parent_iseq != NULL) {
1365 set_catch_except_p(body->parent_iseq->body);
1369 /* Set body->catch_except_p to TRUE if the ISeq may catch an exception. If it is FALSE,
1370 JIT-ed code may be optimized. If we are extremely conservative, we should set TRUE
1371 if catch table exists. But we want to optimize while loop, which always has catch
1372 table entries for break/next/redo.
1374 So this function sets TRUE for limited ISeqs with break/next/redo catch table entries
1375 whose child ISeq would really raise an exception. */
1376 static void
1377 update_catch_except_flags(struct rb_iseq_constant_body *body)
1379 unsigned int pos;
1380 size_t i;
1381 int insn;
1382 const struct iseq_catch_table *ct = body->catch_table;
1384 /* This assumes that a block has parent_iseq which may catch an exception from the block, and that
1385 BREAK/NEXT/REDO catch table entries are used only when `throw` insn is used in the block. */
1386 pos = 0;
1387 while (pos < body->iseq_size) {
1388 insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
1389 if (insn == BIN(throw)) {
1390 set_catch_except_p(body);
1391 break;
1393 pos += insn_len(insn);
1396 if (ct == NULL)
1397 return;
1399 for (i = 0; i < ct->size; i++) {
1400 const struct iseq_catch_table_entry *entry =
1401 UNALIGNED_MEMBER_PTR(ct, entries[i]);
1402 if (entry->type != CATCH_TYPE_BREAK
1403 && entry->type != CATCH_TYPE_NEXT
1404 && entry->type != CATCH_TYPE_REDO) {
1405 body->catch_except_p = TRUE;
1406 break;
1411 static void
1412 iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq)
1414 VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
1415 if (NIL_P(catch_table_ary)) return;
1416 unsigned int i, tlen = (unsigned int)RARRAY_LEN(catch_table_ary);
1417 const VALUE *tptr = RARRAY_CONST_PTR_TRANSIENT(catch_table_ary);
1418 for (i = 0; i < tlen; i++) {
1419 const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]);
1420 LINK_ELEMENT *end = (LINK_ELEMENT *)(ptr[2] & ~1);
1421 LINK_ELEMENT *cont = (LINK_ELEMENT *)(ptr[4] & ~1);
1422 LINK_ELEMENT *e;
1424 enum catch_type ct = (enum catch_type)(ptr[0] & 0xffff);
1426 if (ct != CATCH_TYPE_BREAK
1427 && ct != CATCH_TYPE_NEXT
1428 && ct != CATCH_TYPE_REDO) {
1430 for (e = end; e && (IS_LABEL(e) || IS_TRACE(e)); e = e->next) {
1431 if (e == cont) {
1432 NODE dummy_line_node = generate_dummy_line_node(0, -1);
1433 INSN *nop = new_insn_core(iseq, &dummy_line_node, BIN(nop), 0, 0);
1434 ELEM_INSERT_NEXT(end, &nop->link);
1435 break;
1442 static int
1443 iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
1445 if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
1446 return COMPILE_NG;
1448 /* debugs("[compile step 2] (iseq_array_to_linkedlist)\n"); */
1450 if (compile_debug > 5)
1451 dump_disasm_list(FIRST_ELEMENT(anchor));
1453 debugs("[compile step 3.1 (iseq_optimize)]\n");
1454 iseq_optimize(iseq, anchor);
1456 if (compile_debug > 5)
1457 dump_disasm_list(FIRST_ELEMENT(anchor));
1459 if (ISEQ_COMPILE_DATA(iseq)->option->instructions_unification) {
1460 debugs("[compile step 3.2 (iseq_insns_unification)]\n");
1461 iseq_insns_unification(iseq, anchor);
1462 if (compile_debug > 5)
1463 dump_disasm_list(FIRST_ELEMENT(anchor));
1466 if (ISEQ_COMPILE_DATA(iseq)->option->stack_caching) {
1467 debugs("[compile step 3.3 (iseq_set_sequence_stackcaching)]\n");
1468 iseq_set_sequence_stackcaching(iseq, anchor);
1469 if (compile_debug > 5)
1470 dump_disasm_list(FIRST_ELEMENT(anchor));
1473 debugs("[compile step 3.4 (iseq_insert_nop_between_end_and_cont)]\n");
1474 iseq_insert_nop_between_end_and_cont(iseq);
1475 if (compile_debug > 5)
1476 dump_disasm_list(FIRST_ELEMENT(anchor));
1478 return COMPILE_OK;
1481 static int
1482 iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
1484 if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
1485 return COMPILE_NG;
1487 debugs("[compile step 4.1 (iseq_set_sequence)]\n");
1488 if (!iseq_set_sequence(iseq, anchor)) return COMPILE_NG;
1489 if (compile_debug > 5)
1490 dump_disasm_list(FIRST_ELEMENT(anchor));
1492 debugs("[compile step 4.2 (iseq_set_exception_table)]\n");
1493 if (!iseq_set_exception_table(iseq)) return COMPILE_NG;
1495 debugs("[compile step 4.3 (set_optargs_table)] \n");
1496 if (!iseq_set_optargs_table(iseq)) return COMPILE_NG;
1498 debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
1499 if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG;
1501 debugs("[compile step 6 (update_catch_except_flags)] \n");
1502 update_catch_except_flags(iseq->body);
1504 debugs("[compile step 6.1 (remove unused catch tables)] \n");
1505 if (!iseq->body->catch_except_p && iseq->body->catch_table) {
1506 xfree(iseq->body->catch_table);
1507 iseq->body->catch_table = NULL;
1510 #if VM_INSN_INFO_TABLE_IMPL == 2
1511 if (iseq->body->insns_info.succ_index_table == NULL) {
1512 debugs("[compile step 7 (rb_iseq_insns_info_encode_positions)] \n");
1513 rb_iseq_insns_info_encode_positions(iseq);
1515 #endif
1517 if (compile_debug > 1) {
1518 VALUE str = rb_iseq_disasm(iseq);
1519 printf("%s\n", StringValueCStr(str));
1521 verify_call_cache(iseq);
1522 debugs("[compile step: finish]\n");
1524 return COMPILE_OK;
1527 static int
1528 iseq_set_exception_local_table(rb_iseq_t *iseq)
1530 iseq->body->local_table_size = numberof(rb_iseq_shared_exc_local_tbl);
1531 iseq->body->local_table = rb_iseq_shared_exc_local_tbl;
1532 return COMPILE_OK;
1535 static int
1536 get_lvar_level(const rb_iseq_t *iseq)
1538 int lev = 0;
1539 while (iseq != iseq->body->local_iseq) {
1540 lev++;
1541 iseq = iseq->body->parent_iseq;
1543 return lev;
1546 static int
1547 get_dyna_var_idx_at_raw(const rb_iseq_t *iseq, ID id)
1549 unsigned int i;
1551 for (i = 0; i < iseq->body->local_table_size; i++) {
1552 if (iseq->body->local_table[i] == id) {
1553 return (int)i;
1556 return -1;
1559 static int
1560 get_local_var_idx(const rb_iseq_t *iseq, ID id)
1562 int idx = get_dyna_var_idx_at_raw(iseq->body->local_iseq, id);
1564 if (idx < 0) {
1565 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq),
1566 "get_local_var_idx: %d", idx);
1569 return idx;
1572 static int
1573 get_dyna_var_idx(const rb_iseq_t *iseq, ID id, int *level, int *ls)
1575 int lv = 0, idx = -1;
1576 const rb_iseq_t *const topmost_iseq = iseq;
1578 while (iseq) {
1579 idx = get_dyna_var_idx_at_raw(iseq, id);
1580 if (idx >= 0) {
1581 break;
1583 iseq = iseq->body->parent_iseq;
1584 lv++;
1587 if (idx < 0) {
1588 COMPILE_ERROR(topmost_iseq, ISEQ_LAST_LINE(topmost_iseq),
1589 "get_dyna_var_idx: -1");
1592 *level = lv;
1593 *ls = iseq->body->local_table_size;
1594 return idx;
1597 static int
1598 iseq_local_block_param_p(const rb_iseq_t *iseq, unsigned int idx, unsigned int level)
1600 const struct rb_iseq_constant_body *body;
1601 while (level > 0) {
1602 iseq = iseq->body->parent_iseq;
1603 level--;
1605 body = iseq->body;
1606 if (body->local_iseq == iseq && /* local variables */
1607 body->param.flags.has_block &&
1608 body->local_table_size - body->param.block_start == idx) {
1609 return TRUE;
1611 else {
1612 return FALSE;
1616 static int
1617 iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel)
1619 int level, ls;
1620 int idx = get_dyna_var_idx(iseq, id, &level, &ls);
1621 if (iseq_local_block_param_p(iseq, ls - idx, level)) {
1622 *pidx = ls - idx;
1623 *plevel = level;
1624 return TRUE;
1626 else {
1627 return FALSE;
1631 static void
1632 access_outer_variables(const rb_iseq_t *iseq, int level, ID id, bool write)
1634 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1636 if (isolated_depth && level >= isolated_depth) {
1637 if (id == rb_intern("yield")) {
1638 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not yield from isolated Proc");
1640 else {
1641 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable `%s' from isolated Proc", rb_id2name(id));
1645 for (int i=0; i<level; i++) {
1646 VALUE val;
1647 struct rb_id_table *ovs = iseq->body->outer_variables;
1649 if (!ovs) {
1650 ovs = iseq->body->outer_variables = rb_id_table_create(8);
1653 if (rb_id_table_lookup(iseq->body->outer_variables, id, &val)) {
1654 if (write && !val) {
1655 rb_id_table_insert(iseq->body->outer_variables, id, Qtrue);
1658 else {
1659 rb_id_table_insert(iseq->body->outer_variables, id, RBOOL(write));
1662 iseq = iseq->body->parent_iseq;
1666 static ID
1667 iseq_lvar_id(const rb_iseq_t *iseq, int idx, int level)
1669 for (int i=0; i<level; i++) {
1670 iseq = iseq->body->parent_iseq;
1673 ID id = iseq->body->local_table[iseq->body->local_table_size - idx];
1674 // fprintf(stderr, "idx:%d level:%d ID:%s\n", idx, level, rb_id2name(id));
1675 return id;
1678 static void
1679 iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
1681 if (iseq_local_block_param_p(iseq, idx, level)) {
1682 ADD_INSN2(seq, line_node, getblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1684 else {
1685 ADD_INSN2(seq, line_node, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1687 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
1690 static void
1691 iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
1693 if (iseq_local_block_param_p(iseq, idx, level)) {
1694 ADD_INSN2(seq, line_node, setblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1696 else {
1697 ADD_INSN2(seq, line_node, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1699 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
1704 static void
1705 iseq_calc_param_size(rb_iseq_t *iseq)
1707 struct rb_iseq_constant_body *const body = iseq->body;
1708 if (body->param.flags.has_opt ||
1709 body->param.flags.has_post ||
1710 body->param.flags.has_rest ||
1711 body->param.flags.has_block ||
1712 body->param.flags.has_kw ||
1713 body->param.flags.has_kwrest) {
1715 if (body->param.flags.has_block) {
1716 body->param.size = body->param.block_start + 1;
1718 else if (body->param.flags.has_kwrest) {
1719 body->param.size = body->param.keyword->rest_start + 1;
1721 else if (body->param.flags.has_kw) {
1722 body->param.size = body->param.keyword->bits_start + 1;
1724 else if (body->param.flags.has_post) {
1725 body->param.size = body->param.post_start + body->param.post_num;
1727 else if (body->param.flags.has_rest) {
1728 body->param.size = body->param.rest_start + 1;
1730 else if (body->param.flags.has_opt) {
1731 body->param.size = body->param.lead_num + body->param.opt_num;
1733 else {
1734 UNREACHABLE;
1737 else {
1738 body->param.size = body->param.lead_num;
1742 static int
1743 iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
1744 const struct rb_args_info *args, int arg_size)
1746 const NODE *node = args->kw_args;
1747 struct rb_iseq_constant_body *const body = iseq->body;
1748 struct rb_iseq_param_keyword *keyword;
1749 const VALUE default_values = rb_ary_tmp_new(1);
1750 const VALUE complex_mark = rb_str_tmp_new(0);
1751 int kw = 0, rkw = 0, di = 0, i;
1753 body->param.flags.has_kw = TRUE;
1754 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
1756 while (node) {
1757 kw++;
1758 node = node->nd_next;
1760 arg_size += kw;
1761 keyword->bits_start = arg_size++;
1763 node = args->kw_args;
1764 while (node) {
1765 const NODE *val_node = node->nd_body->nd_value;
1766 VALUE dv;
1768 if (val_node == NODE_SPECIAL_REQUIRED_KEYWORD) {
1769 ++rkw;
1771 else {
1772 switch (nd_type(val_node)) {
1773 case NODE_LIT:
1774 dv = val_node->nd_lit;
1775 break;
1776 case NODE_NIL:
1777 dv = Qnil;
1778 break;
1779 case NODE_TRUE:
1780 dv = Qtrue;
1781 break;
1782 case NODE_FALSE:
1783 dv = Qfalse;
1784 break;
1785 default:
1786 NO_CHECK(COMPILE_POPPED(optargs, "kwarg", node)); /* nd_type_p(node, NODE_KW_ARG) */
1787 dv = complex_mark;
1790 keyword->num = ++di;
1791 rb_ary_push(default_values, dv);
1794 node = node->nd_next;
1797 keyword->num = kw;
1799 if (args->kw_rest_arg->nd_vid != 0) {
1800 keyword->rest_start = arg_size++;
1801 body->param.flags.has_kwrest = TRUE;
1803 keyword->required_num = rkw;
1804 keyword->table = &body->local_table[keyword->bits_start - keyword->num];
1807 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
1809 for (i = 0; i < RARRAY_LEN(default_values); i++) {
1810 VALUE dv = RARRAY_AREF(default_values, i);
1811 if (dv == complex_mark) dv = Qundef;
1812 if (!SPECIAL_CONST_P(dv)) {
1813 RB_OBJ_WRITTEN(iseq, Qundef, dv);
1815 dvs[i] = dv;
1818 keyword->default_values = dvs;
1820 return arg_size;
1823 static int
1824 iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const node_args)
1826 debugs("iseq_set_arguments: %s\n", node_args ? "" : "0");
1828 if (node_args) {
1829 struct rb_iseq_constant_body *const body = iseq->body;
1830 struct rb_args_info *args = node_args->nd_ainfo;
1831 ID rest_id = 0;
1832 int last_comma = 0;
1833 ID block_id = 0;
1834 int arg_size;
1836 EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG);
1838 body->param.flags.ruby2_keywords = args->ruby2_keywords;
1839 body->param.lead_num = arg_size = (int)args->pre_args_num;
1840 if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE;
1841 debugs(" - argc: %d\n", body->param.lead_num);
1843 rest_id = args->rest_arg;
1844 if (rest_id == NODE_SPECIAL_EXCESSIVE_COMMA) {
1845 last_comma = 1;
1846 rest_id = 0;
1848 block_id = args->block_arg;
1850 if (args->opt_args) {
1851 const NODE *node = args->opt_args;
1852 LABEL *label;
1853 VALUE labels = rb_ary_tmp_new(1);
1854 VALUE *opt_table;
1855 int i = 0, j;
1857 while (node) {
1858 label = NEW_LABEL(nd_line(node));
1859 rb_ary_push(labels, (VALUE)label | 1);
1860 ADD_LABEL(optargs, label);
1861 NO_CHECK(COMPILE_POPPED(optargs, "optarg", node->nd_body));
1862 node = node->nd_next;
1863 i += 1;
1866 /* last label */
1867 label = NEW_LABEL(nd_line(node_args));
1868 rb_ary_push(labels, (VALUE)label | 1);
1869 ADD_LABEL(optargs, label);
1871 opt_table = ALLOC_N(VALUE, i+1);
1873 MEMCPY(opt_table, RARRAY_CONST_PTR_TRANSIENT(labels), VALUE, i+1);
1874 for (j = 0; j < i+1; j++) {
1875 opt_table[j] &= ~1;
1877 rb_ary_clear(labels);
1879 body->param.flags.has_opt = TRUE;
1880 body->param.opt_num = i;
1881 body->param.opt_table = opt_table;
1882 arg_size += i;
1885 if (rest_id) {
1886 body->param.rest_start = arg_size++;
1887 body->param.flags.has_rest = TRUE;
1888 assert(body->param.rest_start != -1);
1891 if (args->first_post_arg) {
1892 body->param.post_start = arg_size;
1893 body->param.post_num = args->post_args_num;
1894 body->param.flags.has_post = TRUE;
1895 arg_size += args->post_args_num;
1897 if (body->param.flags.has_rest) { /* TODO: why that? */
1898 body->param.post_start = body->param.rest_start + 1;
1902 if (args->kw_args) {
1903 arg_size = iseq_set_arguments_keywords(iseq, optargs, args, arg_size);
1905 else if (args->kw_rest_arg) {
1906 struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
1907 keyword->rest_start = arg_size++;
1908 body->param.keyword = keyword;
1909 body->param.flags.has_kwrest = TRUE;
1911 else if (args->no_kwarg) {
1912 body->param.flags.accepts_no_kwarg = TRUE;
1915 if (block_id) {
1916 body->param.block_start = arg_size++;
1917 body->param.flags.has_block = TRUE;
1920 iseq_calc_param_size(iseq);
1921 body->param.size = arg_size;
1923 if (args->pre_init) { /* m_init */
1924 NO_CHECK(COMPILE_POPPED(optargs, "init arguments (m)", args->pre_init));
1926 if (args->post_init) { /* p_init */
1927 NO_CHECK(COMPILE_POPPED(optargs, "init arguments (p)", args->post_init));
1930 if (body->type == ISEQ_TYPE_BLOCK) {
1931 if (body->param.flags.has_opt == FALSE &&
1932 body->param.flags.has_post == FALSE &&
1933 body->param.flags.has_rest == FALSE &&
1934 body->param.flags.has_kw == FALSE &&
1935 body->param.flags.has_kwrest == FALSE) {
1937 if (body->param.lead_num == 1 && last_comma == 0) {
1938 /* {|a|} */
1939 body->param.flags.ambiguous_param0 = TRUE;
1945 return COMPILE_OK;
1948 static int
1949 iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl)
1951 unsigned int size = tbl ? tbl->size : 0;
1953 if (size > 0) {
1954 ID *ids = (ID *)ALLOC_N(ID, size);
1955 MEMCPY(ids, tbl->ids, ID, size);
1956 iseq->body->local_table = ids;
1958 iseq->body->local_table_size = size;
1960 debugs("iseq_set_local_table: %u\n", iseq->body->local_table_size);
1961 return COMPILE_OK;
1965 rb_iseq_cdhash_cmp(VALUE val, VALUE lit)
1967 int tval, tlit;
1969 if (val == lit) {
1970 return 0;
1972 else if ((tlit = OBJ_BUILTIN_TYPE(lit)) == -1) {
1973 return val != lit;
1975 else if ((tval = OBJ_BUILTIN_TYPE(val)) == -1) {
1976 return -1;
1978 else if (tlit != tval) {
1979 return -1;
1981 else if (tlit == T_SYMBOL) {
1982 return val != lit;
1984 else if (tlit == T_STRING) {
1985 return rb_str_hash_cmp(lit, val);
1987 else if (tlit == T_BIGNUM) {
1988 long x = FIX2LONG(rb_big_cmp(lit, val));
1990 /* Given lit and val are both Bignum, x must be -1, 0, 1.
1991 * There is no need to call rb_fix2int here. */
1992 RUBY_ASSERT((x == 1) || (x == 0) || (x == -1));
1993 return (int)x;
1995 else if (tlit == T_FLOAT) {
1996 return rb_float_cmp(lit, val);
1998 else if (tlit == T_RATIONAL) {
1999 const struct RRational *rat1 = RRATIONAL(val);
2000 const struct RRational *rat2 = RRATIONAL(lit);
2001 return rb_iseq_cdhash_cmp(rat1->num, rat2->num) || rb_iseq_cdhash_cmp(rat1->den, rat2->den);
2003 else if (tlit == T_COMPLEX) {
2004 const struct RComplex *comp1 = RCOMPLEX(val);
2005 const struct RComplex *comp2 = RCOMPLEX(lit);
2006 return rb_iseq_cdhash_cmp(comp1->real, comp2->real) || rb_iseq_cdhash_cmp(comp1->imag, comp2->imag);
2008 else if (tlit == T_REGEXP) {
2009 return rb_reg_equal(val, lit) ? 0 : -1;
2011 else {
2012 UNREACHABLE_RETURN(-1);
2016 st_index_t
2017 rb_iseq_cdhash_hash(VALUE a)
2019 switch (OBJ_BUILTIN_TYPE(a)) {
2020 case -1:
2021 case T_SYMBOL:
2022 return (st_index_t)a;
2023 case T_STRING:
2024 return rb_str_hash(a);
2025 case T_BIGNUM:
2026 return FIX2LONG(rb_big_hash(a));
2027 case T_FLOAT:
2028 return rb_dbl_long_hash(RFLOAT_VALUE(a));
2029 case T_RATIONAL:
2030 return rb_rational_hash(a);
2031 case T_COMPLEX:
2032 return rb_complex_hash(a);
2033 case T_REGEXP:
2034 return NUM2LONG(rb_reg_hash(a));
2035 default:
2036 UNREACHABLE_RETURN(0);
2040 static const struct st_hash_type cdhash_type = {
2041 rb_iseq_cdhash_cmp,
2042 rb_iseq_cdhash_hash,
2045 struct cdhash_set_label_struct {
2046 VALUE hash;
2047 int pos;
2048 int len;
2051 static int
2052 cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr)
2054 struct cdhash_set_label_struct *data = (struct cdhash_set_label_struct *)ptr;
2055 LABEL *lobj = (LABEL *)(val & ~1);
2056 rb_hash_aset(data->hash, key, INT2FIX(lobj->position - (data->pos+data->len)));
2057 return ST_CONTINUE;
2061 static inline VALUE
2062 get_ivar_ic_value(rb_iseq_t *iseq,ID id)
2064 VALUE val;
2065 struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
2066 if (tbl) {
2067 if (rb_id_table_lookup(tbl,id,&val)) {
2068 return val;
2071 else {
2072 tbl = rb_id_table_create(1);
2073 ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
2075 val = INT2FIX(iseq->body->is_size++);
2076 rb_id_table_insert(tbl,id,val);
2077 return val;
2080 #define BADINSN_DUMP(anchor, list, dest) \
2081 dump_disasm_list_with_cursor(FIRST_ELEMENT(anchor), list, dest)
2083 #define BADINSN_ERROR \
2084 (xfree(generated_iseq), \
2085 xfree(insns_info), \
2086 BADINSN_DUMP(anchor, list, NULL), \
2087 COMPILE_ERROR)
2089 static int
2090 fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
2092 int stack_max = 0, sp = 0, line = 0;
2093 LINK_ELEMENT *list;
2095 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2096 if (list->type == ISEQ_ELEMENT_LABEL) {
2097 LABEL *lobj = (LABEL *)list;
2098 lobj->set = TRUE;
2102 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2103 switch (list->type) {
2104 case ISEQ_ELEMENT_INSN:
2106 int j, len, insn;
2107 const char *types;
2108 VALUE *operands;
2109 INSN *iobj = (INSN *)list;
2111 /* update sp */
2112 sp = calc_sp_depth(sp, iobj);
2113 if (sp < 0) {
2114 BADINSN_DUMP(anchor, list, NULL);
2115 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2116 "argument stack underflow (%d)", sp);
2117 return -1;
2119 if (sp > stack_max) {
2120 stack_max = sp;
2123 line = iobj->insn_info.line_no;
2124 /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
2125 operands = iobj->operands;
2126 insn = iobj->insn_id;
2127 types = insn_op_types(insn);
2128 len = insn_len(insn);
2130 /* operand check */
2131 if (iobj->operand_size != len - 1) {
2132 /* printf("operand size miss! (%d, %d)\n", iobj->operand_size, len); */
2133 BADINSN_DUMP(anchor, list, NULL);
2134 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2135 "operand size miss! (%d for %d)",
2136 iobj->operand_size, len - 1);
2137 return -1;
2140 for (j = 0; types[j]; j++) {
2141 if (types[j] == TS_OFFSET) {
2142 /* label(destination position) */
2143 LABEL *lobj = (LABEL *)operands[j];
2144 if (!lobj->set) {
2145 BADINSN_DUMP(anchor, list, NULL);
2146 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2147 "unknown label: "LABEL_FORMAT, lobj->label_no);
2148 return -1;
2150 if (lobj->sp == -1) {
2151 lobj->sp = sp;
2153 else if (lobj->sp != sp) {
2154 debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2155 RSTRING_PTR(rb_iseq_path(iseq)), line,
2156 lobj->label_no, lobj->sp, sp);
2160 break;
2162 case ISEQ_ELEMENT_LABEL:
2164 LABEL *lobj = (LABEL *)list;
2165 if (lobj->sp == -1) {
2166 lobj->sp = sp;
2168 else {
2169 if (lobj->sp != sp) {
2170 debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2171 RSTRING_PTR(rb_iseq_path(iseq)), line,
2172 lobj->label_no, lobj->sp, sp);
2174 sp = lobj->sp;
2176 break;
2178 case ISEQ_ELEMENT_TRACE:
2180 /* ignore */
2181 break;
2183 case ISEQ_ELEMENT_ADJUST:
2185 ADJUST *adjust = (ADJUST *)list;
2186 int orig_sp = sp;
2188 sp = adjust->label ? adjust->label->sp : 0;
2189 if (adjust->line_no != -1 && orig_sp - sp < 0) {
2190 BADINSN_DUMP(anchor, list, NULL);
2191 COMPILE_ERROR(iseq, adjust->line_no,
2192 "iseq_set_sequence: adjust bug %d < %d",
2193 orig_sp, sp);
2194 return -1;
2196 break;
2198 default:
2199 BADINSN_DUMP(anchor, list, NULL);
2200 COMPILE_ERROR(iseq, line, "unknown list type: %d", list->type);
2201 return -1;
2204 return stack_max;
2207 static int
2208 add_insn_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
2209 int insns_info_index, int code_index, const INSN *iobj)
2211 if (insns_info_index == 0 ||
2212 insns_info[insns_info_index-1].line_no != iobj->insn_info.line_no ||
2213 #ifdef USE_ISEQ_NODE_ID
2214 insns_info[insns_info_index-1].node_id != iobj->insn_info.node_id ||
2215 #endif
2216 insns_info[insns_info_index-1].events != iobj->insn_info.events) {
2217 insns_info[insns_info_index].line_no = iobj->insn_info.line_no;
2218 #ifdef USE_ISEQ_NODE_ID
2219 insns_info[insns_info_index].node_id = iobj->insn_info.node_id;
2220 #endif
2221 insns_info[insns_info_index].events = iobj->insn_info.events;
2222 positions[insns_info_index] = code_index;
2223 return TRUE;
2225 return FALSE;
2228 static int
2229 add_adjust_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
2230 int insns_info_index, int code_index, const ADJUST *adjust)
2232 if (insns_info_index > 0 ||
2233 insns_info[insns_info_index-1].line_no != adjust->line_no) {
2234 insns_info[insns_info_index].line_no = adjust->line_no;
2235 insns_info[insns_info_index].events = 0;
2236 positions[insns_info_index] = code_index;
2237 return TRUE;
2239 return FALSE;
2243 ruby insn object list -> raw instruction sequence
2245 static int
2246 iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
2248 VALUE iseqv = (VALUE)iseq;
2249 struct iseq_insn_info_entry *insns_info;
2250 struct rb_iseq_constant_body *const body = iseq->body;
2251 unsigned int *positions;
2252 LINK_ELEMENT *list;
2253 VALUE *generated_iseq;
2254 rb_event_flag_t events = 0;
2255 long data = 0;
2257 int insn_num, code_index, insns_info_index, sp = 0;
2258 int stack_max = fix_sp_depth(iseq, anchor);
2260 if (stack_max < 0) return COMPILE_NG;
2262 /* fix label position */
2263 insn_num = code_index = 0;
2264 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2265 switch (list->type) {
2266 case ISEQ_ELEMENT_INSN:
2268 INSN *iobj = (INSN *)list;
2269 /* update sp */
2270 sp = calc_sp_depth(sp, iobj);
2271 insn_num++;
2272 events = iobj->insn_info.events |= events;
2273 if (ISEQ_COVERAGE(iseq)) {
2274 if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) &&
2275 !(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) {
2276 int line = iobj->insn_info.line_no;
2277 if (line >= 1) {
2278 RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, INT2FIX(0));
2281 if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) {
2282 while (RARRAY_LEN(ISEQ_PC2BRANCHINDEX(iseq)) <= code_index) {
2283 rb_ary_push(ISEQ_PC2BRANCHINDEX(iseq), Qnil);
2285 RARRAY_ASET(ISEQ_PC2BRANCHINDEX(iseq), code_index, INT2FIX(data));
2288 code_index += insn_data_length(iobj);
2289 events = 0;
2290 data = 0;
2291 break;
2293 case ISEQ_ELEMENT_LABEL:
2295 LABEL *lobj = (LABEL *)list;
2296 lobj->position = code_index;
2297 if (lobj->sp != sp) {
2298 debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2299 RSTRING_PTR(rb_iseq_path(iseq)),
2300 lobj->label_no, lobj->sp, sp);
2302 sp = lobj->sp;
2303 break;
2305 case ISEQ_ELEMENT_TRACE:
2307 TRACE *trace = (TRACE *)list;
2308 events |= trace->event;
2309 if (trace->event & RUBY_EVENT_COVERAGE_BRANCH) data = trace->data;
2310 break;
2312 case ISEQ_ELEMENT_ADJUST:
2314 ADJUST *adjust = (ADJUST *)list;
2315 if (adjust->line_no != -1) {
2316 int orig_sp = sp;
2317 sp = adjust->label ? adjust->label->sp : 0;
2318 if (orig_sp - sp > 0) {
2319 if (orig_sp - sp > 1) code_index++; /* 1 operand */
2320 code_index++; /* insn */
2321 insn_num++;
2324 break;
2326 default: break;
2330 /* make instruction sequence */
2331 generated_iseq = ALLOC_N(VALUE, code_index);
2332 insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num);
2333 positions = ALLOC_N(unsigned int, insn_num);
2334 body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);
2335 body->call_data = ZALLOC_N(struct rb_call_data, body->ci_size);
2336 ISEQ_COMPILE_DATA(iseq)->ci_index = 0;
2338 list = FIRST_ELEMENT(anchor);
2339 insns_info_index = code_index = sp = 0;
2341 while (list) {
2342 switch (list->type) {
2343 case ISEQ_ELEMENT_INSN:
2345 int j, len, insn;
2346 const char *types;
2347 VALUE *operands;
2348 INSN *iobj = (INSN *)list;
2350 /* update sp */
2351 sp = calc_sp_depth(sp, iobj);
2352 /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
2353 operands = iobj->operands;
2354 insn = iobj->insn_id;
2355 generated_iseq[code_index] = insn;
2356 types = insn_op_types(insn);
2357 len = insn_len(insn);
2359 for (j = 0; types[j]; j++) {
2360 char type = types[j];
2361 /* printf("--> [%c - (%d-%d)]\n", type, k, j); */
2362 switch (type) {
2363 case TS_OFFSET:
2365 /* label(destination position) */
2366 LABEL *lobj = (LABEL *)operands[j];
2367 generated_iseq[code_index + 1 + j] = lobj->position - (code_index + len);
2368 break;
2370 case TS_CDHASH:
2372 VALUE map = operands[j];
2373 struct cdhash_set_label_struct data;
2374 data.hash = map;
2375 data.pos = code_index;
2376 data.len = len;
2377 rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
2379 rb_hash_rehash(map);
2380 freeze_hide_obj(map);
2381 generated_iseq[code_index + 1 + j] = map;
2382 RB_OBJ_WRITTEN(iseq, Qundef, map);
2383 FL_SET(iseqv, ISEQ_MARKABLE_ISEQ);
2384 break;
2386 case TS_LINDEX:
2387 case TS_NUM: /* ulong */
2388 generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
2389 break;
2390 case TS_VALUE: /* VALUE */
2391 case TS_ISEQ: /* iseq */
2393 VALUE v = operands[j];
2394 generated_iseq[code_index + 1 + j] = v;
2395 /* to mark ruby object */
2396 if (!SPECIAL_CONST_P(v)) {
2397 RB_OBJ_WRITTEN(iseq, Qundef, v);
2398 FL_SET(iseqv, ISEQ_MARKABLE_ISEQ);
2400 break;
2402 case TS_IC: /* inline cache */
2403 case TS_ISE: /* inline storage entry */
2404 case TS_IVC: /* inline ivar cache */
2406 unsigned int ic_index = FIX2UINT(operands[j]);
2407 IC ic = (IC)&body->is_entries[ic_index];
2408 if (UNLIKELY(ic_index >= body->is_size)) {
2409 BADINSN_DUMP(anchor, &iobj->link, 0);
2410 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2411 "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
2412 ic_index, body->is_size);
2414 generated_iseq[code_index + 1 + j] = (VALUE)ic;
2415 FL_SET(iseqv, ISEQ_MARKABLE_ISEQ);
2417 if (insn == BIN(opt_getinlinecache) && type == TS_IC) {
2418 // Store the instruction index for opt_getinlinecache on the IC for
2419 // YJIT to invalidate code when opt_setinlinecache runs.
2420 ic->get_insn_idx = (unsigned int)code_index;
2422 break;
2424 case TS_CALLDATA:
2426 const struct rb_callinfo *source_ci = (const struct rb_callinfo *)operands[j];
2427 struct rb_call_data *cd = &body->call_data[ISEQ_COMPILE_DATA(iseq)->ci_index++];
2428 assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
2429 cd->ci = source_ci;
2430 cd->cc = vm_cc_empty();
2431 generated_iseq[code_index + 1 + j] = (VALUE)cd;
2432 break;
2434 case TS_ID: /* ID */
2435 generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
2436 break;
2437 case TS_FUNCPTR:
2438 generated_iseq[code_index + 1 + j] = operands[j];
2439 break;
2440 case TS_BUILTIN:
2441 generated_iseq[code_index + 1 + j] = operands[j];
2442 break;
2443 default:
2444 BADINSN_ERROR(iseq, iobj->insn_info.line_no,
2445 "unknown operand type: %c", type);
2446 return COMPILE_NG;
2449 if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
2450 code_index += len;
2451 break;
2453 case ISEQ_ELEMENT_LABEL:
2455 LABEL *lobj = (LABEL *)list;
2456 if (lobj->sp != sp) {
2457 debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2458 RSTRING_PTR(rb_iseq_path(iseq)),
2459 lobj->label_no, lobj->sp, sp);
2461 sp = lobj->sp;
2462 break;
2464 case ISEQ_ELEMENT_ADJUST:
2466 ADJUST *adjust = (ADJUST *)list;
2467 int orig_sp = sp;
2469 if (adjust->label) {
2470 sp = adjust->label->sp;
2472 else {
2473 sp = 0;
2476 if (adjust->line_no != -1) {
2477 const int diff = orig_sp - sp;
2478 if (diff > 0) {
2479 if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
2481 if (diff > 1) {
2482 generated_iseq[code_index++] = BIN(adjuststack);
2483 generated_iseq[code_index++] = orig_sp - sp;
2485 else if (diff == 1) {
2486 generated_iseq[code_index++] = BIN(pop);
2488 else if (diff < 0) {
2489 int label_no = adjust->label ? adjust->label->label_no : -1;
2490 xfree(generated_iseq);
2491 xfree(insns_info);
2492 xfree(positions);
2493 debug_list(anchor, list);
2494 COMPILE_ERROR(iseq, adjust->line_no,
2495 "iseq_set_sequence: adjust bug to %d %d < %d",
2496 label_no, orig_sp, sp);
2497 return COMPILE_NG;
2500 break;
2502 default:
2503 /* ignore */
2504 break;
2506 list = list->next;
2509 body->iseq_encoded = (void *)generated_iseq;
2510 body->iseq_size = code_index;
2511 body->stack_max = stack_max;
2513 /* get rid of memory leak when REALLOC failed */
2514 body->insns_info.body = insns_info;
2515 body->insns_info.positions = positions;
2517 REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index);
2518 body->insns_info.body = insns_info;
2519 REALLOC_N(positions, unsigned int, insns_info_index);
2520 body->insns_info.positions = positions;
2521 body->insns_info.size = insns_info_index;
2523 return COMPILE_OK;
2526 static int
2527 label_get_position(LABEL *lobj)
2529 return lobj->position;
2532 static int
2533 label_get_sp(LABEL *lobj)
2535 return lobj->sp;
2538 static int
2539 iseq_set_exception_table(rb_iseq_t *iseq)
2541 const VALUE *tptr, *ptr;
2542 unsigned int tlen, i;
2543 struct iseq_catch_table_entry *entry;
2545 iseq->body->catch_table = NULL;
2546 if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) return COMPILE_OK;
2547 tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
2548 tptr = RARRAY_CONST_PTR_TRANSIENT(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
2550 if (tlen > 0) {
2551 struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
2552 table->size = tlen;
2554 for (i = 0; i < table->size; i++) {
2555 ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]);
2556 entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
2557 entry->type = (enum catch_type)(ptr[0] & 0xffff);
2558 entry->start = label_get_position((LABEL *)(ptr[1] & ~1));
2559 entry->end = label_get_position((LABEL *)(ptr[2] & ~1));
2560 entry->iseq = (rb_iseq_t *)ptr[3];
2561 RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq);
2563 /* stack depth */
2564 if (ptr[4]) {
2565 LABEL *lobj = (LABEL *)(ptr[4] & ~1);
2566 entry->cont = label_get_position(lobj);
2567 entry->sp = label_get_sp(lobj);
2569 /* TODO: Dirty Hack! Fix me */
2570 if (entry->type == CATCH_TYPE_RESCUE ||
2571 entry->type == CATCH_TYPE_BREAK ||
2572 entry->type == CATCH_TYPE_NEXT) {
2573 entry->sp--;
2576 else {
2577 entry->cont = 0;
2580 iseq->body->catch_table = table;
2581 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
2584 return COMPILE_OK;
2588 * set optional argument table
2589 * def foo(a, b=expr1, c=expr2)
2590 * =>
2591 * b:
2592 * expr1
2593 * c:
2594 * expr2
2596 static int
2597 iseq_set_optargs_table(rb_iseq_t *iseq)
2599 int i;
2600 VALUE *opt_table = (VALUE *)iseq->body->param.opt_table;
2602 if (iseq->body->param.flags.has_opt) {
2603 for (i = 0; i < iseq->body->param.opt_num + 1; i++) {
2604 opt_table[i] = label_get_position((LABEL *)opt_table[i]);
2607 return COMPILE_OK;
2610 static LINK_ELEMENT *
2611 get_destination_insn(INSN *iobj)
2613 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
2614 LINK_ELEMENT *list;
2615 rb_event_flag_t events = 0;
2617 list = lobj->link.next;
2618 while (list) {
2619 switch (list->type) {
2620 case ISEQ_ELEMENT_INSN:
2621 case ISEQ_ELEMENT_ADJUST:
2622 goto found;
2623 case ISEQ_ELEMENT_LABEL:
2624 /* ignore */
2625 break;
2626 case ISEQ_ELEMENT_TRACE:
2628 TRACE *trace = (TRACE *)list;
2629 events |= trace->event;
2631 break;
2632 default: break;
2634 list = list->next;
2636 found:
2637 if (list && IS_INSN(list)) {
2638 INSN *iobj = (INSN *)list;
2639 iobj->insn_info.events |= events;
2641 return list;
2644 static LINK_ELEMENT *
2645 get_next_insn(INSN *iobj)
2647 LINK_ELEMENT *list = iobj->link.next;
2649 while (list) {
2650 if (IS_INSN(list) || IS_ADJUST(list)) {
2651 return list;
2653 list = list->next;
2655 return 0;
2658 static LINK_ELEMENT *
2659 get_prev_insn(INSN *iobj)
2661 LINK_ELEMENT *list = iobj->link.prev;
2663 while (list) {
2664 if (IS_INSN(list) || IS_ADJUST(list)) {
2665 return list;
2667 list = list->prev;
2669 return 0;
2672 static void
2673 unref_destination(INSN *iobj, int pos)
2675 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, pos);
2676 --lobj->refcnt;
2677 if (!lobj->refcnt) ELEM_REMOVE(&lobj->link);
2680 static void
2681 replace_destination(INSN *dobj, INSN *nobj)
2683 VALUE n = OPERAND_AT(nobj, 0);
2684 LABEL *dl = (LABEL *)OPERAND_AT(dobj, 0);
2685 LABEL *nl = (LABEL *)n;
2686 --dl->refcnt;
2687 ++nl->refcnt;
2688 OPERAND_AT(dobj, 0) = n;
2689 if (!dl->refcnt) ELEM_REMOVE(&dl->link);
2692 static LABEL*
2693 find_destination(INSN *i)
2695 int pos, len = insn_len(i->insn_id);
2696 for (pos = 0; pos < len; ++pos) {
2697 if (insn_op_types(i->insn_id)[pos] == TS_OFFSET) {
2698 return (LABEL *)OPERAND_AT(i, pos);
2701 return 0;
2704 static int
2705 remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
2707 LINK_ELEMENT *first = i, *end;
2708 int *unref_counts = 0, nlabels = ISEQ_COMPILE_DATA(iseq)->label_no;
2710 if (!i) return 0;
2711 unref_counts = ALLOCA_N(int, nlabels);
2712 MEMZERO(unref_counts, int, nlabels);
2713 end = i;
2714 do {
2715 LABEL *lab;
2716 if (IS_INSN(i)) {
2717 if (IS_INSN_ID(i, leave)) {
2718 end = i;
2719 break;
2721 else if ((lab = find_destination((INSN *)i)) != 0) {
2722 if (lab->unremovable) break;
2723 unref_counts[lab->label_no]++;
2726 else if (IS_LABEL(i)) {
2727 lab = (LABEL *)i;
2728 if (lab->unremovable) return 0;
2729 if (lab->refcnt > unref_counts[lab->label_no]) {
2730 if (i == first) return 0;
2731 break;
2733 continue;
2735 else if (IS_TRACE(i)) {
2736 /* do nothing */
2738 else if (IS_ADJUST(i)) {
2739 LABEL *dest = ((ADJUST *)i)->label;
2740 if (dest && dest->unremovable) return 0;
2742 end = i;
2743 } while ((i = i->next) != 0);
2744 i = first;
2745 do {
2746 if (IS_INSN(i)) {
2747 struct rb_iseq_constant_body *body = iseq->body;
2748 VALUE insn = INSN_OF(i);
2749 int pos, len = insn_len(insn);
2750 for (pos = 0; pos < len; ++pos) {
2751 switch (insn_op_types(insn)[pos]) {
2752 case TS_OFFSET:
2753 unref_destination((INSN *)i, pos);
2754 break;
2755 case TS_CALLDATA:
2756 --(body->ci_size);
2757 break;
2761 ELEM_REMOVE(i);
2762 } while ((i != end) && (i = i->next) != 0);
2763 return 1;
2766 static int
2767 iseq_pop_newarray(rb_iseq_t *iseq, INSN *iobj)
2769 switch (OPERAND_AT(iobj, 0)) {
2770 case INT2FIX(0): /* empty array */
2771 ELEM_REMOVE(&iobj->link);
2772 return TRUE;
2773 case INT2FIX(1): /* single element array */
2774 ELEM_REMOVE(&iobj->link);
2775 return FALSE;
2776 default:
2777 iobj->insn_id = BIN(adjuststack);
2778 return TRUE;
2782 static int
2783 is_frozen_putstring(INSN *insn, VALUE *op)
2785 if (IS_INSN_ID(insn, putstring)) {
2786 *op = OPERAND_AT(insn, 0);
2787 return 1;
2789 else if (IS_INSN_ID(insn, putobject)) { /* frozen_string_literal */
2790 *op = OPERAND_AT(insn, 0);
2791 return RB_TYPE_P(*op, T_STRING);
2793 return 0;
2796 static int
2797 optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
2800 * putobject obj
2801 * dup
2802 * checktype T_XXX
2803 * branchif l1
2804 * l2:
2805 * ...
2806 * l1:
2808 * => obj is a T_XXX
2810 * putobject obj (T_XXX)
2811 * jump L1
2812 * L1:
2814 * => obj is not a T_XXX
2816 * putobject obj (T_XXX)
2817 * jump L2
2818 * L2:
2820 int line, node_id;
2821 INSN *niobj, *ciobj, *dup = 0;
2822 LABEL *dest = 0;
2823 VALUE type;
2825 switch (INSN_OF(iobj)) {
2826 case BIN(putstring):
2827 type = INT2FIX(T_STRING);
2828 break;
2829 case BIN(putnil):
2830 type = INT2FIX(T_NIL);
2831 break;
2832 case BIN(putobject):
2833 type = INT2FIX(TYPE(OPERAND_AT(iobj, 0)));
2834 break;
2835 default: return FALSE;
2838 ciobj = (INSN *)get_next_insn(iobj);
2839 if (IS_INSN_ID(ciobj, jump)) {
2840 ciobj = (INSN *)get_next_insn((INSN*)OPERAND_AT(ciobj, 0));
2842 if (IS_INSN_ID(ciobj, dup)) {
2843 ciobj = (INSN *)get_next_insn(dup = ciobj);
2845 if (!ciobj || !IS_INSN_ID(ciobj, checktype)) return FALSE;
2846 niobj = (INSN *)get_next_insn(ciobj);
2847 if (!niobj) {
2848 /* TODO: putobject true/false */
2849 return FALSE;
2851 switch (INSN_OF(niobj)) {
2852 case BIN(branchif):
2853 if (OPERAND_AT(ciobj, 0) == type) {
2854 dest = (LABEL *)OPERAND_AT(niobj, 0);
2856 break;
2857 case BIN(branchunless):
2858 if (OPERAND_AT(ciobj, 0) != type) {
2859 dest = (LABEL *)OPERAND_AT(niobj, 0);
2861 break;
2862 default:
2863 return FALSE;
2865 line = ciobj->insn_info.line_no;
2866 node_id = ciobj->insn_info.node_id;
2867 NODE dummy_line_node = generate_dummy_line_node(line, node_id);
2868 if (!dest) {
2869 if (niobj->link.next && IS_LABEL(niobj->link.next)) {
2870 dest = (LABEL *)niobj->link.next; /* reuse label */
2872 else {
2873 dest = NEW_LABEL(line);
2874 ELEM_INSERT_NEXT(&niobj->link, &dest->link);
2877 INSERT_AFTER_INSN1(iobj, &dummy_line_node, jump, dest);
2878 LABEL_REF(dest);
2879 if (!dup) INSERT_AFTER_INSN(iobj, &dummy_line_node, pop);
2880 return TRUE;
2883 static const struct rb_callinfo *
2884 ci_flag_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, unsigned int add)
2886 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
2887 vm_ci_flag(ci) | add,
2888 vm_ci_argc(ci),
2889 vm_ci_kwarg(ci));
2890 RB_OBJ_WRITTEN(iseq, ci, nci);
2891 return nci;
2894 static const struct rb_callinfo *
2895 ci_argc_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, int argc)
2897 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
2898 vm_ci_flag(ci),
2899 argc,
2900 vm_ci_kwarg(ci));
2901 RB_OBJ_WRITTEN(iseq, ci, nci);
2902 return nci;
2905 static int
2906 iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
2908 INSN *const iobj = (INSN *)list;
2910 again:
2911 optimize_checktype(iseq, iobj);
2913 if (IS_INSN_ID(iobj, jump)) {
2914 INSN *niobj, *diobj, *piobj;
2915 diobj = (INSN *)get_destination_insn(iobj);
2916 niobj = (INSN *)get_next_insn(iobj);
2918 if (diobj == niobj) {
2920 * jump LABEL
2921 * LABEL:
2922 * =>
2923 * LABEL:
2925 unref_destination(iobj, 0);
2926 ELEM_REMOVE(&iobj->link);
2927 return COMPILE_OK;
2929 else if (iobj != diobj && IS_INSN(&diobj->link) &&
2930 IS_INSN_ID(diobj, jump) &&
2931 OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0) &&
2932 diobj->insn_info.events == 0) {
2934 * useless jump elimination:
2935 * jump LABEL1
2936 * ...
2937 * LABEL1:
2938 * jump LABEL2
2940 * => in this case, first jump instruction should jump to
2941 * LABEL2 directly
2943 replace_destination(iobj, diobj);
2944 remove_unreachable_chunk(iseq, iobj->link.next);
2945 goto again;
2947 else if (IS_INSN_ID(diobj, leave)) {
2949 * jump LABEL
2950 * ...
2951 * LABEL:
2952 * leave
2953 * =>
2954 * leave
2955 * ...
2956 * LABEL:
2957 * leave
2959 /* replace */
2960 unref_destination(iobj, 0);
2961 iobj->insn_id = BIN(leave);
2962 iobj->operand_size = 0;
2963 iobj->insn_info = diobj->insn_info;
2964 goto again;
2966 else if (IS_INSN(iobj->link.prev) &&
2967 (piobj = (INSN *)iobj->link.prev) &&
2968 (IS_INSN_ID(piobj, branchif) ||
2969 IS_INSN_ID(piobj, branchunless))) {
2970 INSN *pdiobj = (INSN *)get_destination_insn(piobj);
2971 if (niobj == pdiobj) {
2972 int refcnt = IS_LABEL(piobj->link.next) ?
2973 ((LABEL *)piobj->link.next)->refcnt : 0;
2975 * useless jump elimination (if/unless destination):
2976 * if L1
2977 * jump L2
2978 * L1:
2979 * ...
2980 * L2:
2982 * ==>
2983 * unless L2
2984 * L1:
2985 * ...
2986 * L2:
2988 piobj->insn_id = (IS_INSN_ID(piobj, branchif))
2989 ? BIN(branchunless) : BIN(branchif);
2990 replace_destination(piobj, iobj);
2991 if (refcnt <= 1) {
2992 ELEM_REMOVE(&iobj->link);
2994 else {
2995 /* TODO: replace other branch destinations too */
2997 return COMPILE_OK;
2999 else if (diobj == pdiobj) {
3001 * useless jump elimination (if/unless before jump):
3002 * L1:
3003 * ...
3004 * if L1
3005 * jump L1
3007 * ==>
3008 * L1:
3009 * ...
3010 * pop
3011 * jump L1
3013 NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
3014 INSN *popiobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, 0);
3015 ELEM_REPLACE(&piobj->link, &popiobj->link);
3018 if (remove_unreachable_chunk(iseq, iobj->link.next)) {
3019 goto again;
3024 * putstring "beg"
3025 * putstring "end"
3026 * newrange excl
3028 * ==>
3030 * putobject "beg".."end"
3032 if (IS_INSN_ID(iobj, newrange)) {
3033 INSN *const range = iobj;
3034 INSN *beg, *end;
3035 VALUE str_beg, str_end;
3037 if ((end = (INSN *)get_prev_insn(range)) != 0 &&
3038 is_frozen_putstring(end, &str_end) &&
3039 (beg = (INSN *)get_prev_insn(end)) != 0 &&
3040 is_frozen_putstring(beg, &str_beg)) {
3041 int excl = FIX2INT(OPERAND_AT(range, 0));
3042 VALUE lit_range = rb_range_new(str_beg, str_end, excl);
3044 ELEM_REMOVE(&beg->link);
3045 ELEM_REMOVE(&end->link);
3046 range->insn_id = BIN(putobject);
3047 OPERAND_AT(range, 0) = lit_range;
3048 RB_OBJ_WRITTEN(iseq, Qundef, lit_range);
3052 if (IS_INSN_ID(iobj, leave)) {
3053 remove_unreachable_chunk(iseq, iobj->link.next);
3057 * ...
3058 * duparray [...]
3059 * concatarray
3060 * =>
3061 * ...
3062 * putobject [...]
3063 * concatarray
3065 if (IS_INSN_ID(iobj, duparray)) {
3066 LINK_ELEMENT *next = iobj->link.next;
3067 if (IS_INSN(next) && IS_INSN_ID(next, concatarray)) {
3068 iobj->insn_id = BIN(putobject);
3072 if (IS_INSN_ID(iobj, branchif) ||
3073 IS_INSN_ID(iobj, branchnil) ||
3074 IS_INSN_ID(iobj, branchunless)) {
3076 * if L1
3077 * ...
3078 * L1:
3079 * jump L2
3080 * =>
3081 * if L2
3083 INSN *nobj = (INSN *)get_destination_insn(iobj);
3085 /* This is super nasty hack!!!
3087 * This jump-jump optimization may ignore event flags of the jump
3088 * instruction being skipped. Actually, Line 2 TracePoint event
3089 * is never fired in the following code:
3091 * 1: raise if 1 == 2
3092 * 2: while true
3093 * 3: break
3094 * 4: end
3096 * This is critical for coverage measurement. [Bug #15980]
3098 * This is a stopgap measure: stop the jump-jump optimization if
3099 * coverage measurement is enabled and if the skipped instruction
3100 * has any event flag.
3102 * Note that, still, TracePoint Line event does not occur on Line 2.
3103 * This should be fixed in future.
3105 int stop_optimization =
3106 ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
3107 nobj->link.type == ISEQ_ELEMENT_INSN &&
3108 nobj->insn_info.events;
3109 if (!stop_optimization) {
3110 INSN *pobj = (INSN *)iobj->link.prev;
3111 int prev_dup = 0;
3112 if (pobj) {
3113 if (!IS_INSN(&pobj->link))
3114 pobj = 0;
3115 else if (IS_INSN_ID(pobj, dup))
3116 prev_dup = 1;
3119 for (;;) {
3120 if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) {
3121 replace_destination(iobj, nobj);
3123 else if (prev_dup && IS_INSN_ID(nobj, dup) &&
3124 !!(nobj = (INSN *)nobj->link.next) &&
3125 /* basic blocks, with no labels in the middle */
3126 nobj->insn_id == iobj->insn_id) {
3128 * dup
3129 * if L1
3130 * ...
3131 * L1:
3132 * dup
3133 * if L2
3134 * =>
3135 * dup
3136 * if L2
3137 * ...
3138 * L1:
3139 * dup
3140 * if L2
3142 replace_destination(iobj, nobj);
3144 else if (pobj) {
3146 * putnil
3147 * if L1
3148 * =>
3149 * # nothing
3151 * putobject true
3152 * if L1
3153 * =>
3154 * jump L1
3156 * putstring ".."
3157 * if L1
3158 * =>
3159 * jump L1
3161 * putstring ".."
3162 * dup
3163 * if L1
3164 * =>
3165 * putstring ".."
3166 * jump L1
3169 int cond;
3170 if (prev_dup && IS_INSN(pobj->link.prev)) {
3171 pobj = (INSN *)pobj->link.prev;
3173 if (IS_INSN_ID(pobj, putobject)) {
3174 cond = (IS_INSN_ID(iobj, branchif) ?
3175 OPERAND_AT(pobj, 0) != Qfalse :
3176 IS_INSN_ID(iobj, branchunless) ?
3177 OPERAND_AT(pobj, 0) == Qfalse :
3178 FALSE);
3180 else if (IS_INSN_ID(pobj, putstring) ||
3181 IS_INSN_ID(pobj, duparray) ||
3182 IS_INSN_ID(pobj, newarray)) {
3183 cond = IS_INSN_ID(iobj, branchif);
3185 else if (IS_INSN_ID(pobj, putnil)) {
3186 cond = !IS_INSN_ID(iobj, branchif);
3188 else break;
3189 if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
3190 ELEM_REMOVE(iobj->link.prev);
3192 else if (!iseq_pop_newarray(iseq, pobj)) {
3193 NODE dummy_line_node = generate_dummy_line_node(pobj->insn_info.line_no, pobj->insn_info.node_id);
3194 pobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, NULL);
3195 ELEM_INSERT_PREV(&iobj->link, &pobj->link);
3197 if (cond) {
3198 if (prev_dup) {
3199 NODE dummy_line_node = generate_dummy_line_node(pobj->insn_info.line_no, pobj->insn_info.node_id);
3200 pobj = new_insn_core(iseq, &dummy_line_node, BIN(putnil), 0, NULL);
3201 ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
3203 iobj->insn_id = BIN(jump);
3204 goto again;
3206 else {
3207 unref_destination(iobj, 0);
3208 ELEM_REMOVE(&iobj->link);
3210 break;
3212 else break;
3213 nobj = (INSN *)get_destination_insn(nobj);
3218 if (IS_INSN_ID(iobj, pop)) {
3220 * putself / putnil / putobject obj / putstring "..."
3221 * pop
3222 * =>
3223 * # do nothing
3225 LINK_ELEMENT *prev = iobj->link.prev;
3226 if (IS_INSN(prev)) {
3227 enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id;
3228 if (previ == BIN(putobject) || previ == BIN(putnil) ||
3229 previ == BIN(putself) || previ == BIN(putstring) ||
3230 previ == BIN(dup) ||
3231 previ == BIN(getlocal) ||
3232 previ == BIN(getblockparam) ||
3233 previ == BIN(getblockparamproxy) ||
3234 /* getinstancevariable may issue a warning */
3235 previ == BIN(duparray)) {
3236 /* just push operand or static value and pop soon, no
3237 * side effects */
3238 ELEM_REMOVE(prev);
3239 ELEM_REMOVE(&iobj->link);
3241 else if (previ == BIN(newarray) && iseq_pop_newarray(iseq, (INSN*)prev)) {
3242 ELEM_REMOVE(&iobj->link);
3244 else if (previ == BIN(concatarray)) {
3245 INSN *piobj = (INSN *)prev;
3246 NODE dummy_line_node = generate_dummy_line_node(piobj->insn_info.line_no, piobj->insn_info.node_id);
3247 INSERT_BEFORE_INSN1(piobj, &dummy_line_node, splatarray, Qfalse);
3248 INSN_OF(piobj) = BIN(pop);
3250 else if (previ == BIN(concatstrings)) {
3251 if (OPERAND_AT(prev, 0) == INT2FIX(1)) {
3252 ELEM_REMOVE(prev);
3254 else {
3255 ELEM_REMOVE(&iobj->link);
3256 INSN_OF(prev) = BIN(adjuststack);
3262 if (IS_INSN_ID(iobj, newarray) ||
3263 IS_INSN_ID(iobj, duparray) ||
3264 IS_INSN_ID(iobj, expandarray) ||
3265 IS_INSN_ID(iobj, concatarray) ||
3266 IS_INSN_ID(iobj, splatarray) ||
3267 0) {
3269 * newarray N
3270 * splatarray
3271 * =>
3272 * newarray N
3273 * newarray always puts an array
3275 LINK_ELEMENT *next = iobj->link.next;
3276 if (IS_INSN(next) && IS_INSN_ID(next, splatarray)) {
3277 /* remove splatarray following always-array insn */
3278 ELEM_REMOVE(next);
3282 if (IS_INSN_ID(iobj, anytostring)) {
3283 LINK_ELEMENT *next = iobj->link.next;
3285 * anytostring
3286 * concatstrings 1
3287 * =>
3288 * anytostring
3290 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings) &&
3291 OPERAND_AT(next, 0) == INT2FIX(1)) {
3292 ELEM_REMOVE(next);
3296 if (IS_INSN_ID(iobj, putstring) ||
3297 (IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) {
3299 * putstring ""
3300 * concatstrings N
3301 * =>
3302 * concatstrings N-1
3304 if (IS_NEXT_INSN_ID(&iobj->link, concatstrings) &&
3305 RSTRING_LEN(OPERAND_AT(iobj, 0)) == 0) {
3306 INSN *next = (INSN *)iobj->link.next;
3307 if ((OPERAND_AT(next, 0) = FIXNUM_INC(OPERAND_AT(next, 0), -1)) == INT2FIX(1)) {
3308 ELEM_REMOVE(&next->link);
3310 ELEM_REMOVE(&iobj->link);
3314 if (IS_INSN_ID(iobj, concatstrings)) {
3316 * concatstrings N
3317 * concatstrings M
3318 * =>
3319 * concatstrings N+M-1
3321 LINK_ELEMENT *next = iobj->link.next;
3322 INSN *jump = 0;
3323 if (IS_INSN(next) && IS_INSN_ID(next, jump))
3324 next = get_destination_insn(jump = (INSN *)next);
3325 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings)) {
3326 int n = FIX2INT(OPERAND_AT(iobj, 0)) + FIX2INT(OPERAND_AT(next, 0)) - 1;
3327 OPERAND_AT(iobj, 0) = INT2FIX(n);
3328 if (jump) {
3329 LABEL *label = ((LABEL *)OPERAND_AT(jump, 0));
3330 if (!--label->refcnt) {
3331 ELEM_REMOVE(&label->link);
3333 else {
3334 label = NEW_LABEL(0);
3335 OPERAND_AT(jump, 0) = (VALUE)label;
3337 label->refcnt++;
3338 ELEM_INSERT_NEXT(next, &label->link);
3339 CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
3341 else {
3342 ELEM_REMOVE(next);
3347 if (do_tailcallopt &&
3348 (IS_INSN_ID(iobj, send) ||
3349 IS_INSN_ID(iobj, opt_aref_with) ||
3350 IS_INSN_ID(iobj, opt_aset_with) ||
3351 IS_INSN_ID(iobj, invokesuper))) {
3353 * send ...
3354 * leave
3355 * =>
3356 * send ..., ... | VM_CALL_TAILCALL, ...
3357 * leave # unreachable
3359 INSN *piobj = NULL;
3360 if (iobj->link.next) {
3361 LINK_ELEMENT *next = iobj->link.next;
3362 do {
3363 if (!IS_INSN(next)) {
3364 next = next->next;
3365 continue;
3367 switch (INSN_OF(next)) {
3368 case BIN(nop):
3369 next = next->next;
3370 break;
3371 case BIN(jump):
3372 /* if cond
3373 * return tailcall
3374 * end
3376 next = get_destination_insn((INSN *)next);
3377 break;
3378 case BIN(leave):
3379 piobj = iobj;
3380 /* fall through */
3381 default:
3382 next = NULL;
3383 break;
3385 } while (next);
3388 if (piobj) {
3389 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(piobj, 0);
3390 if (IS_INSN_ID(piobj, send) ||
3391 IS_INSN_ID(piobj, invokesuper)) {
3392 if (OPERAND_AT(piobj, 1) == 0) { /* no blockiseq */
3393 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
3394 OPERAND_AT(piobj, 0) = (VALUE)ci;
3395 RB_OBJ_WRITTEN(iseq, Qundef, ci);
3398 else {
3399 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
3400 OPERAND_AT(piobj, 0) = (VALUE)ci;
3401 RB_OBJ_WRITTEN(iseq, Qundef, ci);
3406 if (IS_INSN_ID(iobj, dup)) {
3407 if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
3408 LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
3409 if (IS_NEXT_INSN_ID(set1, setlocal)) {
3410 set2 = set1->next;
3411 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
3412 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
3413 ELEM_REMOVE(set1);
3414 ELEM_REMOVE(&iobj->link);
3417 else if (IS_NEXT_INSN_ID(set1, dup) &&
3418 IS_NEXT_INSN_ID(set1->next, setlocal)) {
3419 set2 = set1->next->next;
3420 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
3421 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
3422 ELEM_REMOVE(set1->next);
3423 ELEM_REMOVE(set2);
3429 if (IS_INSN_ID(iobj, getlocal)) {
3430 LINK_ELEMENT *niobj = &iobj->link;
3431 if (IS_NEXT_INSN_ID(niobj, dup)) {
3432 niobj = niobj->next;
3434 if (IS_NEXT_INSN_ID(niobj, setlocal)) {
3435 LINK_ELEMENT *set1 = niobj->next;
3436 if (OPERAND_AT(iobj, 0) == OPERAND_AT(set1, 0) &&
3437 OPERAND_AT(iobj, 1) == OPERAND_AT(set1, 1)) {
3438 ELEM_REMOVE(set1);
3439 ELEM_REMOVE(niobj);
3444 if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) {
3445 if (IS_TRACE(iobj->link.next)) {
3446 if (IS_NEXT_INSN_ID(iobj->link.next, leave)) {
3447 iobj->insn_id = BIN(opt_invokebuiltin_delegate_leave);
3452 return COMPILE_OK;
3455 static int
3456 insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
3458 iobj->insn_id = insn_id;
3459 iobj->operand_size = insn_len(insn_id) - 1;
3460 iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
3462 if (insn_id == BIN(opt_neq)) {
3463 VALUE original_ci = iobj->operands[0];
3464 iobj->operand_size = 2;
3465 iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE));
3466 iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
3467 iobj->operands[1] = original_ci;
3470 return COMPILE_OK;
3473 static int
3474 iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
3476 if (IS_INSN_ID(iobj, newarray) && iobj->link.next &&
3477 IS_INSN(iobj->link.next)) {
3479 * [a, b, ...].max/min -> a, b, c, opt_newarray_max/min
3481 INSN *niobj = (INSN *)iobj->link.next;
3482 if (IS_INSN_ID(niobj, send)) {
3483 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
3484 if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) {
3485 switch (vm_ci_mid(ci)) {
3486 case idMax:
3487 iobj->insn_id = BIN(opt_newarray_max);
3488 ELEM_REMOVE(&niobj->link);
3489 return COMPILE_OK;
3490 case idMin:
3491 iobj->insn_id = BIN(opt_newarray_min);
3492 ELEM_REMOVE(&niobj->link);
3493 return COMPILE_OK;
3499 if (IS_INSN_ID(iobj, send)) {
3500 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
3501 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1);
3503 #define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
3504 if (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) {
3505 switch (vm_ci_argc(ci)) {
3506 case 0:
3507 switch (vm_ci_mid(ci)) {
3508 case idLength: SP_INSN(length); return COMPILE_OK;
3509 case idSize: SP_INSN(size); return COMPILE_OK;
3510 case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
3511 case idNilP: SP_INSN(nil_p); return COMPILE_OK;
3512 case idSucc: SP_INSN(succ); return COMPILE_OK;
3513 case idNot: SP_INSN(not); return COMPILE_OK;
3515 break;
3516 case 1:
3517 switch (vm_ci_mid(ci)) {
3518 case idPLUS: SP_INSN(plus); return COMPILE_OK;
3519 case idMINUS: SP_INSN(minus); return COMPILE_OK;
3520 case idMULT: SP_INSN(mult); return COMPILE_OK;
3521 case idDIV: SP_INSN(div); return COMPILE_OK;
3522 case idMOD: SP_INSN(mod); return COMPILE_OK;
3523 case idEq: SP_INSN(eq); return COMPILE_OK;
3524 case idNeq: SP_INSN(neq); return COMPILE_OK;
3525 case idEqTilde:SP_INSN(regexpmatch2);return COMPILE_OK;
3526 case idLT: SP_INSN(lt); return COMPILE_OK;
3527 case idLE: SP_INSN(le); return COMPILE_OK;
3528 case idGT: SP_INSN(gt); return COMPILE_OK;
3529 case idGE: SP_INSN(ge); return COMPILE_OK;
3530 case idLTLT: SP_INSN(ltlt); return COMPILE_OK;
3531 case idAREF: SP_INSN(aref); return COMPILE_OK;
3532 case idAnd: SP_INSN(and); return COMPILE_OK;
3533 case idOr: SP_INSN(or); return COMPILE_OK;
3535 break;
3536 case 2:
3537 switch (vm_ci_mid(ci)) {
3538 case idASET: SP_INSN(aset); return COMPILE_OK;
3540 break;
3544 if ((vm_ci_flag(ci) & VM_CALL_ARGS_BLOCKARG) == 0 && blockiseq == NULL) {
3545 iobj->insn_id = BIN(opt_send_without_block);
3546 iobj->operand_size = insn_len(iobj->insn_id) - 1;
3549 #undef SP_INSN
3551 return COMPILE_OK;
3554 static inline int
3555 tailcallable_p(rb_iseq_t *iseq)
3557 switch (iseq->body->type) {
3558 case ISEQ_TYPE_TOP:
3559 case ISEQ_TYPE_EVAL:
3560 case ISEQ_TYPE_MAIN:
3561 /* not tail callable because cfp will be over popped */
3562 case ISEQ_TYPE_RESCUE:
3563 case ISEQ_TYPE_ENSURE:
3564 /* rescue block can't tail call because of errinfo */
3565 return FALSE;
3566 default:
3567 return TRUE;
3571 static int
3572 iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
3574 LINK_ELEMENT *list;
3575 const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
3576 const int do_tailcallopt = tailcallable_p(iseq) &&
3577 ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
3578 const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
3579 const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
3580 int rescue_level = 0;
3581 int tailcallopt = do_tailcallopt;
3583 list = FIRST_ELEMENT(anchor);
3585 int do_block_optimization = 0;
3587 if (iseq->body->type == ISEQ_TYPE_BLOCK && !iseq->body->catch_except_p) {
3588 do_block_optimization = 1;
3591 while (list) {
3592 if (IS_INSN(list)) {
3593 if (do_peepholeopt) {
3594 iseq_peephole_optimize(iseq, list, tailcallopt);
3596 if (do_si) {
3597 iseq_specialized_instruction(iseq, (INSN *)list);
3599 if (do_ou) {
3600 insn_operands_unification((INSN *)list);
3603 if (do_block_optimization) {
3604 INSN * item = (INSN *)list;
3605 if (IS_INSN_ID(item, jump)) {
3606 do_block_optimization = 0;
3610 if (IS_LABEL(list)) {
3611 switch (((LABEL *)list)->rescued) {
3612 case LABEL_RESCUE_BEG:
3613 rescue_level++;
3614 tailcallopt = FALSE;
3615 break;
3616 case LABEL_RESCUE_END:
3617 if (!--rescue_level) tailcallopt = do_tailcallopt;
3618 break;
3621 list = list->next;
3624 if (do_block_optimization) {
3625 LINK_ELEMENT * le = FIRST_ELEMENT(anchor)->next;
3626 if (IS_INSN(le) && IS_INSN_ID((INSN *)le, nop)) {
3627 ELEM_REMOVE(le);
3630 return COMPILE_OK;
3633 #if OPT_INSTRUCTIONS_UNIFICATION
3634 static INSN *
3635 new_unified_insn(rb_iseq_t *iseq,
3636 int insn_id, int size, LINK_ELEMENT *seq_list)
3638 INSN *iobj = 0;
3639 LINK_ELEMENT *list = seq_list;
3640 int i, argc = 0;
3641 VALUE *operands = 0, *ptr = 0;
3644 /* count argc */
3645 for (i = 0; i < size; i++) {
3646 iobj = (INSN *)list;
3647 argc += iobj->operand_size;
3648 list = list->next;
3651 if (argc > 0) {
3652 ptr = operands = compile_data_alloc2(iseq, sizeof(VALUE), argc);
3655 /* copy operands */
3656 list = seq_list;
3657 for (i = 0; i < size; i++) {
3658 iobj = (INSN *)list;
3659 MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
3660 ptr += iobj->operand_size;
3661 list = list->next;
3664 NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
3665 return new_insn_core(iseq, &dummy_line_node, insn_id, argc, operands);
3667 #endif
3670 * This scheme can get more performance if do this optimize with
3671 * label address resolving.
3672 * It's future work (if compile time was bottle neck).
3674 static int
3675 iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
3677 #if OPT_INSTRUCTIONS_UNIFICATION
3678 LINK_ELEMENT *list;
3679 INSN *iobj, *niobj;
3680 int id, k;
3681 intptr_t j;
3683 list = FIRST_ELEMENT(anchor);
3684 while (list) {
3685 if (IS_INSN(list)) {
3686 iobj = (INSN *)list;
3687 id = iobj->insn_id;
3688 if (unified_insns_data[id] != 0) {
3689 const int *const *entry = unified_insns_data[id];
3690 for (j = 1; j < (intptr_t)entry[0]; j++) {
3691 const int *unified = entry[j];
3692 LINK_ELEMENT *li = list->next;
3693 for (k = 2; k < unified[1]; k++) {
3694 if (!IS_INSN(li) ||
3695 ((INSN *)li)->insn_id != unified[k]) {
3696 goto miss;
3698 li = li->next;
3700 /* matched */
3701 niobj =
3702 new_unified_insn(iseq, unified[0], unified[1] - 1,
3703 list);
3705 /* insert to list */
3706 niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
3707 niobj->link.next = li;
3708 if (li) {
3709 li->prev = (LINK_ELEMENT *)niobj;
3712 list->prev->next = (LINK_ELEMENT *)niobj;
3713 list = (LINK_ELEMENT *)niobj;
3714 break;
3715 miss:;
3719 list = list->next;
3721 #endif
3722 return COMPILE_OK;
3725 #if OPT_STACK_CACHING
3727 #define SC_INSN(insn, stat) sc_insn_info[(insn)][(stat)]
3728 #define SC_NEXT(insn) sc_insn_next[(insn)]
3730 #include "opt_sc.inc"
3732 static int
3733 insn_set_sc_state(rb_iseq_t *iseq, const LINK_ELEMENT *anchor, INSN *iobj, int state)
3735 int nstate;
3736 int insn_id;
3738 insn_id = iobj->insn_id;
3739 iobj->insn_id = SC_INSN(insn_id, state);
3740 nstate = SC_NEXT(iobj->insn_id);
3742 if (insn_id == BIN(jump) ||
3743 insn_id == BIN(branchif) || insn_id == BIN(branchunless)) {
3744 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
3746 if (lobj->sc_state != 0) {
3747 if (lobj->sc_state != nstate) {
3748 BADINSN_DUMP(anchor, iobj, lobj);
3749 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
3750 "insn_set_sc_state error: %d at "LABEL_FORMAT
3751 ", %d expected\n",
3752 lobj->sc_state, lobj->label_no, nstate);
3753 return COMPILE_NG;
3756 else {
3757 lobj->sc_state = nstate;
3759 if (insn_id == BIN(jump)) {
3760 nstate = SCS_XX;
3763 else if (insn_id == BIN(leave)) {
3764 nstate = SCS_XX;
3767 return nstate;
3770 static int
3771 label_set_sc_state(LABEL *lobj, int state)
3773 if (lobj->sc_state != 0) {
3774 if (lobj->sc_state != state) {
3775 state = lobj->sc_state;
3778 else {
3779 lobj->sc_state = state;
3782 return state;
3786 #endif
3788 static int
3789 iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
3791 #if OPT_STACK_CACHING
3792 LINK_ELEMENT *list;
3793 int state, insn_id;
3795 /* initialize */
3796 state = SCS_XX;
3797 list = FIRST_ELEMENT(anchor);
3798 /* dump_disasm_list(list); */
3800 /* for each list element */
3801 while (list) {
3802 redo_point:
3803 switch (list->type) {
3804 case ISEQ_ELEMENT_INSN:
3806 INSN *iobj = (INSN *)list;
3807 insn_id = iobj->insn_id;
3809 /* dump_disasm_list(list); */
3811 switch (insn_id) {
3812 case BIN(nop):
3814 /* exception merge point */
3815 if (state != SCS_AX) {
3816 NODE dummy_line_node = generate_dummy_line_node(0, -1);
3817 INSN *rpobj =
3818 new_insn_body(iseq, &dummy_line_node, BIN(reput), 0);
3820 /* replace this insn */
3821 ELEM_REPLACE(list, (LINK_ELEMENT *)rpobj);
3822 list = (LINK_ELEMENT *)rpobj;
3823 goto redo_point;
3825 break;
3827 case BIN(swap):
3829 if (state == SCS_AB || state == SCS_BA) {
3830 state = (state == SCS_AB ? SCS_BA : SCS_AB);
3832 ELEM_REMOVE(list);
3833 list = list->next;
3834 goto redo_point;
3836 break;
3838 case BIN(pop):
3840 switch (state) {
3841 case SCS_AX:
3842 case SCS_BX:
3843 state = SCS_XX;
3844 break;
3845 case SCS_AB:
3846 state = SCS_AX;
3847 break;
3848 case SCS_BA:
3849 state = SCS_BX;
3850 break;
3851 case SCS_XX:
3852 goto normal_insn;
3853 default:
3854 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
3855 "unreachable");
3856 return COMPILE_NG;
3858 /* remove useless pop */
3859 ELEM_REMOVE(list);
3860 list = list->next;
3861 goto redo_point;
3863 default:;
3864 /* none */
3865 } /* end of switch */
3866 normal_insn:
3867 state = insn_set_sc_state(iseq, anchor, iobj, state);
3868 break;
3870 case ISEQ_ELEMENT_LABEL:
3872 LABEL *lobj;
3873 lobj = (LABEL *)list;
3875 state = label_set_sc_state(lobj, state);
3877 default:
3878 break;
3880 list = list->next;
3882 #endif
3883 return COMPILE_OK;
3886 static int
3887 all_string_result_p(const NODE *node)
3889 if (!node) return FALSE;
3890 switch (nd_type(node)) {
3891 case NODE_STR: case NODE_DSTR:
3892 return TRUE;
3893 case NODE_IF: case NODE_UNLESS:
3894 if (!node->nd_body || !node->nd_else) return FALSE;
3895 if (all_string_result_p(node->nd_body))
3896 return all_string_result_p(node->nd_else);
3897 return FALSE;
3898 case NODE_AND: case NODE_OR:
3899 if (!node->nd_2nd)
3900 return all_string_result_p(node->nd_1st);
3901 if (!all_string_result_p(node->nd_1st))
3902 return FALSE;
3903 return all_string_result_p(node->nd_2nd);
3904 default:
3905 return FALSE;
3909 static int
3910 compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp)
3912 const NODE *list = node->nd_next;
3913 VALUE lit = node->nd_lit;
3914 LINK_ELEMENT *first_lit = 0;
3915 int cnt = 0;
3917 debugp_param("nd_lit", lit);
3918 if (!NIL_P(lit)) {
3919 cnt++;
3920 if (!RB_TYPE_P(lit, T_STRING)) {
3921 COMPILE_ERROR(ERROR_ARGS "dstr: must be string: %s",
3922 rb_builtin_type_name(TYPE(lit)));
3923 return COMPILE_NG;
3925 lit = rb_fstring(lit);
3926 ADD_INSN1(ret, node, putobject, lit);
3927 RB_OBJ_WRITTEN(iseq, Qundef, lit);
3928 if (RSTRING_LEN(lit) == 0) first_lit = LAST_ELEMENT(ret);
3931 while (list) {
3932 const NODE *const head = list->nd_head;
3933 if (nd_type_p(head, NODE_STR)) {
3934 lit = rb_fstring(head->nd_lit);
3935 ADD_INSN1(ret, head, putobject, lit);
3936 RB_OBJ_WRITTEN(iseq, Qundef, lit);
3937 lit = Qnil;
3939 else {
3940 CHECK(COMPILE(ret, "each string", head));
3942 cnt++;
3943 list = list->nd_next;
3945 if (NIL_P(lit) && first_lit) {
3946 ELEM_REMOVE(first_lit);
3947 --cnt;
3949 *cntp = cnt;
3951 return COMPILE_OK;
3954 static int
3955 compile_block(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
3957 while (node && nd_type_p(node, NODE_BLOCK)) {
3958 CHECK(COMPILE_(ret, "BLOCK body", node->nd_head,
3959 (node->nd_next ? 1 : popped)));
3960 node = node->nd_next;
3962 if (node) {
3963 CHECK(COMPILE_(ret, "BLOCK next", node->nd_next, popped));
3965 return COMPILE_OK;
3968 static int
3969 compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
3971 int cnt;
3972 if (!node->nd_next) {
3973 VALUE lit = rb_fstring(node->nd_lit);
3974 ADD_INSN1(ret, node, putstring, lit);
3975 RB_OBJ_WRITTEN(iseq, Qundef, lit);
3977 else {
3978 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
3979 ADD_INSN1(ret, node, concatstrings, INT2FIX(cnt));
3981 return COMPILE_OK;
3984 static int
3985 compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
3987 int cnt;
3988 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
3989 ADD_INSN2(ret, node, toregexp, INT2FIX(node->nd_cflag), INT2FIX(cnt));
3990 return COMPILE_OK;
3993 static int
3994 compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int again,
3995 LABEL *then_label, LABEL *else_label)
3997 const int line = nd_line(node);
3998 LABEL *lend = NEW_LABEL(line);
3999 rb_num_t cnt = ISEQ_FLIP_CNT_INCREMENT(iseq->body->local_iseq)
4000 + VM_SVAR_FLIPFLOP_START;
4001 VALUE key = INT2FIX(cnt);
4003 ADD_INSN2(ret, node, getspecial, key, INT2FIX(0));
4004 ADD_INSNL(ret, node, branchif, lend);
4006 /* *flip == 0 */
4007 CHECK(COMPILE(ret, "flip2 beg", node->nd_beg));
4008 ADD_INSNL(ret, node, branchunless, else_label);
4009 ADD_INSN1(ret, node, putobject, Qtrue);
4010 ADD_INSN1(ret, node, setspecial, key);
4011 if (!again) {
4012 ADD_INSNL(ret, node, jump, then_label);
4015 /* *flip == 1 */
4016 ADD_LABEL(ret, lend);
4017 CHECK(COMPILE(ret, "flip2 end", node->nd_end));
4018 ADD_INSNL(ret, node, branchunless, then_label);
4019 ADD_INSN1(ret, node, putobject, Qfalse);
4020 ADD_INSN1(ret, node, setspecial, key);
4021 ADD_INSNL(ret, node, jump, then_label);
4023 return COMPILE_OK;
4026 static int
4027 compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
4028 LABEL *then_label, LABEL *else_label)
4030 again:
4031 switch (nd_type(cond)) {
4032 case NODE_AND:
4034 LABEL *label = NEW_LABEL(nd_line(cond));
4035 CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, label,
4036 else_label));
4037 if (!label->refcnt) {
4038 ADD_INSN(ret, cond, putnil);
4039 break;
4041 ADD_LABEL(ret, label);
4042 cond = cond->nd_2nd;
4043 goto again;
4045 case NODE_OR:
4047 LABEL *label = NEW_LABEL(nd_line(cond));
4048 CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
4049 label));
4050 if (!label->refcnt) {
4051 ADD_INSN(ret, cond, putnil);
4052 break;
4054 ADD_LABEL(ret, label);
4055 cond = cond->nd_2nd;
4056 goto again;
4058 case NODE_LIT: /* NODE_LIT is always true */
4059 case NODE_TRUE:
4060 case NODE_STR:
4061 case NODE_ZLIST:
4062 case NODE_LAMBDA:
4063 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4064 ADD_INSNL(ret, cond, jump, then_label);
4065 return COMPILE_OK;
4066 case NODE_FALSE:
4067 case NODE_NIL:
4068 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4069 ADD_INSNL(ret, cond, jump, else_label);
4070 return COMPILE_OK;
4071 case NODE_LIST:
4072 case NODE_ARGSCAT:
4073 case NODE_DREGX:
4074 case NODE_DSTR:
4075 CHECK(COMPILE_POPPED(ret, "branch condition", cond));
4076 ADD_INSNL(ret, cond, jump, then_label);
4077 return COMPILE_OK;
4078 case NODE_FLIP2:
4079 CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
4080 return COMPILE_OK;
4081 case NODE_FLIP3:
4082 CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
4083 return COMPILE_OK;
4084 case NODE_DEFINED:
4085 CHECK(compile_defined_expr(iseq, ret, cond, Qfalse));
4086 break;
4087 default:
4088 CHECK(COMPILE(ret, "branch condition", cond));
4089 break;
4092 ADD_INSNL(ret, cond, branchunless, else_label);
4093 ADD_INSNL(ret, cond, jump, then_label);
4094 return COMPILE_OK;
4097 #define HASH_BRACE 1
4099 static int
4100 keyword_node_p(const NODE *const node)
4102 return nd_type_p(node, NODE_HASH) && (node->nd_brace & HASH_BRACE) != HASH_BRACE;
4105 static int
4106 compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
4107 const NODE *const root_node,
4108 struct rb_callinfo_kwarg **const kw_arg_ptr,
4109 unsigned int *flag)
4111 if (kw_arg_ptr == NULL) return FALSE;
4113 if (root_node->nd_head && nd_type_p(root_node->nd_head, NODE_LIST)) {
4114 const NODE *node = root_node->nd_head;
4115 int seen_nodes = 0;
4117 while (node) {
4118 const NODE *key_node = node->nd_head;
4119 seen_nodes++;
4121 assert(nd_type_p(node, NODE_LIST));
4122 if (key_node && nd_type_p(key_node, NODE_LIT) && SYMBOL_P(key_node->nd_lit)) {
4123 /* can be keywords */
4125 else {
4126 if (flag) {
4127 *flag |= VM_CALL_KW_SPLAT;
4128 if (seen_nodes > 1 || node->nd_next->nd_next) {
4129 /* A new hash will be created for the keyword arguments
4130 * in this case, so mark the method as passing mutable
4131 * keyword splat.
4133 *flag |= VM_CALL_KW_SPLAT_MUT;
4136 return FALSE;
4138 node = node->nd_next; /* skip value node */
4139 node = node->nd_next;
4142 /* may be keywords */
4143 node = root_node->nd_head;
4145 int len = (int)node->nd_alen / 2;
4146 struct rb_callinfo_kwarg *kw_arg =
4147 rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
4148 VALUE *keywords = kw_arg->keywords;
4149 int i = 0;
4150 kw_arg->keyword_len = len;
4152 *kw_arg_ptr = kw_arg;
4154 for (i=0; node != NULL; i++, node = node->nd_next->nd_next) {
4155 const NODE *key_node = node->nd_head;
4156 const NODE *val_node = node->nd_next->nd_head;
4157 keywords[i] = key_node->nd_lit;
4158 NO_CHECK(COMPILE(ret, "keyword values", val_node));
4160 assert(i == len);
4161 return TRUE;
4164 return FALSE;
4167 static int
4168 compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node,
4169 struct rb_callinfo_kwarg **keywords_ptr, unsigned int *flag)
4171 int len = 0;
4173 for (; node; len++, node = node->nd_next) {
4174 if (CPDEBUG > 0) {
4175 EXPECT_NODE("compile_args", node, NODE_LIST, -1);
4178 if (node->nd_next == NULL && keyword_node_p(node->nd_head)) { /* last node */
4179 if (compile_keyword_arg(iseq, ret, node->nd_head, keywords_ptr, flag)) {
4180 len--;
4182 else {
4183 compile_hash(iseq, ret, node->nd_head, TRUE, FALSE);
4186 else {
4187 NO_CHECK(COMPILE_(ret, "array element", node->nd_head, FALSE));
4191 return len;
4194 static inline int
4195 static_literal_node_p(const NODE *node, const rb_iseq_t *iseq)
4197 switch (nd_type(node)) {
4198 case NODE_LIT:
4199 case NODE_NIL:
4200 case NODE_TRUE:
4201 case NODE_FALSE:
4202 return TRUE;
4203 case NODE_STR:
4204 return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal;
4205 default:
4206 return FALSE;
4210 static inline VALUE
4211 static_literal_value(const NODE *node, rb_iseq_t *iseq)
4213 switch (nd_type(node)) {
4214 case NODE_NIL:
4215 return Qnil;
4216 case NODE_TRUE:
4217 return Qtrue;
4218 case NODE_FALSE:
4219 return Qfalse;
4220 case NODE_STR:
4221 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
4222 VALUE lit;
4223 VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX((int)nd_line(node)));
4224 lit = rb_str_dup(node->nd_lit);
4225 rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
4226 return rb_str_freeze(lit);
4228 else {
4229 return rb_fstring(node->nd_lit);
4231 default:
4232 return node->nd_lit;
4236 static int
4237 compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
4239 const NODE *line_node = node;
4241 if (nd_type_p(node, NODE_ZLIST)) {
4242 if (!popped) {
4243 ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
4245 return 0;
4248 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
4250 if (popped) {
4251 for (; node; node = node->nd_next) {
4252 NO_CHECK(COMPILE_(ret, "array element", node->nd_head, popped));
4254 return 1;
4257 /* Compilation of an array literal.
4258 * The following code is essentially the same as:
4260 * for (int count = 0; node; count++; node->nd_next) {
4261 * compile(node->nd_head);
4263 * ADD_INSN(newarray, count);
4265 * However, there are three points.
4267 * - The code above causes stack overflow for a big string literal.
4268 * The following limits the stack length up to max_stack_len.
4270 * [x1,x2,...,x10000] =>
4271 * push x1 ; push x2 ; ...; push x256; newarray 256;
4272 * push x257; push x258; ...; push x512; newarray 256; concatarray;
4273 * push x513; push x514; ...; push x768; newarray 256; concatarray;
4274 * ...
4276 * - Long subarray can be optimized by pre-allocating a hidden array.
4278 * [1,2,3,...,100] =>
4279 * duparray [1,2,3,...,100]
4281 * [x, 1,2,3,...,100, z] =>
4282 * push x; newarray 1;
4283 * putobject [1,2,3,...,100] (<- hidden array); concatarray;
4284 * push z; newarray 1; concatarray
4286 * - If the last element is a keyword, newarraykwsplat should be emitted
4287 * to check and remove empty keyword arguments hash from array.
4288 * (Note: a keyword is NODE_HASH which is not static_literal_node_p.)
4290 * [1,2,3,**kw] =>
4291 * putobject 1; putobject 2; putobject 3; push kw; newarraykwsplat
4294 const int max_stack_len = 0x100;
4295 const int min_tmp_ary_len = 0x40;
4296 int stack_len = 0;
4297 int first_chunk = 1;
4299 /* Convert pushed elements to an array, and concatarray if needed */
4300 #define FLUSH_CHUNK(newarrayinsn) \
4301 if (stack_len) { \
4302 ADD_INSN1(ret, line_node, newarrayinsn, INT2FIX(stack_len)); \
4303 if (!first_chunk) ADD_INSN(ret, line_node, concatarray); \
4304 first_chunk = stack_len = 0; \
4307 while (node) {
4308 int count = 1;
4310 /* pre-allocation check (this branch can be omittable) */
4311 if (static_literal_node_p(node->nd_head, iseq)) {
4312 /* count the elements that are optimizable */
4313 const NODE *node_tmp = node->nd_next;
4314 for (; node_tmp && static_literal_node_p(node_tmp->nd_head, iseq); node_tmp = node_tmp->nd_next)
4315 count++;
4317 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) {
4318 /* The literal contains only optimizable elements, or the subarray is long enough */
4319 VALUE ary = rb_ary_tmp_new(count);
4321 /* Create a hidden array */
4322 for (; count; count--, node = node->nd_next)
4323 rb_ary_push(ary, static_literal_value(node->nd_head, iseq));
4324 OBJ_FREEZE(ary);
4326 /* Emit optimized code */
4327 FLUSH_CHUNK(newarray);
4328 if (first_chunk) {
4329 ADD_INSN1(ret, line_node, duparray, ary);
4330 first_chunk = 0;
4332 else {
4333 ADD_INSN1(ret, line_node, putobject, ary);
4334 ADD_INSN(ret, line_node, concatarray);
4336 RB_OBJ_WRITTEN(iseq, Qundef, ary);
4340 /* Base case: Compile "count" elements */
4341 for (; count; count--, node = node->nd_next) {
4342 if (CPDEBUG > 0) {
4343 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
4346 NO_CHECK(COMPILE_(ret, "array element", node->nd_head, 0));
4347 stack_len++;
4349 if (!node->nd_next && keyword_node_p(node->nd_head)) {
4350 /* Reached the end, and the last element is a keyword */
4351 FLUSH_CHUNK(newarraykwsplat);
4352 return 1;
4355 /* If there are many pushed elements, flush them to avoid stack overflow */
4356 if (stack_len >= max_stack_len) FLUSH_CHUNK(newarray);
4360 FLUSH_CHUNK(newarray);
4361 #undef FLUSH_CHUNK
4362 return 1;
4365 /* Compile an array containing the single element represented by node */
4366 static int
4367 compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node)
4369 if (static_literal_node_p(node, iseq)) {
4370 VALUE ary = rb_ary_tmp_new(1);
4371 rb_ary_push(ary, static_literal_value(node, iseq));
4372 OBJ_FREEZE(ary);
4374 ADD_INSN1(ret, node, duparray, ary);
4376 else {
4377 CHECK(COMPILE_(ret, "array element", node, FALSE));
4378 ADD_INSN1(ret, node, newarray, INT2FIX(1));
4381 return 1;
4384 static inline int
4385 static_literal_node_pair_p(const NODE *node, const rb_iseq_t *iseq)
4387 return node->nd_head && static_literal_node_p(node->nd_head, iseq) && static_literal_node_p(node->nd_next->nd_head, iseq);
4390 static int
4391 compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped)
4393 const NODE *line_node = node;
4395 node = node->nd_head;
4397 if (!node || nd_type_p(node, NODE_ZLIST)) {
4398 if (!popped) {
4399 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
4401 return 0;
4404 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
4406 if (popped) {
4407 for (; node; node = node->nd_next) {
4408 NO_CHECK(COMPILE_(ret, "hash element", node->nd_head, popped));
4410 return 1;
4413 /* Compilation of a hash literal (or keyword arguments).
4414 * This is very similar to compile_array, but there are some differences:
4416 * - It contains key-value pairs. So we need to take every two elements.
4417 * We can assume that the length is always even.
4419 * - Merging is done by a method call (id_core_hash_merge_ptr).
4420 * Sometimes we need to insert the receiver, so "anchor" is needed.
4421 * In addition, a method call is much slower than concatarray.
4422 * So it pays only when the subsequence is really long.
4423 * (min_tmp_hash_len must be much larger than min_tmp_ary_len.)
4425 * - We need to handle keyword splat: **kw.
4426 * For **kw, the key part (node->nd_head) is NULL, and the value part
4427 * (node->nd_next->nd_head) is "kw".
4428 * The code is a bit difficult to avoid hash allocation for **{}.
4431 const int max_stack_len = 0x100;
4432 const int min_tmp_hash_len = 0x800;
4433 int stack_len = 0;
4434 int first_chunk = 1;
4435 DECL_ANCHOR(anchor);
4436 INIT_ANCHOR(anchor);
4438 /* Convert pushed elements to a hash, and merge if needed */
4439 #define FLUSH_CHUNK() \
4440 if (stack_len) { \
4441 if (first_chunk) { \
4442 APPEND_LIST(ret, anchor); \
4443 ADD_INSN1(ret, line_node, newhash, INT2FIX(stack_len)); \
4445 else { \
4446 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
4447 ADD_INSN(ret, line_node, swap); \
4448 APPEND_LIST(ret, anchor); \
4449 ADD_SEND(ret, line_node, id_core_hash_merge_ptr, INT2FIX(stack_len + 1)); \
4451 INIT_ANCHOR(anchor); \
4452 first_chunk = stack_len = 0; \
4455 while (node) {
4456 int count = 1;
4458 /* pre-allocation check (this branch can be omittable) */
4459 if (static_literal_node_pair_p(node, iseq)) {
4460 /* count the elements that are optimizable */
4461 const NODE *node_tmp = node->nd_next->nd_next;
4462 for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = node_tmp->nd_next->nd_next)
4463 count++;
4465 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_hash_len) {
4466 /* The literal contains only optimizable elements, or the subsequence is long enough */
4467 VALUE ary = rb_ary_tmp_new(count);
4469 /* Create a hidden hash */
4470 for (; count; count--, node = node->nd_next->nd_next) {
4471 VALUE elem[2];
4472 elem[0] = static_literal_value(node->nd_head, iseq);
4473 elem[1] = static_literal_value(node->nd_next->nd_head, iseq);
4474 rb_ary_cat(ary, elem, 2);
4476 VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
4477 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR_TRANSIENT(ary), hash);
4478 hash = rb_obj_hide(hash);
4479 OBJ_FREEZE(hash);
4481 /* Emit optimized code */
4482 FLUSH_CHUNK();
4483 if (first_chunk) {
4484 ADD_INSN1(ret, line_node, duphash, hash);
4485 first_chunk = 0;
4487 else {
4488 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
4489 ADD_INSN(ret, line_node, swap);
4491 ADD_INSN1(ret, line_node, putobject, hash);
4493 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
4495 RB_OBJ_WRITTEN(iseq, Qundef, hash);
4499 /* Base case: Compile "count" elements */
4500 for (; count; count--, node = node->nd_next->nd_next) {
4502 if (CPDEBUG > 0) {
4503 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
4506 if (node->nd_head) {
4507 /* Normal key-value pair */
4508 NO_CHECK(COMPILE_(anchor, "hash key element", node->nd_head, 0));
4509 NO_CHECK(COMPILE_(anchor, "hash value element", node->nd_next->nd_head, 0));
4510 stack_len += 2;
4512 /* If there are many pushed elements, flush them to avoid stack overflow */
4513 if (stack_len >= max_stack_len) FLUSH_CHUNK();
4515 else {
4516 /* kwsplat case: foo(..., **kw, ...) */
4517 FLUSH_CHUNK();
4519 const NODE *kw = node->nd_next->nd_head;
4520 int empty_kw = nd_type_p(kw, NODE_LIT) && RB_TYPE_P(kw->nd_lit, T_HASH); /* foo( ..., **{}, ...) */
4521 int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */
4522 int last_kw = !node->nd_next->nd_next; /* foo( ..., **kw) */
4523 int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
4525 if (empty_kw) {
4526 if (only_kw && method_call_keywords) {
4527 /* **{} appears at the only keyword argument in method call,
4528 * so it won't be modified.
4529 * kw is a special NODE_LIT that contains a special empty hash,
4530 * so this emits: putobject {}.
4531 * This is only done for method calls and not for literal hashes,
4532 * because literal hashes should always result in a new hash.
4534 NO_CHECK(COMPILE(ret, "keyword splat", kw));
4536 else if (first_kw) {
4537 /* **{} appears as the first keyword argument, so it may be modified.
4538 * We need to create a fresh hash object.
4540 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
4542 /* Any empty keyword splats that are not the first can be ignored.
4543 * since merging an empty hash into the existing hash is the same
4544 * as not merging it. */
4546 else {
4547 if (only_kw && method_call_keywords) {
4548 /* **kw is only keyword argument in method call.
4549 * Use directly. This will be not be flagged as mutable.
4550 * This is only done for method calls and not for literal hashes,
4551 * because literal hashes should always result in a new hash.
4553 NO_CHECK(COMPILE(ret, "keyword splat", kw));
4555 else {
4556 /* There is more than one keyword argument, or this is not a method
4557 * call. In that case, we need to add an empty hash (if first keyword),
4558 * or merge the hash to the accumulated hash (if not the first keyword).
4560 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
4561 if (first_kw) ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
4562 else ADD_INSN(ret, line_node, swap);
4564 NO_CHECK(COMPILE(ret, "keyword splat", kw));
4566 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
4570 first_chunk = 0;
4575 FLUSH_CHUNK();
4576 #undef FLUSH_CHUNK
4577 return 1;
4580 VALUE
4581 rb_node_case_when_optimizable_literal(const NODE *const node)
4583 switch (nd_type(node)) {
4584 case NODE_LIT: {
4585 VALUE v = node->nd_lit;
4586 double ival;
4587 if (RB_FLOAT_TYPE_P(v) &&
4588 modf(RFLOAT_VALUE(v), &ival) == 0.0) {
4589 return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
4591 if (RB_TYPE_P(v, T_RATIONAL) || RB_TYPE_P(v, T_COMPLEX)) {
4592 return Qundef;
4594 if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) {
4595 return v;
4597 break;
4599 case NODE_NIL:
4600 return Qnil;
4601 case NODE_TRUE:
4602 return Qtrue;
4603 case NODE_FALSE:
4604 return Qfalse;
4605 case NODE_STR:
4606 return rb_fstring(node->nd_lit);
4608 return Qundef;
4611 static int
4612 when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
4613 LABEL *l1, int only_special_literals, VALUE literals)
4615 while (vals) {
4616 const NODE *val = vals->nd_head;
4617 VALUE lit = rb_node_case_when_optimizable_literal(val);
4619 if (lit == Qundef) {
4620 only_special_literals = 0;
4622 else if (NIL_P(rb_hash_lookup(literals, lit))) {
4623 rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
4626 if (nd_type_p(val, NODE_STR)) {
4627 debugp_param("nd_lit", val->nd_lit);
4628 lit = rb_fstring(val->nd_lit);
4629 ADD_INSN1(cond_seq, val, putobject, lit);
4630 RB_OBJ_WRITTEN(iseq, Qundef, lit);
4632 else {
4633 if (!COMPILE(cond_seq, "when cond", val)) return -1;
4636 // Emit patern === target
4637 ADD_INSN1(cond_seq, vals, topn, INT2FIX(1));
4638 ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1));
4639 ADD_INSNL(cond_seq, val, branchif, l1);
4640 vals = vals->nd_next;
4642 return only_special_literals;
4645 static int
4646 when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
4647 LABEL *l1, int only_special_literals, VALUE literals)
4649 const NODE *line_node = vals;
4651 switch (nd_type(vals)) {
4652 case NODE_LIST:
4653 if (when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals) < 0)
4654 return COMPILE_NG;
4655 break;
4656 case NODE_SPLAT:
4657 ADD_INSN (cond_seq, line_node, dup);
4658 CHECK(COMPILE(cond_seq, "when splat", vals->nd_head));
4659 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
4660 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
4661 ADD_INSNL(cond_seq, line_node, branchif, l1);
4662 break;
4663 case NODE_ARGSCAT:
4664 CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals));
4665 CHECK(when_splat_vals(iseq, cond_seq, vals->nd_body, l1, only_special_literals, literals));
4666 break;
4667 case NODE_ARGSPUSH:
4668 CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals));
4669 ADD_INSN (cond_seq, line_node, dup);
4670 CHECK(COMPILE(cond_seq, "when argspush body", vals->nd_body));
4671 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
4672 ADD_INSNL(cond_seq, line_node, branchif, l1);
4673 break;
4674 default:
4675 ADD_INSN (cond_seq, line_node, dup);
4676 CHECK(COMPILE(cond_seq, "when val", vals));
4677 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
4678 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
4679 ADD_INSNL(cond_seq, line_node, branchif, l1);
4680 break;
4682 return COMPILE_OK;
4685 /* Multiple Assignment Handling
4687 * In order to handle evaluation of multiple assignment such that the left hand side
4688 * is evaluated before the right hand side, we need to process the left hand side
4689 * and see if there are any attributes that need to be assigned, or constants set
4690 * on explicit objects. If so, we add instructions to evaluate the receiver of
4691 * any assigned attributes or constants before we process the right hand side.
4693 * For a multiple assignment such as:
4695 * l1.m1, l2[0] = r3, r4
4697 * We start off evaluating l1 and l2, then we evaluate r3 and r4, then we
4698 * assign the result of r3 to l1.m1, and then the result of r4 to l2.m2.
4699 * On the VM stack, this looks like:
4701 * self # putself
4702 * l1 # send
4703 * l1, self # putself
4704 * l1, l2 # send
4705 * l1, l2, 0 # putobject 0
4706 * l1, l2, 0, [r3, r4] # after evaluation of RHS
4707 * l1, l2, 0, [r3, r4], r4, r3 # expandarray
4708 * l1, l2, 0, [r3, r4], r4, r3, l1 # topn 5
4709 * l1, l2, 0, [r3, r4], r4, l1, r3 # swap
4710 * l1, l2, 0, [r3, r4], r4, m1= # send
4711 * l1, l2, 0, [r3, r4], r4 # pop
4712 * l1, l2, 0, [r3, r4], r4, l2 # topn 3
4713 * l1, l2, 0, [r3, r4], r4, l2, 0 # topn 3
4714 * l1, l2, 0, [r3, r4], r4, l2, 0, r4 # topn 2
4715 * l1, l2, 0, [r3, r4], r4, []= # send
4716 * l1, l2, 0, [r3, r4], r4 # pop
4717 * l1, l2, 0, [r3, r4] # pop
4718 * [r3, r4], l2, 0, [r3, r4] # setn 3
4719 * [r3, r4], l2, 0 # pop
4720 * [r3, r4], l2 # pop
4721 * [r3, r4] # pop
4723 * This is made more complex when you have to handle splats, post args,
4724 * and arbitrary levels of nesting. You need to keep track of the total
4725 * number of attributes to set, and for each attribute, how many entries
4726 * are on the stack before the final attribute, in order to correctly
4727 * calculate the topn value to use to get the receiver of the attribute
4728 * setter method.
4730 * A brief description of the VM stack for simple multiple assignment
4731 * with no splat (rhs_array will not be present if the return value of
4732 * the multiple assignment is not needed):
4734 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., rhs_arg2, rhs_arg1
4736 * For multiple assignment with splats, while processing the part before
4737 * the splat (splat+post here is an array of the splat and the post arguments):
4739 * lhs_attr1, lhs_attr2, ..., rhs_array, splat+post, ..., rhs_arg2, rhs_arg1
4741 * When processing the splat and post arguments:
4743 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., post_arg2, post_arg1, splat
4745 * When processing nested multiple assignment, existing values on the stack
4746 * are kept. So for:
4748 * (l1.m1, l2.m2), l3.m3, l4* = [r1, r2], r3, r4
4750 * The stack layout would be the following before processing the nested
4751 * multiple assignment:
4753 * l1, l2, [[r1, r2], r3, r4], [r4], r3, [r1, r2]
4755 * In order to handle this correctly, we need to keep track of the nesting
4756 * level for each attribute assignment, as well as the attribute number
4757 * (left hand side attributes are processed left to right) and number of
4758 * arguments to pass to the setter method. struct masgn_lhs_node tracks
4759 * this information.
4761 * We also need to track information for the entire multiple assignment, such
4762 * as the total number of arguments, and the current nesting level, to
4763 * handle both nested multiple assignment as well as cases where the
4764 * rhs is not needed. We also need to keep track of all attribute
4765 * assignments in this, which we do using a linked listed. struct masgn_state
4766 * tracks this information.
4769 struct masgn_lhs_node {
4770 INSN *before_insn;
4771 struct masgn_lhs_node *next;
4772 const NODE *line_node;
4773 int argn;
4774 int num_args;
4775 int lhs_pos;
4778 struct masgn_state {
4779 struct masgn_lhs_node *first_memo;
4780 struct masgn_lhs_node *last_memo;
4781 int lhs_level;
4782 int num_args;
4783 bool nested;
4786 static int
4787 add_masgn_lhs_node(struct masgn_state *state, int lhs_pos, const NODE *line_node, int argc, INSN *before_insn) {
4788 if (!state) {
4789 rb_bug("no masgn_state");
4792 struct masgn_lhs_node *memo;
4793 memo = malloc(sizeof(struct masgn_lhs_node));
4794 if (!memo) {
4795 return COMPILE_NG;
4798 memo->before_insn = before_insn;
4799 memo->line_node = line_node;
4800 memo->argn = state->num_args + 1;
4801 memo->num_args = argc;
4802 state->num_args += argc;
4803 memo->lhs_pos = lhs_pos;
4804 memo->next = NULL;
4805 if (!state->first_memo) {
4806 state->first_memo = memo;
4808 else {
4809 state->last_memo->next = memo;
4811 state->last_memo = memo;
4813 return COMPILE_OK;
4816 static int compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int popped);
4818 static int
4819 compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int lhs_pos)
4821 switch (nd_type(node)) {
4822 case NODE_ATTRASGN: {
4823 INSN *iobj;
4824 const NODE *line_node = node;
4826 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
4828 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
4829 iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
4830 ASSUME(iobj);
4831 ELEM_REMOVE(LAST_ELEMENT(pre));
4832 ELEM_REMOVE((LINK_ELEMENT *)iobj);
4833 pre->last = iobj->link.prev;
4835 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
4836 int argc = vm_ci_argc(ci) + 1;
4837 ci = ci_argc_set(iseq, ci, argc);
4838 OPERAND_AT(iobj, 0) = (VALUE)ci;
4839 RB_OBJ_WRITTEN(iseq, Qundef, ci);
4841 if (argc == 1) {
4842 ADD_INSN(lhs, line_node, swap);
4844 else {
4845 ADD_INSN1(lhs, line_node, topn, INT2FIX(argc));
4848 if (!add_masgn_lhs_node(state, lhs_pos, line_node, argc, (INSN *)LAST_ELEMENT(lhs))) {
4849 return COMPILE_NG;
4852 ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
4853 if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
4854 int argc = vm_ci_argc(ci);
4855 ci = ci_argc_set(iseq, ci, argc - 1);
4856 OPERAND_AT(iobj, 0) = (VALUE)ci;
4857 RB_OBJ_WRITTEN(iseq, Qundef, iobj);
4858 INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1));
4859 INSERT_BEFORE_INSN(iobj, line_node, concatarray);
4861 ADD_INSN(lhs, line_node, pop);
4862 if (argc != 1) {
4863 ADD_INSN(lhs, line_node, pop);
4865 for (int i=0; i < argc; i++) {
4866 ADD_INSN(post, line_node, pop);
4868 break;
4870 case NODE_MASGN: {
4871 DECL_ANCHOR(nest_rhs);
4872 INIT_ANCHOR(nest_rhs);
4873 DECL_ANCHOR(nest_lhs);
4874 INIT_ANCHOR(nest_lhs);
4876 int prev_level = state->lhs_level;
4877 bool prev_nested = state->nested;
4878 state->nested = 1;
4879 state->lhs_level = lhs_pos - 1;
4880 CHECK(compile_massign0(iseq, pre, nest_rhs, nest_lhs, post, node, state, 1));
4881 state->lhs_level = prev_level;
4882 state->nested = prev_nested;
4884 ADD_SEQ(lhs, nest_rhs);
4885 ADD_SEQ(lhs, nest_lhs);
4886 break;
4888 case NODE_CDECL:
4889 if (!node->nd_vid) {
4890 /* Special handling only needed for expr::C, not for C */
4891 INSN *iobj;
4893 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_CDECL)", node));
4895 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
4896 iobj = (INSN *)insn_element; /* setconstant insn */
4897 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn((INSN *)get_prev_insn(iobj)));
4898 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn(iobj));
4899 ELEM_REMOVE(insn_element);
4900 pre->last = iobj->link.prev;
4901 ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
4903 if (!add_masgn_lhs_node(state, lhs_pos, node, 1, (INSN *)LAST_ELEMENT(lhs))) {
4904 return COMPILE_NG;
4907 ADD_INSN(post, node, pop);
4908 break;
4910 /* Fallthrough */
4911 default: {
4912 DECL_ANCHOR(anchor);
4913 INIT_ANCHOR(anchor);
4914 CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
4915 ELEM_REMOVE(FIRST_ELEMENT(anchor));
4916 ADD_SEQ(lhs, anchor);
4920 return COMPILE_OK;
4923 static int
4924 compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
4926 if (lhsn) {
4927 CHECK(compile_massign_opt_lhs(iseq, ret, lhsn->nd_next));
4928 CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, lhsn->nd_head, NULL, 0));
4930 return COMPILE_OK;
4933 static int
4934 compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
4935 const NODE *rhsn, const NODE *orig_lhsn)
4937 VALUE mem[64];
4938 const int memsize = numberof(mem);
4939 int memindex = 0;
4940 int llen = 0, rlen = 0;
4941 int i;
4942 const NODE *lhsn = orig_lhsn;
4944 #define MEMORY(v) { \
4945 int i; \
4946 if (memindex == memsize) return 0; \
4947 for (i=0; i<memindex; i++) { \
4948 if (mem[i] == (v)) return 0; \
4950 mem[memindex++] = (v); \
4953 if (rhsn == 0 || !nd_type_p(rhsn, NODE_LIST)) {
4954 return 0;
4957 while (lhsn) {
4958 const NODE *ln = lhsn->nd_head;
4959 switch (nd_type(ln)) {
4960 case NODE_LASGN:
4961 MEMORY(ln->nd_vid);
4962 break;
4963 case NODE_DASGN:
4964 case NODE_IASGN:
4965 case NODE_CVASGN:
4966 MEMORY(ln->nd_vid);
4967 break;
4968 default:
4969 return 0;
4971 lhsn = lhsn->nd_next;
4972 llen++;
4975 while (rhsn) {
4976 if (llen <= rlen) {
4977 NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", rhsn->nd_head));
4979 else {
4980 NO_CHECK(COMPILE(ret, "masgn val", rhsn->nd_head));
4982 rhsn = rhsn->nd_next;
4983 rlen++;
4986 if (llen > rlen) {
4987 for (i=0; i<llen-rlen; i++) {
4988 ADD_INSN(ret, orig_lhsn, putnil);
4992 compile_massign_opt_lhs(iseq, ret, orig_lhsn);
4993 return 1;
4996 static int
4997 compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int popped)
4999 const NODE *rhsn = node->nd_value;
5000 const NODE *splatn = node->nd_args;
5001 const NODE *lhsn = node->nd_head;
5002 const NODE *lhsn_count = lhsn;
5003 int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0;
5005 int llen = 0;
5006 int lpos = 0;
5007 int expand = 1;
5009 while (lhsn_count) {
5010 llen++;
5011 lhsn_count = lhsn_count->nd_next;
5013 while (lhsn) {
5014 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, lhsn->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level));
5015 lpos++;
5016 lhsn = lhsn->nd_next;
5019 if (lhs_splat) {
5020 if (nd_type_p(splatn, NODE_POSTARG)) {
5021 /*a, b, *r, p1, p2 */
5022 const NODE *postn = splatn->nd_2nd;
5023 const NODE *restn = splatn->nd_1st;
5024 int plen = (int)postn->nd_alen;
5025 int ppos = 0;
5026 int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00);
5028 ADD_INSN2(lhs, splatn, expandarray, INT2FIX(plen), INT2FIX(flag));
5030 if (NODE_NAMED_REST_P(restn)) {
5031 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, restn, state, 1 + plen + state->lhs_level));
5033 while (postn) {
5034 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, postn->nd_head, state, (plen - ppos) + state->lhs_level));
5035 ppos++;
5036 postn = postn->nd_next;
5039 else {
5040 /* a, b, *r */
5041 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, splatn, state, 1 + state->lhs_level));
5046 if (!state->nested) {
5047 NO_CHECK(COMPILE(rhs, "normal masgn rhs", rhsn));
5050 if (!popped) {
5051 ADD_INSN(rhs, node, dup);
5053 if (expand) {
5054 ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
5056 return COMPILE_OK;
5059 static int
5060 compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
5062 if (!popped || node->nd_args || !compile_massign_opt(iseq, ret, node->nd_value, node->nd_head)) {
5063 struct masgn_state state;
5064 state.lhs_level = popped ? 0 : 1;
5065 state.nested = 0;
5066 state.num_args = 0;
5067 state.first_memo = NULL;
5068 state.last_memo = NULL;
5070 DECL_ANCHOR(pre);
5071 INIT_ANCHOR(pre);
5072 DECL_ANCHOR(rhs);
5073 INIT_ANCHOR(rhs);
5074 DECL_ANCHOR(lhs);
5075 INIT_ANCHOR(lhs);
5076 DECL_ANCHOR(post);
5077 INIT_ANCHOR(post);
5078 int ok = compile_massign0(iseq, pre, rhs, lhs, post, node, &state, popped);
5080 struct masgn_lhs_node *memo = state.first_memo, *tmp_memo;
5081 while (memo) {
5082 VALUE topn_arg = INT2FIX((state.num_args - memo->argn) + memo->lhs_pos);
5083 for (int i = 0; i < memo->num_args; i++) {
5084 INSERT_BEFORE_INSN1(memo->before_insn, memo->line_node, topn, topn_arg);
5086 tmp_memo = memo->next;
5087 free(memo);
5088 memo = tmp_memo;
5090 CHECK(ok);
5092 ADD_SEQ(ret, pre);
5093 ADD_SEQ(ret, rhs);
5094 ADD_SEQ(ret, lhs);
5095 if (!popped && state.num_args >= 1) {
5096 /* make sure rhs array is returned before popping */
5097 ADD_INSN1(ret, node, setn, INT2FIX(state.num_args));
5099 ADD_SEQ(ret, post);
5101 return COMPILE_OK;
5104 static int
5105 compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
5106 LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
5108 switch (nd_type(node)) {
5109 case NODE_CONST:
5110 debugi("compile_const_prefix - colon", node->nd_vid);
5111 ADD_INSN1(body, node, putobject, Qtrue);
5112 ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_vid));
5113 break;
5114 case NODE_COLON3:
5115 debugi("compile_const_prefix - colon3", node->nd_mid);
5116 ADD_INSN(body, node, pop);
5117 ADD_INSN1(body, node, putobject, rb_cObject);
5118 ADD_INSN1(body, node, putobject, Qtrue);
5119 ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid));
5120 break;
5121 case NODE_COLON2:
5122 CHECK(compile_const_prefix(iseq, node->nd_head, pref, body));
5123 debugi("compile_const_prefix - colon2", node->nd_mid);
5124 ADD_INSN1(body, node, putobject, Qfalse);
5125 ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid));
5126 break;
5127 default:
5128 CHECK(COMPILE(pref, "const colon2 prefix", node));
5129 break;
5131 return COMPILE_OK;
5134 static int
5135 compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
5137 if (nd_type_p(cpath, NODE_COLON3)) {
5138 /* toplevel class ::Foo */
5139 ADD_INSN1(ret, cpath, putobject, rb_cObject);
5140 return VM_DEFINECLASS_FLAG_SCOPED;
5142 else if (cpath->nd_head) {
5143 /* Bar::Foo */
5144 NO_CHECK(COMPILE(ret, "nd_else->nd_head", cpath->nd_head));
5145 return VM_DEFINECLASS_FLAG_SCOPED;
5147 else {
5148 /* class at cbase Foo */
5149 ADD_INSN1(ret, cpath, putspecialobject,
5150 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5151 return 0;
5155 static inline int
5156 private_recv_p(const NODE *node)
5158 if (nd_type_p(node->nd_recv, NODE_SELF)) {
5159 NODE *self = node->nd_recv;
5160 return self->nd_state != 0;
5162 return 0;
5165 static void
5166 defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5167 const NODE *const node, LABEL **lfinish, VALUE needstr);
5169 static int
5170 compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const enum node_type type, const NODE *const line_node, int popped, bool assume_receiver);
5172 static void
5173 defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5174 const NODE *const node, LABEL **lfinish, VALUE needstr,
5175 bool keep_result)
5177 enum defined_type expr_type = DEFINED_NOT_DEFINED;
5178 enum node_type type;
5179 const int line = nd_line(node);
5180 const NODE *line_node = node;
5182 switch (type = nd_type(node)) {
5184 /* easy literals */
5185 case NODE_NIL:
5186 expr_type = DEFINED_NIL;
5187 break;
5188 case NODE_SELF:
5189 expr_type = DEFINED_SELF;
5190 break;
5191 case NODE_TRUE:
5192 expr_type = DEFINED_TRUE;
5193 break;
5194 case NODE_FALSE:
5195 expr_type = DEFINED_FALSE;
5196 break;
5198 case NODE_LIST:{
5199 const NODE *vals = node;
5201 do {
5202 defined_expr0(iseq, ret, vals->nd_head, lfinish, Qfalse, false);
5204 if (!lfinish[1]) {
5205 lfinish[1] = NEW_LABEL(line);
5207 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5208 } while ((vals = vals->nd_next) != NULL);
5210 /* fall through */
5211 case NODE_STR:
5212 case NODE_LIT:
5213 case NODE_ZLIST:
5214 case NODE_AND:
5215 case NODE_OR:
5216 default:
5217 expr_type = DEFINED_EXPR;
5218 break;
5220 /* variables */
5221 case NODE_LVAR:
5222 case NODE_DVAR:
5223 expr_type = DEFINED_LVAR;
5224 break;
5226 #define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type))
5227 case NODE_IVAR:
5228 ADD_INSN(ret, line_node, putnil);
5229 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_IVAR),
5230 ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_IVAR));
5231 return;
5233 case NODE_GVAR:
5234 ADD_INSN(ret, line_node, putnil);
5235 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR),
5236 ID2SYM(node->nd_entry), PUSH_VAL(DEFINED_GVAR));
5237 return;
5239 case NODE_CVAR:
5240 ADD_INSN(ret, line_node, putnil);
5241 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR),
5242 ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CVAR));
5243 return;
5245 case NODE_CONST:
5246 ADD_INSN(ret, line_node, putnil);
5247 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST),
5248 ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CONST));
5249 return;
5250 case NODE_COLON2:
5251 if (!lfinish[1]) {
5252 lfinish[1] = NEW_LABEL(line);
5254 defined_expr0(iseq, ret, node->nd_head, lfinish, Qfalse, false);
5255 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5256 NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", node->nd_head));
5258 if (rb_is_const_id(node->nd_mid)) {
5259 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST_FROM),
5260 ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST));
5262 else {
5263 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
5264 ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
5266 return;
5267 case NODE_COLON3:
5268 ADD_INSN1(ret, line_node, putobject, rb_cObject);
5269 ADD_INSN3(ret, line_node, defined,
5270 INT2FIX(DEFINED_CONST_FROM), ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST));
5271 return;
5273 /* method dispatch */
5274 case NODE_CALL:
5275 case NODE_OPCALL:
5276 case NODE_VCALL:
5277 case NODE_FCALL:
5278 case NODE_ATTRASGN:{
5279 const int explicit_receiver =
5280 (type == NODE_CALL || type == NODE_OPCALL ||
5281 (type == NODE_ATTRASGN && !private_recv_p(node)));
5283 if (node->nd_args || explicit_receiver) {
5284 if (!lfinish[1]) {
5285 lfinish[1] = NEW_LABEL(line);
5287 if (!lfinish[2]) {
5288 lfinish[2] = NEW_LABEL(line);
5291 if (node->nd_args) {
5292 defined_expr0(iseq, ret, node->nd_args, lfinish, Qfalse, false);
5293 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5295 if (explicit_receiver) {
5296 defined_expr0(iseq, ret, node->nd_recv, lfinish, Qfalse, true);
5297 switch (nd_type(node->nd_recv)) {
5298 case NODE_CALL:
5299 case NODE_OPCALL:
5300 case NODE_VCALL:
5301 case NODE_FCALL:
5302 case NODE_ATTRASGN:
5303 ADD_INSNL(ret, line_node, branchunless, lfinish[2]);
5304 compile_call(iseq, ret, node->nd_recv, nd_type(node->nd_recv), line_node, 0, true);
5305 break;
5306 default:
5307 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5308 NO_CHECK(COMPILE(ret, "defined/recv", node->nd_recv));
5309 break;
5311 if (keep_result) {
5312 ADD_INSN(ret, line_node, dup);
5314 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
5315 ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
5317 else {
5318 ADD_INSN(ret, line_node, putself);
5319 if (keep_result) {
5320 ADD_INSN(ret, line_node, dup);
5322 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC),
5323 ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
5325 return;
5328 case NODE_YIELD:
5329 ADD_INSN(ret, line_node, putnil);
5330 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
5331 PUSH_VAL(DEFINED_YIELD));
5332 return;
5334 case NODE_BACK_REF:
5335 case NODE_NTH_REF:
5336 ADD_INSN(ret, line_node, putnil);
5337 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF),
5338 INT2FIX((node->nd_nth << 1) | (type == NODE_BACK_REF)),
5339 PUSH_VAL(DEFINED_GVAR));
5340 return;
5342 case NODE_SUPER:
5343 case NODE_ZSUPER:
5344 ADD_INSN(ret, line_node, putnil);
5345 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_ZSUPER), 0,
5346 PUSH_VAL(DEFINED_ZSUPER));
5347 return;
5349 #undef PUSH_VAL
5350 case NODE_OP_ASGN1:
5351 case NODE_OP_ASGN2:
5352 case NODE_OP_ASGN_OR:
5353 case NODE_OP_ASGN_AND:
5354 case NODE_MASGN:
5355 case NODE_LASGN:
5356 case NODE_DASGN:
5357 case NODE_GASGN:
5358 case NODE_IASGN:
5359 case NODE_CDECL:
5360 case NODE_CVASGN:
5361 expr_type = DEFINED_ASGN;
5362 break;
5365 assert(expr_type != DEFINED_NOT_DEFINED);
5367 if (needstr != Qfalse) {
5368 VALUE str = rb_iseq_defined_string(expr_type);
5369 ADD_INSN1(ret, line_node, putobject, str);
5371 else {
5372 ADD_INSN1(ret, line_node, putobject, Qtrue);
5376 static void
5377 build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *unused)
5379 NODE dummy_line_node = generate_dummy_line_node(0, -1);
5380 ADD_INSN(ret, &dummy_line_node, putnil);
5381 iseq_set_exception_local_table(iseq);
5384 static void
5385 defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5386 const NODE *const node, LABEL **lfinish, VALUE needstr)
5388 LINK_ELEMENT *lcur = ret->last;
5389 defined_expr0(iseq, ret, node, lfinish, needstr, false);
5390 if (lfinish[1]) {
5391 int line = nd_line(node);
5392 LABEL *lstart = NEW_LABEL(line);
5393 LABEL *lend = NEW_LABEL(line);
5394 const rb_iseq_t *rescue;
5395 struct rb_iseq_new_with_callback_callback_func *ifunc =
5396 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
5397 rescue = new_child_iseq_with_callback(iseq, ifunc,
5398 rb_str_concat(rb_str_new2("defined guard in "),
5399 iseq->body->location.label),
5400 iseq, ISEQ_TYPE_RESCUE, 0);
5401 lstart->rescued = LABEL_RESCUE_BEG;
5402 lend->rescued = LABEL_RESCUE_END;
5403 APPEND_LABEL(ret, lcur, lstart);
5404 ADD_LABEL(ret, lend);
5405 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
5409 static int
5410 compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr)
5412 const int line = nd_line(node);
5413 const NODE *line_node = node;
5414 if (!node->nd_head) {
5415 VALUE str = rb_iseq_defined_string(DEFINED_NIL);
5416 ADD_INSN1(ret, line_node, putobject, str);
5418 else {
5419 LABEL *lfinish[3];
5420 LINK_ELEMENT *last = ret->last;
5421 lfinish[0] = NEW_LABEL(line);
5422 lfinish[1] = 0;
5423 lfinish[2] = 0;
5424 defined_expr(iseq, ret, node->nd_head, lfinish, needstr);
5425 if (lfinish[1]) {
5426 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, line_node, BIN(putnil), 0)->link);
5427 ADD_INSN(ret, line_node, swap);
5428 if (lfinish[2]) {
5429 ADD_LABEL(ret, lfinish[2]);
5431 ADD_INSN(ret, line_node, pop);
5432 ADD_LABEL(ret, lfinish[1]);
5434 ADD_LABEL(ret, lfinish[0]);
5436 return COMPILE_OK;
5439 static VALUE
5440 make_name_for_block(const rb_iseq_t *orig_iseq)
5442 int level = 1;
5443 const rb_iseq_t *iseq = orig_iseq;
5445 if (orig_iseq->body->parent_iseq != 0) {
5446 while (orig_iseq->body->local_iseq != iseq) {
5447 if (iseq->body->type == ISEQ_TYPE_BLOCK) {
5448 level++;
5450 iseq = iseq->body->parent_iseq;
5454 if (level == 1) {
5455 return rb_sprintf("block in %"PRIsVALUE, iseq->body->location.label);
5457 else {
5458 return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, iseq->body->location.label);
5462 static void
5463 push_ensure_entry(rb_iseq_t *iseq,
5464 struct iseq_compile_data_ensure_node_stack *enl,
5465 struct ensure_range *er, const NODE *const node)
5467 enl->ensure_node = node;
5468 enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
5469 enl->erange = er;
5470 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl;
5473 static void
5474 add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
5475 LABEL *lstart, LABEL *lend)
5477 struct ensure_range *ne =
5478 compile_data_alloc(iseq, sizeof(struct ensure_range));
5480 while (erange->next != 0) {
5481 erange = erange->next;
5483 ne->next = 0;
5484 ne->begin = lend;
5485 ne->end = erange->end;
5486 erange->end = lstart;
5488 erange->next = ne;
5491 static bool
5492 can_add_ensure_iseq(const rb_iseq_t *iseq)
5494 struct iseq_compile_data_ensure_node_stack *e;
5495 if (ISEQ_COMPILE_DATA(iseq)->in_rescue && (e = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack) != NULL) {
5496 while (e) {
5497 if (e->ensure_node) return false;
5498 e = e->prev;
5501 return true;
5504 static void
5505 add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
5507 assert(can_add_ensure_iseq(iseq));
5509 struct iseq_compile_data_ensure_node_stack *enlp =
5510 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
5511 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
5512 DECL_ANCHOR(ensure);
5514 INIT_ANCHOR(ensure);
5515 while (enlp) {
5516 if (enlp->erange != NULL) {
5517 DECL_ANCHOR(ensure_part);
5518 LABEL *lstart = NEW_LABEL(0);
5519 LABEL *lend = NEW_LABEL(0);
5520 INIT_ANCHOR(ensure_part);
5522 add_ensure_range(iseq, enlp->erange, lstart, lend);
5524 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
5525 ADD_LABEL(ensure_part, lstart);
5526 NO_CHECK(COMPILE_POPPED(ensure_part, "ensure part", enlp->ensure_node));
5527 ADD_LABEL(ensure_part, lend);
5528 ADD_SEQ(ensure, ensure_part);
5530 else {
5531 if (!is_return) {
5532 break;
5535 enlp = enlp->prev;
5537 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
5538 ADD_SEQ(ret, ensure);
5541 static int
5542 check_keyword(const NODE *node)
5544 /* This check is essentially a code clone of compile_keyword_arg. */
5546 if (nd_type_p(node, NODE_LIST)) {
5547 while (node->nd_next) {
5548 node = node->nd_next;
5550 node = node->nd_head;
5553 return keyword_node_p(node);
5556 static VALUE
5557 setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
5558 int dup_rest, unsigned int *flag, struct rb_callinfo_kwarg **keywords)
5560 if (argn) {
5561 switch (nd_type(argn)) {
5562 case NODE_SPLAT: {
5563 NO_CHECK(COMPILE(args, "args (splat)", argn->nd_head));
5564 ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest));
5565 if (flag) *flag |= VM_CALL_ARGS_SPLAT;
5566 return INT2FIX(1);
5568 case NODE_ARGSCAT:
5569 case NODE_ARGSPUSH: {
5570 int next_is_list = (nd_type_p(argn->nd_head, NODE_LIST));
5571 VALUE argc = setup_args_core(iseq, args, argn->nd_head, 1, NULL, NULL);
5572 if (nd_type_p(argn->nd_body, NODE_LIST)) {
5573 /* This branch is needed to avoid "newarraykwsplat" [Bug #16442] */
5574 int rest_len = compile_args(iseq, args, argn->nd_body, NULL, NULL);
5575 ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
5577 else {
5578 NO_CHECK(COMPILE(args, "args (cat: splat)", argn->nd_body));
5580 if (flag) {
5581 *flag |= VM_CALL_ARGS_SPLAT;
5582 /* This is a dirty hack. It traverses the AST twice.
5583 * In a long term, it should be fixed by a redesign of keyword arguments */
5584 if (check_keyword(argn->nd_body))
5585 *flag |= VM_CALL_KW_SPLAT;
5587 if (nd_type_p(argn, NODE_ARGSCAT)) {
5588 if (next_is_list) {
5589 ADD_INSN1(args, argn, splatarray, Qtrue);
5590 return INT2FIX(FIX2INT(argc) + 1);
5592 else {
5593 ADD_INSN1(args, argn, splatarray, Qfalse);
5594 ADD_INSN(args, argn, concatarray);
5595 return argc;
5598 else {
5599 ADD_INSN1(args, argn, newarray, INT2FIX(1));
5600 ADD_INSN(args, argn, concatarray);
5601 return argc;
5604 case NODE_LIST: {
5605 int len = compile_args(iseq, args, argn, keywords, flag);
5606 return INT2FIX(len);
5608 default: {
5609 UNKNOWN_NODE("setup_arg", argn, Qnil);
5613 return INT2FIX(0);
5616 static VALUE
5617 setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
5618 unsigned int *flag, struct rb_callinfo_kwarg **keywords)
5620 VALUE ret;
5621 if (argn && nd_type_p(argn, NODE_BLOCK_PASS)) {
5622 unsigned int dup_rest = 1;
5623 DECL_ANCHOR(arg_block);
5624 INIT_ANCHOR(arg_block);
5625 NO_CHECK(COMPILE(arg_block, "block", argn->nd_body));
5627 *flag |= VM_CALL_ARGS_BLOCKARG;
5629 if (LIST_INSN_SIZE_ONE(arg_block)) {
5630 LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
5631 if (elem->type == ISEQ_ELEMENT_INSN) {
5632 INSN *iobj = (INSN *)elem;
5633 if (iobj->insn_id == BIN(getblockparam)) {
5634 iobj->insn_id = BIN(getblockparamproxy);
5636 dup_rest = 0;
5639 ret = setup_args_core(iseq, args, argn->nd_head, dup_rest, flag, keywords);
5640 ADD_SEQ(args, arg_block);
5642 else {
5643 ret = setup_args_core(iseq, args, argn, 0, flag, keywords);
5645 return ret;
5648 static void
5649 build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *ptr)
5651 const NODE *body = ptr;
5652 int line = nd_line(body);
5653 VALUE argc = INT2FIX(0);
5654 const rb_iseq_t *block = NEW_CHILD_ISEQ(body, make_name_for_block(iseq->body->parent_iseq), ISEQ_TYPE_BLOCK, line);
5656 ADD_INSN1(ret, body, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5657 ADD_CALL_WITH_BLOCK(ret, body, id_core_set_postexe, argc, block);
5658 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
5659 iseq_set_local_table(iseq, 0);
5662 static void
5663 compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
5665 const NODE *vars;
5666 LINK_ELEMENT *last;
5667 int line = nd_line(node);
5668 const NODE *line_node = node;
5669 LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line);
5671 #if !(defined(NAMED_CAPTURE_BY_SVAR) && NAMED_CAPTURE_BY_SVAR-0)
5672 ADD_INSN1(ret, line_node, getglobal, ID2SYM(idBACKREF));
5673 #else
5674 ADD_INSN2(ret, line_node, getspecial, INT2FIX(1) /* '~' */, INT2FIX(0));
5675 #endif
5676 ADD_INSN(ret, line_node, dup);
5677 ADD_INSNL(ret, line_node, branchunless, fail_label);
5679 for (vars = node; vars; vars = vars->nd_next) {
5680 INSN *cap;
5681 if (vars->nd_next) {
5682 ADD_INSN(ret, line_node, dup);
5684 last = ret->last;
5685 NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head));
5686 last = last->next; /* putobject :var */
5687 cap = new_insn_send(iseq, line_node, idAREF, INT2FIX(1),
5688 NULL, INT2FIX(0), NULL);
5689 ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
5690 #if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
5691 if (!vars->nd_next && vars == node) {
5692 /* only one name */
5693 DECL_ANCHOR(nom);
5695 INIT_ANCHOR(nom);
5696 ADD_INSNL(nom, line_node, jump, end_label);
5697 ADD_LABEL(nom, fail_label);
5698 # if 0 /* $~ must be MatchData or nil */
5699 ADD_INSN(nom, line_node, pop);
5700 ADD_INSN(nom, line_node, putnil);
5701 # endif
5702 ADD_LABEL(nom, end_label);
5703 (nom->last->next = cap->link.next)->prev = nom->last;
5704 (cap->link.next = nom->anchor.next)->prev = &cap->link;
5705 return;
5707 #endif
5709 ADD_INSNL(ret, line_node, jump, end_label);
5710 ADD_LABEL(ret, fail_label);
5711 ADD_INSN(ret, line_node, pop);
5712 for (vars = node; vars; vars = vars->nd_next) {
5713 last = ret->last;
5714 NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head));
5715 last = last->next; /* putobject :var */
5716 ((INSN*)last)->insn_id = BIN(putnil);
5717 ((INSN*)last)->operand_size = 0;
5719 ADD_LABEL(ret, end_label);
5722 static int
5723 optimizable_range_item_p(const NODE *n)
5725 if (!n) return FALSE;
5726 switch (nd_type(n)) {
5727 case NODE_LIT:
5728 return RB_INTEGER_TYPE_P(n->nd_lit);
5729 case NODE_NIL:
5730 return TRUE;
5731 default:
5732 return FALSE;
5736 static int
5737 compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
5739 struct rb_iseq_constant_body *const body = iseq->body;
5740 const NODE *const node_body = type == NODE_IF ? node->nd_body : node->nd_else;
5741 const NODE *const node_else = type == NODE_IF ? node->nd_else : node->nd_body;
5743 const int line = nd_line(node);
5744 const NODE *line_node = node;
5745 DECL_ANCHOR(cond_seq);
5746 DECL_ANCHOR(then_seq);
5747 DECL_ANCHOR(else_seq);
5748 LABEL *then_label, *else_label, *end_label;
5749 VALUE branches = Qfalse;
5750 int ci_size;
5751 VALUE catch_table = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
5752 long catch_table_size = NIL_P(catch_table) ? 0 : RARRAY_LEN(catch_table);
5754 INIT_ANCHOR(cond_seq);
5755 INIT_ANCHOR(then_seq);
5756 INIT_ANCHOR(else_seq);
5757 then_label = NEW_LABEL(line);
5758 else_label = NEW_LABEL(line);
5759 end_label = 0;
5761 compile_branch_condition(iseq, cond_seq, node->nd_cond,
5762 then_label, else_label);
5764 ci_size = body->ci_size;
5765 CHECK(COMPILE_(then_seq, "then", node_body, popped));
5766 catch_table = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
5767 if (!then_label->refcnt) {
5768 body->ci_size = ci_size;
5769 if (!NIL_P(catch_table)) rb_ary_set_len(catch_table, catch_table_size);
5771 else {
5772 if (!NIL_P(catch_table)) catch_table_size = RARRAY_LEN(catch_table);
5775 ci_size = body->ci_size;
5776 CHECK(COMPILE_(else_seq, "else", node_else, popped));
5777 catch_table = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
5778 if (!else_label->refcnt) {
5779 body->ci_size = ci_size;
5780 if (!NIL_P(catch_table)) rb_ary_set_len(catch_table, catch_table_size);
5782 else {
5783 if (!NIL_P(catch_table)) catch_table_size = RARRAY_LEN(catch_table);
5786 ADD_SEQ(ret, cond_seq);
5788 if (then_label->refcnt && else_label->refcnt) {
5789 branches = decl_branch_base(iseq, node, type == NODE_IF ? "if" : "unless");
5792 if (then_label->refcnt) {
5793 ADD_LABEL(ret, then_label);
5794 if (else_label->refcnt) {
5795 add_trace_branch_coverage(
5796 iseq,
5797 ret,
5798 node_body ? node_body : node,
5800 type == NODE_IF ? "then" : "else",
5801 branches);
5802 end_label = NEW_LABEL(line);
5803 ADD_INSNL(then_seq, line_node, jump, end_label);
5804 if (!popped) {
5805 ADD_INSN(then_seq, line_node, pop);
5808 ADD_SEQ(ret, then_seq);
5811 if (else_label->refcnt) {
5812 ADD_LABEL(ret, else_label);
5813 if (then_label->refcnt) {
5814 add_trace_branch_coverage(
5815 iseq,
5816 ret,
5817 node_else ? node_else : node,
5819 type == NODE_IF ? "else" : "then",
5820 branches);
5822 ADD_SEQ(ret, else_seq);
5825 if (end_label) {
5826 ADD_LABEL(ret, end_label);
5829 return COMPILE_OK;
5832 static int
5833 compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
5835 const NODE *vals;
5836 const NODE *node = orig_node;
5837 LABEL *endlabel, *elselabel;
5838 DECL_ANCHOR(head);
5839 DECL_ANCHOR(body_seq);
5840 DECL_ANCHOR(cond_seq);
5841 int only_special_literals = 1;
5842 VALUE literals = rb_hash_new();
5843 int line;
5844 enum node_type type;
5845 const NODE *line_node;
5846 VALUE branches = Qfalse;
5847 int branch_id = 0;
5849 INIT_ANCHOR(head);
5850 INIT_ANCHOR(body_seq);
5851 INIT_ANCHOR(cond_seq);
5853 RHASH_TBL_RAW(literals)->type = &cdhash_type;
5855 CHECK(COMPILE(head, "case base", node->nd_head));
5857 branches = decl_branch_base(iseq, node, "case");
5859 node = node->nd_body;
5860 EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG);
5861 type = nd_type(node);
5862 line = nd_line(node);
5863 line_node = node;
5865 endlabel = NEW_LABEL(line);
5866 elselabel = NEW_LABEL(line);
5868 ADD_SEQ(ret, head); /* case VAL */
5870 while (type == NODE_WHEN) {
5871 LABEL *l1;
5873 l1 = NEW_LABEL(line);
5874 ADD_LABEL(body_seq, l1);
5875 ADD_INSN(body_seq, line_node, pop);
5876 add_trace_branch_coverage(
5877 iseq,
5878 body_seq,
5879 node->nd_body ? node->nd_body : node,
5880 branch_id++,
5881 "when",
5882 branches);
5883 CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped));
5884 ADD_INSNL(body_seq, line_node, jump, endlabel);
5886 vals = node->nd_head;
5887 if (vals) {
5888 switch (nd_type(vals)) {
5889 case NODE_LIST:
5890 only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
5891 if (only_special_literals < 0) return COMPILE_NG;
5892 break;
5893 case NODE_SPLAT:
5894 case NODE_ARGSCAT:
5895 case NODE_ARGSPUSH:
5896 only_special_literals = 0;
5897 CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
5898 break;
5899 default:
5900 UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
5903 else {
5904 EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
5907 node = node->nd_next;
5908 if (!node) {
5909 break;
5911 type = nd_type(node);
5912 line = nd_line(node);
5913 line_node = node;
5915 /* else */
5916 if (node) {
5917 ADD_LABEL(cond_seq, elselabel);
5918 ADD_INSN(cond_seq, line_node, pop);
5919 add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches);
5920 CHECK(COMPILE_(cond_seq, "else", node, popped));
5921 ADD_INSNL(cond_seq, line_node, jump, endlabel);
5923 else {
5924 debugs("== else (implicit)\n");
5925 ADD_LABEL(cond_seq, elselabel);
5926 ADD_INSN(cond_seq, orig_node, pop);
5927 add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches);
5928 if (!popped) {
5929 ADD_INSN(cond_seq, orig_node, putnil);
5931 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
5934 if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
5935 ADD_INSN(ret, orig_node, dup);
5936 ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
5937 RB_OBJ_WRITTEN(iseq, Qundef, literals);
5938 LABEL_REF(elselabel);
5941 ADD_SEQ(ret, cond_seq);
5942 ADD_SEQ(ret, body_seq);
5943 ADD_LABEL(ret, endlabel);
5944 return COMPILE_OK;
5947 static int
5948 compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
5950 const NODE *vals;
5951 const NODE *val;
5952 const NODE *node = orig_node->nd_body;
5953 LABEL *endlabel;
5954 DECL_ANCHOR(body_seq);
5955 VALUE branches = Qfalse;
5956 int branch_id = 0;
5958 branches = decl_branch_base(iseq, orig_node, "case");
5960 INIT_ANCHOR(body_seq);
5961 endlabel = NEW_LABEL(nd_line(node));
5963 while (node && nd_type_p(node, NODE_WHEN)) {
5964 const int line = nd_line(node);
5965 LABEL *l1 = NEW_LABEL(line);
5966 ADD_LABEL(body_seq, l1);
5967 add_trace_branch_coverage(
5968 iseq,
5969 body_seq,
5970 node->nd_body ? node->nd_body : node,
5971 branch_id++,
5972 "when",
5973 branches);
5974 CHECK(COMPILE_(body_seq, "when", node->nd_body, popped));
5975 ADD_INSNL(body_seq, node, jump, endlabel);
5977 vals = node->nd_head;
5978 if (!vals) {
5979 EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG);
5981 switch (nd_type(vals)) {
5982 case NODE_LIST:
5983 while (vals) {
5984 LABEL *lnext;
5985 val = vals->nd_head;
5986 lnext = NEW_LABEL(nd_line(val));
5987 debug_compile("== when2\n", (void)0);
5988 CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
5989 ADD_LABEL(ret, lnext);
5990 vals = vals->nd_next;
5992 break;
5993 case NODE_SPLAT:
5994 case NODE_ARGSCAT:
5995 case NODE_ARGSPUSH:
5996 ADD_INSN(ret, vals, putnil);
5997 CHECK(COMPILE(ret, "when2/cond splat", vals));
5998 ADD_INSN1(ret, vals, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
5999 ADD_INSNL(ret, vals, branchif, l1);
6000 break;
6001 default:
6002 UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
6004 node = node->nd_next;
6006 /* else */
6007 add_trace_branch_coverage(
6008 iseq,
6009 ret,
6010 node ? node : orig_node,
6011 branch_id,
6012 "else",
6013 branches);
6014 CHECK(COMPILE_(ret, "else", node, popped));
6015 ADD_INSNL(ret, orig_node, jump, endlabel);
6017 ADD_SEQ(ret, body_seq);
6018 ADD_LABEL(ret, endlabel);
6019 return COMPILE_OK;
6022 static int iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *unmatched, bool in_single_pattern, bool in_alt_pattern, int base_index, bool use_deconstructed_cache);
6024 static int iseq_compile_pattern_constant(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *match_failed, bool in_single_pattern, int base_index);
6025 static int iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, bool in_single_pattern, int base_index, bool use_deconstructed_cache);
6026 static int iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index);
6027 static int iseq_compile_pattern_set_length_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, VALUE pattern_length, int base_index);
6028 static int iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index);
6030 #define CASE3_BI_OFFSET_DECONSTRUCTED_CACHE 0
6031 #define CASE3_BI_OFFSET_ERROR_STRING 1
6032 #define CASE3_BI_OFFSET_KEY_ERROR_P 2
6033 #define CASE3_BI_OFFSET_KEY_ERROR_MATCHEE 3
6034 #define CASE3_BI_OFFSET_KEY_ERROR_KEY 4
6036 static int
6037 iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *matched, LABEL *unmatched, bool in_single_pattern, bool in_alt_pattern, int base_index, bool use_deconstructed_cache)
6039 const int line = nd_line(node);
6040 const NODE *line_node = node;
6042 switch (nd_type(node)) {
6043 case NODE_ARYPTN: {
6045 * if pattern.use_rest_num?
6046 * rest_num = 0
6047 * end
6048 * if pattern.has_constant_node?
6049 * unless pattern.constant === obj
6050 * goto match_failed
6051 * end
6052 * end
6053 * unless obj.respond_to?(:deconstruct)
6054 * goto match_failed
6055 * end
6056 * d = obj.deconstruct
6057 * unless Array === d
6058 * goto type_error
6059 * end
6060 * min_argc = pattern.pre_args_num + pattern.post_args_num
6061 * if pattern.has_rest_arg?
6062 * unless d.length >= min_argc
6063 * goto match_failed
6064 * end
6065 * else
6066 * unless d.length == min_argc
6067 * goto match_failed
6068 * end
6069 * end
6070 * pattern.pre_args_num.each do |i|
6071 * unless pattern.pre_args[i].match?(d[i])
6072 * goto match_failed
6073 * end
6074 * end
6075 * if pattern.use_rest_num?
6076 * rest_num = d.length - min_argc
6077 * if pattern.has_rest_arg? && pattern.has_rest_arg_id # not `*`, but `*rest`
6078 * unless pattern.rest_arg.match?(d[pattern.pre_args_num, rest_num])
6079 * goto match_failed
6080 * end
6081 * end
6082 * end
6083 * pattern.post_args_num.each do |i|
6084 * j = pattern.pre_args_num + i
6085 * j += rest_num
6086 * unless pattern.post_args[i].match?(d[j])
6087 * goto match_failed
6088 * end
6089 * end
6090 * goto matched
6091 * type_error:
6092 * FrozenCore.raise TypeError
6093 * match_failed:
6094 * goto unmatched
6096 struct rb_ary_pattern_info *apinfo = node->nd_apinfo;
6097 const NODE *args = apinfo->pre_args;
6098 const int pre_args_num = apinfo->pre_args ? rb_long2int(apinfo->pre_args->nd_alen) : 0;
6099 const int post_args_num = apinfo->post_args ? rb_long2int(apinfo->post_args->nd_alen) : 0;
6101 const int min_argc = pre_args_num + post_args_num;
6102 const int use_rest_num = apinfo->rest_arg && (NODE_NAMED_REST_P(apinfo->rest_arg) ||
6103 (!NODE_NAMED_REST_P(apinfo->rest_arg) && post_args_num > 0));
6105 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
6106 int i;
6107 match_failed = NEW_LABEL(line);
6108 type_error = NEW_LABEL(line);
6109 deconstruct = NEW_LABEL(line);
6110 deconstructed = NEW_LABEL(line);
6112 if (use_rest_num) {
6113 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for rest_num */
6114 ADD_INSN(ret, line_node, swap);
6115 if (base_index) {
6116 base_index++;
6120 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
6122 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
6124 ADD_INSN(ret, line_node, dup);
6125 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6126 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
6127 ADD_SEND(ret, line_node, apinfo->rest_arg ? idGE : idEq, INT2FIX(1)); // (1)
6128 if (in_single_pattern) {
6129 CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node,
6130 apinfo->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") :
6131 rb_fstring_lit("%p length mismatch (given %p, expected %p)"),
6132 INT2FIX(min_argc), base_index + 1 /* (1) */));
6134 ADD_INSNL(ret, line_node, branchunless, match_failed);
6136 for (i = 0; i < pre_args_num; i++) {
6137 ADD_INSN(ret, line_node, dup);
6138 ADD_INSN1(ret, line_node, putobject, INT2FIX(i));
6139 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (2)
6140 CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (2) */, false));
6141 args = args->nd_next;
6144 if (apinfo->rest_arg) {
6145 if (NODE_NAMED_REST_P(apinfo->rest_arg)) {
6146 ADD_INSN(ret, line_node, dup);
6147 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num));
6148 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
6149 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6150 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
6151 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
6152 ADD_INSN1(ret, line_node, setn, INT2FIX(4));
6153 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (3)
6155 CHECK(iseq_compile_pattern_match(iseq, ret, apinfo->rest_arg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (3) */, false));
6157 else {
6158 if (post_args_num > 0) {
6159 ADD_INSN(ret, line_node, dup);
6160 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6161 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
6162 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
6163 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
6164 ADD_INSN(ret, line_node, pop);
6169 args = apinfo->post_args;
6170 for (i = 0; i < post_args_num; i++) {
6171 ADD_INSN(ret, line_node, dup);
6173 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num + i));
6174 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6175 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
6177 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (4)
6178 CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (4) */, false));
6179 args = args->nd_next;
6182 ADD_INSN(ret, line_node, pop);
6183 if (use_rest_num) {
6184 ADD_INSN(ret, line_node, pop);
6186 ADD_INSNL(ret, line_node, jump, matched);
6187 ADD_INSN(ret, line_node, putnil);
6188 if (use_rest_num) {
6189 ADD_INSN(ret, line_node, putnil);
6192 ADD_LABEL(ret, type_error);
6193 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6194 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
6195 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
6196 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
6197 ADD_INSN(ret, line_node, pop);
6199 ADD_LABEL(ret, match_failed);
6200 ADD_INSN(ret, line_node, pop);
6201 if (use_rest_num) {
6202 ADD_INSN(ret, line_node, pop);
6204 ADD_INSNL(ret, line_node, jump, unmatched);
6206 break;
6208 case NODE_FNDPTN: {
6210 * if pattern.has_constant_node?
6211 * unless pattern.constant === obj
6212 * goto match_failed
6213 * end
6214 * end
6215 * unless obj.respond_to?(:deconstruct)
6216 * goto match_failed
6217 * end
6218 * d = obj.deconstruct
6219 * unless Array === d
6220 * goto type_error
6221 * end
6222 * unless d.length >= pattern.args_num
6223 * goto match_failed
6224 * end
6226 * begin
6227 * len = d.length
6228 * limit = d.length - pattern.args_num
6229 * i = 0
6230 * while i <= limit
6231 * if pattern.args_num.times.all? {|j| pattern.args[j].match?(d[i+j]) }
6232 * if pattern.has_pre_rest_arg_id
6233 * unless pattern.pre_rest_arg.match?(d[0, i])
6234 * goto find_failed
6235 * end
6236 * end
6237 * if pattern.has_post_rest_arg_id
6238 * unless pattern.post_rest_arg.match?(d[i+pattern.args_num, len])
6239 * goto find_failed
6240 * end
6241 * end
6242 * goto find_succeeded
6243 * end
6244 * i+=1
6245 * end
6246 * find_failed:
6247 * goto match_failed
6248 * find_succeeded:
6249 * end
6251 * goto matched
6252 * type_error:
6253 * FrozenCore.raise TypeError
6254 * match_failed:
6255 * goto unmatched
6257 struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo;
6258 const NODE *args = fpinfo->args;
6259 const int args_num = fpinfo->args ? rb_long2int(fpinfo->args->nd_alen) : 0;
6261 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
6262 match_failed = NEW_LABEL(line);
6263 type_error = NEW_LABEL(line);
6264 deconstruct = NEW_LABEL(line);
6265 deconstructed = NEW_LABEL(line);
6267 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
6269 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
6271 ADD_INSN(ret, line_node, dup);
6272 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6273 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
6274 ADD_SEND(ret, line_node, idGE, INT2FIX(1)); // (1)
6275 if (in_single_pattern) {
6276 CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node, rb_fstring_lit("%p length mismatch (given %p, expected %p+)"), INT2FIX(args_num), base_index + 1 /* (1) */));
6278 ADD_INSNL(ret, line_node, branchunless, match_failed);
6281 LABEL *while_begin = NEW_LABEL(nd_line(node));
6282 LABEL *next_loop = NEW_LABEL(nd_line(node));
6283 LABEL *find_succeeded = NEW_LABEL(line);
6284 LABEL *find_failed = NEW_LABEL(nd_line(node));
6285 int j;
6287 ADD_INSN(ret, line_node, dup); /* allocate stack for len */
6288 ADD_SEND(ret, line_node, idLength, INT2FIX(0)); // (2)
6290 ADD_INSN(ret, line_node, dup); /* allocate stack for limit */
6291 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
6292 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1)); // (3)
6294 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for i */ // (4)
6296 ADD_LABEL(ret, while_begin);
6298 ADD_INSN(ret, line_node, dup);
6299 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
6300 ADD_SEND(ret, line_node, idLE, INT2FIX(1));
6301 ADD_INSNL(ret, line_node, branchunless, find_failed);
6303 for (j = 0; j < args_num; j++) {
6304 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6305 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
6306 if (j != 0) {
6307 ADD_INSN1(ret, line_node, putobject, INT2FIX(j));
6308 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
6310 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (5)
6312 CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (5) */, false));
6313 args = args->nd_next;
6316 if (NODE_NAMED_REST_P(fpinfo->pre_rest_arg)) {
6317 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6318 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
6319 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
6320 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (6)
6321 CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (6) */, false));
6323 if (NODE_NAMED_REST_P(fpinfo->post_rest_arg)) {
6324 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6325 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
6326 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
6327 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
6328 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6329 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (7)
6330 CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3),(4), (7) */, false));
6332 ADD_INSNL(ret, line_node, jump, find_succeeded);
6334 ADD_LABEL(ret, next_loop);
6335 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
6336 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
6337 ADD_INSNL(ret, line_node, jump, while_begin);
6339 ADD_LABEL(ret, find_failed);
6340 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
6341 if (in_single_pattern) {
6342 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6343 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p does not match to find pattern"));
6344 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
6345 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (8)
6346 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (8) */)); // (9)
6348 ADD_INSN1(ret, line_node, putobject, Qfalse);
6349 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (8), (9) */));
6351 ADD_INSN(ret, line_node, pop);
6352 ADD_INSN(ret, line_node, pop);
6354 ADD_INSNL(ret, line_node, jump, match_failed);
6355 ADD_INSN1(ret, line_node, dupn, INT2FIX(3));
6357 ADD_LABEL(ret, find_succeeded);
6358 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
6361 ADD_INSN(ret, line_node, pop);
6362 ADD_INSNL(ret, line_node, jump, matched);
6363 ADD_INSN(ret, line_node, putnil);
6365 ADD_LABEL(ret, type_error);
6366 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6367 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
6368 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
6369 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
6370 ADD_INSN(ret, line_node, pop);
6372 ADD_LABEL(ret, match_failed);
6373 ADD_INSN(ret, line_node, pop);
6374 ADD_INSNL(ret, line_node, jump, unmatched);
6376 break;
6378 case NODE_HSHPTN: {
6380 * keys = nil
6381 * if pattern.has_kw_args_node? && !pattern.has_kw_rest_arg_node?
6382 * keys = pattern.kw_args_node.keys
6383 * end
6384 * if pattern.has_constant_node?
6385 * unless pattern.constant === obj
6386 * goto match_failed
6387 * end
6388 * end
6389 * unless obj.respond_to?(:deconstruct_keys)
6390 * goto match_failed
6391 * end
6392 * d = obj.deconstruct_keys(keys)
6393 * unless Hash === d
6394 * goto type_error
6395 * end
6396 * if pattern.has_kw_rest_arg_node?
6397 * d = d.dup
6398 * end
6399 * if pattern.has_kw_args_node?
6400 * pattern.kw_args_node.each |k,|
6401 * unless d.key?(k)
6402 * goto match_failed
6403 * end
6404 * end
6405 * pattern.kw_args_node.each |k, pat|
6406 * if pattern.has_kw_rest_arg_node?
6407 * unless pat.match?(d.delete(k))
6408 * goto match_failed
6409 * end
6410 * else
6411 * unless pat.match?(d[k])
6412 * goto match_failed
6413 * end
6414 * end
6415 * end
6416 * else
6417 * unless d.empty?
6418 * goto match_failed
6419 * end
6420 * end
6421 * if pattern.has_kw_rest_arg_node?
6422 * if pattern.no_rest_keyword?
6423 * unless d.empty?
6424 * goto match_failed
6425 * end
6426 * else
6427 * unless pattern.kw_rest_arg_node.match?(d)
6428 * goto match_failed
6429 * end
6430 * end
6431 * end
6432 * goto matched
6433 * type_error:
6434 * FrozenCore.raise TypeError
6435 * match_failed:
6436 * goto unmatched
6438 LABEL *match_failed, *type_error;
6439 VALUE keys = Qnil;
6441 match_failed = NEW_LABEL(line);
6442 type_error = NEW_LABEL(line);
6444 if (node->nd_pkwargs && !node->nd_pkwrestarg) {
6445 const NODE *kw_args = node->nd_pkwargs->nd_head;
6446 keys = rb_ary_new_capa(kw_args ? kw_args->nd_alen/2 : 0);
6447 while (kw_args) {
6448 rb_ary_push(keys, kw_args->nd_head->nd_lit);
6449 kw_args = kw_args->nd_next->nd_next;
6453 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
6455 ADD_INSN(ret, line_node, dup);
6456 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct_keys")));
6457 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (1)
6458 if (in_single_pattern) {
6459 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1 /* (1) */));
6461 ADD_INSNL(ret, line_node, branchunless, match_failed);
6463 if (NIL_P(keys)) {
6464 ADD_INSN(ret, line_node, putnil);
6466 else {
6467 ADD_INSN1(ret, line_node, duparray, keys);
6468 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
6470 ADD_SEND(ret, line_node, rb_intern("deconstruct_keys"), INT2FIX(1)); // (2)
6472 ADD_INSN(ret, line_node, dup);
6473 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_HASH));
6474 ADD_INSNL(ret, line_node, branchunless, type_error);
6476 if (node->nd_pkwrestarg) {
6477 ADD_SEND(ret, line_node, rb_intern("dup"), INT2FIX(0));
6480 if (node->nd_pkwargs) {
6481 int i;
6482 int keys_num;
6483 const NODE *args;
6484 args = node->nd_pkwargs->nd_head;
6485 if (args) {
6486 DECL_ANCHOR(match_values);
6487 INIT_ANCHOR(match_values);
6488 keys_num = rb_long2int(args->nd_alen) / 2;
6489 for (i = 0; i < keys_num; i++) {
6490 NODE *key_node = args->nd_head;
6491 NODE *value_node = args->nd_next->nd_head;
6492 VALUE key;
6494 if (!nd_type_p(key_node, NODE_LIT)) {
6495 UNKNOWN_NODE("NODE_IN", key_node, COMPILE_NG);
6497 key = key_node->nd_lit;
6499 ADD_INSN(ret, line_node, dup);
6500 ADD_INSN1(ret, line_node, putobject, key);
6501 ADD_SEND(ret, line_node, rb_intern("key?"), INT2FIX(1)); // (3)
6502 if (in_single_pattern) {
6503 LABEL *match_succeeded;
6504 match_succeeded = NEW_LABEL(line);
6506 ADD_INSN(ret, line_node, dup);
6507 ADD_INSNL(ret, line_node, branchif, match_succeeded);
6509 ADD_INSN1(ret, line_node, putobject, rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, key))); // (4)
6510 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 2 /* (3), (4) */));
6511 ADD_INSN1(ret, line_node, putobject, Qtrue); // (5)
6512 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 3 /* (3), (4), (5) */));
6513 ADD_INSN1(ret, line_node, topn, INT2FIX(3)); // (6)
6514 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4 /* (3), (4), (5), (6) */));
6515 ADD_INSN1(ret, line_node, putobject, key); // (7)
6516 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_KEY + 5 /* (3), (4), (5), (6), (7) */));
6518 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(4));
6520 ADD_LABEL(ret, match_succeeded);
6522 ADD_INSNL(ret, line_node, branchunless, match_failed);
6524 ADD_INSN(match_values, line_node, dup);
6525 ADD_INSN1(match_values, line_node, putobject, key);
6526 ADD_SEND(match_values, line_node, node->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8)
6527 CHECK(iseq_compile_pattern_match(iseq, match_values, value_node, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (8) */, false));
6528 args = args->nd_next->nd_next;
6530 ADD_SEQ(ret, match_values);
6533 else {
6534 ADD_INSN(ret, line_node, dup);
6535 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (9)
6536 if (in_single_pattern) {
6537 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p is not empty"), base_index + 1 /* (9) */));
6539 ADD_INSNL(ret, line_node, branchunless, match_failed);
6542 if (node->nd_pkwrestarg) {
6543 if (node->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
6544 ADD_INSN(ret, line_node, dup);
6545 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (10)
6546 if (in_single_pattern) {
6547 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("rest of %p is not empty"), base_index + 1 /* (10) */));
6549 ADD_INSNL(ret, line_node, branchunless, match_failed);
6551 else {
6552 ADD_INSN(ret, line_node, dup); // (11)
6553 CHECK(iseq_compile_pattern_match(iseq, ret, node->nd_pkwrestarg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (11) */, false));
6557 ADD_INSN(ret, line_node, pop);
6558 ADD_INSNL(ret, line_node, jump, matched);
6559 ADD_INSN(ret, line_node, putnil);
6561 ADD_LABEL(ret, type_error);
6562 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6563 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
6564 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct_keys must return Hash"));
6565 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
6566 ADD_INSN(ret, line_node, pop);
6568 ADD_LABEL(ret, match_failed);
6569 ADD_INSN(ret, line_node, pop);
6570 ADD_INSNL(ret, line_node, jump, unmatched);
6571 break;
6573 case NODE_LIT:
6574 case NODE_STR:
6575 case NODE_XSTR:
6576 case NODE_DSTR:
6577 case NODE_DSYM:
6578 case NODE_DREGX:
6579 case NODE_LIST:
6580 case NODE_ZLIST:
6581 case NODE_LAMBDA:
6582 case NODE_DOT2:
6583 case NODE_DOT3:
6584 case NODE_CONST:
6585 case NODE_LVAR:
6586 case NODE_DVAR:
6587 case NODE_IVAR:
6588 case NODE_CVAR:
6589 case NODE_GVAR:
6590 case NODE_TRUE:
6591 case NODE_FALSE:
6592 case NODE_SELF:
6593 case NODE_NIL:
6594 case NODE_COLON2:
6595 case NODE_COLON3:
6596 case NODE_BEGIN:
6597 CHECK(COMPILE(ret, "case in literal", node)); // (1)
6598 if (in_single_pattern) {
6599 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
6601 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (2)
6602 if (in_single_pattern) {
6603 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 2 /* (1), (2) */));
6605 ADD_INSNL(ret, line_node, branchif, matched);
6606 ADD_INSNL(ret, line_node, jump, unmatched);
6607 break;
6608 case NODE_LASGN: {
6609 struct rb_iseq_constant_body *const body = iseq->body;
6610 ID id = node->nd_vid;
6611 int idx = body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
6613 if (in_alt_pattern) {
6614 const char *name = rb_id2name(id);
6615 if (name && strlen(name) > 0 && name[0] != '_') {
6616 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
6617 rb_id2str(id));
6618 return COMPILE_NG;
6622 ADD_SETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
6623 ADD_INSNL(ret, line_node, jump, matched);
6624 break;
6626 case NODE_DASGN: {
6627 int idx, lv, ls;
6628 ID id = node->nd_vid;
6630 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
6632 if (in_alt_pattern) {
6633 const char *name = rb_id2name(id);
6634 if (name && strlen(name) > 0 && name[0] != '_') {
6635 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
6636 rb_id2str(id));
6637 return COMPILE_NG;
6641 if (idx < 0) {
6642 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
6643 rb_id2str(id));
6644 return COMPILE_NG;
6646 ADD_SETLOCAL(ret, line_node, ls - idx, lv);
6647 ADD_INSNL(ret, line_node, jump, matched);
6648 break;
6650 case NODE_IF:
6651 case NODE_UNLESS: {
6652 LABEL *match_failed;
6653 match_failed = unmatched;
6654 CHECK(iseq_compile_pattern_match(iseq, ret, node->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
6655 CHECK(COMPILE(ret, "case in if", node->nd_cond));
6656 if (in_single_pattern) {
6657 LABEL *match_succeeded;
6658 match_succeeded = NEW_LABEL(line);
6660 ADD_INSN(ret, line_node, dup);
6661 if (nd_type_p(node, NODE_IF)) {
6662 ADD_INSNL(ret, line_node, branchif, match_succeeded);
6664 else {
6665 ADD_INSNL(ret, line_node, branchunless, match_succeeded);
6668 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("guard clause does not return true")); // (1)
6669 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
6670 ADD_INSN1(ret, line_node, putobject, Qfalse);
6671 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
6673 ADD_INSN(ret, line_node, pop);
6674 ADD_INSN(ret, line_node, pop);
6676 ADD_LABEL(ret, match_succeeded);
6678 if (nd_type_p(node, NODE_IF)) {
6679 ADD_INSNL(ret, line_node, branchunless, match_failed);
6681 else {
6682 ADD_INSNL(ret, line_node, branchif, match_failed);
6684 ADD_INSNL(ret, line_node, jump, matched);
6685 break;
6687 case NODE_HASH: {
6688 NODE *n;
6689 LABEL *match_failed;
6690 match_failed = NEW_LABEL(line);
6692 n = node->nd_head;
6693 if (! (nd_type_p(n, NODE_LIST) && n->nd_alen == 2)) {
6694 COMPILE_ERROR(ERROR_ARGS "unexpected node");
6695 return COMPILE_NG;
6698 ADD_INSN(ret, line_node, dup); // (1)
6699 CHECK(iseq_compile_pattern_match(iseq, ret, n->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (1) */, use_deconstructed_cache));
6700 CHECK(iseq_compile_pattern_each(iseq, ret, n->nd_next->nd_head, matched, match_failed, in_single_pattern, in_alt_pattern, base_index, false));
6701 ADD_INSN(ret, line_node, putnil);
6703 ADD_LABEL(ret, match_failed);
6704 ADD_INSN(ret, line_node, pop);
6705 ADD_INSNL(ret, line_node, jump, unmatched);
6706 break;
6708 case NODE_OR: {
6709 LABEL *match_succeeded, *fin;
6710 match_succeeded = NEW_LABEL(line);
6711 fin = NEW_LABEL(line);
6713 ADD_INSN(ret, line_node, dup); // (1)
6714 CHECK(iseq_compile_pattern_each(iseq, ret, node->nd_1st, match_succeeded, fin, in_single_pattern, true, base_index + 1 /* (1) */, use_deconstructed_cache));
6715 ADD_LABEL(ret, match_succeeded);
6716 ADD_INSN(ret, line_node, pop);
6717 ADD_INSNL(ret, line_node, jump, matched);
6718 ADD_INSN(ret, line_node, putnil);
6719 ADD_LABEL(ret, fin);
6720 CHECK(iseq_compile_pattern_each(iseq, ret, node->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache));
6721 break;
6723 default:
6724 UNKNOWN_NODE("NODE_IN", node, COMPILE_NG);
6726 return COMPILE_OK;
6729 static int
6730 iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *unmatched, bool in_single_pattern, bool in_alt_pattern, int base_index, bool use_deconstructed_cache)
6732 LABEL *fin = NEW_LABEL(nd_line(node));
6733 CHECK(iseq_compile_pattern_each(iseq, ret, node, fin, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
6734 ADD_LABEL(ret, fin);
6735 return COMPILE_OK;
6738 static int
6739 iseq_compile_pattern_constant(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *match_failed, bool in_single_pattern, int base_index)
6741 const NODE *line_node = node;
6743 if (node->nd_pconst) {
6744 ADD_INSN(ret, line_node, dup); // (1)
6745 CHECK(COMPILE(ret, "constant", node->nd_pconst)); // (2)
6746 if (in_single_pattern) {
6747 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
6749 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (3)
6750 if (in_single_pattern) {
6751 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 3 /* (1), (2), (3) */));
6753 ADD_INSNL(ret, line_node, branchunless, match_failed);
6755 return COMPILE_OK;
6759 static int
6760 iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, bool in_single_pattern, int base_index, bool use_deconstructed_cache)
6762 const NODE *line_node = node;
6764 // NOTE: this optimization allows us to re-use the #deconstruct value
6765 // (or its absence).
6766 if (use_deconstructed_cache) {
6767 // If value is nil then we haven't tried to deconstruct
6768 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
6769 ADD_INSNL(ret, line_node, branchnil, deconstruct);
6771 // If false then the value is not deconstructable
6772 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
6773 ADD_INSNL(ret, line_node, branchunless, match_failed);
6775 // Drop value, add deconstructed to the stack and jump
6776 ADD_INSN(ret, line_node, pop); // (1)
6777 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE - 1 /* (1) */));
6778 ADD_INSNL(ret, line_node, jump, deconstructed);
6780 else {
6781 ADD_INSNL(ret, line_node, jump, deconstruct);
6784 ADD_LABEL(ret, deconstruct);
6785 ADD_INSN(ret, line_node, dup);
6786 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct")));
6787 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (2)
6789 // Cache the result of respond_to? (in case it's false is stays there, if true - it's overwritten after #deconstruct)
6790 if (use_deconstructed_cache) {
6791 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE + 1 /* (2) */));
6794 if (in_single_pattern) {
6795 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1 /* (2) */));
6798 ADD_INSNL(ret, line_node, branchunless, match_failed);
6800 ADD_SEND(ret, line_node, rb_intern("deconstruct"), INT2FIX(0));
6802 // Cache the result (if it's cacheable - currently, only top-level array patterns)
6803 if (use_deconstructed_cache) {
6804 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
6807 ADD_INSN(ret, line_node, dup);
6808 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_ARRAY));
6809 ADD_INSNL(ret, line_node, branchunless, type_error);
6811 ADD_LABEL(ret, deconstructed);
6813 return COMPILE_OK;
6816 static int
6817 iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index)
6820 * if match_succeeded?
6821 * goto match_succeeded
6822 * end
6823 * error_string = FrozenCore.sprintf(errmsg, matchee)
6824 * key_error_p = false
6825 * match_succeeded:
6827 const int line = nd_line(node);
6828 const NODE *line_node = node;
6829 LABEL *match_succeeded = NEW_LABEL(line);
6831 ADD_INSN(ret, line_node, dup);
6832 ADD_INSNL(ret, line_node, branchif, match_succeeded);
6834 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6835 ADD_INSN1(ret, line_node, putobject, errmsg);
6836 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6837 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (1)
6838 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
6840 ADD_INSN1(ret, line_node, putobject, Qfalse);
6841 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
6843 ADD_INSN(ret, line_node, pop);
6844 ADD_INSN(ret, line_node, pop);
6845 ADD_LABEL(ret, match_succeeded);
6847 return COMPILE_OK;
6850 static int
6851 iseq_compile_pattern_set_length_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, VALUE pattern_length, int base_index)
6854 * if match_succeeded?
6855 * goto match_succeeded
6856 * end
6857 * error_string = FrozenCore.sprintf(errmsg, matchee, matchee.length, pat.length)
6858 * key_error_p = false
6859 * match_succeeded:
6861 const int line = nd_line(node);
6862 const NODE *line_node = node;
6863 LABEL *match_succeeded = NEW_LABEL(line);
6865 ADD_INSN(ret, line_node, dup);
6866 ADD_INSNL(ret, line_node, branchif, match_succeeded);
6868 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6869 ADD_INSN1(ret, line_node, putobject, errmsg);
6870 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6871 ADD_INSN(ret, line_node, dup);
6872 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6873 ADD_INSN1(ret, line_node, putobject, pattern_length);
6874 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(4)); // (1)
6875 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
6877 ADD_INSN1(ret, line_node, putobject, Qfalse);
6878 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2/* (1), (2) */));
6880 ADD_INSN(ret, line_node, pop);
6881 ADD_INSN(ret, line_node, pop);
6882 ADD_LABEL(ret, match_succeeded);
6884 return COMPILE_OK;
6887 static int
6888 iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index)
6891 * if match_succeeded?
6892 * goto match_succeeded
6893 * end
6894 * error_string = FrozenCore.sprintf("%p === %p does not return true", pat, matchee)
6895 * key_error_p = false
6896 * match_succeeded:
6898 const int line = nd_line(node);
6899 const NODE *line_node = node;
6900 LABEL *match_succeeded = NEW_LABEL(line);
6902 ADD_INSN(ret, line_node, dup);
6903 ADD_INSNL(ret, line_node, branchif, match_succeeded);
6905 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6906 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p === %p does not return true"));
6907 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6908 ADD_INSN1(ret, line_node, topn, INT2FIX(5));
6909 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(3)); // (1)
6910 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
6912 ADD_INSN1(ret, line_node, putobject, Qfalse);
6913 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
6915 ADD_INSN(ret, line_node, pop);
6916 ADD_INSN(ret, line_node, pop);
6918 ADD_LABEL(ret, match_succeeded);
6919 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
6920 ADD_INSN(ret, line_node, pop);
6921 ADD_INSN(ret, line_node, pop);
6923 return COMPILE_OK;
6926 static int
6927 compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
6929 const NODE *pattern;
6930 const NODE *node = orig_node;
6931 LABEL *endlabel, *elselabel;
6932 DECL_ANCHOR(head);
6933 DECL_ANCHOR(body_seq);
6934 DECL_ANCHOR(cond_seq);
6935 int line;
6936 enum node_type type;
6937 const NODE *line_node;
6938 VALUE branches = 0;
6939 int branch_id = 0;
6940 bool single_pattern;
6942 INIT_ANCHOR(head);
6943 INIT_ANCHOR(body_seq);
6944 INIT_ANCHOR(cond_seq);
6946 branches = decl_branch_base(iseq, node, "case");
6948 node = node->nd_body;
6949 EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG);
6950 type = nd_type(node);
6951 line = nd_line(node);
6952 line_node = node;
6953 single_pattern = !node->nd_next;
6955 endlabel = NEW_LABEL(line);
6956 elselabel = NEW_LABEL(line);
6958 if (single_pattern) {
6959 /* allocate stack for ... */
6960 ADD_INSN(head, line_node, putnil); /* key_error_key */
6961 ADD_INSN(head, line_node, putnil); /* key_error_matchee */
6962 ADD_INSN1(head, line_node, putobject, Qfalse); /* key_error_p */
6963 ADD_INSN(head, line_node, putnil); /* error_string */
6965 ADD_INSN(head, line_node, putnil); /* allocate stack for cached #deconstruct value */
6967 CHECK(COMPILE(head, "case base", orig_node->nd_head));
6969 ADD_SEQ(ret, head); /* case VAL */
6971 while (type == NODE_IN) {
6972 LABEL *l1;
6974 if (branch_id) {
6975 ADD_INSN(body_seq, line_node, putnil);
6977 l1 = NEW_LABEL(line);
6978 ADD_LABEL(body_seq, l1);
6979 ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2));
6980 add_trace_branch_coverage(
6981 iseq,
6982 body_seq,
6983 node->nd_body ? node->nd_body : node,
6984 branch_id++,
6985 "in",
6986 branches);
6987 CHECK(COMPILE_(body_seq, "in body", node->nd_body, popped));
6988 ADD_INSNL(body_seq, line_node, jump, endlabel);
6990 pattern = node->nd_head;
6991 if (pattern) {
6992 int pat_line = nd_line(pattern);
6993 LABEL *next_pat = NEW_LABEL(pat_line);
6994 ADD_INSN (cond_seq, pattern, dup); /* dup case VAL */
6995 // NOTE: set base_index (it's "under" the matchee value, so it's position is 2)
6996 CHECK(iseq_compile_pattern_each(iseq, cond_seq, pattern, l1, next_pat, single_pattern, false, 2, true));
6997 ADD_LABEL(cond_seq, next_pat);
6998 LABEL_UNREMOVABLE(next_pat);
7000 else {
7001 COMPILE_ERROR(ERROR_ARGS "unexpected node");
7002 return COMPILE_NG;
7005 node = node->nd_next;
7006 if (!node) {
7007 break;
7009 type = nd_type(node);
7010 line = nd_line(node);
7011 line_node = node;
7013 /* else */
7014 if (node) {
7015 ADD_LABEL(cond_seq, elselabel);
7016 ADD_INSN(cond_seq, line_node, pop);
7017 ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */
7018 add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches);
7019 CHECK(COMPILE_(cond_seq, "else", node, popped));
7020 ADD_INSNL(cond_seq, line_node, jump, endlabel);
7021 ADD_INSN(cond_seq, line_node, putnil);
7022 if (popped) {
7023 ADD_INSN(cond_seq, line_node, putnil);
7026 else {
7027 debugs("== else (implicit)\n");
7028 ADD_LABEL(cond_seq, elselabel);
7029 add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches);
7030 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7032 if (single_pattern) {
7034 * if key_error_p
7035 * FrozenCore.raise NoMatchingPatternKeyError.new(FrozenCore.sprintf("%p: %s", case_val, error_string), matchee: key_error_matchee, key: key_error_key)
7036 * else
7037 * FrozenCore.raise NoMatchingPatternError, FrozenCore.sprintf("%p: %s", case_val, error_string)
7038 * end
7040 LABEL *key_error, *fin;
7041 struct rb_callinfo_kwarg *kw_arg;
7043 key_error = NEW_LABEL(line);
7044 fin = NEW_LABEL(line);
7046 kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
7047 kw_arg->keyword_len = 2;
7048 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
7049 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
7051 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_P + 2));
7052 ADD_INSNL(cond_seq, orig_node, branchif, key_error);
7053 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
7054 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7055 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
7056 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
7057 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
7058 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
7059 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
7060 ADD_INSNL(cond_seq, orig_node, jump, fin);
7062 ADD_LABEL(cond_seq, key_error);
7063 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternKeyError);
7064 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7065 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
7066 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
7067 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
7068 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
7069 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4));
7070 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_KEY + 5));
7071 ADD_SEND_R(cond_seq, orig_node, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
7072 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(1));
7074 ADD_LABEL(cond_seq, fin);
7076 else {
7077 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
7078 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(2));
7079 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
7081 ADD_INSN1(cond_seq, orig_node, adjuststack, INT2FIX(single_pattern ? 7 : 3));
7082 if (!popped) {
7083 ADD_INSN(cond_seq, orig_node, putnil);
7085 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
7086 ADD_INSN1(cond_seq, orig_node, dupn, INT2FIX(single_pattern ? 5 : 1));
7087 if (popped) {
7088 ADD_INSN(cond_seq, line_node, putnil);
7092 ADD_SEQ(ret, cond_seq);
7093 ADD_SEQ(ret, body_seq);
7094 ADD_LABEL(ret, endlabel);
7095 return COMPILE_OK;
7098 #undef CASE3_BI_OFFSET_DECONSTRUCTED_CACHE
7099 #undef CASE3_BI_OFFSET_ERROR_STRING
7100 #undef CASE3_BI_OFFSET_KEY_ERROR_P
7101 #undef CASE3_BI_OFFSET_KEY_ERROR_MATCHEE
7102 #undef CASE3_BI_OFFSET_KEY_ERROR_KEY
7104 static int
7105 compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
7107 const int line = (int)nd_line(node);
7108 const NODE *line_node = node;
7110 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
7111 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
7112 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
7113 int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
7114 VALUE branches = Qfalse;
7116 struct iseq_compile_data_ensure_node_stack enl;
7118 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line); /* next */
7119 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line); /* redo */
7120 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line); /* break */
7121 LABEL *end_label = NEW_LABEL(line);
7122 LABEL *adjust_label = NEW_LABEL(line);
7124 LABEL *next_catch_label = NEW_LABEL(line);
7125 LABEL *tmp_label = NULL;
7127 ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
7128 push_ensure_entry(iseq, &enl, NULL, NULL);
7130 if (node->nd_state == 1) {
7131 ADD_INSNL(ret, line_node, jump, next_label);
7133 else {
7134 tmp_label = NEW_LABEL(line);
7135 ADD_INSNL(ret, line_node, jump, tmp_label);
7137 ADD_LABEL(ret, adjust_label);
7138 ADD_INSN(ret, line_node, putnil);
7139 ADD_LABEL(ret, next_catch_label);
7140 ADD_INSN(ret, line_node, pop);
7141 ADD_INSNL(ret, line_node, jump, next_label);
7142 if (tmp_label) ADD_LABEL(ret, tmp_label);
7144 ADD_LABEL(ret, redo_label);
7145 branches = decl_branch_base(iseq, node, type == NODE_WHILE ? "while" : "until");
7146 add_trace_branch_coverage(
7147 iseq,
7148 ret,
7149 node->nd_body ? node->nd_body : node,
7151 "body",
7152 branches);
7153 CHECK(COMPILE_POPPED(ret, "while body", node->nd_body));
7154 ADD_LABEL(ret, next_label); /* next */
7156 if (type == NODE_WHILE) {
7157 compile_branch_condition(iseq, ret, node->nd_cond,
7158 redo_label, end_label);
7160 else {
7161 /* until */
7162 compile_branch_condition(iseq, ret, node->nd_cond,
7163 end_label, redo_label);
7166 ADD_LABEL(ret, end_label);
7167 ADD_ADJUST_RESTORE(ret, adjust_label);
7169 if (node->nd_state == Qundef) {
7170 /* ADD_INSN(ret, line_node, putundef); */
7171 COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
7172 return COMPILE_NG;
7174 else {
7175 ADD_INSN(ret, line_node, putnil);
7178 ADD_LABEL(ret, break_label); /* break */
7180 if (popped) {
7181 ADD_INSN(ret, line_node, pop);
7184 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
7185 break_label);
7186 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
7187 next_catch_label);
7188 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
7189 ISEQ_COMPILE_DATA(iseq)->redo_label);
7191 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
7192 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
7193 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
7194 ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
7195 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
7196 return COMPILE_OK;
7199 static int
7200 compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7202 const int line = nd_line(node);
7203 const NODE *line_node = node;
7204 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
7205 LABEL *retry_label = NEW_LABEL(line);
7206 LABEL *retry_end_l = NEW_LABEL(line);
7207 const rb_iseq_t *child_iseq;
7209 ADD_LABEL(ret, retry_label);
7210 if (nd_type_p(node, NODE_FOR)) {
7211 CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter));
7213 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
7214 NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
7215 ISEQ_TYPE_BLOCK, line);
7216 ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
7218 else {
7219 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
7220 NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
7221 ISEQ_TYPE_BLOCK, line);
7222 CHECK(COMPILE(ret, "iter caller", node->nd_iter));
7224 ADD_LABEL(ret, retry_end_l);
7226 if (popped) {
7227 ADD_INSN(ret, line_node, pop);
7230 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
7232 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
7233 return COMPILE_OK;
7236 static int
7237 compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7239 /* massign to var in "for"
7240 * (args.length == 1 && Array.try_convert(args[0])) || args
7242 const NODE *line_node = node;
7243 const NODE *var = node->nd_var;
7244 LABEL *not_single = NEW_LABEL(nd_line(var));
7245 LABEL *not_ary = NEW_LABEL(nd_line(var));
7246 CHECK(COMPILE(ret, "for var", var));
7247 ADD_INSN(ret, line_node, dup);
7248 ADD_CALL(ret, line_node, idLength, INT2FIX(0));
7249 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
7250 ADD_CALL(ret, line_node, idEq, INT2FIX(1));
7251 ADD_INSNL(ret, line_node, branchunless, not_single);
7252 ADD_INSN(ret, line_node, dup);
7253 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
7254 ADD_CALL(ret, line_node, idAREF, INT2FIX(1));
7255 ADD_INSN1(ret, line_node, putobject, rb_cArray);
7256 ADD_INSN(ret, line_node, swap);
7257 ADD_CALL(ret, line_node, rb_intern("try_convert"), INT2FIX(1));
7258 ADD_INSN(ret, line_node, dup);
7259 ADD_INSNL(ret, line_node, branchunless, not_ary);
7260 ADD_INSN(ret, line_node, swap);
7261 ADD_LABEL(ret, not_ary);
7262 ADD_INSN(ret, line_node, pop);
7263 ADD_LABEL(ret, not_single);
7264 return COMPILE_OK;
7267 static int
7268 compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7270 const NODE *line_node = node;
7271 unsigned long throw_flag = 0;
7273 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7274 /* while/until */
7275 LABEL *splabel = NEW_LABEL(0);
7276 ADD_LABEL(ret, splabel);
7277 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
7278 CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts,
7279 ISEQ_COMPILE_DATA(iseq)->loopval_popped));
7280 add_ensure_iseq(ret, iseq, 0);
7281 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7282 ADD_ADJUST_RESTORE(ret, splabel);
7284 if (!popped) {
7285 ADD_INSN(ret, line_node, putnil);
7288 else {
7289 const rb_iseq_t *ip = iseq;
7291 while (ip) {
7292 if (!ISEQ_COMPILE_DATA(ip)) {
7293 ip = 0;
7294 break;
7297 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7298 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7300 else if (ip->body->type == ISEQ_TYPE_BLOCK) {
7301 throw_flag = 0;
7303 else if (ip->body->type == ISEQ_TYPE_EVAL) {
7304 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
7305 return COMPILE_NG;
7307 else {
7308 ip = ip->body->parent_iseq;
7309 continue;
7312 /* escape from block */
7313 CHECK(COMPILE(ret, "break val (block)", node->nd_stts));
7314 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_BREAK));
7315 if (popped) {
7316 ADD_INSN(ret, line_node, pop);
7318 return COMPILE_OK;
7320 COMPILE_ERROR(ERROR_ARGS "Invalid break");
7321 return COMPILE_NG;
7323 return COMPILE_OK;
7326 static int
7327 compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7329 const NODE *line_node = node;
7330 unsigned long throw_flag = 0;
7332 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7333 LABEL *splabel = NEW_LABEL(0);
7334 debugs("next in while loop\n");
7335 ADD_LABEL(ret, splabel);
7336 CHECK(COMPILE(ret, "next val/valid syntax?", node->nd_stts));
7337 add_ensure_iseq(ret, iseq, 0);
7338 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
7339 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
7340 ADD_ADJUST_RESTORE(ret, splabel);
7341 if (!popped) {
7342 ADD_INSN(ret, line_node, putnil);
7345 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
7346 LABEL *splabel = NEW_LABEL(0);
7347 debugs("next in block\n");
7348 ADD_LABEL(ret, splabel);
7349 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
7350 CHECK(COMPILE(ret, "next val", node->nd_stts));
7351 add_ensure_iseq(ret, iseq, 0);
7352 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7353 ADD_ADJUST_RESTORE(ret, splabel);
7354 splabel->unremovable = FALSE;
7356 if (!popped) {
7357 ADD_INSN(ret, line_node, putnil);
7360 else {
7361 const rb_iseq_t *ip = iseq;
7363 while (ip) {
7364 if (!ISEQ_COMPILE_DATA(ip)) {
7365 ip = 0;
7366 break;
7369 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7370 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7371 /* while loop */
7372 break;
7374 else if (ip->body->type == ISEQ_TYPE_BLOCK) {
7375 break;
7377 else if (ip->body->type == ISEQ_TYPE_EVAL) {
7378 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
7379 return COMPILE_NG;
7382 ip = ip->body->parent_iseq;
7384 if (ip != 0) {
7385 CHECK(COMPILE(ret, "next val", node->nd_stts));
7386 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT));
7388 if (popped) {
7389 ADD_INSN(ret, line_node, pop);
7392 else {
7393 COMPILE_ERROR(ERROR_ARGS "Invalid next");
7394 return COMPILE_NG;
7397 return COMPILE_OK;
7400 static int
7401 compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7403 const NODE *line_node = node;
7405 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
7406 LABEL *splabel = NEW_LABEL(0);
7407 debugs("redo in while");
7408 ADD_LABEL(ret, splabel);
7409 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
7410 add_ensure_iseq(ret, iseq, 0);
7411 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
7412 ADD_ADJUST_RESTORE(ret, splabel);
7413 if (!popped) {
7414 ADD_INSN(ret, line_node, putnil);
7417 else if (iseq->body->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
7418 LABEL *splabel = NEW_LABEL(0);
7420 debugs("redo in block");
7421 ADD_LABEL(ret, splabel);
7422 add_ensure_iseq(ret, iseq, 0);
7423 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
7424 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
7425 ADD_ADJUST_RESTORE(ret, splabel);
7427 if (!popped) {
7428 ADD_INSN(ret, line_node, putnil);
7431 else {
7432 const rb_iseq_t *ip = iseq;
7434 while (ip) {
7435 if (!ISEQ_COMPILE_DATA(ip)) {
7436 ip = 0;
7437 break;
7440 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7441 break;
7443 else if (ip->body->type == ISEQ_TYPE_BLOCK) {
7444 break;
7446 else if (ip->body->type == ISEQ_TYPE_EVAL) {
7447 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
7448 return COMPILE_NG;
7451 ip = ip->body->parent_iseq;
7453 if (ip != 0) {
7454 ADD_INSN(ret, line_node, putnil);
7455 ADD_INSN1(ret, line_node, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
7457 if (popped) {
7458 ADD_INSN(ret, line_node, pop);
7461 else {
7462 COMPILE_ERROR(ERROR_ARGS "Invalid redo");
7463 return COMPILE_NG;
7466 return COMPILE_OK;
7469 static int
7470 compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7472 const NODE *line_node = node;
7474 if (iseq->body->type == ISEQ_TYPE_RESCUE) {
7475 ADD_INSN(ret, line_node, putnil);
7476 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETRY));
7478 if (popped) {
7479 ADD_INSN(ret, line_node, pop);
7482 else {
7483 COMPILE_ERROR(ERROR_ARGS "Invalid retry");
7484 return COMPILE_NG;
7486 return COMPILE_OK;
7489 static int
7490 compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7492 const int line = nd_line(node);
7493 const NODE *line_node = node;
7494 LABEL *lstart = NEW_LABEL(line);
7495 LABEL *lend = NEW_LABEL(line);
7496 LABEL *lcont = NEW_LABEL(line);
7497 const rb_iseq_t *rescue = NEW_CHILD_ISEQ(node->nd_resq,
7498 rb_str_concat(rb_str_new2("rescue in "), iseq->body->location.label),
7499 ISEQ_TYPE_RESCUE, line);
7501 lstart->rescued = LABEL_RESCUE_BEG;
7502 lend->rescued = LABEL_RESCUE_END;
7503 ADD_LABEL(ret, lstart);
7505 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
7506 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
7508 CHECK(COMPILE(ret, "rescue head", node->nd_head));
7510 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
7512 ADD_LABEL(ret, lend);
7513 if (node->nd_else) {
7514 ADD_INSN(ret, line_node, pop);
7515 CHECK(COMPILE(ret, "rescue else", node->nd_else));
7517 ADD_INSN(ret, line_node, nop);
7518 ADD_LABEL(ret, lcont);
7520 if (popped) {
7521 ADD_INSN(ret, line_node, pop);
7524 /* register catch entry */
7525 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
7526 ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
7527 return COMPILE_OK;
7530 static int
7531 compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7533 const int line = nd_line(node);
7534 const NODE *line_node = node;
7535 const NODE *resq = node;
7536 const NODE *narg;
7537 LABEL *label_miss, *label_hit;
7539 while (resq) {
7540 label_miss = NEW_LABEL(line);
7541 label_hit = NEW_LABEL(line);
7543 narg = resq->nd_args;
7544 if (narg) {
7545 switch (nd_type(narg)) {
7546 case NODE_LIST:
7547 while (narg) {
7548 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
7549 CHECK(COMPILE(ret, "rescue arg", narg->nd_head));
7550 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
7551 ADD_INSNL(ret, line_node, branchif, label_hit);
7552 narg = narg->nd_next;
7554 break;
7555 case NODE_SPLAT:
7556 case NODE_ARGSCAT:
7557 case NODE_ARGSPUSH:
7558 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
7559 CHECK(COMPILE(ret, "rescue/cond splat", narg));
7560 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
7561 ADD_INSNL(ret, line_node, branchif, label_hit);
7562 break;
7563 default:
7564 UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
7567 else {
7568 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
7569 ADD_INSN1(ret, line_node, putobject, rb_eStandardError);
7570 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
7571 ADD_INSNL(ret, line_node, branchif, label_hit);
7573 ADD_INSNL(ret, line_node, jump, label_miss);
7574 ADD_LABEL(ret, label_hit);
7575 CHECK(COMPILE(ret, "resbody body", resq->nd_body));
7576 if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
7577 ADD_INSN(ret, line_node, nop);
7579 ADD_INSN(ret, line_node, leave);
7580 ADD_LABEL(ret, label_miss);
7581 resq = resq->nd_head;
7583 return COMPILE_OK;
7586 static int
7587 compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7589 const int line = nd_line(node);
7590 const NODE *line_node = node;
7591 DECL_ANCHOR(ensr);
7592 const rb_iseq_t *ensure = NEW_CHILD_ISEQ(node->nd_ensr,
7593 rb_str_concat(rb_str_new2 ("ensure in "), iseq->body->location.label),
7594 ISEQ_TYPE_ENSURE, line);
7595 LABEL *lstart = NEW_LABEL(line);
7596 LABEL *lend = NEW_LABEL(line);
7597 LABEL *lcont = NEW_LABEL(line);
7598 LINK_ELEMENT *last;
7599 int last_leave = 0;
7600 struct ensure_range er;
7601 struct iseq_compile_data_ensure_node_stack enl;
7602 struct ensure_range *erange;
7604 INIT_ANCHOR(ensr);
7605 CHECK(COMPILE_POPPED(ensr, "ensure ensr", node->nd_ensr));
7606 last = ensr->last;
7607 last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
7609 er.begin = lstart;
7610 er.end = lend;
7611 er.next = 0;
7612 push_ensure_entry(iseq, &enl, &er, node->nd_ensr);
7614 ADD_LABEL(ret, lstart);
7615 CHECK(COMPILE_(ret, "ensure head", node->nd_head, (popped | last_leave)));
7616 ADD_LABEL(ret, lend);
7617 ADD_SEQ(ret, ensr);
7618 if (!popped && last_leave) ADD_INSN(ret, line_node, putnil);
7619 ADD_LABEL(ret, lcont);
7620 if (last_leave) ADD_INSN(ret, line_node, pop);
7622 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
7623 if (lstart->link.next != &lend->link) {
7624 while (erange) {
7625 ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
7626 ensure, lcont);
7627 erange = erange->next;
7631 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
7632 return COMPILE_OK;
7635 static int
7636 compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7638 const NODE *line_node = node;
7640 if (iseq) {
7641 enum iseq_type type = iseq->body->type;
7642 const rb_iseq_t *is = iseq;
7643 enum iseq_type t = type;
7644 const NODE *retval = node->nd_stts;
7645 LABEL *splabel = 0;
7647 while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
7648 if (!(is = is->body->parent_iseq)) break;
7649 t = is->body->type;
7651 switch (t) {
7652 case ISEQ_TYPE_TOP:
7653 case ISEQ_TYPE_MAIN:
7654 if (retval) {
7655 rb_warn("argument of top-level return is ignored");
7657 if (is == iseq) {
7658 /* plain top-level, leave directly */
7659 type = ISEQ_TYPE_METHOD;
7661 break;
7662 default:
7663 break;
7666 if (type == ISEQ_TYPE_METHOD) {
7667 splabel = NEW_LABEL(0);
7668 ADD_LABEL(ret, splabel);
7669 ADD_ADJUST(ret, line_node, 0);
7672 CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
7674 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
7675 add_ensure_iseq(ret, iseq, 1);
7676 ADD_TRACE(ret, RUBY_EVENT_RETURN);
7677 ADD_INSN(ret, line_node, leave);
7678 ADD_ADJUST_RESTORE(ret, splabel);
7680 if (!popped) {
7681 ADD_INSN(ret, line_node, putnil);
7684 else {
7685 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETURN));
7686 if (popped) {
7687 ADD_INSN(ret, line_node, pop);
7691 return COMPILE_OK;
7694 static int
7695 compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7697 CHECK(COMPILE_(ret, "nd_body", node, popped));
7699 if (!popped && !all_string_result_p(node)) {
7700 const NODE *line_node = node;
7701 const unsigned int flag = VM_CALL_FCALL;
7703 // Note, this dup could be removed if we are willing to change anytostring. It pops
7704 // two VALUEs off the stack when it could work by replacing the top most VALUE.
7705 ADD_INSN(ret, line_node, dup);
7706 ADD_INSN1(ret, line_node, objtostring, new_callinfo(iseq, idTo_s, 0, flag, NULL, FALSE));
7707 ADD_INSN(ret, line_node, anytostring);
7709 return COMPILE_OK;
7712 static void
7713 compile_lvar(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *line_node, ID id)
7715 int idx = iseq->body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
7717 debugs("id: %s idx: %d\n", rb_id2name(id), idx);
7718 ADD_GETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
7721 static LABEL *
7722 qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, const NODE *node, const NODE *line_node)
7724 LABEL *else_label = NEW_LABEL(nd_line(line_node));
7725 VALUE br = 0;
7727 br = decl_branch_base(iseq, node, "&.");
7728 *branches = br;
7729 ADD_INSN(recv, line_node, dup);
7730 ADD_INSNL(recv, line_node, branchnil, else_label);
7731 add_trace_branch_coverage(iseq, recv, node, 0, "then", br);
7732 return else_label;
7735 static void
7736 qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VALUE branches, const NODE *node, const NODE *line_node)
7738 LABEL *end_label;
7739 if (!else_label) return;
7740 end_label = NEW_LABEL(nd_line(line_node));
7741 ADD_INSNL(ret, line_node, jump, end_label);
7742 ADD_LABEL(ret, else_label);
7743 add_trace_branch_coverage(iseq, ret, node, 1, "else", branches);
7744 ADD_LABEL(ret, end_label);
7747 static int
7748 compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped)
7750 /* optimization shortcut
7751 * "literal".freeze -> opt_str_freeze("literal")
7753 if (node->nd_recv && nd_type_p(node->nd_recv, NODE_STR) &&
7754 (node->nd_mid == idFreeze || node->nd_mid == idUMinus) &&
7755 node->nd_args == NULL &&
7756 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
7757 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7758 VALUE str = rb_fstring(node->nd_recv->nd_lit);
7759 if (node->nd_mid == idUMinus) {
7760 ADD_INSN2(ret, line_node, opt_str_uminus, str,
7761 new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
7763 else {
7764 ADD_INSN2(ret, line_node, opt_str_freeze, str,
7765 new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
7767 RB_OBJ_WRITTEN(iseq, Qundef, str);
7768 if (popped) {
7769 ADD_INSN(ret, line_node, pop);
7771 return TRUE;
7773 /* optimization shortcut
7774 * obj["literal"] -> opt_aref_with(obj, "literal")
7776 if (node->nd_mid == idAREF && !private_recv_p(node) && node->nd_args &&
7777 nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 1 &&
7778 nd_type_p(node->nd_args->nd_head, NODE_STR) &&
7779 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
7780 !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
7781 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7782 VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
7783 CHECK(COMPILE(ret, "recv", node->nd_recv));
7784 ADD_INSN2(ret, line_node, opt_aref_with, str,
7785 new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE));
7786 RB_OBJ_WRITTEN(iseq, Qundef, str);
7787 if (popped) {
7788 ADD_INSN(ret, line_node, pop);
7790 return TRUE;
7792 return FALSE;
7795 static int
7796 iseq_has_builtin_function_table(const rb_iseq_t *iseq)
7798 return ISEQ_COMPILE_DATA(iseq)->builtin_function_table != NULL;
7801 static const struct rb_builtin_function *
7802 iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name)
7804 int i;
7805 const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
7806 for (i=0; table[i].index != -1; i++) {
7807 if (strcmp(table[i].name, name) == 0) {
7808 return &table[i];
7811 return NULL;
7814 static const char *
7815 iseq_builtin_function_name(const enum node_type type, const NODE *recv, ID mid)
7817 const char *name = rb_id2name(mid);
7818 static const char prefix[] = "__builtin_";
7819 const size_t prefix_len = sizeof(prefix) - 1;
7821 switch (type) {
7822 case NODE_CALL:
7823 if (recv) {
7824 switch (nd_type(recv)) {
7825 case NODE_VCALL:
7826 if (recv->nd_mid == rb_intern("__builtin")) {
7827 return name;
7829 break;
7830 case NODE_CONST:
7831 if (recv->nd_vid == rb_intern("Primitive")) {
7832 return name;
7834 break;
7835 default: break;
7838 break;
7839 case NODE_VCALL:
7840 case NODE_FCALL:
7841 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
7842 return &name[prefix_len];
7844 break;
7845 default: break;
7847 return NULL;
7850 static int
7851 delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args, unsigned int *pstart_index)
7854 if (argc == 0) {
7855 *pstart_index = 0;
7856 return TRUE;
7858 else if (argc <= iseq->body->local_table_size) {
7859 unsigned int start=0;
7861 // local_table: [p1, p2, p3, l1, l2, l3]
7862 // arguments: [p3, l1, l2] -> 2
7863 for (start = 0;
7864 argc + start <= iseq->body->local_table_size;
7865 start++) {
7866 const LINK_ELEMENT *elem = FIRST_ELEMENT(args);
7868 for (unsigned int i=start; i-start<argc; i++) {
7869 if (elem->type == ISEQ_ELEMENT_INSN &&
7870 INSN_OF(elem) == BIN(getlocal)) {
7871 int local_index = FIX2INT(OPERAND_AT(elem, 0));
7872 int local_level = FIX2INT(OPERAND_AT(elem, 1));
7874 if (local_level == 0) {
7875 unsigned int index = iseq->body->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1);
7876 if (0) { // for debug
7877 fprintf(stderr, "lvar:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n",
7878 rb_id2name(iseq->body->local_table[i]), i,
7879 rb_id2name(iseq->body->local_table[index]), index,
7880 local_index, (int)iseq->body->local_table_size);
7882 if (i == index) {
7883 elem = elem->next;
7884 continue; /* for */
7886 else {
7887 goto next;
7890 else {
7891 goto fail; // level != 0 is unsupported
7894 else {
7895 goto fail; // insn is not a getlocal
7898 goto success;
7899 next:;
7901 fail:
7902 return FALSE;
7903 success:
7904 *pstart_index = start;
7905 return TRUE;
7907 else {
7908 return FALSE;
7912 static int
7913 compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, const NODE *line_node, int popped)
7915 if (!node) goto no_arg;
7916 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
7917 if (node->nd_next) goto too_many_arg;
7918 node = node->nd_head;
7919 if (!node) goto no_arg;
7920 if (!nd_type_p(node, NODE_LIT)) goto bad_arg;
7921 VALUE name = node->nd_lit;
7922 if (!SYMBOL_P(name)) goto non_symbol_arg;
7923 if (!popped) {
7924 compile_lvar(iseq, ret, line_node, SYM2ID(name));
7926 return COMPILE_OK;
7927 no_arg:
7928 COMPILE_ERROR(ERROR_ARGS "arg!: no argument");
7929 return COMPILE_NG;
7930 too_many_arg:
7931 COMPILE_ERROR(ERROR_ARGS "arg!: too many argument");
7932 return COMPILE_NG;
7933 non_symbol_arg:
7934 COMPILE_ERROR(ERROR_ARGS "non symbol argument to arg!: %s",
7935 rb_builtin_class_name(name));
7936 return COMPILE_NG;
7937 bad_arg:
7938 UNKNOWN_NODE("arg!", node, COMPILE_NG);
7941 static NODE *
7942 mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node)
7944 const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node;
7945 if (nd_type(node) == NODE_IF && node->nd_cond == cond_node) {
7946 return node->nd_body;
7948 else {
7949 rb_bug("mandatory_node: can't find mandatory node");
7953 static int
7954 compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const NODE *line_node)
7956 // arguments
7957 struct rb_args_info args = {
7958 .pre_args_num = iseq->body->param.lead_num,
7960 NODE args_node;
7961 rb_node_init(&args_node, NODE_ARGS, 0, 0, (VALUE)&args);
7963 // local table without non-mandatory parameters
7964 const int skip_local_size = iseq->body->param.size - iseq->body->param.lead_num;
7965 const int table_size = iseq->body->local_table_size - skip_local_size;
7967 VALUE idtmp = 0;
7968 rb_ast_id_table_t *tbl = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
7969 tbl->size = table_size;
7971 int i;
7973 // lead parameters
7974 for (i=0; i<iseq->body->param.lead_num; i++) {
7975 tbl->ids[i] = iseq->body->local_table[i];
7977 // local variables
7978 for (; i<table_size; i++) {
7979 tbl->ids[i] = iseq->body->local_table[i + skip_local_size];
7982 NODE scope_node;
7983 rb_node_init(&scope_node, NODE_SCOPE, (VALUE)tbl, (VALUE)mandatory_node(iseq, node), (VALUE)&args_node);
7985 rb_ast_body_t ast = {
7986 .root = &scope_node,
7987 .compile_option = 0,
7988 .script_lines = iseq->body->variable.script_lines,
7991 int prev_inline_index = GET_VM()->builtin_inline_index;
7993 iseq->body->mandatory_only_iseq =
7994 rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq),
7995 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
7996 INT2FIX(nd_line(line_node)), NULL, 0,
7997 ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option);
7999 GET_VM()->builtin_inline_index = prev_inline_index;
8000 ALLOCV_END(idtmp);
8001 return COMPILE_OK;
8004 static int
8005 compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped,
8006 const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func)
8008 NODE *args_node = node->nd_args;
8010 if (parent_block != NULL) {
8011 COMPILE_ERROR(iseq, nd_line(line_node), "should not call builtins here.");
8012 return COMPILE_NG;
8014 else {
8015 # define BUILTIN_INLINE_PREFIX "_bi"
8016 char inline_func[DECIMAL_SIZE_OF_BITS(sizeof(int) * CHAR_BIT) + sizeof(BUILTIN_INLINE_PREFIX)];
8017 bool cconst = false;
8018 retry:;
8019 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
8021 if (bf == NULL) {
8022 if (strcmp("cstmt!", builtin_func) == 0 ||
8023 strcmp("cexpr!", builtin_func) == 0) {
8024 // ok
8026 else if (strcmp("cconst!", builtin_func) == 0) {
8027 cconst = true;
8029 else if (strcmp("cinit!", builtin_func) == 0) {
8030 // ignore
8031 GET_VM()->builtin_inline_index++;
8032 return COMPILE_OK;
8034 else if (strcmp("attr!", builtin_func) == 0) {
8035 // There's only "inline" attribute for now
8036 iseq->body->builtin_inline_p = true;
8037 return COMPILE_OK;
8039 else if (strcmp("arg!", builtin_func) == 0) {
8040 return compile_builtin_arg(iseq, ret, args_node, line_node, popped);
8042 else if (strcmp("mandatory_only?", builtin_func) == 0) {
8043 if (popped) {
8044 rb_bug("mandatory_only? should be in if condition");
8046 else if (!LIST_INSN_SIZE_ZERO(ret)) {
8047 rb_bug("mandatory_only? should be put on top");
8050 ADD_INSN1(ret, line_node, putobject, Qfalse);
8051 return compile_builtin_mandatory_only_method(iseq, node, line_node);
8053 else if (1) {
8054 rb_bug("can't find builtin function:%s", builtin_func);
8056 else {
8057 COMPILE_ERROR(ERROR_ARGS "can't find builtin function:%s", builtin_func);
8058 return COMPILE_NG;
8061 if (GET_VM()->builtin_inline_index == INT_MAX) {
8062 rb_bug("builtin inline function index overflow:%s", builtin_func);
8064 int inline_index = GET_VM()->builtin_inline_index++;
8065 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
8066 builtin_func = inline_func;
8067 args_node = NULL;
8068 goto retry;
8071 if (cconst) {
8072 typedef VALUE(*builtin_func0)(void *, VALUE);
8073 VALUE const_val = (*(builtin_func0)bf->func_ptr)(NULL, Qnil);
8074 ADD_INSN1(ret, line_node, putobject, const_val);
8075 return COMPILE_OK;
8078 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
8080 unsigned int flag = 0;
8081 struct rb_callinfo_kwarg *keywords = NULL;
8082 VALUE argc = setup_args(iseq, args, args_node, &flag, &keywords);
8084 if (FIX2INT(argc) != bf->argc) {
8085 COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
8086 builtin_func, bf->argc, FIX2INT(argc));
8087 return COMPILE_NG;
8090 unsigned int start_index;
8091 if (delegate_call_p(iseq, FIX2INT(argc), args, &start_index)) {
8092 ADD_INSN2(ret, line_node, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
8094 else {
8095 ADD_SEQ(ret, args);
8096 ADD_INSN1(ret, line_node, invokebuiltin, bf);
8099 if (popped) ADD_INSN(ret, line_node, pop);
8100 return COMPILE_OK;
8104 static int
8105 compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const enum node_type type, const NODE *const line_node, int popped, bool assume_receiver)
8107 /* call: obj.method(...)
8108 * fcall: func(...)
8109 * vcall: func
8111 DECL_ANCHOR(recv);
8112 DECL_ANCHOR(args);
8113 ID mid = node->nd_mid;
8114 VALUE argc;
8115 unsigned int flag = 0;
8116 struct rb_callinfo_kwarg *keywords = NULL;
8117 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8118 LABEL *else_label = NULL;
8119 VALUE branches = Qfalse;
8121 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
8123 INIT_ANCHOR(recv);
8124 INIT_ANCHOR(args);
8125 #if OPT_SUPPORT_JOKE
8126 if (nd_type_p(node, NODE_VCALL)) {
8127 ID id_bitblt;
8128 ID id_answer;
8130 CONST_ID(id_bitblt, "bitblt");
8131 CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
8133 if (mid == id_bitblt) {
8134 ADD_INSN(ret, line_node, bitblt);
8135 return COMPILE_OK;
8137 else if (mid == id_answer) {
8138 ADD_INSN(ret, line_node, answer);
8139 return COMPILE_OK;
8142 /* only joke */
8144 ID goto_id;
8145 ID label_id;
8147 CONST_ID(goto_id, "__goto__");
8148 CONST_ID(label_id, "__label__");
8150 if (nd_type_p(node, NODE_FCALL) &&
8151 (mid == goto_id || mid == label_id)) {
8152 LABEL *label;
8153 st_data_t data;
8154 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
8155 VALUE label_name;
8157 if (!labels_table) {
8158 labels_table = st_init_numtable();
8159 ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
8161 if (nd_type_p(node->nd_args->nd_head, NODE_LIT) &&
8162 SYMBOL_P(node->nd_args->nd_head->nd_lit)) {
8164 label_name = node->nd_args->nd_head->nd_lit;
8165 if (!st_lookup(labels_table, (st_data_t)label_name, &data)) {
8166 label = NEW_LABEL(nd_line(line_node));
8167 label->position = nd_line(line_node);
8168 st_insert(labels_table, (st_data_t)label_name, (st_data_t)label);
8170 else {
8171 label = (LABEL *)data;
8174 else {
8175 COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
8176 return COMPILE_NG;
8179 if (mid == goto_id) {
8180 ADD_INSNL(ret, line_node, jump, label);
8182 else {
8183 ADD_LABEL(ret, label);
8185 return COMPILE_OK;
8188 #endif
8190 const char *builtin_func;
8191 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
8192 (builtin_func = iseq_builtin_function_name(type, node->nd_recv, mid)) != NULL) {
8193 return compile_builtin_function_call(iseq, ret, node, line_node, popped, parent_block, args, builtin_func);
8196 /* receiver */
8197 if (!assume_receiver) {
8198 if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
8199 int idx, level;
8201 if (mid == idCall &&
8202 nd_type_p(node->nd_recv, NODE_LVAR) &&
8203 iseq_block_param_id_p(iseq, node->nd_recv->nd_vid, &idx, &level)) {
8204 ADD_INSN2(recv, node->nd_recv, getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
8206 else if (private_recv_p(node)) {
8207 ADD_INSN(recv, node, putself);
8208 flag |= VM_CALL_FCALL;
8210 else {
8211 CHECK(COMPILE(recv, "recv", node->nd_recv));
8214 if (type == NODE_QCALL) {
8215 else_label = qcall_branch_start(iseq, recv, &branches, node, line_node);
8218 else if (type == NODE_FCALL || type == NODE_VCALL) {
8219 ADD_CALL_RECEIVER(recv, line_node);
8223 /* args */
8224 if (type != NODE_VCALL) {
8225 argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
8226 CHECK(!NIL_P(argc));
8228 else {
8229 argc = INT2FIX(0);
8232 ADD_SEQ(ret, recv);
8233 ADD_SEQ(ret, args);
8235 debugp_param("call args argc", argc);
8236 debugp_param("call method", ID2SYM(mid));
8238 switch ((int)type) {
8239 case NODE_VCALL:
8240 flag |= VM_CALL_VCALL;
8241 /* VCALL is funcall, so fall through */
8242 case NODE_FCALL:
8243 flag |= VM_CALL_FCALL;
8246 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
8248 qcall_branch_end(iseq, ret, else_label, branches, node, line_node);
8249 if (popped) {
8250 ADD_INSN(ret, line_node, pop);
8252 return COMPILE_OK;
8255 static int
8256 compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8258 const int line = nd_line(node);
8259 VALUE argc;
8260 unsigned int flag = 0;
8261 int asgnflag = 0;
8262 ID id = node->nd_mid;
8263 int boff = 0;
8266 * a[x] (op)= y
8268 * nil # nil
8269 * eval a # nil a
8270 * eval x # nil a x
8271 * dupn 2 # nil a x a x
8272 * send :[] # nil a x a[x]
8273 * eval y # nil a x a[x] y
8274 * send op # nil a x ret
8275 * setn 3 # ret a x ret
8276 * send []= # ret ?
8277 * pop # ret
8281 * nd_recv[nd_args->nd_body] (nd_mid)= nd_args->nd_head;
8282 * NODE_OP_ASGN nd_recv
8283 * nd_args->nd_head
8284 * nd_args->nd_body
8285 * nd_mid
8288 if (!popped) {
8289 ADD_INSN(ret, node, putnil);
8291 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node);
8292 CHECK(asgnflag != -1);
8293 switch (nd_type(node->nd_args->nd_head)) {
8294 case NODE_ZLIST:
8295 argc = INT2FIX(0);
8296 break;
8297 case NODE_BLOCK_PASS:
8298 boff = 1;
8299 /* fall through */
8300 default:
8301 argc = setup_args(iseq, ret, node->nd_args->nd_head, &flag, NULL);
8302 CHECK(!NIL_P(argc));
8304 ADD_INSN1(ret, node, dupn, FIXNUM_INC(argc, 1 + boff));
8305 flag |= asgnflag;
8306 ADD_SEND_WITH_FLAG(ret, node, idAREF, argc, INT2FIX(flag));
8308 if (id == idOROP || id == idANDOP) {
8309 /* a[x] ||= y or a[x] &&= y
8311 unless/if a[x]
8312 a[x]= y
8313 else
8317 LABEL *label = NEW_LABEL(line);
8318 LABEL *lfin = NEW_LABEL(line);
8320 ADD_INSN(ret, node, dup);
8321 if (id == idOROP) {
8322 ADD_INSNL(ret, node, branchif, label);
8324 else { /* idANDOP */
8325 ADD_INSNL(ret, node, branchunless, label);
8327 ADD_INSN(ret, node, pop);
8329 CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
8330 if (!popped) {
8331 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
8333 if (flag & VM_CALL_ARGS_SPLAT) {
8334 ADD_INSN1(ret, node, newarray, INT2FIX(1));
8335 if (boff > 0) {
8336 ADD_INSN1(ret, node, dupn, INT2FIX(3));
8337 ADD_INSN(ret, node, swap);
8338 ADD_INSN(ret, node, pop);
8340 ADD_INSN(ret, node, concatarray);
8341 if (boff > 0) {
8342 ADD_INSN1(ret, node, setn, INT2FIX(3));
8343 ADD_INSN(ret, node, pop);
8344 ADD_INSN(ret, node, pop);
8346 ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
8348 else {
8349 if (boff > 0)
8350 ADD_INSN(ret, node, swap);
8351 ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
8353 ADD_INSN(ret, node, pop);
8354 ADD_INSNL(ret, node, jump, lfin);
8355 ADD_LABEL(ret, label);
8356 if (!popped) {
8357 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
8359 ADD_INSN1(ret, node, adjuststack, FIXNUM_INC(argc, 2+boff));
8360 ADD_LABEL(ret, lfin);
8362 else {
8363 CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
8364 ADD_SEND(ret, node, id, INT2FIX(1));
8365 if (!popped) {
8366 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
8368 if (flag & VM_CALL_ARGS_SPLAT) {
8369 ADD_INSN1(ret, node, newarray, INT2FIX(1));
8370 if (boff > 0) {
8371 ADD_INSN1(ret, node, dupn, INT2FIX(3));
8372 ADD_INSN(ret, node, swap);
8373 ADD_INSN(ret, node, pop);
8375 ADD_INSN(ret, node, concatarray);
8376 if (boff > 0) {
8377 ADD_INSN1(ret, node, setn, INT2FIX(3));
8378 ADD_INSN(ret, node, pop);
8379 ADD_INSN(ret, node, pop);
8381 ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
8383 else {
8384 if (boff > 0)
8385 ADD_INSN(ret, node, swap);
8386 ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
8388 ADD_INSN(ret, node, pop);
8390 return COMPILE_OK;
8393 static int
8394 compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8396 const int line = nd_line(node);
8397 ID atype = node->nd_next->nd_mid;
8398 ID vid = node->nd_next->nd_vid, aid = rb_id_attrset(vid);
8399 int asgnflag;
8400 LABEL *lfin = NEW_LABEL(line);
8401 LABEL *lcfin = NEW_LABEL(line);
8402 LABEL *lskip = 0;
8404 class C; attr_accessor :c; end
8405 r = C.new
8406 r.a &&= v # asgn2
8408 eval r # r
8409 dup # r r
8410 eval r.a # r o
8412 # or
8413 dup # r o o
8414 if lcfin # r o
8415 pop # r
8416 eval v # r v
8417 swap # v r
8418 topn 1 # v r v
8419 send a= # v ?
8420 jump lfin # v ?
8422 lcfin: # r o
8423 swap # o r
8425 lfin: # o ?
8426 pop # o
8428 # and
8429 dup # r o o
8430 unless lcfin
8431 pop # r
8432 eval v # r v
8433 swap # v r
8434 topn 1 # v r v
8435 send a= # v ?
8436 jump lfin # v ?
8438 # others
8439 eval v # r o v
8440 send ?? # r w
8441 send a= # w
8445 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node);
8446 CHECK(asgnflag != -1);
8447 if (node->nd_next->nd_aid) {
8448 lskip = NEW_LABEL(line);
8449 ADD_INSN(ret, node, dup);
8450 ADD_INSNL(ret, node, branchnil, lskip);
8452 ADD_INSN(ret, node, dup);
8453 ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag));
8455 if (atype == idOROP || atype == idANDOP) {
8456 ADD_INSN(ret, node, dup);
8457 if (atype == idOROP) {
8458 ADD_INSNL(ret, node, branchif, lcfin);
8460 else { /* idANDOP */
8461 ADD_INSNL(ret, node, branchunless, lcfin);
8463 ADD_INSN(ret, node, pop);
8464 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
8465 ADD_INSN(ret, node, swap);
8466 ADD_INSN1(ret, node, topn, INT2FIX(1));
8467 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
8468 ADD_INSNL(ret, node, jump, lfin);
8470 ADD_LABEL(ret, lcfin);
8471 ADD_INSN(ret, node, swap);
8473 ADD_LABEL(ret, lfin);
8474 ADD_INSN(ret, node, pop);
8475 if (lskip) {
8476 ADD_LABEL(ret, lskip);
8478 if (popped) {
8479 /* we can apply more optimize */
8480 ADD_INSN(ret, node, pop);
8483 else {
8484 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
8485 ADD_SEND(ret, node, atype, INT2FIX(1));
8486 if (!popped) {
8487 ADD_INSN(ret, node, swap);
8488 ADD_INSN1(ret, node, topn, INT2FIX(1));
8490 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
8491 if (lskip && popped) {
8492 ADD_LABEL(ret, lskip);
8494 ADD_INSN(ret, node, pop);
8495 if (lskip && !popped) {
8496 ADD_LABEL(ret, lskip);
8499 return COMPILE_OK;
8502 static int
8503 compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8505 const int line = nd_line(node);
8506 LABEL *lfin = 0;
8507 LABEL *lassign = 0;
8508 ID mid;
8510 switch (nd_type(node->nd_head)) {
8511 case NODE_COLON3:
8512 ADD_INSN1(ret, node, putobject, rb_cObject);
8513 break;
8514 case NODE_COLON2:
8515 CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head));
8516 break;
8517 default:
8518 COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
8519 ruby_node_name(nd_type(node->nd_head)));
8520 return COMPILE_NG;
8522 mid = node->nd_head->nd_mid;
8523 /* cref */
8524 if (node->nd_aid == idOROP) {
8525 lassign = NEW_LABEL(line);
8526 ADD_INSN(ret, node, dup); /* cref cref */
8527 ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
8528 ID2SYM(mid), Qtrue); /* cref bool */
8529 ADD_INSNL(ret, node, branchunless, lassign); /* cref */
8531 ADD_INSN(ret, node, dup); /* cref cref */
8532 ADD_INSN1(ret, node, putobject, Qtrue);
8533 ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */
8535 if (node->nd_aid == idOROP || node->nd_aid == idANDOP) {
8536 lfin = NEW_LABEL(line);
8537 if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
8538 if (node->nd_aid == idOROP)
8539 ADD_INSNL(ret, node, branchif, lfin);
8540 else /* idANDOP */
8541 ADD_INSNL(ret, node, branchunless, lfin);
8542 /* cref [obj] */
8543 if (!popped) ADD_INSN(ret, node, pop); /* cref */
8544 if (lassign) ADD_LABEL(ret, lassign);
8545 CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
8546 /* cref value */
8547 if (popped)
8548 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
8549 else {
8550 ADD_INSN1(ret, node, dupn, INT2FIX(2)); /* cref value cref value */
8551 ADD_INSN(ret, node, swap); /* cref value value cref */
8553 ADD_INSN1(ret, node, setconstant, ID2SYM(mid)); /* cref [value] */
8554 ADD_LABEL(ret, lfin); /* cref [value] */
8555 if (!popped) ADD_INSN(ret, node, swap); /* [value] cref */
8556 ADD_INSN(ret, node, pop); /* [value] */
8558 else {
8559 CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
8560 /* cref obj value */
8561 ADD_CALL(ret, node, node->nd_aid, INT2FIX(1));
8562 /* cref value */
8563 ADD_INSN(ret, node, swap); /* value cref */
8564 if (!popped) {
8565 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* value cref value */
8566 ADD_INSN(ret, node, swap); /* value value cref */
8568 ADD_INSN1(ret, node, setconstant, ID2SYM(mid));
8570 return COMPILE_OK;
8573 static int
8574 compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
8576 const int line = nd_line(node);
8577 LABEL *lfin = NEW_LABEL(line);
8578 LABEL *lassign;
8580 if (type == NODE_OP_ASGN_OR && !nd_type_p(node->nd_head, NODE_IVAR)) {
8581 LABEL *lfinish[2];
8582 lfinish[0] = lfin;
8583 lfinish[1] = 0;
8584 defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
8585 lassign = lfinish[1];
8586 if (!lassign) {
8587 lassign = NEW_LABEL(line);
8589 ADD_INSNL(ret, node, branchunless, lassign);
8591 else {
8592 lassign = NEW_LABEL(line);
8595 CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head));
8596 ADD_INSN(ret, node, dup);
8598 if (type == NODE_OP_ASGN_AND) {
8599 ADD_INSNL(ret, node, branchunless, lfin);
8601 else {
8602 ADD_INSNL(ret, node, branchif, lfin);
8605 ADD_INSN(ret, node, pop);
8606 ADD_LABEL(ret, lassign);
8607 CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value));
8608 ADD_LABEL(ret, lfin);
8610 if (popped) {
8611 /* we can apply more optimize */
8612 ADD_INSN(ret, node, pop);
8614 return COMPILE_OK;
8617 static int
8618 compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
8620 struct rb_iseq_constant_body *const body = iseq->body;
8621 DECL_ANCHOR(args);
8622 int argc;
8623 unsigned int flag = 0;
8624 struct rb_callinfo_kwarg *keywords = NULL;
8625 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8627 INIT_ANCHOR(args);
8628 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
8629 if (type == NODE_SUPER) {
8630 VALUE vargc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
8631 CHECK(!NIL_P(vargc));
8632 argc = FIX2INT(vargc);
8634 else {
8635 /* NODE_ZSUPER */
8636 int i;
8637 const rb_iseq_t *liseq = body->local_iseq;
8638 const struct rb_iseq_constant_body *const local_body = liseq->body;
8639 const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
8640 int lvar_level = get_lvar_level(iseq);
8642 argc = local_body->param.lead_num;
8644 /* normal arguments */
8645 for (i = 0; i < local_body->param.lead_num; i++) {
8646 int idx = local_body->local_table_size - i;
8647 ADD_GETLOCAL(args, node, idx, lvar_level);
8650 if (local_body->param.flags.has_opt) {
8651 /* optional arguments */
8652 int j;
8653 for (j = 0; j < local_body->param.opt_num; j++) {
8654 int idx = local_body->local_table_size - (i + j);
8655 ADD_GETLOCAL(args, node, idx, lvar_level);
8657 i += j;
8658 argc = i;
8660 if (local_body->param.flags.has_rest) {
8661 /* rest argument */
8662 int idx = local_body->local_table_size - local_body->param.rest_start;
8663 ADD_GETLOCAL(args, node, idx, lvar_level);
8664 ADD_INSN1(args, node, splatarray, Qfalse);
8666 argc = local_body->param.rest_start + 1;
8667 flag |= VM_CALL_ARGS_SPLAT;
8669 if (local_body->param.flags.has_post) {
8670 /* post arguments */
8671 int post_len = local_body->param.post_num;
8672 int post_start = local_body->param.post_start;
8674 if (local_body->param.flags.has_rest) {
8675 int j;
8676 for (j=0; j<post_len; j++) {
8677 int idx = local_body->local_table_size - (post_start + j);
8678 ADD_GETLOCAL(args, node, idx, lvar_level);
8680 ADD_INSN1(args, node, newarray, INT2FIX(j));
8681 ADD_INSN (args, node, concatarray);
8682 /* argc is settled at above */
8684 else {
8685 int j;
8686 for (j=0; j<post_len; j++) {
8687 int idx = local_body->local_table_size - (post_start + j);
8688 ADD_GETLOCAL(args, node, idx, lvar_level);
8690 argc = post_len + post_start;
8694 if (local_body->param.flags.has_kw) { /* TODO: support keywords */
8695 int local_size = local_body->local_table_size;
8696 argc++;
8698 ADD_INSN1(args, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8700 if (local_body->param.flags.has_kwrest) {
8701 int idx = local_body->local_table_size - local_kwd->rest_start;
8702 ADD_GETLOCAL(args, node, idx, lvar_level);
8703 if (local_kwd->num > 0) {
8704 ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
8705 flag |= VM_CALL_KW_SPLAT_MUT;
8708 else {
8709 ADD_INSN1(args, node, newhash, INT2FIX(0));
8710 flag |= VM_CALL_KW_SPLAT_MUT;
8712 for (i = 0; i < local_kwd->num; ++i) {
8713 ID id = local_kwd->table[i];
8714 int idx = local_size - get_local_var_idx(liseq, id);
8715 ADD_INSN1(args, node, putobject, ID2SYM(id));
8716 ADD_GETLOCAL(args, node, idx, lvar_level);
8718 ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
8719 if (local_body->param.flags.has_rest) {
8720 ADD_INSN1(args, node, newarray, INT2FIX(1));
8721 ADD_INSN (args, node, concatarray);
8722 --argc;
8724 flag |= VM_CALL_KW_SPLAT;
8726 else if (local_body->param.flags.has_kwrest) {
8727 int idx = local_body->local_table_size - local_kwd->rest_start;
8728 ADD_GETLOCAL(args, node, idx, lvar_level);
8730 if (local_body->param.flags.has_rest) {
8731 ADD_INSN1(args, node, newarray, INT2FIX(1));
8732 ADD_INSN (args, node, concatarray);
8734 else {
8735 argc++;
8737 flag |= VM_CALL_KW_SPLAT;
8741 flag |= VM_CALL_SUPER | VM_CALL_FCALL;
8742 if (type == NODE_ZSUPER) flag |= VM_CALL_ZSUPER;
8743 ADD_INSN(ret, node, putself);
8744 ADD_SEQ(ret, args);
8745 ADD_INSN2(ret, node, invokesuper,
8746 new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL),
8747 parent_block);
8749 if (popped) {
8750 ADD_INSN(ret, node, pop);
8752 return COMPILE_OK;
8755 static int
8756 compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8758 DECL_ANCHOR(args);
8759 VALUE argc;
8760 unsigned int flag = 0;
8761 struct rb_callinfo_kwarg *keywords = NULL;
8763 INIT_ANCHOR(args);
8765 switch (iseq->body->local_iseq->body->type) {
8766 case ISEQ_TYPE_TOP:
8767 case ISEQ_TYPE_MAIN:
8768 case ISEQ_TYPE_CLASS:
8769 COMPILE_ERROR(ERROR_ARGS "Invalid yield");
8770 return COMPILE_NG;
8771 default: /* valid */;
8774 if (node->nd_head) {
8775 argc = setup_args(iseq, args, node->nd_head, &flag, &keywords);
8776 CHECK(!NIL_P(argc));
8778 else {
8779 argc = INT2FIX(0);
8782 ADD_SEQ(ret, args);
8783 ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
8785 if (popped) {
8786 ADD_INSN(ret, node, pop);
8789 int level = 0;
8790 const rb_iseq_t *tmp_iseq = iseq;
8791 for (; tmp_iseq != iseq->body->local_iseq; level++ ) {
8792 tmp_iseq = tmp_iseq->body->parent_iseq;
8794 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
8796 return COMPILE_OK;
8799 static int
8800 compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
8802 DECL_ANCHOR(recv);
8803 DECL_ANCHOR(val);
8805 INIT_ANCHOR(recv);
8806 INIT_ANCHOR(val);
8807 switch ((int)type) {
8808 case NODE_MATCH:
8809 ADD_INSN1(recv, node, putobject, node->nd_lit);
8810 ADD_INSN2(val, node, getspecial, INT2FIX(0),
8811 INT2FIX(0));
8812 break;
8813 case NODE_MATCH2:
8814 CHECK(COMPILE(recv, "receiver", node->nd_recv));
8815 CHECK(COMPILE(val, "value", node->nd_value));
8816 break;
8817 case NODE_MATCH3:
8818 CHECK(COMPILE(recv, "receiver", node->nd_value));
8819 CHECK(COMPILE(val, "value", node->nd_recv));
8820 break;
8823 ADD_SEQ(ret, recv);
8824 ADD_SEQ(ret, val);
8825 ADD_SEND(ret, node, idEqTilde, INT2FIX(1));
8827 if (node->nd_args) {
8828 compile_named_capture_assign(iseq, ret, node->nd_args);
8831 if (popped) {
8832 ADD_INSN(ret, node, pop);
8834 return COMPILE_OK;
8837 static int
8838 compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8840 const int line = nd_line(node);
8841 if (rb_is_const_id(node->nd_mid)) {
8842 /* constant */
8843 LABEL *lend = NEW_LABEL(line);
8844 int ic_index = iseq->body->is_size++;
8846 DECL_ANCHOR(pref);
8847 DECL_ANCHOR(body);
8849 INIT_ANCHOR(pref);
8850 INIT_ANCHOR(body);
8851 CHECK(compile_const_prefix(iseq, node, pref, body));
8852 if (LIST_INSN_SIZE_ZERO(pref)) {
8853 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
8854 ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
8856 else {
8857 ADD_INSN(ret, node, putnil);
8860 ADD_SEQ(ret, body);
8862 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
8863 ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
8864 ADD_LABEL(ret, lend);
8867 else {
8868 ADD_SEQ(ret, pref);
8869 ADD_SEQ(ret, body);
8872 else {
8873 /* function call */
8874 ADD_CALL_RECEIVER(ret, node);
8875 CHECK(COMPILE(ret, "colon2#nd_head", node->nd_head));
8876 ADD_CALL(ret, node, node->nd_mid, INT2FIX(1));
8878 if (popped) {
8879 ADD_INSN(ret, node, pop);
8881 return COMPILE_OK;
8884 static int
8885 compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8887 const int line = nd_line(node);
8888 LABEL *lend = NEW_LABEL(line);
8889 int ic_index = iseq->body->is_size++;
8891 debugi("colon3#nd_mid", node->nd_mid);
8893 /* add cache insn */
8894 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
8895 ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
8896 ADD_INSN(ret, node, pop);
8899 ADD_INSN1(ret, node, putobject, rb_cObject);
8900 ADD_INSN1(ret, node, putobject, Qtrue);
8901 ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_mid));
8903 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
8904 ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
8905 ADD_LABEL(ret, lend);
8908 if (popped) {
8909 ADD_INSN(ret, node, pop);
8911 return COMPILE_OK;
8914 static int
8915 compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const int excl)
8917 VALUE flag = INT2FIX(excl);
8918 const NODE *b = node->nd_beg;
8919 const NODE *e = node->nd_end;
8921 if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
8922 if (!popped) {
8923 VALUE bv = nd_type_p(b, NODE_LIT) ? b->nd_lit : Qnil;
8924 VALUE ev = nd_type_p(e, NODE_LIT) ? e->nd_lit : Qnil;
8925 VALUE val = rb_range_new(bv, ev, excl);
8926 ADD_INSN1(ret, node, putobject, val);
8927 RB_OBJ_WRITTEN(iseq, Qundef, val);
8930 else {
8931 CHECK(COMPILE_(ret, "min", b, popped));
8932 CHECK(COMPILE_(ret, "max", e, popped));
8933 if (!popped) {
8934 ADD_INSN1(ret, node, newrange, flag);
8937 return COMPILE_OK;
8940 static int
8941 compile_errinfo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8943 if (!popped) {
8944 if (iseq->body->type == ISEQ_TYPE_RESCUE) {
8945 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, 0);
8947 else {
8948 const rb_iseq_t *ip = iseq;
8949 int level = 0;
8950 while (ip) {
8951 if (ip->body->type == ISEQ_TYPE_RESCUE) {
8952 break;
8954 ip = ip->body->parent_iseq;
8955 level++;
8957 if (ip) {
8958 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, level);
8960 else {
8961 ADD_INSN(ret, node, putnil);
8965 return COMPILE_OK;
8968 static int
8969 compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8971 struct rb_iseq_constant_body *const body = iseq->body;
8972 LABEL *end_label = NEW_LABEL(nd_line(node));
8973 const NODE *default_value = node->nd_body->nd_value;
8975 if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) {
8976 /* required argument. do nothing */
8977 COMPILE_ERROR(ERROR_ARGS "unreachable");
8978 return COMPILE_NG;
8980 else if (nd_type_p(default_value, NODE_LIT) ||
8981 nd_type_p(default_value, NODE_NIL) ||
8982 nd_type_p(default_value, NODE_TRUE) ||
8983 nd_type_p(default_value, NODE_FALSE)) {
8984 COMPILE_ERROR(ERROR_ARGS "unreachable");
8985 return COMPILE_NG;
8987 else {
8988 /* if keywordcheck(_kw_bits, nth_keyword)
8989 * kw = default_value
8990 * end
8992 int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
8993 int keyword_idx = body->param.keyword->num;
8995 ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
8996 ADD_INSNL(ret, node, branchif, end_label);
8997 CHECK(COMPILE_POPPED(ret, "keyword default argument", node->nd_body));
8998 ADD_LABEL(ret, end_label);
9000 return COMPILE_OK;
9003 static int
9004 compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9006 DECL_ANCHOR(recv);
9007 DECL_ANCHOR(args);
9008 unsigned int flag = 0;
9009 ID mid = node->nd_mid;
9010 VALUE argc;
9011 LABEL *else_label = NULL;
9012 VALUE branches = Qfalse;
9014 /* optimization shortcut
9015 * obj["literal"] = value -> opt_aset_with(obj, "literal", value)
9017 if (mid == idASET && !private_recv_p(node) && node->nd_args &&
9018 nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 2 &&
9019 nd_type_p(node->nd_args->nd_head, NODE_STR) &&
9020 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
9021 !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
9022 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction)
9024 VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
9025 CHECK(COMPILE(ret, "recv", node->nd_recv));
9026 CHECK(COMPILE(ret, "value", node->nd_args->nd_next->nd_head));
9027 if (!popped) {
9028 ADD_INSN(ret, node, swap);
9029 ADD_INSN1(ret, node, topn, INT2FIX(1));
9031 ADD_INSN2(ret, node, opt_aset_with, str,
9032 new_callinfo(iseq, idASET, 2, 0, NULL, FALSE));
9033 RB_OBJ_WRITTEN(iseq, Qundef, str);
9034 ADD_INSN(ret, node, pop);
9035 return COMPILE_OK;
9038 INIT_ANCHOR(recv);
9039 INIT_ANCHOR(args);
9040 argc = setup_args(iseq, args, node->nd_args, &flag, NULL);
9041 CHECK(!NIL_P(argc));
9043 int asgnflag = COMPILE_RECV(recv, "recv", node);
9044 CHECK(asgnflag != -1);
9045 flag |= (unsigned int)asgnflag;
9047 debugp_param("argc", argc);
9048 debugp_param("nd_mid", ID2SYM(mid));
9050 if (!rb_is_attrset_id(mid)) {
9051 /* safe nav attr */
9052 mid = rb_id_attrset(mid);
9053 else_label = qcall_branch_start(iseq, recv, &branches, node, node);
9055 if (!popped) {
9056 ADD_INSN(ret, node, putnil);
9057 ADD_SEQ(ret, recv);
9058 ADD_SEQ(ret, args);
9060 if (flag & VM_CALL_ARGS_BLOCKARG) {
9061 ADD_INSN1(ret, node, topn, INT2FIX(1));
9062 if (flag & VM_CALL_ARGS_SPLAT) {
9063 ADD_INSN1(ret, node, putobject, INT2FIX(-1));
9064 ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
9066 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 3));
9067 ADD_INSN (ret, node, pop);
9069 else if (flag & VM_CALL_ARGS_SPLAT) {
9070 ADD_INSN(ret, node, dup);
9071 ADD_INSN1(ret, node, putobject, INT2FIX(-1));
9072 ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
9073 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2));
9074 ADD_INSN (ret, node, pop);
9076 else {
9077 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 1));
9080 else {
9081 ADD_SEQ(ret, recv);
9082 ADD_SEQ(ret, args);
9084 ADD_SEND_WITH_FLAG(ret, node, mid, argc, INT2FIX(flag));
9085 qcall_branch_end(iseq, ret, else_label, branches, node, node);
9086 ADD_INSN(ret, node, pop);
9087 return COMPILE_OK;
9090 static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped);
9092 compile each node
9094 self: InstructionSequence
9095 node: Ruby compiled node
9096 popped: This node will be popped
9098 static int
9099 iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
9101 if (node == 0) {
9102 if (!popped) {
9103 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
9104 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
9105 debugs("node: NODE_NIL(implicit)\n");
9106 NODE dummy_line_node = generate_dummy_line_node(lineno, -1);
9107 ADD_INSN(ret, &dummy_line_node, putnil);
9109 return COMPILE_OK;
9111 return iseq_compile_each0(iseq, ret, node, popped);
9114 static int
9115 iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9117 const int line = (int)nd_line(node);
9118 const enum node_type type = nd_type(node);
9119 struct rb_iseq_constant_body *const body = iseq->body;
9121 if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
9122 /* ignore */
9124 else {
9125 if (node->flags & NODE_FL_NEWLINE) {
9126 int event = RUBY_EVENT_LINE;
9127 ISEQ_COMPILE_DATA(iseq)->last_line = line;
9128 if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
9129 event |= RUBY_EVENT_COVERAGE_LINE;
9131 ADD_TRACE(ret, event);
9135 debug_node_start(node);
9136 #undef BEFORE_RETURN
9137 #define BEFORE_RETURN debug_node_end()
9139 switch (type) {
9140 case NODE_BLOCK:
9141 CHECK(compile_block(iseq, ret, node, popped));
9142 break;
9143 case NODE_IF:
9144 case NODE_UNLESS:
9145 CHECK(compile_if(iseq, ret, node, popped, type));
9146 break;
9147 case NODE_CASE:
9148 CHECK(compile_case(iseq, ret, node, popped));
9149 break;
9150 case NODE_CASE2:
9151 CHECK(compile_case2(iseq, ret, node, popped));
9152 break;
9153 case NODE_CASE3:
9154 CHECK(compile_case3(iseq, ret, node, popped));
9155 break;
9156 case NODE_WHILE:
9157 case NODE_UNTIL:
9158 CHECK(compile_loop(iseq, ret, node, popped, type));
9159 break;
9160 case NODE_FOR:
9161 case NODE_ITER:
9162 CHECK(compile_iter(iseq, ret, node, popped));
9163 break;
9164 case NODE_FOR_MASGN:
9165 CHECK(compile_for_masgn(iseq, ret, node, popped));
9166 break;
9167 case NODE_BREAK:
9168 CHECK(compile_break(iseq, ret, node, popped));
9169 break;
9170 case NODE_NEXT:
9171 CHECK(compile_next(iseq, ret, node, popped));
9172 break;
9173 case NODE_REDO:
9174 CHECK(compile_redo(iseq, ret, node, popped));
9175 break;
9176 case NODE_RETRY:
9177 CHECK(compile_retry(iseq, ret, node, popped));
9178 break;
9179 case NODE_BEGIN:{
9180 CHECK(COMPILE_(ret, "NODE_BEGIN", node->nd_body, popped));
9181 break;
9183 case NODE_RESCUE:
9184 CHECK(compile_rescue(iseq, ret, node, popped));
9185 break;
9186 case NODE_RESBODY:
9187 CHECK(compile_resbody(iseq, ret, node, popped));
9188 break;
9189 case NODE_ENSURE:
9190 CHECK(compile_ensure(iseq, ret, node, popped));
9191 break;
9193 case NODE_AND:
9194 case NODE_OR:{
9195 LABEL *end_label = NEW_LABEL(line);
9196 CHECK(COMPILE(ret, "nd_1st", node->nd_1st));
9197 if (!popped) {
9198 ADD_INSN(ret, node, dup);
9200 if (type == NODE_AND) {
9201 ADD_INSNL(ret, node, branchunless, end_label);
9203 else {
9204 ADD_INSNL(ret, node, branchif, end_label);
9206 if (!popped) {
9207 ADD_INSN(ret, node, pop);
9209 CHECK(COMPILE_(ret, "nd_2nd", node->nd_2nd, popped));
9210 ADD_LABEL(ret, end_label);
9211 break;
9214 case NODE_MASGN:{
9215 compile_massign(iseq, ret, node, popped);
9216 break;
9219 case NODE_LASGN:{
9220 ID id = node->nd_vid;
9221 int idx = body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
9223 debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
9224 CHECK(COMPILE(ret, "rvalue", node->nd_value));
9226 if (!popped) {
9227 ADD_INSN(ret, node, dup);
9229 ADD_SETLOCAL(ret, node, idx, get_lvar_level(iseq));
9230 break;
9232 case NODE_DASGN: {
9233 int idx, lv, ls;
9234 ID id = node->nd_vid;
9235 CHECK(COMPILE(ret, "dvalue", node->nd_value));
9236 debugi("dassn id", rb_id2str(id) ? id : '*');
9238 if (!popped) {
9239 ADD_INSN(ret, node, dup);
9242 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
9244 if (idx < 0) {
9245 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
9246 rb_id2str(id));
9247 goto ng;
9249 ADD_SETLOCAL(ret, node, ls - idx, lv);
9250 break;
9252 case NODE_GASGN:{
9253 CHECK(COMPILE(ret, "lvalue", node->nd_value));
9255 if (!popped) {
9256 ADD_INSN(ret, node, dup);
9258 ADD_INSN1(ret, node, setglobal, ID2SYM(node->nd_entry));
9259 break;
9261 case NODE_IASGN:{
9262 CHECK(COMPILE(ret, "lvalue", node->nd_value));
9263 if (!popped) {
9264 ADD_INSN(ret, node, dup);
9266 ADD_INSN2(ret, node, setinstancevariable,
9267 ID2SYM(node->nd_vid),
9268 get_ivar_ic_value(iseq,node->nd_vid));
9269 break;
9271 case NODE_CDECL:{
9272 if (node->nd_vid) {
9273 CHECK(COMPILE(ret, "lvalue", node->nd_value));
9275 if (!popped) {
9276 ADD_INSN(ret, node, dup);
9279 ADD_INSN1(ret, node, putspecialobject,
9280 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
9281 ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_vid));
9283 else {
9284 compile_cpath(ret, iseq, node->nd_else);
9285 CHECK(COMPILE(ret, "lvalue", node->nd_value));
9286 ADD_INSN(ret, node, swap);
9288 if (!popped) {
9289 ADD_INSN1(ret, node, topn, INT2FIX(1));
9290 ADD_INSN(ret, node, swap);
9293 ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_else->nd_mid));
9295 break;
9297 case NODE_CVASGN:{
9298 CHECK(COMPILE(ret, "cvasgn val", node->nd_value));
9299 if (!popped) {
9300 ADD_INSN(ret, node, dup);
9302 ADD_INSN2(ret, node, setclassvariable,
9303 ID2SYM(node->nd_vid),
9304 get_ivar_ic_value(iseq,node->nd_vid));
9305 break;
9307 case NODE_OP_ASGN1:
9308 CHECK(compile_op_asgn1(iseq, ret, node, popped));
9309 break;
9310 case NODE_OP_ASGN2:
9311 CHECK(compile_op_asgn2(iseq, ret, node, popped));
9312 break;
9313 case NODE_OP_CDECL:
9314 CHECK(compile_op_cdecl(iseq, ret, node, popped));
9315 break;
9316 case NODE_OP_ASGN_AND:
9317 case NODE_OP_ASGN_OR:
9318 CHECK(compile_op_log(iseq, ret, node, popped, type));
9319 break;
9320 case NODE_CALL: /* obj.foo */
9321 case NODE_OPCALL: /* foo[] */
9322 if (compile_call_precheck_freeze(iseq, ret, node, node, popped) == TRUE) {
9323 break;
9325 case NODE_QCALL: /* obj&.foo */
9326 case NODE_FCALL: /* foo() */
9327 case NODE_VCALL: /* foo (variable or call) */
9328 if (compile_call(iseq, ret, node, type, node, popped, false) == COMPILE_NG) {
9329 goto ng;
9331 break;
9332 case NODE_SUPER:
9333 case NODE_ZSUPER:
9334 CHECK(compile_super(iseq, ret, node, popped, type));
9335 break;
9336 case NODE_LIST:{
9337 CHECK(compile_array(iseq, ret, node, popped) >= 0);
9338 break;
9340 case NODE_ZLIST:{
9341 if (!popped) {
9342 ADD_INSN1(ret, node, newarray, INT2FIX(0));
9344 break;
9346 case NODE_VALUES:{
9347 const NODE *n = node;
9348 if (popped) {
9349 COMPILE_ERROR(ERROR_ARGS "NODE_VALUES: must not be popped");
9351 while (n) {
9352 CHECK(COMPILE(ret, "values item", n->nd_head));
9353 n = n->nd_next;
9355 ADD_INSN1(ret, node, newarray, INT2FIX(node->nd_alen));
9356 break;
9358 case NODE_HASH:
9359 CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0);
9360 break;
9361 case NODE_RETURN:
9362 CHECK(compile_return(iseq, ret, node, popped));
9363 break;
9364 case NODE_YIELD:
9365 CHECK(compile_yield(iseq, ret, node, popped));
9366 break;
9367 case NODE_LVAR:{
9368 if (!popped) {
9369 compile_lvar(iseq, ret, node, node->nd_vid);
9371 break;
9373 case NODE_DVAR:{
9374 int lv, idx, ls;
9375 debugi("nd_vid", node->nd_vid);
9376 if (!popped) {
9377 idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
9378 if (idx < 0) {
9379 COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
9380 rb_id2str(node->nd_vid));
9381 goto ng;
9383 ADD_GETLOCAL(ret, node, ls - idx, lv);
9385 break;
9387 case NODE_GVAR:{
9388 ADD_INSN1(ret, node, getglobal, ID2SYM(node->nd_entry));
9389 if (popped) {
9390 ADD_INSN(ret, node, pop);
9392 break;
9394 case NODE_IVAR:{
9395 debugi("nd_vid", node->nd_vid);
9396 if (!popped) {
9397 ADD_INSN2(ret, node, getinstancevariable,
9398 ID2SYM(node->nd_vid),
9399 get_ivar_ic_value(iseq,node->nd_vid));
9401 break;
9403 case NODE_CONST:{
9404 debugi("nd_vid", node->nd_vid);
9406 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
9407 LABEL *lend = NEW_LABEL(line);
9408 int ic_index = body->is_size++;
9410 ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
9411 ADD_INSN1(ret, node, putobject, Qtrue);
9412 ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid));
9413 ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
9414 ADD_LABEL(ret, lend);
9416 else {
9417 ADD_INSN(ret, node, putnil);
9418 ADD_INSN1(ret, node, putobject, Qtrue);
9419 ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid));
9422 if (popped) {
9423 ADD_INSN(ret, node, pop);
9425 break;
9427 case NODE_CVAR:{
9428 if (!popped) {
9429 ADD_INSN2(ret, node, getclassvariable,
9430 ID2SYM(node->nd_vid),
9431 get_ivar_ic_value(iseq,node->nd_vid));
9433 break;
9435 case NODE_NTH_REF:{
9436 if (!popped) {
9437 if (!node->nd_nth) {
9438 ADD_INSN(ret, node, putnil);
9439 break;
9441 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
9442 INT2FIX(node->nd_nth << 1));
9444 break;
9446 case NODE_BACK_REF:{
9447 if (!popped) {
9448 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
9449 INT2FIX(0x01 | (node->nd_nth << 1)));
9451 break;
9453 case NODE_MATCH:
9454 case NODE_MATCH2:
9455 case NODE_MATCH3:
9456 CHECK(compile_match(iseq, ret, node, popped, type));
9457 break;
9458 case NODE_LIT:{
9459 debugp_param("lit", node->nd_lit);
9460 if (!popped) {
9461 ADD_INSN1(ret, node, putobject, node->nd_lit);
9462 RB_OBJ_WRITTEN(iseq, Qundef, node->nd_lit);
9464 break;
9466 case NODE_STR:{
9467 debugp_param("nd_lit", node->nd_lit);
9468 if (!popped) {
9469 VALUE lit = node->nd_lit;
9470 if (!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
9471 lit = rb_fstring(lit);
9472 ADD_INSN1(ret, node, putstring, lit);
9473 RB_OBJ_WRITTEN(iseq, Qundef, lit);
9475 else {
9476 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
9477 VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
9478 lit = rb_str_dup(lit);
9479 rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
9480 lit = rb_str_freeze(lit);
9482 else {
9483 lit = rb_fstring(lit);
9485 ADD_INSN1(ret, node, putobject, lit);
9486 RB_OBJ_WRITTEN(iseq, Qundef, lit);
9489 break;
9491 case NODE_DSTR:{
9492 compile_dstr(iseq, ret, node);
9494 if (popped) {
9495 ADD_INSN(ret, node, pop);
9497 break;
9499 case NODE_XSTR:{
9500 ADD_CALL_RECEIVER(ret, node);
9501 VALUE str = rb_fstring(node->nd_lit);
9502 ADD_INSN1(ret, node, putobject, str);
9503 RB_OBJ_WRITTEN(iseq, Qundef, str);
9504 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
9506 if (popped) {
9507 ADD_INSN(ret, node, pop);
9509 break;
9511 case NODE_DXSTR:{
9512 ADD_CALL_RECEIVER(ret, node);
9513 compile_dstr(iseq, ret, node);
9514 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
9516 if (popped) {
9517 ADD_INSN(ret, node, pop);
9519 break;
9521 case NODE_EVSTR:
9522 CHECK(compile_evstr(iseq, ret, node->nd_body, popped));
9523 break;
9524 case NODE_DREGX:{
9525 compile_dregx(iseq, ret, node);
9527 if (popped) {
9528 ADD_INSN(ret, node, pop);
9530 break;
9532 case NODE_ONCE:{
9533 int ic_index = body->is_size++;
9534 const rb_iseq_t *block_iseq;
9535 block_iseq = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
9537 ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
9538 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq);
9540 if (popped) {
9541 ADD_INSN(ret, node, pop);
9543 break;
9545 case NODE_ARGSCAT:{
9546 if (popped) {
9547 CHECK(COMPILE(ret, "argscat head", node->nd_head));
9548 ADD_INSN1(ret, node, splatarray, Qfalse);
9549 ADD_INSN(ret, node, pop);
9550 CHECK(COMPILE(ret, "argscat body", node->nd_body));
9551 ADD_INSN1(ret, node, splatarray, Qfalse);
9552 ADD_INSN(ret, node, pop);
9554 else {
9555 CHECK(COMPILE(ret, "argscat head", node->nd_head));
9556 CHECK(COMPILE(ret, "argscat body", node->nd_body));
9557 ADD_INSN(ret, node, concatarray);
9559 break;
9561 case NODE_ARGSPUSH:{
9562 if (popped) {
9563 CHECK(COMPILE(ret, "argspush head", node->nd_head));
9564 ADD_INSN1(ret, node, splatarray, Qfalse);
9565 ADD_INSN(ret, node, pop);
9566 CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
9568 else {
9569 CHECK(COMPILE(ret, "argspush head", node->nd_head));
9570 CHECK(compile_array_1(iseq, ret, node->nd_body));
9571 ADD_INSN(ret, node, concatarray);
9573 break;
9575 case NODE_SPLAT:{
9576 CHECK(COMPILE(ret, "splat", node->nd_head));
9577 ADD_INSN1(ret, node, splatarray, Qtrue);
9579 if (popped) {
9580 ADD_INSN(ret, node, pop);
9582 break;
9584 case NODE_DEFN:{
9585 ID mid = node->nd_mid;
9586 const rb_iseq_t *method_iseq = NEW_ISEQ(node->nd_defn,
9587 rb_id2str(mid),
9588 ISEQ_TYPE_METHOD, line);
9590 debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
9591 ADD_INSN2(ret, node, definemethod, ID2SYM(mid), method_iseq);
9592 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)method_iseq);
9594 if (!popped) {
9595 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
9598 break;
9600 case NODE_DEFS:{
9601 ID mid = node->nd_mid;
9602 const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(node->nd_defn,
9603 rb_id2str(mid),
9604 ISEQ_TYPE_METHOD, line);
9606 debugp_param("defs/iseq", rb_iseqw_new(singleton_method_iseq));
9607 CHECK(COMPILE(ret, "defs: recv", node->nd_recv));
9608 ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq);
9609 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq);
9611 if (!popped) {
9612 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
9614 break;
9616 case NODE_ALIAS:{
9617 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9618 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
9619 CHECK(COMPILE(ret, "alias arg1", node->nd_1st));
9620 CHECK(COMPILE(ret, "alias arg2", node->nd_2nd));
9621 ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
9623 if (popped) {
9624 ADD_INSN(ret, node, pop);
9626 break;
9628 case NODE_VALIAS:{
9629 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9630 ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_alias));
9631 ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_orig));
9632 ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
9634 if (popped) {
9635 ADD_INSN(ret, node, pop);
9637 break;
9639 case NODE_UNDEF:{
9640 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9641 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
9642 CHECK(COMPILE(ret, "undef arg", node->nd_undef));
9643 ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
9645 if (popped) {
9646 ADD_INSN(ret, node, pop);
9648 break;
9650 case NODE_CLASS:{
9651 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(node->nd_body,
9652 rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
9653 ISEQ_TYPE_CLASS, line);
9654 const int flags = VM_DEFINECLASS_TYPE_CLASS |
9655 (node->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
9656 compile_cpath(ret, iseq, node->nd_cpath);
9658 CHECK(COMPILE(ret, "super", node->nd_super));
9659 ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), class_iseq, INT2FIX(flags));
9660 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
9662 if (popped) {
9663 ADD_INSN(ret, node, pop);
9665 break;
9667 case NODE_MODULE:{
9668 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(node->nd_body,
9669 rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
9670 ISEQ_TYPE_CLASS, line);
9671 const int flags = VM_DEFINECLASS_TYPE_MODULE |
9672 compile_cpath(ret, iseq, node->nd_cpath);
9674 ADD_INSN (ret, node, putnil); /* dummy */
9675 ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), module_iseq, INT2FIX(flags));
9676 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq);
9678 if (popped) {
9679 ADD_INSN(ret, node, pop);
9681 break;
9683 case NODE_SCLASS:{
9684 ID singletonclass;
9685 const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_fstring_lit("singleton class"),
9686 ISEQ_TYPE_CLASS, line);
9688 CHECK(COMPILE(ret, "sclass#recv", node->nd_recv));
9689 ADD_INSN (ret, node, putnil);
9690 CONST_ID(singletonclass, "singletonclass");
9691 ADD_INSN3(ret, node, defineclass,
9692 ID2SYM(singletonclass), singleton_class,
9693 INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
9694 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_class);
9696 if (popped) {
9697 ADD_INSN(ret, node, pop);
9699 break;
9701 case NODE_COLON2:
9702 CHECK(compile_colon2(iseq, ret, node, popped));
9703 break;
9704 case NODE_COLON3:
9705 CHECK(compile_colon3(iseq, ret, node, popped));
9706 break;
9707 case NODE_DOT2:
9708 CHECK(compile_dots(iseq, ret, node, popped, FALSE));
9709 break;
9710 case NODE_DOT3:
9711 CHECK(compile_dots(iseq, ret, node, popped, TRUE));
9712 break;
9713 case NODE_FLIP2:
9714 case NODE_FLIP3:{
9715 LABEL *lend = NEW_LABEL(line);
9716 LABEL *ltrue = NEW_LABEL(line);
9717 LABEL *lfalse = NEW_LABEL(line);
9718 CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
9719 ltrue, lfalse));
9720 ADD_LABEL(ret, ltrue);
9721 ADD_INSN1(ret, node, putobject, Qtrue);
9722 ADD_INSNL(ret, node, jump, lend);
9723 ADD_LABEL(ret, lfalse);
9724 ADD_INSN1(ret, node, putobject, Qfalse);
9725 ADD_LABEL(ret, lend);
9726 break;
9728 case NODE_SELF:{
9729 if (!popped) {
9730 ADD_INSN(ret, node, putself);
9732 break;
9734 case NODE_NIL:{
9735 if (!popped) {
9736 ADD_INSN(ret, node, putnil);
9738 break;
9740 case NODE_TRUE:{
9741 if (!popped) {
9742 ADD_INSN1(ret, node, putobject, Qtrue);
9744 break;
9746 case NODE_FALSE:{
9747 if (!popped) {
9748 ADD_INSN1(ret, node, putobject, Qfalse);
9750 break;
9752 case NODE_ERRINFO:
9753 CHECK(compile_errinfo(iseq, ret, node, popped));
9754 break;
9755 case NODE_DEFINED:
9756 if (!popped) {
9757 CHECK(compile_defined_expr(iseq, ret, node, Qtrue));
9759 break;
9760 case NODE_POSTEXE:{
9761 /* compiled to:
9762 * ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
9764 int is_index = body->is_size++;
9765 struct rb_iseq_new_with_callback_callback_func *ifunc =
9766 rb_iseq_new_with_callback_new_callback(build_postexe_iseq, node->nd_body);
9767 const rb_iseq_t *once_iseq =
9768 new_child_iseq_with_callback(iseq, ifunc,
9769 rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line);
9771 ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index));
9772 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)once_iseq);
9774 if (popped) {
9775 ADD_INSN(ret, node, pop);
9777 break;
9779 case NODE_KW_ARG:
9780 CHECK(compile_kw_arg(iseq, ret, node, popped));
9781 break;
9782 case NODE_DSYM:{
9783 compile_dstr(iseq, ret, node);
9784 if (!popped) {
9785 ADD_INSN(ret, node, intern);
9787 else {
9788 ADD_INSN(ret, node, pop);
9790 break;
9792 case NODE_ATTRASGN:
9793 CHECK(compile_attrasgn(iseq, ret, node, popped));
9794 break;
9795 case NODE_LAMBDA:{
9796 /* compile same as lambda{...} */
9797 const rb_iseq_t *block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
9798 VALUE argc = INT2FIX(0);
9800 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9801 ADD_CALL_WITH_BLOCK(ret, node, idLambda, argc, block);
9802 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
9804 if (popped) {
9805 ADD_INSN(ret, node, pop);
9807 break;
9809 default:
9810 UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
9812 debug_node_end();
9813 return COMPILE_NG;
9816 debug_node_end();
9817 return COMPILE_OK;
9820 /***************************/
9821 /* instruction information */
9822 /***************************/
9824 static int
9825 insn_data_length(INSN *iobj)
9827 return insn_len(iobj->insn_id);
9830 static int
9831 calc_sp_depth(int depth, INSN *insn)
9833 return comptime_insn_stack_increase(depth, insn->insn_id, insn->operands);
9836 static VALUE
9837 opobj_inspect(VALUE obj)
9839 if (!SPECIAL_CONST_P(obj) && !RBASIC_CLASS(obj)) {
9840 switch (BUILTIN_TYPE(obj)) {
9841 case T_STRING:
9842 obj = rb_str_new_cstr(RSTRING_PTR(obj));
9843 break;
9844 case T_ARRAY:
9845 obj = rb_ary_dup(obj);
9846 break;
9847 default:
9848 break;
9851 return rb_inspect(obj);
9856 static VALUE
9857 insn_data_to_s_detail(INSN *iobj)
9859 VALUE str = rb_sprintf("%-20s ", insn_name(iobj->insn_id));
9861 if (iobj->operands) {
9862 const char *types = insn_op_types(iobj->insn_id);
9863 int j;
9865 for (j = 0; types[j]; j++) {
9866 char type = types[j];
9868 switch (type) {
9869 case TS_OFFSET: /* label(destination position) */
9871 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
9872 rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
9873 break;
9875 break;
9876 case TS_ISEQ: /* iseq */
9878 rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
9879 VALUE val = Qnil;
9880 if (0 && iseq) { /* TODO: invalidate now */
9881 val = (VALUE)iseq;
9883 rb_str_concat(str, opobj_inspect(val));
9885 break;
9886 case TS_LINDEX:
9887 case TS_NUM: /* ulong */
9888 case TS_VALUE: /* VALUE */
9890 VALUE v = OPERAND_AT(iobj, j);
9891 if (!CLASS_OF(v))
9892 rb_str_cat2(str, "<hidden>");
9893 else {
9894 rb_str_concat(str, opobj_inspect(v));
9896 break;
9898 case TS_ID: /* ID */
9899 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
9900 break;
9901 case TS_IC: /* inline cache */
9902 case TS_IVC: /* inline ivar cache */
9903 case TS_ISE: /* inline storage entry */
9904 rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
9905 break;
9906 case TS_CALLDATA: /* we store these as call infos at compile time */
9908 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, j);
9909 rb_str_cat2(str, "<calldata:");
9910 if (vm_ci_mid(ci)) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(vm_ci_mid(ci)));
9911 rb_str_catf(str, ", %d>", vm_ci_argc(ci));
9912 break;
9914 case TS_CDHASH: /* case/when condition cache */
9915 rb_str_cat2(str, "<ch>");
9916 break;
9917 case TS_FUNCPTR:
9919 void *func = (void *)OPERAND_AT(iobj, j);
9920 #ifdef HAVE_DLADDR
9921 Dl_info info;
9922 if (dladdr(func, &info) && info.dli_sname) {
9923 rb_str_cat2(str, info.dli_sname);
9924 break;
9926 #endif
9927 rb_str_catf(str, "<%p>", func);
9929 break;
9930 case TS_BUILTIN:
9931 rb_str_cat2(str, "<TS_BUILTIN>");
9932 break;
9933 default:{
9934 rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
9937 if (types[j + 1]) {
9938 rb_str_cat2(str, ", ");
9942 return str;
9945 static void
9946 dump_disasm_list(const LINK_ELEMENT *link)
9948 dump_disasm_list_with_cursor(link, NULL, NULL);
9951 static void
9952 dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest)
9954 int pos = 0;
9955 INSN *iobj;
9956 LABEL *lobj;
9957 VALUE str;
9959 printf("-- raw disasm--------\n");
9961 while (link) {
9962 if (curr) printf(curr == link ? "*" : " ");
9963 switch (link->type) {
9964 case ISEQ_ELEMENT_INSN:
9966 iobj = (INSN *)link;
9967 str = insn_data_to_s_detail(iobj);
9968 printf(" %04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
9969 pos += insn_data_length(iobj);
9970 break;
9972 case ISEQ_ELEMENT_LABEL:
9974 lobj = (LABEL *)link;
9975 printf(LABEL_FORMAT" [sp: %d]%s\n", lobj->label_no, lobj->sp,
9976 dest == lobj ? " <---" : "");
9977 break;
9979 case ISEQ_ELEMENT_TRACE:
9981 TRACE *trace = (TRACE *)link;
9982 printf(" trace: %0x\n", trace->event);
9983 break;
9985 case ISEQ_ELEMENT_ADJUST:
9987 ADJUST *adjust = (ADJUST *)link;
9988 printf(" adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
9989 break;
9991 default:
9992 /* ignore */
9993 rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type));
9995 link = link->next;
9997 printf("---------------------\n");
9998 fflush(stdout);
10001 const char *
10002 rb_insns_name(int i)
10004 return insn_name(i);
10007 VALUE
10008 rb_insns_name_array(void)
10010 VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
10011 int i;
10012 for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
10013 rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
10015 return rb_obj_freeze(ary);
10018 static LABEL *
10019 register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
10021 LABEL *label = 0;
10022 st_data_t tmp;
10023 obj = rb_to_symbol_type(obj);
10025 if (st_lookup(labels_table, obj, &tmp) == 0) {
10026 label = NEW_LABEL(0);
10027 st_insert(labels_table, obj, (st_data_t)label);
10029 else {
10030 label = (LABEL *)tmp;
10032 LABEL_REF(label);
10033 return label;
10036 static VALUE
10037 get_exception_sym2type(VALUE sym)
10039 static VALUE symRescue, symEnsure, symRetry;
10040 static VALUE symBreak, symRedo, symNext;
10042 if (symRescue == 0) {
10043 symRescue = ID2SYM(rb_intern_const("rescue"));
10044 symEnsure = ID2SYM(rb_intern_const("ensure"));
10045 symRetry = ID2SYM(rb_intern_const("retry"));
10046 symBreak = ID2SYM(rb_intern_const("break"));
10047 symRedo = ID2SYM(rb_intern_const("redo"));
10048 symNext = ID2SYM(rb_intern_const("next"));
10051 if (sym == symRescue) return CATCH_TYPE_RESCUE;
10052 if (sym == symEnsure) return CATCH_TYPE_ENSURE;
10053 if (sym == symRetry) return CATCH_TYPE_RETRY;
10054 if (sym == symBreak) return CATCH_TYPE_BREAK;
10055 if (sym == symRedo) return CATCH_TYPE_REDO;
10056 if (sym == symNext) return CATCH_TYPE_NEXT;
10057 rb_raise(rb_eSyntaxError, "invalid exception symbol: %+"PRIsVALUE, sym);
10058 return 0;
10061 static int
10062 iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
10063 VALUE exception)
10065 int i;
10067 for (i=0; i<RARRAY_LEN(exception); i++) {
10068 const rb_iseq_t *eiseq;
10069 VALUE v, type;
10070 LABEL *lstart, *lend, *lcont;
10071 unsigned int sp;
10073 v = rb_to_array_type(RARRAY_AREF(exception, i));
10074 if (RARRAY_LEN(v) != 6) {
10075 rb_raise(rb_eSyntaxError, "wrong exception entry");
10077 type = get_exception_sym2type(RARRAY_AREF(v, 0));
10078 if (NIL_P(RARRAY_AREF(v, 1))) {
10079 eiseq = NULL;
10081 else {
10082 eiseq = rb_iseqw_to_iseq(rb_iseq_load(RARRAY_AREF(v, 1), (VALUE)iseq, Qnil));
10085 lstart = register_label(iseq, labels_table, RARRAY_AREF(v, 2));
10086 lend = register_label(iseq, labels_table, RARRAY_AREF(v, 3));
10087 lcont = register_label(iseq, labels_table, RARRAY_AREF(v, 4));
10088 sp = NUM2UINT(RARRAY_AREF(v, 5));
10090 /* TODO: Dirty Hack! Fix me */
10091 if (type == CATCH_TYPE_RESCUE ||
10092 type == CATCH_TYPE_BREAK ||
10093 type == CATCH_TYPE_NEXT) {
10094 ++sp;
10097 lcont->sp = sp;
10099 ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
10101 RB_GC_GUARD(v);
10103 return COMPILE_OK;
10106 static struct st_table *
10107 insn_make_insn_table(void)
10109 struct st_table *table;
10110 int i;
10111 table = st_init_numtable_with_size(VM_INSTRUCTION_SIZE);
10113 for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
10114 st_insert(table, ID2SYM(rb_intern_const(insn_name(i))), i);
10117 return table;
10120 static const rb_iseq_t *
10121 iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
10123 VALUE iseqw;
10124 const rb_iseq_t *loaded_iseq;
10126 if (RB_TYPE_P(op, T_ARRAY)) {
10127 iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
10129 else if (CLASS_OF(op) == rb_cISeq) {
10130 iseqw = op;
10132 else {
10133 rb_raise(rb_eSyntaxError, "ISEQ is required");
10136 loaded_iseq = rb_iseqw_to_iseq(iseqw);
10137 return loaded_iseq;
10140 static VALUE
10141 iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
10143 ID mid = 0;
10144 int orig_argc = 0;
10145 unsigned int flag = 0;
10146 struct rb_callinfo_kwarg *kw_arg = 0;
10148 if (!NIL_P(op)) {
10149 VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern_const("mid")));
10150 VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern_const("flag")));
10151 VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern_const("orig_argc")));
10152 VALUE vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern_const("kw_arg")));
10154 if (!NIL_P(vmid)) mid = SYM2ID(vmid);
10155 if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
10156 if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
10158 if (!NIL_P(vkw_arg)) {
10159 int i;
10160 int len = RARRAY_LENINT(vkw_arg);
10161 size_t n = rb_callinfo_kwarg_bytes(len);
10163 kw_arg = xmalloc(n);
10164 kw_arg->keyword_len = len;
10165 for (i = 0; i < len; i++) {
10166 VALUE kw = RARRAY_AREF(vkw_arg, i);
10167 SYM2ID(kw); /* make immortal */
10168 kw_arg->keywords[i] = kw;
10173 const struct rb_callinfo *ci = new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
10174 RB_OBJ_WRITTEN(iseq, Qundef, ci);
10175 return (VALUE)ci;
10178 static rb_event_flag_t
10179 event_name_to_flag(VALUE sym)
10181 #define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern_const(#ev))) return ev;
10182 CHECK_EVENT(RUBY_EVENT_LINE);
10183 CHECK_EVENT(RUBY_EVENT_CLASS);
10184 CHECK_EVENT(RUBY_EVENT_END);
10185 CHECK_EVENT(RUBY_EVENT_CALL);
10186 CHECK_EVENT(RUBY_EVENT_RETURN);
10187 CHECK_EVENT(RUBY_EVENT_B_CALL);
10188 CHECK_EVENT(RUBY_EVENT_B_RETURN);
10189 #undef CHECK_EVENT
10190 return RUBY_EVENT_NONE;
10193 static int
10194 iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
10195 VALUE body, VALUE node_ids, VALUE labels_wrapper)
10197 /* TODO: body should be frozen */
10198 long i, len = RARRAY_LEN(body);
10199 struct st_table *labels_table = DATA_PTR(labels_wrapper);
10200 int j;
10201 int line_no = 0, node_id = -1, insn_idx = 0;
10202 int ret = COMPILE_OK;
10205 * index -> LABEL *label
10207 static struct st_table *insn_table;
10209 if (insn_table == 0) {
10210 insn_table = insn_make_insn_table();
10213 for (i=0; i<len; i++) {
10214 VALUE obj = RARRAY_AREF(body, i);
10216 if (SYMBOL_P(obj)) {
10217 rb_event_flag_t event;
10218 if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
10219 ADD_TRACE(anchor, event);
10221 else {
10222 LABEL *label = register_label(iseq, labels_table, obj);
10223 ADD_LABEL(anchor, label);
10226 else if (FIXNUM_P(obj)) {
10227 line_no = NUM2INT(obj);
10229 else if (RB_TYPE_P(obj, T_ARRAY)) {
10230 VALUE *argv = 0;
10231 int argc = RARRAY_LENINT(obj) - 1;
10232 st_data_t insn_id;
10233 VALUE insn;
10235 if (node_ids) {
10236 node_id = NUM2INT(rb_ary_entry(node_ids, insn_idx++));
10239 insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
10240 if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
10241 /* TODO: exception */
10242 COMPILE_ERROR(iseq, line_no,
10243 "unknown instruction: %+"PRIsVALUE, insn);
10244 ret = COMPILE_NG;
10245 break;
10248 if (argc != insn_len((VALUE)insn_id)-1) {
10249 COMPILE_ERROR(iseq, line_no,
10250 "operand size mismatch");
10251 ret = COMPILE_NG;
10252 break;
10255 if (argc > 0) {
10256 argv = compile_data_calloc2(iseq, sizeof(VALUE), argc);
10258 // add element before operand setup to make GC root
10259 NODE dummy_line_node = generate_dummy_line_node(line_no, node_id);
10260 ADD_ELEM(anchor,
10261 (LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node,
10262 (enum ruby_vminsn_type)insn_id, argc, argv));
10264 for (j=0; j<argc; j++) {
10265 VALUE op = rb_ary_entry(obj, j+1);
10266 switch (insn_op_type((VALUE)insn_id, j)) {
10267 case TS_OFFSET: {
10268 LABEL *label = register_label(iseq, labels_table, op);
10269 argv[j] = (VALUE)label;
10270 break;
10272 case TS_LINDEX:
10273 case TS_NUM:
10274 (void)NUM2INT(op);
10275 argv[j] = op;
10276 break;
10277 case TS_VALUE:
10278 argv[j] = op;
10279 RB_OBJ_WRITTEN(iseq, Qundef, op);
10280 break;
10281 case TS_ISEQ:
10283 if (op != Qnil) {
10284 VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
10285 argv[j] = v;
10286 RB_OBJ_WRITTEN(iseq, Qundef, v);
10288 else {
10289 argv[j] = 0;
10292 break;
10293 case TS_ISE:
10294 case TS_IC:
10295 case TS_IVC: /* inline ivar cache */
10296 argv[j] = op;
10297 if (NUM2UINT(op) >= iseq->body->is_size) {
10298 iseq->body->is_size = NUM2INT(op) + 1;
10300 FL_SET((VALUE)iseq, ISEQ_MARKABLE_ISEQ);
10301 break;
10302 case TS_CALLDATA:
10303 argv[j] = iseq_build_callinfo_from_hash(iseq, op);
10304 break;
10305 case TS_ID:
10306 argv[j] = rb_to_symbol_type(op);
10307 break;
10308 case TS_CDHASH:
10310 int i;
10311 VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
10313 RHASH_TBL_RAW(map)->type = &cdhash_type;
10314 op = rb_to_array_type(op);
10315 for (i=0; i<RARRAY_LEN(op); i+=2) {
10316 VALUE key = RARRAY_AREF(op, i);
10317 VALUE sym = RARRAY_AREF(op, i+1);
10318 LABEL *label =
10319 register_label(iseq, labels_table, sym);
10320 rb_hash_aset(map, key, (VALUE)label | 1);
10322 RB_GC_GUARD(op);
10323 argv[j] = map;
10324 RB_OBJ_WRITTEN(iseq, Qundef, map);
10326 break;
10327 case TS_FUNCPTR:
10329 #if SIZEOF_VALUE <= SIZEOF_LONG
10330 long funcptr = NUM2LONG(op);
10331 #else
10332 LONG_LONG funcptr = NUM2LL(op);
10333 #endif
10334 argv[j] = (VALUE)funcptr;
10336 break;
10337 default:
10338 rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
10342 else {
10343 NODE dummy_line_node = generate_dummy_line_node(line_no, node_id);
10344 ADD_ELEM(anchor,
10345 (LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node,
10346 (enum ruby_vminsn_type)insn_id, argc, NULL));
10349 else {
10350 rb_raise(rb_eTypeError, "unexpected object for instruction");
10353 DATA_PTR(labels_wrapper) = 0;
10354 validate_labels(iseq, labels_table);
10355 if (!ret) return ret;
10356 return iseq_setup(iseq, anchor);
10359 #define CHECK_ARRAY(v) rb_to_array_type(v)
10360 #define CHECK_SYMBOL(v) rb_to_symbol_type(v)
10362 static int
10363 int_param(int *dst, VALUE param, VALUE sym)
10365 VALUE val = rb_hash_aref(param, sym);
10366 if (FIXNUM_P(val)) {
10367 *dst = FIX2INT(val);
10368 return TRUE;
10370 else if (!NIL_P(val)) {
10371 rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
10372 sym, val);
10374 return FALSE;
10377 static const struct rb_iseq_param_keyword *
10378 iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
10380 int i, j;
10381 int len = RARRAY_LENINT(keywords);
10382 int default_len;
10383 VALUE key, sym, default_val;
10384 VALUE *dvs;
10385 ID *ids;
10386 struct rb_iseq_param_keyword *keyword = ZALLOC(struct rb_iseq_param_keyword);
10388 iseq->body->param.flags.has_kw = TRUE;
10390 keyword->num = len;
10391 #define SYM(s) ID2SYM(rb_intern_const(#s))
10392 (void)int_param(&keyword->bits_start, params, SYM(kwbits));
10393 i = keyword->bits_start - keyword->num;
10394 ids = (ID *)&iseq->body->local_table[i];
10395 #undef SYM
10397 /* required args */
10398 for (i = 0; i < len; i++) {
10399 VALUE val = RARRAY_AREF(keywords, i);
10401 if (!SYMBOL_P(val)) {
10402 goto default_values;
10404 ids[i] = SYM2ID(val);
10405 keyword->required_num++;
10408 default_values: /* note: we intentionally preserve `i' from previous loop */
10409 default_len = len - i;
10410 if (default_len == 0) {
10411 keyword->table = ids;
10412 return keyword;
10414 else if (default_len < 0) {
10415 UNREACHABLE;
10418 dvs = ALLOC_N(VALUE, (unsigned int)default_len);
10420 for (j = 0; i < len; i++, j++) {
10421 key = RARRAY_AREF(keywords, i);
10422 CHECK_ARRAY(key);
10424 switch (RARRAY_LEN(key)) {
10425 case 1:
10426 sym = RARRAY_AREF(key, 0);
10427 default_val = Qundef;
10428 break;
10429 case 2:
10430 sym = RARRAY_AREF(key, 0);
10431 default_val = RARRAY_AREF(key, 1);
10432 break;
10433 default:
10434 rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
10436 ids[i] = SYM2ID(sym);
10437 dvs[j] = default_val;
10440 keyword->table = ids;
10441 keyword->default_values = dvs;
10443 return keyword;
10446 void
10447 rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage)
10449 INSN *iobj = 0;
10450 size_t size = sizeof(INSN);
10451 unsigned int pos = 0;
10453 while (storage) {
10454 #ifdef STRICT_ALIGNMENT
10455 size_t padding = calc_padding((void *)&storage->buff[pos], size);
10456 #else
10457 const size_t padding = 0; /* expected to be optimized by compiler */
10458 #endif /* STRICT_ALIGNMENT */
10459 size_t offset = pos + size + padding;
10460 if (offset > storage->size || offset > storage->pos) {
10461 pos = 0;
10462 storage = storage->next;
10464 else {
10465 #ifdef STRICT_ALIGNMENT
10466 pos += (int)padding;
10467 #endif /* STRICT_ALIGNMENT */
10469 iobj = (INSN *)&storage->buff[pos];
10471 if (iobj->operands) {
10472 int j;
10473 const char *types = insn_op_types(iobj->insn_id);
10475 for (j = 0; types[j]; j++) {
10476 char type = types[j];
10477 switch (type) {
10478 case TS_CDHASH:
10479 case TS_ISEQ:
10480 case TS_VALUE:
10481 case TS_CALLDATA: // ci is stored.
10483 VALUE op = OPERAND_AT(iobj, j);
10485 if (!SPECIAL_CONST_P(op)) {
10486 rb_gc_mark(op);
10489 break;
10490 default:
10491 break;
10495 pos += (int)size;
10500 void
10501 rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
10502 VALUE exception, VALUE body)
10504 #define SYM(s) ID2SYM(rb_intern_const(#s))
10505 int i, len;
10506 unsigned int arg_size, local_size, stack_max;
10507 ID *tbl;
10508 struct st_table *labels_table = st_init_numtable();
10509 VALUE labels_wrapper = Data_Wrap_Struct(0, rb_mark_set, st_free_table, labels_table);
10510 VALUE arg_opt_labels = rb_hash_aref(params, SYM(opt));
10511 VALUE keywords = rb_hash_aref(params, SYM(keyword));
10512 VALUE sym_arg_rest = ID2SYM(rb_intern_const("#arg_rest"));
10513 DECL_ANCHOR(anchor);
10514 INIT_ANCHOR(anchor);
10516 len = RARRAY_LENINT(locals);
10517 iseq->body->local_table_size = len;
10518 iseq->body->local_table = tbl = len > 0 ? (ID *)ALLOC_N(ID, iseq->body->local_table_size) : NULL;
10520 for (i = 0; i < len; i++) {
10521 VALUE lv = RARRAY_AREF(locals, i);
10523 if (sym_arg_rest == lv) {
10524 tbl[i] = 0;
10526 else {
10527 tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
10531 #define INT_PARAM(F) int_param(&iseq->body->param.F, params, SYM(F))
10532 if (INT_PARAM(lead_num)) {
10533 iseq->body->param.flags.has_lead = TRUE;
10535 if (INT_PARAM(post_num)) iseq->body->param.flags.has_post = TRUE;
10536 if (INT_PARAM(post_start)) iseq->body->param.flags.has_post = TRUE;
10537 if (INT_PARAM(rest_start)) iseq->body->param.flags.has_rest = TRUE;
10538 if (INT_PARAM(block_start)) iseq->body->param.flags.has_block = TRUE;
10539 #undef INT_PARAM
10541 #define INT_PARAM(F) F = (int_param(&x, misc, SYM(F)) ? (unsigned int)x : 0)
10542 int x;
10543 INT_PARAM(arg_size);
10544 INT_PARAM(local_size);
10545 INT_PARAM(stack_max);
10546 #undef INT_PARAM
10549 VALUE node_ids = Qfalse;
10550 #ifdef USE_ISEQ_NODE_ID
10551 node_ids = rb_hash_aref(misc, ID2SYM(rb_intern("node_ids")));
10552 if (!RB_TYPE_P(node_ids, T_ARRAY)) {
10553 rb_raise(rb_eTypeError, "node_ids is not an array");
10555 #endif
10557 if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
10558 len = RARRAY_LENINT(arg_opt_labels);
10559 iseq->body->param.flags.has_opt = !!(len - 1 >= 0);
10561 if (iseq->body->param.flags.has_opt) {
10562 VALUE *opt_table = ALLOC_N(VALUE, len);
10564 for (i = 0; i < len; i++) {
10565 VALUE ent = RARRAY_AREF(arg_opt_labels, i);
10566 LABEL *label = register_label(iseq, labels_table, ent);
10567 opt_table[i] = (VALUE)label;
10570 iseq->body->param.opt_num = len - 1;
10571 iseq->body->param.opt_table = opt_table;
10574 else if (!NIL_P(arg_opt_labels)) {
10575 rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
10576 arg_opt_labels);
10579 if (RB_TYPE_P(keywords, T_ARRAY)) {
10580 iseq->body->param.keyword = iseq_build_kw(iseq, params, keywords);
10582 else if (!NIL_P(keywords)) {
10583 rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
10584 keywords);
10587 if (Qtrue == rb_hash_aref(params, SYM(ambiguous_param0))) {
10588 iseq->body->param.flags.ambiguous_param0 = TRUE;
10591 if (int_param(&i, params, SYM(kwrest))) {
10592 struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)iseq->body->param.keyword;
10593 if (keyword == NULL) {
10594 iseq->body->param.keyword = keyword = ZALLOC(struct rb_iseq_param_keyword);
10596 keyword->rest_start = i;
10597 iseq->body->param.flags.has_kwrest = TRUE;
10599 #undef SYM
10600 iseq_calc_param_size(iseq);
10602 /* exception */
10603 iseq_build_from_ary_exception(iseq, labels_table, exception);
10605 /* body */
10606 iseq_build_from_ary_body(iseq, anchor, body, node_ids, labels_wrapper);
10608 iseq->body->param.size = arg_size;
10609 iseq->body->local_table_size = local_size;
10610 iseq->body->stack_max = stack_max;
10613 /* for parser */
10616 rb_dvar_defined(ID id, const rb_iseq_t *iseq)
10618 if (iseq) {
10619 const struct rb_iseq_constant_body *body = iseq->body;
10620 while (body->type == ISEQ_TYPE_BLOCK ||
10621 body->type == ISEQ_TYPE_RESCUE ||
10622 body->type == ISEQ_TYPE_ENSURE ||
10623 body->type == ISEQ_TYPE_EVAL ||
10624 body->type == ISEQ_TYPE_MAIN
10626 unsigned int i;
10628 for (i = 0; i < body->local_table_size; i++) {
10629 if (body->local_table[i] == id) {
10630 return 1;
10633 iseq = body->parent_iseq;
10634 body = iseq->body;
10637 return 0;
10641 rb_local_defined(ID id, const rb_iseq_t *iseq)
10643 if (iseq) {
10644 unsigned int i;
10645 const struct rb_iseq_constant_body *const body = iseq->body->local_iseq->body;
10647 for (i=0; i<body->local_table_size; i++) {
10648 if (body->local_table[i] == id) {
10649 return 1;
10653 return 0;
10656 /* ISeq binary format */
10658 #ifndef IBF_ISEQ_DEBUG
10659 #define IBF_ISEQ_DEBUG 0
10660 #endif
10662 #ifndef IBF_ISEQ_ENABLE_LOCAL_BUFFER
10663 #define IBF_ISEQ_ENABLE_LOCAL_BUFFER 0
10664 #endif
10666 typedef unsigned int ibf_offset_t;
10667 #define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
10669 #define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
10670 #if RUBY_DEVEL
10671 #define IBF_DEVEL_VERSION 3
10672 #define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
10673 #else
10674 #define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
10675 #endif
10677 struct ibf_header {
10678 char magic[4]; /* YARB */
10679 unsigned int major_version;
10680 unsigned int minor_version;
10681 unsigned int size;
10682 unsigned int extra_size;
10684 unsigned int iseq_list_size;
10685 unsigned int global_object_list_size;
10686 ibf_offset_t iseq_list_offset;
10687 ibf_offset_t global_object_list_offset;
10690 struct ibf_dump_buffer {
10691 VALUE str;
10692 st_table *obj_table; /* obj -> obj number */
10695 struct ibf_dump {
10696 st_table *iseq_table; /* iseq -> iseq number */
10697 struct ibf_dump_buffer global_buffer;
10698 struct ibf_dump_buffer *current_buffer;
10701 rb_iseq_t * iseq_alloc(void);
10703 struct ibf_load_buffer {
10704 const char *buff;
10705 ibf_offset_t size;
10707 VALUE obj_list; /* [obj0, ...] */
10708 unsigned int obj_list_size;
10709 ibf_offset_t obj_list_offset;
10712 struct ibf_load {
10713 const struct ibf_header *header;
10714 VALUE iseq_list; /* [iseq0, ...] */
10715 struct ibf_load_buffer global_buffer;
10716 VALUE loader_obj;
10717 rb_iseq_t *iseq;
10718 VALUE str;
10719 struct ibf_load_buffer *current_buffer;
10722 struct pinned_list {
10723 long size;
10724 VALUE * buffer;
10727 static void
10728 pinned_list_mark(void *ptr)
10730 long i;
10731 struct pinned_list *list = (struct pinned_list *)ptr;
10732 for (i = 0; i < list->size; i++) {
10733 if (list->buffer[i]) {
10734 rb_gc_mark(list->buffer[i]);
10739 static void
10740 pinned_list_free(void *ptr)
10742 struct pinned_list *list = (struct pinned_list *)ptr;
10743 xfree(list->buffer);
10744 xfree(ptr);
10747 static size_t
10748 pinned_list_memsize(const void *ptr)
10750 struct pinned_list *list = (struct pinned_list *)ptr;
10751 return sizeof(struct pinned_list) + (list->size * sizeof(VALUE *));
10754 static const rb_data_type_t pinned_list_type = {
10755 "pinned_list",
10756 {pinned_list_mark, pinned_list_free, pinned_list_memsize,},
10757 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
10760 static VALUE
10761 pinned_list_fetch(VALUE list, long offset)
10763 struct pinned_list * ptr;
10765 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
10767 if (offset >= ptr->size) {
10768 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
10771 return ptr->buffer[offset];
10774 static void
10775 pinned_list_store(VALUE list, long offset, VALUE object)
10777 struct pinned_list * ptr;
10779 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
10781 if (offset >= ptr->size) {
10782 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
10785 RB_OBJ_WRITE(list, &ptr->buffer[offset], object);
10788 static VALUE
10789 pinned_list_new(long size)
10791 struct pinned_list * ptr;
10792 VALUE obj_list =
10793 TypedData_Make_Struct(0, struct pinned_list, &pinned_list_type, ptr);
10795 ptr->buffer = xcalloc(size, sizeof(VALUE));
10796 ptr->size = size;
10798 return obj_list;
10801 static ibf_offset_t
10802 ibf_dump_pos(struct ibf_dump *dump)
10804 long pos = RSTRING_LEN(dump->current_buffer->str);
10805 #if SIZEOF_LONG > SIZEOF_INT
10806 if (pos >= UINT_MAX) {
10807 rb_raise(rb_eRuntimeError, "dump size exceeds");
10809 #endif
10810 return (unsigned int)pos;
10813 static void
10814 ibf_dump_align(struct ibf_dump *dump, size_t align)
10816 ibf_offset_t pos = ibf_dump_pos(dump);
10817 if (pos % align) {
10818 static const char padding[sizeof(VALUE)];
10819 size_t size = align - ((size_t)pos % align);
10820 #if SIZEOF_LONG > SIZEOF_INT
10821 if (pos + size >= UINT_MAX) {
10822 rb_raise(rb_eRuntimeError, "dump size exceeds");
10824 #endif
10825 for (; size > sizeof(padding); size -= sizeof(padding)) {
10826 rb_str_cat(dump->current_buffer->str, padding, sizeof(padding));
10828 rb_str_cat(dump->current_buffer->str, padding, size);
10832 static ibf_offset_t
10833 ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size)
10835 ibf_offset_t pos = ibf_dump_pos(dump);
10836 rb_str_cat(dump->current_buffer->str, (const char *)buff, size);
10837 /* TODO: overflow check */
10838 return pos;
10841 static ibf_offset_t
10842 ibf_dump_write_byte(struct ibf_dump *dump, unsigned char byte)
10844 return ibf_dump_write(dump, &byte, sizeof(unsigned char));
10847 static void
10848 ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long offset)
10850 VALUE str = dump->current_buffer->str;
10851 char *ptr = RSTRING_PTR(str);
10852 if ((unsigned long)(size + offset) > (unsigned long)RSTRING_LEN(str))
10853 rb_bug("ibf_dump_overwrite: overflow");
10854 memcpy(ptr + offset, buff, size);
10857 static const void *
10858 ibf_load_ptr(const struct ibf_load *load, ibf_offset_t *offset, int size)
10860 ibf_offset_t beg = *offset;
10861 *offset += size;
10862 return load->current_buffer->buff + beg;
10865 static void *
10866 ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, size_t x, size_t y)
10868 void *buff = ruby_xmalloc2(x, y);
10869 size_t size = x * y;
10870 memcpy(buff, load->current_buffer->buff + offset, size);
10871 return buff;
10874 #define IBF_W_ALIGN(type) (RUBY_ALIGNOF(type) > 1 ? ibf_dump_align(dump, RUBY_ALIGNOF(type)) : (void)0)
10876 #define IBF_W(b, type, n) (IBF_W_ALIGN(type), (type *)(VALUE)IBF_WP(b, type, n))
10877 #define IBF_WV(variable) ibf_dump_write(dump, &(variable), sizeof(variable))
10878 #define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n))
10879 #define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type), (n))
10880 #define IBF_ZERO(variable) memset(&(variable), 0, sizeof(variable))
10882 static int
10883 ibf_table_lookup(struct st_table *table, st_data_t key)
10885 st_data_t val;
10887 if (st_lookup(table, key, &val)) {
10888 return (int)val;
10890 else {
10891 return -1;
10895 static int
10896 ibf_table_find_or_insert(struct st_table *table, st_data_t key)
10898 int index = ibf_table_lookup(table, key);
10900 if (index < 0) { /* not found */
10901 index = (int)table->num_entries;
10902 st_insert(table, key, (st_data_t)index);
10905 return index;
10908 /* dump/load generic */
10910 static void ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size);
10912 static VALUE ibf_load_object(const struct ibf_load *load, VALUE object_index);
10913 static rb_iseq_t *ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq);
10915 static st_table *
10916 ibf_dump_object_table_new(void)
10918 st_table *obj_table = st_init_numtable(); /* need free */
10919 st_insert(obj_table, (st_data_t)Qnil, (st_data_t)0); /* 0th is nil */
10921 return obj_table;
10924 static VALUE
10925 ibf_dump_object(struct ibf_dump *dump, VALUE obj)
10927 return ibf_table_find_or_insert(dump->current_buffer->obj_table, (st_data_t)obj);
10930 static VALUE
10931 ibf_dump_id(struct ibf_dump *dump, ID id)
10933 if (id == 0 || rb_id2name(id) == NULL) {
10934 return 0;
10936 return ibf_dump_object(dump, rb_id2sym(id));
10939 static ID
10940 ibf_load_id(const struct ibf_load *load, const ID id_index)
10942 if (id_index == 0) {
10943 return 0;
10945 VALUE sym = ibf_load_object(load, id_index);
10946 return rb_sym2id(sym);
10949 /* dump/load: code */
10951 static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq);
10953 static int
10954 ibf_dump_iseq(struct ibf_dump *dump, const rb_iseq_t *iseq)
10956 if (iseq == NULL) {
10957 return -1;
10959 else {
10960 return ibf_table_find_or_insert(dump->iseq_table, (st_data_t)iseq);
10964 static unsigned char
10965 ibf_load_byte(const struct ibf_load *load, ibf_offset_t *offset)
10967 if (*offset >= load->current_buffer->size) { rb_raise(rb_eRuntimeError, "invalid bytecode"); }
10968 return (unsigned char)load->current_buffer->buff[(*offset)++];
10972 * Small uint serialization
10973 * 0x00000000_00000000 - 0x00000000_0000007f: 1byte | XXXX XXX1 |
10974 * 0x00000000_00000080 - 0x00000000_00003fff: 2byte | XXXX XX10 | XXXX XXXX |
10975 * 0x00000000_00004000 - 0x00000000_001fffff: 3byte | XXXX X100 | XXXX XXXX | XXXX XXXX |
10976 * 0x00000000_00020000 - 0x00000000_0fffffff: 4byte | XXXX 1000 | XXXX XXXX | XXXX XXXX | XXXX XXXX |
10977 * ...
10978 * 0x00010000_00000000 - 0x00ffffff_ffffffff: 8byte | 1000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
10979 * 0x01000000_00000000 - 0xffffffff_ffffffff: 9byte | 0000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
10981 static void
10982 ibf_dump_write_small_value(struct ibf_dump *dump, VALUE x)
10984 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
10985 ibf_dump_write(dump, &x, sizeof(VALUE));
10986 return;
10989 enum { max_byte_length = sizeof(VALUE) + 1 };
10991 unsigned char bytes[max_byte_length];
10992 ibf_offset_t n;
10994 for (n = 0; n < sizeof(VALUE) && (x >> (7 - n)); n++, x >>= 8) {
10995 bytes[max_byte_length - 1 - n] = (unsigned char)x;
10998 x <<= 1;
10999 x |= 1;
11000 x <<= n;
11001 bytes[max_byte_length - 1 - n] = (unsigned char)x;
11002 n++;
11004 ibf_dump_write(dump, bytes + max_byte_length - n, n);
11007 static VALUE
11008 ibf_load_small_value(const struct ibf_load *load, ibf_offset_t *offset)
11010 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
11011 union { char s[sizeof(VALUE)]; VALUE v; } x;
11013 memcpy(x.s, load->current_buffer->buff + *offset, sizeof(VALUE));
11014 *offset += sizeof(VALUE);
11016 return x.v;
11019 enum { max_byte_length = sizeof(VALUE) + 1 };
11021 const unsigned char *buffer = (const unsigned char *)load->current_buffer->buff;
11022 const unsigned char c = buffer[*offset];
11024 ibf_offset_t n =
11025 c & 1 ? 1 :
11026 c == 0 ? 9 : ntz_int32(c) + 1;
11027 VALUE x = (VALUE)c >> n;
11029 if (*offset + n > load->current_buffer->size) {
11030 rb_raise(rb_eRuntimeError, "invalid byte sequence");
11033 ibf_offset_t i;
11034 for (i = 1; i < n; i++) {
11035 x <<= 8;
11036 x |= (VALUE)buffer[*offset + i];
11039 *offset += n;
11040 return x;
11043 static void
11044 ibf_dump_builtin(struct ibf_dump *dump, const struct rb_builtin_function *bf)
11046 // short: index
11047 // short: name.length
11048 // bytes: name
11049 // // omit argc (only verify with name)
11050 ibf_dump_write_small_value(dump, (VALUE)bf->index);
11052 size_t len = strlen(bf->name);
11053 ibf_dump_write_small_value(dump, (VALUE)len);
11054 ibf_dump_write(dump, bf->name, len);
11057 static const struct rb_builtin_function *
11058 ibf_load_builtin(const struct ibf_load *load, ibf_offset_t *offset)
11060 int i = (int)ibf_load_small_value(load, offset);
11061 int len = (int)ibf_load_small_value(load, offset);
11062 const char *name = (char *)ibf_load_ptr(load, offset, len);
11064 if (0) {
11065 fprintf(stderr, "%.*s!!\n", len, name);
11068 const struct rb_builtin_function *table = GET_VM()->builtin_function_table;
11069 if (table == NULL) rb_raise(rb_eArgError, "builtin function table is not provided");
11070 if (strncmp(table[i].name, name, len) != 0) {
11071 rb_raise(rb_eArgError, "builtin function index (%d) mismatch (expect %s but %s)", i, name, table[i].name);
11073 // fprintf(stderr, "load-builtin: name:%s(%d)\n", table[i].name, table[i].argc);
11075 return &table[i];
11078 static ibf_offset_t
11079 ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
11081 const struct rb_iseq_constant_body *const body = iseq->body;
11082 const int iseq_size = body->iseq_size;
11083 int code_index;
11084 const VALUE *orig_code = rb_iseq_original_iseq(iseq);
11086 ibf_offset_t offset = ibf_dump_pos(dump);
11088 for (code_index=0; code_index<iseq_size;) {
11089 const VALUE insn = orig_code[code_index++];
11090 const char *types = insn_op_types(insn);
11091 int op_index;
11093 /* opcode */
11094 if (insn >= 0x100) { rb_raise(rb_eRuntimeError, "invalid instruction"); }
11095 ibf_dump_write_small_value(dump, insn);
11097 /* operands */
11098 for (op_index=0; types[op_index]; op_index++, code_index++) {
11099 VALUE op = orig_code[code_index];
11100 VALUE wv;
11102 switch (types[op_index]) {
11103 case TS_CDHASH:
11104 case TS_VALUE:
11105 wv = ibf_dump_object(dump, op);
11106 break;
11107 case TS_ISEQ:
11108 wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
11109 break;
11110 case TS_IC:
11111 case TS_IVC:
11112 case TS_ISE:
11114 unsigned int i;
11115 for (i=0; i<body->is_size; i++) {
11116 if (op == (VALUE)&body->is_entries[i]) {
11117 break;
11120 wv = (VALUE)i;
11122 break;
11123 case TS_CALLDATA:
11125 goto skip_wv;
11127 case TS_ID:
11128 wv = ibf_dump_id(dump, (ID)op);
11129 break;
11130 case TS_FUNCPTR:
11131 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
11132 goto skip_wv;
11133 case TS_BUILTIN:
11134 ibf_dump_builtin(dump, (const struct rb_builtin_function *)op);
11135 goto skip_wv;
11136 default:
11137 wv = op;
11138 break;
11140 ibf_dump_write_small_value(dump, wv);
11141 skip_wv:;
11143 assert(insn_len(insn) == op_index+1);
11146 return offset;
11149 static VALUE *
11150 ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecode_offset, ibf_offset_t bytecode_size, unsigned int iseq_size)
11152 VALUE iseqv = (VALUE)iseq;
11153 unsigned int code_index;
11154 ibf_offset_t reading_pos = bytecode_offset;
11155 VALUE *code = ALLOC_N(VALUE, iseq_size);
11157 struct rb_iseq_constant_body *load_body = iseq->body;
11158 struct rb_call_data *cd_entries = load_body->call_data;
11159 union iseq_inline_storage_entry *is_entries = load_body->is_entries;
11161 for (code_index=0; code_index<iseq_size;) {
11162 /* opcode */
11163 const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos);
11164 const unsigned int insn_index = code_index;
11165 const char *types = insn_op_types(insn);
11166 int op_index;
11168 code_index++;
11170 /* operands */
11171 for (op_index=0; types[op_index]; op_index++, code_index++) {
11172 const char operand_type = types[op_index];
11173 switch (operand_type) {
11174 case TS_VALUE:
11176 VALUE op = ibf_load_small_value(load, &reading_pos);
11177 VALUE v = ibf_load_object(load, op);
11178 code[code_index] = v;
11179 if (!SPECIAL_CONST_P(v)) {
11180 RB_OBJ_WRITTEN(iseqv, Qundef, v);
11181 FL_SET(iseqv, ISEQ_MARKABLE_ISEQ);
11183 break;
11185 case TS_CDHASH:
11187 VALUE op = ibf_load_small_value(load, &reading_pos);
11188 VALUE v = ibf_load_object(load, op);
11189 v = rb_hash_dup(v); // hash dumped as frozen
11190 RHASH_TBL_RAW(v)->type = &cdhash_type;
11191 rb_hash_rehash(v); // hash function changed
11192 freeze_hide_obj(v);
11194 // Overwrite the existing hash in the object list. This
11195 // is to keep the object alive during load time.
11196 // [Bug #17984] [ruby-core:104259]
11197 pinned_list_store(load->current_buffer->obj_list, (long)op, v);
11199 code[code_index] = v;
11200 RB_OBJ_WRITTEN(iseqv, Qundef, v);
11201 FL_SET(iseqv, ISEQ_MARKABLE_ISEQ);
11202 break;
11204 case TS_ISEQ:
11206 VALUE op = (VALUE)ibf_load_small_value(load, &reading_pos);
11207 VALUE v = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
11208 code[code_index] = v;
11209 if (!SPECIAL_CONST_P(v)) {
11210 RB_OBJ_WRITTEN(iseqv, Qundef, v);
11211 FL_SET(iseqv, ISEQ_MARKABLE_ISEQ);
11213 break;
11215 case TS_ISE:
11216 case TS_IC:
11217 case TS_IVC:
11219 VALUE op = ibf_load_small_value(load, &reading_pos);
11220 code[code_index] = (VALUE)&is_entries[op];
11222 if (insn == BIN(opt_getinlinecache) && operand_type == TS_IC) {
11223 // Store the instruction index for opt_getinlinecache on the IC for
11224 // YJIT to invalidate code when opt_setinlinecache runs.
11225 is_entries[op].ic_cache.get_insn_idx = insn_index;
11228 FL_SET(iseqv, ISEQ_MARKABLE_ISEQ);
11229 break;
11230 case TS_CALLDATA:
11232 code[code_index] = (VALUE)cd_entries++;
11234 break;
11235 case TS_ID:
11237 VALUE op = ibf_load_small_value(load, &reading_pos);
11238 code[code_index] = ibf_load_id(load, (ID)(VALUE)op);
11240 break;
11241 case TS_FUNCPTR:
11242 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
11243 break;
11244 case TS_BUILTIN:
11245 code[code_index] = (VALUE)ibf_load_builtin(load, &reading_pos);
11246 break;
11247 default:
11248 code[code_index] = ibf_load_small_value(load, &reading_pos);
11249 continue;
11252 if (insn_len(insn) != op_index+1) {
11253 rb_raise(rb_eRuntimeError, "operand size mismatch");
11256 load_body->iseq_encoded = code;
11257 load_body->iseq_size = code_index;
11259 assert(code_index == iseq_size);
11260 assert(reading_pos == bytecode_offset + bytecode_size);
11261 return code;
11264 static ibf_offset_t
11265 ibf_dump_param_opt_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
11267 int opt_num = iseq->body->param.opt_num;
11269 if (opt_num > 0) {
11270 IBF_W_ALIGN(VALUE);
11271 return ibf_dump_write(dump, iseq->body->param.opt_table, sizeof(VALUE) * (opt_num + 1));
11273 else {
11274 return ibf_dump_pos(dump);
11278 static VALUE *
11279 ibf_load_param_opt_table(const struct ibf_load *load, ibf_offset_t opt_table_offset, int opt_num)
11281 if (opt_num > 0) {
11282 VALUE *table = ALLOC_N(VALUE, opt_num+1);
11283 MEMCPY(table, load->current_buffer->buff + opt_table_offset, VALUE, opt_num+1);
11284 return table;
11286 else {
11287 return NULL;
11291 static ibf_offset_t
11292 ibf_dump_param_keyword(struct ibf_dump *dump, const rb_iseq_t *iseq)
11294 const struct rb_iseq_param_keyword *kw = iseq->body->param.keyword;
11296 if (kw) {
11297 struct rb_iseq_param_keyword dump_kw = *kw;
11298 int dv_num = kw->num - kw->required_num;
11299 ID *ids = kw->num > 0 ? ALLOCA_N(ID, kw->num) : NULL;
11300 VALUE *dvs = dv_num > 0 ? ALLOCA_N(VALUE, dv_num) : NULL;
11301 int i;
11303 for (i=0; i<kw->num; i++) ids[i] = (ID)ibf_dump_id(dump, kw->table[i]);
11304 for (i=0; i<dv_num; i++) dvs[i] = (VALUE)ibf_dump_object(dump, kw->default_values[i]);
11306 dump_kw.table = IBF_W(ids, ID, kw->num);
11307 dump_kw.default_values = IBF_W(dvs, VALUE, dv_num);
11308 IBF_W_ALIGN(struct rb_iseq_param_keyword);
11309 return ibf_dump_write(dump, &dump_kw, sizeof(struct rb_iseq_param_keyword) * 1);
11311 else {
11312 return 0;
11316 static const struct rb_iseq_param_keyword *
11317 ibf_load_param_keyword(const struct ibf_load *load, ibf_offset_t param_keyword_offset)
11319 if (param_keyword_offset) {
11320 struct rb_iseq_param_keyword *kw = IBF_R(param_keyword_offset, struct rb_iseq_param_keyword, 1);
11321 ID *ids = IBF_R(kw->table, ID, kw->num);
11322 int dv_num = kw->num - kw->required_num;
11323 VALUE *dvs = IBF_R(kw->default_values, VALUE, dv_num);
11324 int i;
11326 for (i=0; i<kw->num; i++) {
11327 ids[i] = ibf_load_id(load, ids[i]);
11329 for (i=0; i<dv_num; i++) {
11330 dvs[i] = ibf_load_object(load, dvs[i]);
11333 kw->table = ids;
11334 kw->default_values = dvs;
11335 return kw;
11337 else {
11338 return NULL;
11342 static ibf_offset_t
11343 ibf_dump_insns_info_body(struct ibf_dump *dump, const rb_iseq_t *iseq)
11345 ibf_offset_t offset = ibf_dump_pos(dump);
11346 const struct iseq_insn_info_entry *entries = iseq->body->insns_info.body;
11348 unsigned int i;
11349 for (i = 0; i < iseq->body->insns_info.size; i++) {
11350 ibf_dump_write_small_value(dump, entries[i].line_no);
11351 #ifdef USE_ISEQ_NODE_ID
11352 ibf_dump_write_small_value(dump, entries[i].node_id);
11353 #endif
11354 ibf_dump_write_small_value(dump, entries[i].events);
11357 return offset;
11360 static struct iseq_insn_info_entry *
11361 ibf_load_insns_info_body(const struct ibf_load *load, ibf_offset_t body_offset, unsigned int size)
11363 ibf_offset_t reading_pos = body_offset;
11364 struct iseq_insn_info_entry *entries = ALLOC_N(struct iseq_insn_info_entry, size);
11366 unsigned int i;
11367 for (i = 0; i < size; i++) {
11368 entries[i].line_no = (int)ibf_load_small_value(load, &reading_pos);
11369 #ifdef USE_ISEQ_NODE_ID
11370 entries[i].node_id = (int)ibf_load_small_value(load, &reading_pos);
11371 #endif
11372 entries[i].events = (rb_event_flag_t)ibf_load_small_value(load, &reading_pos);
11375 return entries;
11378 static ibf_offset_t
11379 ibf_dump_insns_info_positions(struct ibf_dump *dump, const unsigned int *positions, unsigned int size)
11381 ibf_offset_t offset = ibf_dump_pos(dump);
11383 unsigned int last = 0;
11384 unsigned int i;
11385 for (i = 0; i < size; i++) {
11386 ibf_dump_write_small_value(dump, positions[i] - last);
11387 last = positions[i];
11390 return offset;
11393 static unsigned int *
11394 ibf_load_insns_info_positions(const struct ibf_load *load, ibf_offset_t positions_offset, unsigned int size)
11396 ibf_offset_t reading_pos = positions_offset;
11397 unsigned int *positions = ALLOC_N(unsigned int, size);
11399 unsigned int last = 0;
11400 unsigned int i;
11401 for (i = 0; i < size; i++) {
11402 positions[i] = last + (unsigned int)ibf_load_small_value(load, &reading_pos);
11403 last = positions[i];
11406 return positions;
11409 static ibf_offset_t
11410 ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
11412 const struct rb_iseq_constant_body *const body = iseq->body;
11413 const int size = body->local_table_size;
11414 ID *table = ALLOCA_N(ID, size);
11415 int i;
11417 for (i=0; i<size; i++) {
11418 table[i] = ibf_dump_id(dump, body->local_table[i]);
11421 IBF_W_ALIGN(ID);
11422 return ibf_dump_write(dump, table, sizeof(ID) * size);
11425 static ID *
11426 ibf_load_local_table(const struct ibf_load *load, ibf_offset_t local_table_offset, int size)
11428 if (size > 0) {
11429 ID *table = IBF_R(local_table_offset, ID, size);
11430 int i;
11432 for (i=0; i<size; i++) {
11433 table[i] = ibf_load_id(load, table[i]);
11435 return table;
11437 else {
11438 return NULL;
11442 static ibf_offset_t
11443 ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
11445 const struct iseq_catch_table *table = iseq->body->catch_table;
11447 if (table) {
11448 int *iseq_indices = ALLOCA_N(int, table->size);
11449 unsigned int i;
11451 for (i=0; i<table->size; i++) {
11452 iseq_indices[i] = ibf_dump_iseq(dump, table->entries[i].iseq);
11455 const ibf_offset_t offset = ibf_dump_pos(dump);
11457 for (i=0; i<table->size; i++) {
11458 ibf_dump_write_small_value(dump, iseq_indices[i]);
11459 ibf_dump_write_small_value(dump, table->entries[i].type);
11460 ibf_dump_write_small_value(dump, table->entries[i].start);
11461 ibf_dump_write_small_value(dump, table->entries[i].end);
11462 ibf_dump_write_small_value(dump, table->entries[i].cont);
11463 ibf_dump_write_small_value(dump, table->entries[i].sp);
11465 return offset;
11467 else {
11468 return ibf_dump_pos(dump);
11472 static struct iseq_catch_table *
11473 ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size)
11475 if (size) {
11476 struct iseq_catch_table *table = ruby_xmalloc(iseq_catch_table_bytes(size));
11477 table->size = size;
11479 ibf_offset_t reading_pos = catch_table_offset;
11481 unsigned int i;
11482 for (i=0; i<table->size; i++) {
11483 int iseq_index = (int)ibf_load_small_value(load, &reading_pos);
11484 table->entries[i].type = (enum catch_type)ibf_load_small_value(load, &reading_pos);
11485 table->entries[i].start = (unsigned int)ibf_load_small_value(load, &reading_pos);
11486 table->entries[i].end = (unsigned int)ibf_load_small_value(load, &reading_pos);
11487 table->entries[i].cont = (unsigned int)ibf_load_small_value(load, &reading_pos);
11488 table->entries[i].sp = (unsigned int)ibf_load_small_value(load, &reading_pos);
11490 table->entries[i].iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index);
11492 return table;
11494 else {
11495 return NULL;
11499 static ibf_offset_t
11500 ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
11502 const struct rb_iseq_constant_body *const body = iseq->body;
11503 const unsigned int ci_size = body->ci_size;
11504 const struct rb_call_data *cds = body->call_data;
11506 ibf_offset_t offset = ibf_dump_pos(dump);
11508 unsigned int i;
11510 for (i = 0; i < ci_size; i++) {
11511 const struct rb_callinfo *ci = cds[i].ci;
11512 if (ci != NULL) {
11513 ibf_dump_write_small_value(dump, ibf_dump_id(dump, vm_ci_mid(ci)));
11514 ibf_dump_write_small_value(dump, vm_ci_flag(ci));
11515 ibf_dump_write_small_value(dump, vm_ci_argc(ci));
11517 const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
11518 if (kwarg) {
11519 int len = kwarg->keyword_len;
11520 ibf_dump_write_small_value(dump, len);
11521 for (int j=0; j<len; j++) {
11522 VALUE keyword = ibf_dump_object(dump, kwarg->keywords[j]);
11523 ibf_dump_write_small_value(dump, keyword);
11526 else {
11527 ibf_dump_write_small_value(dump, 0);
11530 else {
11531 // TODO: truncate NULL ci from call_data.
11532 ibf_dump_write_small_value(dump, (VALUE)-1);
11536 return offset;
11539 static enum rb_id_table_iterator_result
11540 dump_outer_variable(ID id, VALUE val, void *dump)
11542 ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
11543 ibf_dump_write_small_value(dump, val);
11545 return ID_TABLE_CONTINUE;
11548 static ibf_offset_t
11549 ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
11551 struct rb_id_table * ovs = iseq->body->outer_variables;
11553 ibf_offset_t offset = ibf_dump_pos(dump);
11555 if (ovs) {
11556 ibf_dump_write_small_value(dump, (VALUE)rb_id_table_size(ovs));
11557 rb_id_table_foreach(ovs, dump_outer_variable, (void *)dump);
11559 else {
11560 ibf_dump_write_small_value(dump, (VALUE)0);
11563 return offset;
11566 /* note that we dump out rb_call_info but load back rb_call_data */
11567 static void
11568 ibf_load_ci_entries(const struct ibf_load *load,
11569 ibf_offset_t ci_entries_offset,
11570 unsigned int ci_size,
11571 struct rb_call_data **cd_ptr)
11573 ibf_offset_t reading_pos = ci_entries_offset;
11575 unsigned int i;
11577 struct rb_call_data *cds = ZALLOC_N(struct rb_call_data, ci_size);
11578 *cd_ptr = cds;
11580 for (i = 0; i < ci_size; i++) {
11581 VALUE mid_index = ibf_load_small_value(load, &reading_pos);
11582 if (mid_index != (VALUE)-1) {
11583 ID mid = ibf_load_id(load, mid_index);
11584 unsigned int flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
11585 unsigned int argc = (unsigned int)ibf_load_small_value(load, &reading_pos);
11587 struct rb_callinfo_kwarg *kwarg = NULL;
11588 int kwlen = (int)ibf_load_small_value(load, &reading_pos);
11589 if (kwlen > 0) {
11590 kwarg = rb_xmalloc_mul_add(kwlen, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
11591 kwarg->keyword_len = kwlen;
11592 for (int j=0; j<kwlen; j++) {
11593 VALUE keyword = ibf_load_small_value(load, &reading_pos);
11594 kwarg->keywords[j] = ibf_load_object(load, keyword);
11598 cds[i].ci = vm_ci_new(mid, flag, argc, kwarg);
11599 RB_OBJ_WRITTEN(load->iseq, Qundef, cds[i].ci);
11600 cds[i].cc = vm_cc_empty();
11602 else {
11603 // NULL ci
11604 cds[i].ci = NULL;
11605 cds[i].cc = NULL;
11610 static struct rb_id_table *
11611 ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variables_offset)
11613 ibf_offset_t reading_pos = outer_variables_offset;
11615 struct rb_id_table *tbl = NULL;
11617 size_t table_size = (size_t)ibf_load_small_value(load, &reading_pos);
11619 if (table_size > 0) {
11620 tbl = rb_id_table_create(table_size);
11623 for (size_t i = 0; i < table_size; i++) {
11624 ID key = ibf_load_id(load, (ID)ibf_load_small_value(load, &reading_pos));
11625 VALUE value = ibf_load_small_value(load, &reading_pos);
11626 if (!key) key = rb_make_temporary_id(i);
11627 rb_id_table_insert(tbl, key, value);
11630 return tbl;
11633 static ibf_offset_t
11634 ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
11636 assert(dump->current_buffer == &dump->global_buffer);
11638 unsigned int *positions;
11640 const struct rb_iseq_constant_body *body = iseq->body;
11642 const VALUE location_pathobj_index = ibf_dump_object(dump, body->location.pathobj); /* TODO: freeze */
11643 const VALUE location_base_label_index = ibf_dump_object(dump, body->location.base_label);
11644 const VALUE location_label_index = ibf_dump_object(dump, body->location.label);
11646 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11647 ibf_offset_t iseq_start = ibf_dump_pos(dump);
11649 struct ibf_dump_buffer *saved_buffer = dump->current_buffer;
11650 struct ibf_dump_buffer buffer;
11651 buffer.str = rb_str_new(0, 0);
11652 buffer.obj_table = ibf_dump_object_table_new();
11653 dump->current_buffer = &buffer;
11654 #endif
11656 const ibf_offset_t bytecode_offset = ibf_dump_code(dump, iseq);
11657 const ibf_offset_t bytecode_size = ibf_dump_pos(dump) - bytecode_offset;
11658 const ibf_offset_t param_opt_table_offset = ibf_dump_param_opt_table(dump, iseq);
11659 const ibf_offset_t param_keyword_offset = ibf_dump_param_keyword(dump, iseq);
11660 const ibf_offset_t insns_info_body_offset = ibf_dump_insns_info_body(dump, iseq);
11662 positions = rb_iseq_insns_info_decode_positions(iseq->body);
11663 const ibf_offset_t insns_info_positions_offset = ibf_dump_insns_info_positions(dump, positions, body->insns_info.size);
11664 ruby_xfree(positions);
11666 const ibf_offset_t local_table_offset = ibf_dump_local_table(dump, iseq);
11667 const unsigned int catch_table_size = body->catch_table ? body->catch_table->size : 0;
11668 const ibf_offset_t catch_table_offset = ibf_dump_catch_table(dump, iseq);
11669 const int parent_iseq_index = ibf_dump_iseq(dump, iseq->body->parent_iseq);
11670 const int local_iseq_index = ibf_dump_iseq(dump, iseq->body->local_iseq);
11671 const int mandatory_only_iseq_index = ibf_dump_iseq(dump, iseq->body->mandatory_only_iseq);
11672 const ibf_offset_t ci_entries_offset = ibf_dump_ci_entries(dump, iseq);
11673 const ibf_offset_t outer_variables_offset = ibf_dump_outer_variables(dump, iseq);
11675 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11676 ibf_offset_t local_obj_list_offset;
11677 unsigned int local_obj_list_size;
11679 ibf_dump_object_list(dump, &local_obj_list_offset, &local_obj_list_size);
11680 #endif
11682 ibf_offset_t body_offset = ibf_dump_pos(dump);
11684 /* dump the constant body */
11685 unsigned int param_flags =
11686 (body->param.flags.has_lead << 0) |
11687 (body->param.flags.has_opt << 1) |
11688 (body->param.flags.has_rest << 2) |
11689 (body->param.flags.has_post << 3) |
11690 (body->param.flags.has_kw << 4) |
11691 (body->param.flags.has_kwrest << 5) |
11692 (body->param.flags.has_block << 6) |
11693 (body->param.flags.ambiguous_param0 << 7) |
11694 (body->param.flags.accepts_no_kwarg << 8) |
11695 (body->param.flags.ruby2_keywords << 9);
11697 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11698 # define IBF_BODY_OFFSET(x) (x)
11699 #else
11700 # define IBF_BODY_OFFSET(x) (body_offset - (x))
11701 #endif
11703 ibf_dump_write_small_value(dump, body->type);
11704 ibf_dump_write_small_value(dump, body->iseq_size);
11705 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(bytecode_offset));
11706 ibf_dump_write_small_value(dump, bytecode_size);
11707 ibf_dump_write_small_value(dump, param_flags);
11708 ibf_dump_write_small_value(dump, body->param.size);
11709 ibf_dump_write_small_value(dump, body->param.lead_num);
11710 ibf_dump_write_small_value(dump, body->param.opt_num);
11711 ibf_dump_write_small_value(dump, body->param.rest_start);
11712 ibf_dump_write_small_value(dump, body->param.post_start);
11713 ibf_dump_write_small_value(dump, body->param.post_num);
11714 ibf_dump_write_small_value(dump, body->param.block_start);
11715 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(param_opt_table_offset));
11716 ibf_dump_write_small_value(dump, param_keyword_offset);
11717 ibf_dump_write_small_value(dump, location_pathobj_index);
11718 ibf_dump_write_small_value(dump, location_base_label_index);
11719 ibf_dump_write_small_value(dump, location_label_index);
11720 ibf_dump_write_small_value(dump, body->location.first_lineno);
11721 ibf_dump_write_small_value(dump, body->location.node_id);
11722 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.lineno);
11723 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.column);
11724 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.lineno);
11725 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.column);
11726 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_body_offset));
11727 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_positions_offset));
11728 ibf_dump_write_small_value(dump, body->insns_info.size);
11729 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(local_table_offset));
11730 ibf_dump_write_small_value(dump, catch_table_size);
11731 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(catch_table_offset));
11732 ibf_dump_write_small_value(dump, parent_iseq_index);
11733 ibf_dump_write_small_value(dump, local_iseq_index);
11734 ibf_dump_write_small_value(dump, mandatory_only_iseq_index);
11735 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(ci_entries_offset));
11736 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(outer_variables_offset));
11737 ibf_dump_write_small_value(dump, body->variable.flip_count);
11738 ibf_dump_write_small_value(dump, body->local_table_size);
11739 ibf_dump_write_small_value(dump, body->is_size);
11740 ibf_dump_write_small_value(dump, body->ci_size);
11741 ibf_dump_write_small_value(dump, body->stack_max);
11742 ibf_dump_write_small_value(dump, body->catch_except_p);
11743 ibf_dump_write_small_value(dump, body->builtin_inline_p);
11745 #undef IBF_BODY_OFFSET
11747 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11748 ibf_offset_t iseq_length_bytes = ibf_dump_pos(dump);
11750 dump->current_buffer = saved_buffer;
11751 ibf_dump_write(dump, RSTRING_PTR(buffer.str), iseq_length_bytes);
11753 ibf_offset_t offset = ibf_dump_pos(dump);
11754 ibf_dump_write_small_value(dump, iseq_start);
11755 ibf_dump_write_small_value(dump, iseq_length_bytes);
11756 ibf_dump_write_small_value(dump, body_offset);
11758 ibf_dump_write_small_value(dump, local_obj_list_offset);
11759 ibf_dump_write_small_value(dump, local_obj_list_size);
11761 st_free_table(buffer.obj_table); // TODO: this leaks in case of exception
11763 return offset;
11764 #else
11765 return body_offset;
11766 #endif
11769 static VALUE
11770 ibf_load_location_str(const struct ibf_load *load, VALUE str_index)
11772 VALUE str = ibf_load_object(load, str_index);
11773 if (str != Qnil) {
11774 str = rb_fstring(str);
11776 return str;
11779 static void
11780 ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
11782 struct rb_iseq_constant_body *load_body = iseq->body = rb_iseq_constant_body_alloc();
11784 ibf_offset_t reading_pos = offset;
11786 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11787 struct ibf_load_buffer *saved_buffer = load->current_buffer;
11788 load->current_buffer = &load->global_buffer;
11790 const ibf_offset_t iseq_start = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
11791 const ibf_offset_t iseq_length_bytes = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
11792 const ibf_offset_t body_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
11794 struct ibf_load_buffer buffer;
11795 buffer.buff = load->global_buffer.buff + iseq_start;
11796 buffer.size = iseq_length_bytes;
11797 buffer.obj_list_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
11798 buffer.obj_list_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
11799 buffer.obj_list = pinned_list_new(buffer.obj_list_size);
11801 load->current_buffer = &buffer;
11802 reading_pos = body_offset;
11803 #endif
11805 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11806 # define IBF_BODY_OFFSET(x) (x)
11807 #else
11808 # define IBF_BODY_OFFSET(x) (offset - (x))
11809 #endif
11811 const unsigned int type = (unsigned int)ibf_load_small_value(load, &reading_pos);
11812 const unsigned int iseq_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
11813 const ibf_offset_t bytecode_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11814 const ibf_offset_t bytecode_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
11815 const unsigned int param_flags = (unsigned int)ibf_load_small_value(load, &reading_pos);
11816 const unsigned int param_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
11817 const int param_lead_num = (int)ibf_load_small_value(load, &reading_pos);
11818 const int param_opt_num = (int)ibf_load_small_value(load, &reading_pos);
11819 const int param_rest_start = (int)ibf_load_small_value(load, &reading_pos);
11820 const int param_post_start = (int)ibf_load_small_value(load, &reading_pos);
11821 const int param_post_num = (int)ibf_load_small_value(load, &reading_pos);
11822 const int param_block_start = (int)ibf_load_small_value(load, &reading_pos);
11823 const ibf_offset_t param_opt_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11824 const ibf_offset_t param_keyword_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
11825 const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos);
11826 const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos);
11827 const VALUE location_label_index = ibf_load_small_value(load, &reading_pos);
11828 const VALUE location_first_lineno = ibf_load_small_value(load, &reading_pos);
11829 const int location_node_id = (int)ibf_load_small_value(load, &reading_pos);
11830 const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
11831 const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos);
11832 const int location_code_location_end_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
11833 const int location_code_location_end_pos_column = (int)ibf_load_small_value(load, &reading_pos);
11834 const ibf_offset_t insns_info_body_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11835 const ibf_offset_t insns_info_positions_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11836 const unsigned int insns_info_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
11837 const ibf_offset_t local_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11838 const unsigned int catch_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
11839 const ibf_offset_t catch_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11840 const int parent_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
11841 const int local_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
11842 const int mandatory_only_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
11843 const ibf_offset_t ci_entries_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11844 const ibf_offset_t outer_variables_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
11845 const rb_snum_t variable_flip_count = (rb_snum_t)ibf_load_small_value(load, &reading_pos);
11846 const unsigned int local_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
11847 const unsigned int is_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
11848 const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
11849 const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos);
11850 const char catch_except_p = (char)ibf_load_small_value(load, &reading_pos);
11851 const bool builtin_inline_p = (bool)ibf_load_small_value(load, &reading_pos);
11853 #undef IBF_BODY_OFFSET
11855 load_body->type = type;
11856 load_body->stack_max = stack_max;
11857 load_body->param.flags.has_lead = (param_flags >> 0) & 1;
11858 load_body->param.flags.has_opt = (param_flags >> 1) & 1;
11859 load_body->param.flags.has_rest = (param_flags >> 2) & 1;
11860 load_body->param.flags.has_post = (param_flags >> 3) & 1;
11861 load_body->param.flags.has_kw = FALSE;
11862 load_body->param.flags.has_kwrest = (param_flags >> 5) & 1;
11863 load_body->param.flags.has_block = (param_flags >> 6) & 1;
11864 load_body->param.flags.ambiguous_param0 = (param_flags >> 7) & 1;
11865 load_body->param.flags.accepts_no_kwarg = (param_flags >> 8) & 1;
11866 load_body->param.flags.ruby2_keywords = (param_flags >> 9) & 1;
11867 load_body->param.size = param_size;
11868 load_body->param.lead_num = param_lead_num;
11869 load_body->param.opt_num = param_opt_num;
11870 load_body->param.rest_start = param_rest_start;
11871 load_body->param.post_start = param_post_start;
11872 load_body->param.post_num = param_post_num;
11873 load_body->param.block_start = param_block_start;
11874 load_body->local_table_size = local_table_size;
11875 load_body->is_size = is_size;
11876 load_body->ci_size = ci_size;
11877 load_body->insns_info.size = insns_info_size;
11879 ISEQ_COVERAGE_SET(iseq, Qnil);
11880 ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
11881 load_body->variable.flip_count = variable_flip_count;
11882 load_body->variable.script_lines = Qnil;
11884 load_body->location.first_lineno = location_first_lineno;
11885 load_body->location.node_id = location_node_id;
11886 load_body->location.code_location.beg_pos.lineno = location_code_location_beg_pos_lineno;
11887 load_body->location.code_location.beg_pos.column = location_code_location_beg_pos_column;
11888 load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno;
11889 load_body->location.code_location.end_pos.column = location_code_location_end_pos_column;
11890 load_body->catch_except_p = catch_except_p;
11891 load_body->builtin_inline_p = builtin_inline_p;
11893 load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, is_size);
11894 ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
11895 load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset);
11896 load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
11897 load_body->param.keyword = ibf_load_param_keyword(load, param_keyword_offset);
11898 load_body->param.flags.has_kw = (param_flags >> 4) & 1;
11899 load_body->insns_info.body = ibf_load_insns_info_body(load, insns_info_body_offset, insns_info_size);
11900 load_body->insns_info.positions = ibf_load_insns_info_positions(load, insns_info_positions_offset, insns_info_size);
11901 load_body->local_table = ibf_load_local_table(load, local_table_offset, local_table_size);
11902 load_body->catch_table = ibf_load_catch_table(load, catch_table_offset, catch_table_size);
11903 load_body->parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index);
11904 load_body->local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index);
11905 load_body->mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index);
11907 ibf_load_code(load, iseq, bytecode_offset, bytecode_size, iseq_size);
11908 #if VM_INSN_INFO_TABLE_IMPL == 2
11909 rb_iseq_insns_info_encode_positions(iseq);
11910 #endif
11912 rb_iseq_translate_threaded_code(iseq);
11914 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11915 load->current_buffer = &load->global_buffer;
11916 #endif
11919 VALUE realpath = Qnil, path = ibf_load_object(load, location_pathobj_index);
11920 if (RB_TYPE_P(path, T_STRING)) {
11921 realpath = path = rb_fstring(path);
11923 else if (RB_TYPE_P(path, T_ARRAY)) {
11924 VALUE pathobj = path;
11925 if (RARRAY_LEN(pathobj) != 2) {
11926 rb_raise(rb_eRuntimeError, "path object size mismatch");
11928 path = rb_fstring(RARRAY_AREF(pathobj, 0));
11929 realpath = RARRAY_AREF(pathobj, 1);
11930 if (!NIL_P(realpath)) {
11931 if (!RB_TYPE_P(realpath, T_STRING)) {
11932 rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
11933 "(%x), path=%+"PRIsVALUE,
11934 realpath, TYPE(realpath), path);
11936 realpath = rb_fstring(realpath);
11939 else {
11940 rb_raise(rb_eRuntimeError, "unexpected path object");
11942 rb_iseq_pathobj_set(iseq, path, realpath);
11945 RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, location_base_label_index));
11946 RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, location_label_index));
11948 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
11949 load->current_buffer = saved_buffer;
11950 #endif
11951 verify_call_cache(iseq);
11954 struct ibf_dump_iseq_list_arg
11956 struct ibf_dump *dump;
11957 VALUE offset_list;
11960 static int
11961 ibf_dump_iseq_list_i(st_data_t key, st_data_t val, st_data_t ptr)
11963 const rb_iseq_t *iseq = (const rb_iseq_t *)key;
11964 struct ibf_dump_iseq_list_arg *args = (struct ibf_dump_iseq_list_arg *)ptr;
11966 ibf_offset_t offset = ibf_dump_iseq_each(args->dump, iseq);
11967 rb_ary_push(args->offset_list, UINT2NUM(offset));
11969 return ST_CONTINUE;
11972 static void
11973 ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header)
11975 VALUE offset_list = rb_ary_tmp_new(dump->iseq_table->num_entries);
11977 struct ibf_dump_iseq_list_arg args;
11978 args.dump = dump;
11979 args.offset_list = offset_list;
11981 st_foreach(dump->iseq_table, ibf_dump_iseq_list_i, (st_data_t)&args);
11983 st_index_t i;
11984 st_index_t size = dump->iseq_table->num_entries;
11985 ibf_offset_t *offsets = ALLOCA_N(ibf_offset_t, size);
11987 for (i = 0; i < size; i++) {
11988 offsets[i] = NUM2UINT(RARRAY_AREF(offset_list, i));
11991 ibf_dump_align(dump, sizeof(ibf_offset_t));
11992 header->iseq_list_offset = ibf_dump_write(dump, offsets, sizeof(ibf_offset_t) * size);
11993 header->iseq_list_size = (unsigned int)size;
11996 #define IBF_OBJECT_INTERNAL FL_PROMOTED0
11999 * Binary format
12000 * - ibf_object_header
12001 * - ibf_object_xxx (xxx is type)
12004 struct ibf_object_header {
12005 unsigned int type: 5;
12006 unsigned int special_const: 1;
12007 unsigned int frozen: 1;
12008 unsigned int internal: 1;
12011 enum ibf_object_class_index {
12012 IBF_OBJECT_CLASS_OBJECT,
12013 IBF_OBJECT_CLASS_ARRAY,
12014 IBF_OBJECT_CLASS_STANDARD_ERROR,
12015 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR,
12016 IBF_OBJECT_CLASS_TYPE_ERROR,
12017 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR,
12020 struct ibf_object_regexp {
12021 long srcstr;
12022 char option;
12025 struct ibf_object_hash {
12026 long len;
12027 long keyval[FLEX_ARY_LEN];
12030 struct ibf_object_struct_range {
12031 long class_index;
12032 long len;
12033 long beg;
12034 long end;
12035 int excl;
12038 struct ibf_object_bignum {
12039 ssize_t slen;
12040 BDIGIT digits[FLEX_ARY_LEN];
12043 enum ibf_object_data_type {
12044 IBF_OBJECT_DATA_ENCODING,
12047 struct ibf_object_complex_rational {
12048 long a, b;
12051 struct ibf_object_symbol {
12052 long str;
12055 #define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \
12056 ((((offset) - 1) / (align) + 1) * (align))
12057 #define IBF_OBJBODY(type, offset) (const type *)\
12058 ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset))
12060 static const void *
12061 ibf_load_check_offset(const struct ibf_load *load, size_t offset)
12063 if (offset >= load->current_buffer->size) {
12064 rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, offset);
12066 return load->current_buffer->buff + offset;
12069 NORETURN(static void ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj));
12071 static void
12072 ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj)
12074 char buff[0x100];
12075 rb_raw_obj_info(buff, sizeof(buff), obj);
12076 rb_raise(rb_eNotImpError, "ibf_dump_object_unsupported: %s", buff);
12079 NORETURN(static VALUE ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset));
12081 static VALUE
12082 ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12084 rb_raise(rb_eArgError, "unsupported");
12085 UNREACHABLE_RETURN(Qnil);
12088 static void
12089 ibf_dump_object_class(struct ibf_dump *dump, VALUE obj)
12091 enum ibf_object_class_index cindex;
12092 if (obj == rb_cObject) {
12093 cindex = IBF_OBJECT_CLASS_OBJECT;
12095 else if (obj == rb_cArray) {
12096 cindex = IBF_OBJECT_CLASS_ARRAY;
12098 else if (obj == rb_eStandardError) {
12099 cindex = IBF_OBJECT_CLASS_STANDARD_ERROR;
12101 else if (obj == rb_eNoMatchingPatternError) {
12102 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR;
12104 else if (obj == rb_eTypeError) {
12105 cindex = IBF_OBJECT_CLASS_TYPE_ERROR;
12107 else if (obj == rb_eNoMatchingPatternKeyError) {
12108 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR;
12110 else {
12111 rb_obj_info_dump(obj);
12112 rb_p(obj);
12113 rb_bug("unsupported class");
12115 ibf_dump_write_small_value(dump, (VALUE)cindex);
12118 static VALUE
12119 ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12121 enum ibf_object_class_index cindex = (enum ibf_object_class_index)ibf_load_small_value(load, &offset);
12123 switch (cindex) {
12124 case IBF_OBJECT_CLASS_OBJECT:
12125 return rb_cObject;
12126 case IBF_OBJECT_CLASS_ARRAY:
12127 return rb_cArray;
12128 case IBF_OBJECT_CLASS_STANDARD_ERROR:
12129 return rb_eStandardError;
12130 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR:
12131 return rb_eNoMatchingPatternError;
12132 case IBF_OBJECT_CLASS_TYPE_ERROR:
12133 return rb_eTypeError;
12134 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR:
12135 return rb_eNoMatchingPatternKeyError;
12138 rb_raise(rb_eArgError, "ibf_load_object_class: unknown class (%d)", (int)cindex);
12142 static void
12143 ibf_dump_object_float(struct ibf_dump *dump, VALUE obj)
12145 double dbl = RFLOAT_VALUE(obj);
12146 (void)IBF_W(&dbl, double, 1);
12149 static VALUE
12150 ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12152 const double *dblp = IBF_OBJBODY(double, offset);
12153 return DBL2NUM(*dblp);
12156 static void
12157 ibf_dump_object_string(struct ibf_dump *dump, VALUE obj)
12159 long encindex = (long)rb_enc_get_index(obj);
12160 long len = RSTRING_LEN(obj);
12161 const char *ptr = RSTRING_PTR(obj);
12163 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
12164 rb_encoding *enc = rb_enc_from_index((int)encindex);
12165 const char *enc_name = rb_enc_name(enc);
12166 encindex = RUBY_ENCINDEX_BUILTIN_MAX + ibf_dump_object(dump, rb_str_new2(enc_name));
12169 ibf_dump_write_small_value(dump, encindex);
12170 ibf_dump_write_small_value(dump, len);
12171 IBF_WP(ptr, char, len);
12174 static VALUE
12175 ibf_load_object_string(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12177 ibf_offset_t reading_pos = offset;
12179 int encindex = (int)ibf_load_small_value(load, &reading_pos);
12180 const long len = (long)ibf_load_small_value(load, &reading_pos);
12181 const char *ptr = load->current_buffer->buff + reading_pos;
12183 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
12184 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
12185 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
12188 VALUE str;
12189 if (header->frozen && !header->internal) {
12190 str = rb_enc_interned_str(ptr, len, rb_enc_from_index(encindex));
12192 else {
12193 str = rb_enc_str_new(ptr, len, rb_enc_from_index(encindex));
12195 if (header->internal) rb_obj_hide(str);
12196 if (header->frozen) str = rb_fstring(str);
12198 return str;
12201 static void
12202 ibf_dump_object_regexp(struct ibf_dump *dump, VALUE obj)
12204 VALUE srcstr = RREGEXP_SRC(obj);
12205 struct ibf_object_regexp regexp;
12206 regexp.option = (char)rb_reg_options(obj);
12207 regexp.srcstr = (long)ibf_dump_object(dump, srcstr);
12209 ibf_dump_write_byte(dump, (unsigned char)regexp.option);
12210 ibf_dump_write_small_value(dump, regexp.srcstr);
12213 static VALUE
12214 ibf_load_object_regexp(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12216 struct ibf_object_regexp regexp;
12217 regexp.option = ibf_load_byte(load, &offset);
12218 regexp.srcstr = ibf_load_small_value(load, &offset);
12220 VALUE srcstr = ibf_load_object(load, regexp.srcstr);
12221 VALUE reg = rb_reg_compile(srcstr, (int)regexp.option, NULL, 0);
12223 if (header->internal) rb_obj_hide(reg);
12224 if (header->frozen) rb_obj_freeze(reg);
12226 return reg;
12229 static void
12230 ibf_dump_object_array(struct ibf_dump *dump, VALUE obj)
12232 long i, len = RARRAY_LEN(obj);
12233 ibf_dump_write_small_value(dump, len);
12234 for (i=0; i<len; i++) {
12235 long index = (long)ibf_dump_object(dump, RARRAY_AREF(obj, i));
12236 ibf_dump_write_small_value(dump, index);
12240 static VALUE
12241 ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12243 ibf_offset_t reading_pos = offset;
12245 const long len = (long)ibf_load_small_value(load, &reading_pos);
12247 VALUE ary = rb_ary_new_capa(len);
12248 int i;
12250 for (i=0; i<len; i++) {
12251 const VALUE index = ibf_load_small_value(load, &reading_pos);
12252 rb_ary_push(ary, ibf_load_object(load, index));
12255 if (header->internal) rb_obj_hide(ary);
12256 if (header->frozen) rb_obj_freeze(ary);
12258 return ary;
12261 static int
12262 ibf_dump_object_hash_i(st_data_t key, st_data_t val, st_data_t ptr)
12264 struct ibf_dump *dump = (struct ibf_dump *)ptr;
12266 VALUE key_index = ibf_dump_object(dump, (VALUE)key);
12267 VALUE val_index = ibf_dump_object(dump, (VALUE)val);
12269 ibf_dump_write_small_value(dump, key_index);
12270 ibf_dump_write_small_value(dump, val_index);
12271 return ST_CONTINUE;
12274 static void
12275 ibf_dump_object_hash(struct ibf_dump *dump, VALUE obj)
12277 long len = RHASH_SIZE(obj);
12278 ibf_dump_write_small_value(dump, (VALUE)len);
12280 if (len > 0) rb_hash_foreach(obj, ibf_dump_object_hash_i, (VALUE)dump);
12283 static VALUE
12284 ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12286 long len = (long)ibf_load_small_value(load, &offset);
12287 VALUE obj = rb_hash_new_with_size(len);
12288 int i;
12290 for (i = 0; i < len; i++) {
12291 VALUE key_index = ibf_load_small_value(load, &offset);
12292 VALUE val_index = ibf_load_small_value(load, &offset);
12294 VALUE key = ibf_load_object(load, key_index);
12295 VALUE val = ibf_load_object(load, val_index);
12296 rb_hash_aset(obj, key, val);
12298 rb_hash_rehash(obj);
12300 if (header->internal) rb_obj_hide(obj);
12301 if (header->frozen) rb_obj_freeze(obj);
12303 return obj;
12306 static void
12307 ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
12309 if (rb_obj_is_kind_of(obj, rb_cRange)) {
12310 struct ibf_object_struct_range range;
12311 VALUE beg, end;
12312 IBF_ZERO(range);
12313 range.len = 3;
12314 range.class_index = 0;
12316 rb_range_values(obj, &beg, &end, &range.excl);
12317 range.beg = (long)ibf_dump_object(dump, beg);
12318 range.end = (long)ibf_dump_object(dump, end);
12320 IBF_W_ALIGN(struct ibf_object_struct_range);
12321 IBF_WV(range);
12323 else {
12324 rb_raise(rb_eNotImpError, "ibf_dump_object_struct: unsupported class %"PRIsVALUE,
12325 rb_class_name(CLASS_OF(obj)));
12329 static VALUE
12330 ibf_load_object_struct(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12332 const struct ibf_object_struct_range *range = IBF_OBJBODY(struct ibf_object_struct_range, offset);
12333 VALUE beg = ibf_load_object(load, range->beg);
12334 VALUE end = ibf_load_object(load, range->end);
12335 VALUE obj = rb_range_new(beg, end, range->excl);
12336 if (header->internal) rb_obj_hide(obj);
12337 if (header->frozen) rb_obj_freeze(obj);
12338 return obj;
12341 static void
12342 ibf_dump_object_bignum(struct ibf_dump *dump, VALUE obj)
12344 ssize_t len = BIGNUM_LEN(obj);
12345 ssize_t slen = BIGNUM_SIGN(obj) > 0 ? len : len * -1;
12346 BDIGIT *d = BIGNUM_DIGITS(obj);
12348 (void)IBF_W(&slen, ssize_t, 1);
12349 IBF_WP(d, BDIGIT, len);
12352 static VALUE
12353 ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12355 const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset);
12356 int sign = bignum->slen > 0;
12357 ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
12358 VALUE obj = rb_integer_unpack(bignum->digits, len * 2, 2, 0,
12359 INTEGER_PACK_LITTLE_ENDIAN | (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
12360 if (header->internal) rb_obj_hide(obj);
12361 if (header->frozen) rb_obj_freeze(obj);
12362 return obj;
12365 static void
12366 ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
12368 if (rb_data_is_encoding(obj)) {
12369 rb_encoding *enc = rb_to_encoding(obj);
12370 const char *name = rb_enc_name(enc);
12371 long len = strlen(name) + 1;
12372 long data[2];
12373 data[0] = IBF_OBJECT_DATA_ENCODING;
12374 data[1] = len;
12375 (void)IBF_W(data, long, 2);
12376 IBF_WP(name, char, len);
12378 else {
12379 ibf_dump_object_unsupported(dump, obj);
12383 static VALUE
12384 ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12386 const long *body = IBF_OBJBODY(long, offset);
12387 const enum ibf_object_data_type type = (enum ibf_object_data_type)body[0];
12388 /* const long len = body[1]; */
12389 const char *data = (const char *)&body[2];
12391 switch (type) {
12392 case IBF_OBJECT_DATA_ENCODING:
12394 VALUE encobj = rb_enc_from_encoding(rb_enc_find(data));
12395 return encobj;
12399 return ibf_load_object_unsupported(load, header, offset);
12402 static void
12403 ibf_dump_object_complex_rational(struct ibf_dump *dump, VALUE obj)
12405 long data[2];
12406 data[0] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
12407 data[1] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
12409 (void)IBF_W(data, long, 2);
12412 static VALUE
12413 ibf_load_object_complex_rational(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12415 const struct ibf_object_complex_rational *nums = IBF_OBJBODY(struct ibf_object_complex_rational, offset);
12416 VALUE a = ibf_load_object(load, nums->a);
12417 VALUE b = ibf_load_object(load, nums->b);
12418 VALUE obj = header->type == T_COMPLEX ?
12419 rb_complex_new(a, b) : rb_rational_new(a, b);
12421 if (header->internal) rb_obj_hide(obj);
12422 if (header->frozen) rb_obj_freeze(obj);
12423 return obj;
12426 static void
12427 ibf_dump_object_symbol(struct ibf_dump *dump, VALUE obj)
12429 ibf_dump_object_string(dump, rb_sym2str(obj));
12432 static VALUE
12433 ibf_load_object_symbol(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
12435 ibf_offset_t reading_pos = offset;
12437 int encindex = (int)ibf_load_small_value(load, &reading_pos);
12438 const long len = (long)ibf_load_small_value(load, &reading_pos);
12439 const char *ptr = load->current_buffer->buff + reading_pos;
12441 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
12442 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
12443 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
12446 ID id = rb_intern3(ptr, len, rb_enc_from_index(encindex));
12447 return ID2SYM(id);
12450 typedef void (*ibf_dump_object_function)(struct ibf_dump *dump, VALUE obj);
12451 static ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = {
12452 ibf_dump_object_unsupported, /* T_NONE */
12453 ibf_dump_object_unsupported, /* T_OBJECT */
12454 ibf_dump_object_class, /* T_CLASS */
12455 ibf_dump_object_unsupported, /* T_MODULE */
12456 ibf_dump_object_float, /* T_FLOAT */
12457 ibf_dump_object_string, /* T_STRING */
12458 ibf_dump_object_regexp, /* T_REGEXP */
12459 ibf_dump_object_array, /* T_ARRAY */
12460 ibf_dump_object_hash, /* T_HASH */
12461 ibf_dump_object_struct, /* T_STRUCT */
12462 ibf_dump_object_bignum, /* T_BIGNUM */
12463 ibf_dump_object_unsupported, /* T_FILE */
12464 ibf_dump_object_data, /* T_DATA */
12465 ibf_dump_object_unsupported, /* T_MATCH */
12466 ibf_dump_object_complex_rational, /* T_COMPLEX */
12467 ibf_dump_object_complex_rational, /* T_RATIONAL */
12468 ibf_dump_object_unsupported, /* 0x10 */
12469 ibf_dump_object_unsupported, /* 0x11 T_NIL */
12470 ibf_dump_object_unsupported, /* 0x12 T_TRUE */
12471 ibf_dump_object_unsupported, /* 0x13 T_FALSE */
12472 ibf_dump_object_symbol, /* 0x14 T_SYMBOL */
12473 ibf_dump_object_unsupported, /* T_FIXNUM */
12474 ibf_dump_object_unsupported, /* T_UNDEF */
12475 ibf_dump_object_unsupported, /* 0x17 */
12476 ibf_dump_object_unsupported, /* 0x18 */
12477 ibf_dump_object_unsupported, /* 0x19 */
12478 ibf_dump_object_unsupported, /* T_IMEMO 0x1a */
12479 ibf_dump_object_unsupported, /* T_NODE 0x1b */
12480 ibf_dump_object_unsupported, /* T_ICLASS 0x1c */
12481 ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */
12482 ibf_dump_object_unsupported, /* 0x1e */
12483 ibf_dump_object_unsupported, /* 0x1f */
12486 static void
12487 ibf_dump_object_object_header(struct ibf_dump *dump, const struct ibf_object_header header)
12489 unsigned char byte =
12490 (header.type << 0) |
12491 (header.special_const << 5) |
12492 (header.frozen << 6) |
12493 (header.internal << 7);
12495 IBF_WV(byte);
12498 static struct ibf_object_header
12499 ibf_load_object_object_header(const struct ibf_load *load, ibf_offset_t *offset)
12501 unsigned char byte = ibf_load_byte(load, offset);
12503 struct ibf_object_header header;
12504 header.type = (byte >> 0) & 0x1f;
12505 header.special_const = (byte >> 5) & 0x01;
12506 header.frozen = (byte >> 6) & 0x01;
12507 header.internal = (byte >> 7) & 0x01;
12509 return header;
12512 static ibf_offset_t
12513 ibf_dump_object_object(struct ibf_dump *dump, VALUE obj)
12515 struct ibf_object_header obj_header;
12516 ibf_offset_t current_offset;
12517 IBF_ZERO(obj_header);
12518 obj_header.type = TYPE(obj);
12520 IBF_W_ALIGN(ibf_offset_t);
12521 current_offset = ibf_dump_pos(dump);
12523 if (SPECIAL_CONST_P(obj) &&
12524 ! (SYMBOL_P(obj) ||
12525 RB_FLOAT_TYPE_P(obj))) {
12526 obj_header.special_const = TRUE;
12527 obj_header.frozen = TRUE;
12528 obj_header.internal = TRUE;
12529 ibf_dump_object_object_header(dump, obj_header);
12530 ibf_dump_write_small_value(dump, obj);
12532 else {
12533 obj_header.internal = SPECIAL_CONST_P(obj) ? FALSE : (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
12534 obj_header.special_const = FALSE;
12535 obj_header.frozen = FL_TEST(obj, FL_FREEZE) ? TRUE : FALSE;
12536 ibf_dump_object_object_header(dump, obj_header);
12537 (*dump_object_functions[obj_header.type])(dump, obj);
12540 return current_offset;
12543 typedef VALUE (*ibf_load_object_function)(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset);
12544 static ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = {
12545 ibf_load_object_unsupported, /* T_NONE */
12546 ibf_load_object_unsupported, /* T_OBJECT */
12547 ibf_load_object_class, /* T_CLASS */
12548 ibf_load_object_unsupported, /* T_MODULE */
12549 ibf_load_object_float, /* T_FLOAT */
12550 ibf_load_object_string, /* T_STRING */
12551 ibf_load_object_regexp, /* T_REGEXP */
12552 ibf_load_object_array, /* T_ARRAY */
12553 ibf_load_object_hash, /* T_HASH */
12554 ibf_load_object_struct, /* T_STRUCT */
12555 ibf_load_object_bignum, /* T_BIGNUM */
12556 ibf_load_object_unsupported, /* T_FILE */
12557 ibf_load_object_data, /* T_DATA */
12558 ibf_load_object_unsupported, /* T_MATCH */
12559 ibf_load_object_complex_rational, /* T_COMPLEX */
12560 ibf_load_object_complex_rational, /* T_RATIONAL */
12561 ibf_load_object_unsupported, /* 0x10 */
12562 ibf_load_object_unsupported, /* T_NIL */
12563 ibf_load_object_unsupported, /* T_TRUE */
12564 ibf_load_object_unsupported, /* T_FALSE */
12565 ibf_load_object_symbol,
12566 ibf_load_object_unsupported, /* T_FIXNUM */
12567 ibf_load_object_unsupported, /* T_UNDEF */
12568 ibf_load_object_unsupported, /* 0x17 */
12569 ibf_load_object_unsupported, /* 0x18 */
12570 ibf_load_object_unsupported, /* 0x19 */
12571 ibf_load_object_unsupported, /* T_IMEMO 0x1a */
12572 ibf_load_object_unsupported, /* T_NODE 0x1b */
12573 ibf_load_object_unsupported, /* T_ICLASS 0x1c */
12574 ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */
12575 ibf_load_object_unsupported, /* 0x1e */
12576 ibf_load_object_unsupported, /* 0x1f */
12579 static VALUE
12580 ibf_load_object(const struct ibf_load *load, VALUE object_index)
12582 if (object_index == 0) {
12583 return Qnil;
12585 else {
12586 VALUE obj = pinned_list_fetch(load->current_buffer->obj_list, (long)object_index);
12587 if (!obj) {
12588 ibf_offset_t *offsets = (ibf_offset_t *)(load->current_buffer->obj_list_offset + load->current_buffer->buff);
12589 ibf_offset_t offset = offsets[object_index];
12590 const struct ibf_object_header header = ibf_load_object_object_header(load, &offset);
12592 #if IBF_ISEQ_DEBUG
12593 fprintf(stderr, "ibf_load_object: list=%#x offsets=%p offset=%#x\n",
12594 load->current_buffer->obj_list_offset, (void *)offsets, offset);
12595 fprintf(stderr, "ibf_load_object: type=%#x special=%d frozen=%d internal=%d\n",
12596 header.type, header.special_const, header.frozen, header.internal);
12597 #endif
12598 if (offset >= load->current_buffer->size) {
12599 rb_raise(rb_eIndexError, "object offset out of range: %u", offset);
12602 if (header.special_const) {
12603 ibf_offset_t reading_pos = offset;
12605 obj = ibf_load_small_value(load, &reading_pos);
12607 else {
12608 obj = (*load_object_functions[header.type])(load, &header, offset);
12611 pinned_list_store(load->current_buffer->obj_list, (long)object_index, obj);
12613 #if IBF_ISEQ_DEBUG
12614 fprintf(stderr, "ibf_load_object: index=%#"PRIxVALUE" obj=%#"PRIxVALUE"\n",
12615 object_index, obj);
12616 #endif
12617 return obj;
12621 struct ibf_dump_object_list_arg
12623 struct ibf_dump *dump;
12624 VALUE offset_list;
12627 static int
12628 ibf_dump_object_list_i(st_data_t key, st_data_t val, st_data_t ptr)
12630 VALUE obj = (VALUE)key;
12631 struct ibf_dump_object_list_arg *args = (struct ibf_dump_object_list_arg *)ptr;
12633 ibf_offset_t offset = ibf_dump_object_object(args->dump, obj);
12634 rb_ary_push(args->offset_list, UINT2NUM(offset));
12636 return ST_CONTINUE;
12639 static void
12640 ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size)
12642 st_table *obj_table = dump->current_buffer->obj_table;
12643 VALUE offset_list = rb_ary_tmp_new(obj_table->num_entries);
12645 struct ibf_dump_object_list_arg args;
12646 args.dump = dump;
12647 args.offset_list = offset_list;
12649 st_foreach(obj_table, ibf_dump_object_list_i, (st_data_t)&args);
12651 IBF_W_ALIGN(ibf_offset_t);
12652 *obj_list_offset = ibf_dump_pos(dump);
12654 st_index_t size = obj_table->num_entries;
12655 st_index_t i;
12657 for (i=0; i<size; i++) {
12658 ibf_offset_t offset = NUM2UINT(RARRAY_AREF(offset_list, i));
12659 IBF_WV(offset);
12662 *obj_list_size = (unsigned int)size;
12665 static void
12666 ibf_dump_mark(void *ptr)
12668 struct ibf_dump *dump = (struct ibf_dump *)ptr;
12669 rb_gc_mark(dump->global_buffer.str);
12671 rb_mark_set(dump->global_buffer.obj_table);
12672 rb_mark_set(dump->iseq_table);
12675 static void
12676 ibf_dump_free(void *ptr)
12678 struct ibf_dump *dump = (struct ibf_dump *)ptr;
12679 if (dump->global_buffer.obj_table) {
12680 st_free_table(dump->global_buffer.obj_table);
12681 dump->global_buffer.obj_table = 0;
12683 if (dump->iseq_table) {
12684 st_free_table(dump->iseq_table);
12685 dump->iseq_table = 0;
12687 ruby_xfree(dump);
12690 static size_t
12691 ibf_dump_memsize(const void *ptr)
12693 struct ibf_dump *dump = (struct ibf_dump *)ptr;
12694 size_t size = sizeof(*dump);
12695 if (dump->iseq_table) size += st_memsize(dump->iseq_table);
12696 if (dump->global_buffer.obj_table) size += st_memsize(dump->global_buffer.obj_table);
12697 return size;
12700 static const rb_data_type_t ibf_dump_type = {
12701 "ibf_dump",
12702 {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
12703 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
12706 static void
12707 ibf_dump_setup(struct ibf_dump *dump, VALUE dumper_obj)
12709 dump->global_buffer.obj_table = NULL; // GC may run before a value is assigned
12710 dump->iseq_table = NULL;
12712 RB_OBJ_WRITE(dumper_obj, &dump->global_buffer.str, rb_str_new(0, 0));
12713 dump->global_buffer.obj_table = ibf_dump_object_table_new();
12714 dump->iseq_table = st_init_numtable(); /* need free */
12716 dump->current_buffer = &dump->global_buffer;
12719 VALUE
12720 rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
12722 struct ibf_dump *dump;
12723 struct ibf_header header = {{0}};
12724 VALUE dump_obj;
12725 VALUE str;
12727 if (iseq->body->parent_iseq != NULL ||
12728 iseq->body->local_iseq != iseq) {
12729 rb_raise(rb_eRuntimeError, "should be top of iseq");
12731 if (RTEST(ISEQ_COVERAGE(iseq))) {
12732 rb_raise(rb_eRuntimeError, "should not compile with coverage");
12735 dump_obj = TypedData_Make_Struct(0, struct ibf_dump, &ibf_dump_type, dump);
12736 ibf_dump_setup(dump, dump_obj);
12738 ibf_dump_write(dump, &header, sizeof(header));
12739 ibf_dump_write(dump, RUBY_PLATFORM, strlen(RUBY_PLATFORM) + 1);
12740 ibf_dump_iseq(dump, iseq);
12742 header.magic[0] = 'Y'; /* YARB */
12743 header.magic[1] = 'A';
12744 header.magic[2] = 'R';
12745 header.magic[3] = 'B';
12746 header.major_version = IBF_MAJOR_VERSION;
12747 header.minor_version = IBF_MINOR_VERSION;
12748 ibf_dump_iseq_list(dump, &header);
12749 ibf_dump_object_list(dump, &header.global_object_list_offset, &header.global_object_list_size);
12750 header.size = ibf_dump_pos(dump);
12752 if (RTEST(opt)) {
12753 VALUE opt_str = opt;
12754 const char *ptr = StringValuePtr(opt_str);
12755 header.extra_size = RSTRING_LENINT(opt_str);
12756 ibf_dump_write(dump, ptr, header.extra_size);
12758 else {
12759 header.extra_size = 0;
12762 ibf_dump_overwrite(dump, &header, sizeof(header), 0);
12764 str = dump->global_buffer.str;
12765 ibf_dump_free(dump);
12766 DATA_PTR(dump_obj) = NULL;
12767 RB_GC_GUARD(dump_obj);
12768 return str;
12771 static const ibf_offset_t *
12772 ibf_iseq_list(const struct ibf_load *load)
12774 return (const ibf_offset_t *)(load->global_buffer.buff + load->header->iseq_list_offset);
12777 void
12778 rb_ibf_load_iseq_complete(rb_iseq_t *iseq)
12780 struct ibf_load *load = RTYPEDDATA_DATA(iseq->aux.loader.obj);
12781 rb_iseq_t *prev_src_iseq = load->iseq;
12782 ibf_offset_t offset = ibf_iseq_list(load)[iseq->aux.loader.index];
12783 load->iseq = iseq;
12784 #if IBF_ISEQ_DEBUG
12785 fprintf(stderr, "rb_ibf_load_iseq_complete: index=%#x offset=%#x size=%#x\n",
12786 iseq->aux.loader.index, offset,
12787 load->header->size);
12788 #endif
12789 ibf_load_iseq_each(load, iseq, offset);
12790 ISEQ_COMPILE_DATA_CLEAR(iseq);
12791 FL_UNSET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
12792 rb_iseq_init_trace(iseq);
12793 load->iseq = prev_src_iseq;
12796 #if USE_LAZY_LOAD
12797 MJIT_FUNC_EXPORTED const rb_iseq_t *
12798 rb_iseq_complete(const rb_iseq_t *iseq)
12800 rb_ibf_load_iseq_complete((rb_iseq_t *)iseq);
12801 return iseq;
12803 #endif
12805 static rb_iseq_t *
12806 ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
12808 int iseq_index = (int)(VALUE)index_iseq;
12810 #if IBF_ISEQ_DEBUG
12811 fprintf(stderr, "ibf_load_iseq: index_iseq=%p iseq_list=%p\n",
12812 (void *)index_iseq, (void *)load->iseq_list);
12813 #endif
12814 if (iseq_index == -1) {
12815 return NULL;
12817 else {
12818 VALUE iseqv = pinned_list_fetch(load->iseq_list, iseq_index);
12820 #if IBF_ISEQ_DEBUG
12821 fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
12822 #endif
12823 if (iseqv) {
12824 return (rb_iseq_t *)iseqv;
12826 else {
12827 rb_iseq_t *iseq = iseq_imemo_alloc();
12828 #if IBF_ISEQ_DEBUG
12829 fprintf(stderr, "ibf_load_iseq: new iseq=%p\n", (void *)iseq);
12830 #endif
12831 FL_SET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
12832 iseq->aux.loader.obj = load->loader_obj;
12833 iseq->aux.loader.index = iseq_index;
12834 #if IBF_ISEQ_DEBUG
12835 fprintf(stderr, "ibf_load_iseq: iseq=%p loader_obj=%p index=%d\n",
12836 (void *)iseq, (void *)load->loader_obj, iseq_index);
12837 #endif
12838 pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
12840 #if !USE_LAZY_LOAD
12841 #if IBF_ISEQ_DEBUG
12842 fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
12843 #endif
12844 rb_ibf_load_iseq_complete(iseq);
12845 #else
12846 if (GET_VM()->builtin_function_table) {
12847 rb_ibf_load_iseq_complete(iseq);
12849 #endif /* !USE_LAZY_LOAD */
12851 #if IBF_ISEQ_DEBUG
12852 fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
12853 (void *)iseq, (void *)load->iseq);
12854 #endif
12855 return iseq;
12860 static void
12861 ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, size_t size)
12863 load->loader_obj = loader_obj;
12864 load->global_buffer.buff = bytes;
12865 load->header = (struct ibf_header *)load->global_buffer.buff;
12866 load->global_buffer.size = load->header->size;
12867 load->global_buffer.obj_list_offset = load->header->global_object_list_offset;
12868 load->global_buffer.obj_list_size = load->header->global_object_list_size;
12869 RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(load->header->iseq_list_size));
12870 RB_OBJ_WRITE(loader_obj, &load->global_buffer.obj_list, pinned_list_new(load->global_buffer.obj_list_size));
12871 load->iseq = NULL;
12873 load->current_buffer = &load->global_buffer;
12875 if (size < load->header->size) {
12876 rb_raise(rb_eRuntimeError, "broken binary format");
12878 if (strncmp(load->header->magic, "YARB", 4) != 0) {
12879 rb_raise(rb_eRuntimeError, "unknown binary format");
12881 if (load->header->major_version != IBF_MAJOR_VERSION ||
12882 load->header->minor_version != IBF_MINOR_VERSION) {
12883 rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
12884 load->header->major_version, load->header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
12886 if (strcmp(load->global_buffer.buff + sizeof(struct ibf_header), RUBY_PLATFORM) != 0) {
12887 rb_raise(rb_eRuntimeError, "unmatched platform");
12889 if (load->header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
12890 rb_raise(rb_eArgError, "unaligned iseq list offset: %u",
12891 load->header->iseq_list_offset);
12893 if (load->global_buffer.obj_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
12894 rb_raise(rb_eArgError, "unaligned object list offset: %u",
12895 load->global_buffer.obj_list_offset);
12899 static void
12900 ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
12902 if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
12903 rb_raise(rb_eRuntimeError, "broken binary format");
12906 #if USE_LAZY_LOAD
12907 str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
12908 #endif
12910 ibf_load_setup_bytes(load, loader_obj, StringValuePtr(str), RSTRING_LEN(str));
12911 RB_OBJ_WRITE(loader_obj, &load->str, str);
12914 static void
12915 ibf_loader_mark(void *ptr)
12917 struct ibf_load *load = (struct ibf_load *)ptr;
12918 rb_gc_mark(load->str);
12919 rb_gc_mark(load->iseq_list);
12920 rb_gc_mark(load->global_buffer.obj_list);
12923 static void
12924 ibf_loader_free(void *ptr)
12926 struct ibf_load *load = (struct ibf_load *)ptr;
12927 ruby_xfree(load);
12930 static size_t
12931 ibf_loader_memsize(const void *ptr)
12933 return sizeof(struct ibf_load);
12936 static const rb_data_type_t ibf_load_type = {
12937 "ibf_loader",
12938 {ibf_loader_mark, ibf_loader_free, ibf_loader_memsize,},
12939 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
12942 const rb_iseq_t *
12943 rb_iseq_ibf_load(VALUE str)
12945 struct ibf_load *load;
12946 rb_iseq_t *iseq;
12947 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
12949 ibf_load_setup(load, loader_obj, str);
12950 iseq = ibf_load_iseq(load, 0);
12952 RB_GC_GUARD(loader_obj);
12953 return iseq;
12956 const rb_iseq_t *
12957 rb_iseq_ibf_load_bytes(const char *bytes, size_t size)
12959 struct ibf_load *load;
12960 rb_iseq_t *iseq;
12961 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
12963 ibf_load_setup_bytes(load, loader_obj, bytes, size);
12964 iseq = ibf_load_iseq(load, 0);
12966 RB_GC_GUARD(loader_obj);
12967 return iseq;
12970 VALUE
12971 rb_iseq_ibf_load_extra_data(VALUE str)
12973 struct ibf_load *load;
12974 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
12975 VALUE extra_str;
12977 ibf_load_setup(load, loader_obj, str);
12978 extra_str = rb_str_new(load->global_buffer.buff + load->header->size, load->header->extra_size);
12979 RB_GC_GUARD(loader_obj);
12980 return extra_str;