[rubygems/rubygems] Use a constant empty tar header to avoid extra allocations
[ruby.git] / compile.c
blobc319b72b524d78df79536cfa86e62f1735bd8a4f
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 "id_table.h"
21 #include "internal.h"
22 #include "internal/array.h"
23 #include "internal/compile.h"
24 #include "internal/complex.h"
25 #include "internal/encoding.h"
26 #include "internal/error.h"
27 #include "internal/gc.h"
28 #include "internal/hash.h"
29 #include "internal/io.h"
30 #include "internal/numeric.h"
31 #include "internal/object.h"
32 #include "internal/rational.h"
33 #include "internal/re.h"
34 #include "internal/ruby_parser.h"
35 #include "internal/symbol.h"
36 #include "internal/thread.h"
37 #include "internal/variable.h"
38 #include "iseq.h"
39 #include "ruby/ractor.h"
40 #include "ruby/re.h"
41 #include "ruby/util.h"
42 #include "vm_core.h"
43 #include "vm_callinfo.h"
44 #include "vm_debug.h"
45 #include "yjit.h"
47 #include "builtin.h"
48 #include "insns.inc"
49 #include "insns_info.inc"
51 #undef RUBY_UNTYPED_DATA_WARNING
52 #define RUBY_UNTYPED_DATA_WARNING 0
54 #define FIXNUM_INC(n, i) ((n)+(INT2FIX(i)&~FIXNUM_FLAG))
55 #define FIXNUM_OR(n, i) ((n)|INT2FIX(i))
57 typedef struct iseq_link_element {
58 enum {
59 ISEQ_ELEMENT_ANCHOR,
60 ISEQ_ELEMENT_LABEL,
61 ISEQ_ELEMENT_INSN,
62 ISEQ_ELEMENT_ADJUST,
63 ISEQ_ELEMENT_TRACE,
64 } type;
65 struct iseq_link_element *next;
66 struct iseq_link_element *prev;
67 } LINK_ELEMENT;
69 typedef struct iseq_link_anchor {
70 LINK_ELEMENT anchor;
71 LINK_ELEMENT *last;
72 } LINK_ANCHOR;
74 typedef enum {
75 LABEL_RESCUE_NONE,
76 LABEL_RESCUE_BEG,
77 LABEL_RESCUE_END,
78 LABEL_RESCUE_TYPE_MAX
79 } LABEL_RESCUE_TYPE;
81 typedef struct iseq_label_data {
82 LINK_ELEMENT link;
83 int label_no;
84 int position;
85 int sc_state;
86 int sp;
87 int refcnt;
88 unsigned int set: 1;
89 unsigned int rescued: 2;
90 unsigned int unremovable: 1;
91 } LABEL;
93 typedef struct iseq_insn_data {
94 LINK_ELEMENT link;
95 enum ruby_vminsn_type insn_id;
96 int operand_size;
97 int sc_state;
98 VALUE *operands;
99 struct {
100 int line_no;
101 int node_id;
102 rb_event_flag_t events;
103 } insn_info;
104 } INSN;
106 typedef struct iseq_adjust_data {
107 LINK_ELEMENT link;
108 LABEL *label;
109 int line_no;
110 } ADJUST;
112 typedef struct iseq_trace_data {
113 LINK_ELEMENT link;
114 rb_event_flag_t event;
115 long data;
116 } TRACE;
118 struct ensure_range {
119 LABEL *begin;
120 LABEL *end;
121 struct ensure_range *next;
124 struct iseq_compile_data_ensure_node_stack {
125 const void *ensure_node;
126 struct iseq_compile_data_ensure_node_stack *prev;
127 struct ensure_range *erange;
130 const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO};
133 * debug function(macro) interface depend on CPDEBUG
134 * if it is less than 0, runtime option is in effect.
136 * debug level:
137 * 0: no debug output
138 * 1: show node type
139 * 2: show node important parameters
140 * ...
141 * 5: show other parameters
142 * 10: show every AST array
145 #ifndef CPDEBUG
146 #define CPDEBUG 0
147 #endif
149 #if CPDEBUG >= 0
150 #define compile_debug CPDEBUG
151 #else
152 #define compile_debug ISEQ_COMPILE_DATA(iseq)->option->debug_level
153 #endif
155 #if CPDEBUG
157 #define compile_debug_print_indent(level) \
158 ruby_debug_print_indent((level), compile_debug, gl_node_level * 2)
160 #define debugp(header, value) (void) \
161 (compile_debug_print_indent(1) && \
162 ruby_debug_print_value(1, compile_debug, (header), (value)))
164 #define debugi(header, id) (void) \
165 (compile_debug_print_indent(1) && \
166 ruby_debug_print_id(1, compile_debug, (header), (id)))
168 #define debugp_param(header, value) (void) \
169 (compile_debug_print_indent(1) && \
170 ruby_debug_print_value(1, compile_debug, (header), (value)))
172 #define debugp_verbose(header, value) (void) \
173 (compile_debug_print_indent(2) && \
174 ruby_debug_print_value(2, compile_debug, (header), (value)))
176 #define debugp_verbose_node(header, value) (void) \
177 (compile_debug_print_indent(10) && \
178 ruby_debug_print_value(10, compile_debug, (header), (value)))
180 #define debug_node_start(node) ((void) \
181 (compile_debug_print_indent(1) && \
182 (ruby_debug_print_node(1, CPDEBUG, "", (const NODE *)(node)), gl_node_level)), \
183 gl_node_level++)
185 #define debug_node_end() gl_node_level --
187 #else
189 #define debugi(header, id) ((void)0)
190 #define debugp(header, value) ((void)0)
191 #define debugp_verbose(header, value) ((void)0)
192 #define debugp_verbose_node(header, value) ((void)0)
193 #define debugp_param(header, value) ((void)0)
194 #define debug_node_start(node) ((void)0)
195 #define debug_node_end() ((void)0)
196 #endif
198 #if CPDEBUG > 1 || CPDEBUG < 0
199 #undef printf
200 #define printf ruby_debug_printf
201 #define debugs if (compile_debug_print_indent(1)) ruby_debug_printf
202 #define debug_compile(msg, v) ((void)(compile_debug_print_indent(1) && fputs((msg), stderr)), (v))
203 #else
204 #define debugs if(0)printf
205 #define debug_compile(msg, v) (v)
206 #endif
208 #define LVAR_ERRINFO (1)
210 /* create new label */
211 #define NEW_LABEL(l) new_label_body(iseq, (l))
212 #define LABEL_FORMAT "<L%03d>"
214 #define NEW_ISEQ(node, name, type, line_no) \
215 new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
217 #define NEW_CHILD_ISEQ(node, name, type, line_no) \
218 new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
220 /* add instructions */
221 #define ADD_SEQ(seq1, seq2) \
222 APPEND_LIST((seq1), (seq2))
224 /* add an instruction */
225 #define ADD_INSN(seq, line_node, insn) \
226 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 0))
228 /* add an instruction with the given line number and node id */
229 #define ADD_SYNTHETIC_INSN(seq, line_no, node_id, insn) \
230 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line_no), (node_id), BIN(insn), 0))
232 /* insert an instruction before next */
233 #define INSERT_BEFORE_INSN(next, line_no, node_id, insn) \
234 ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0))
236 /* insert an instruction after prev */
237 #define INSERT_AFTER_INSN(prev, line_no, node_id, insn) \
238 ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0))
240 /* add an instruction with some operands (1, 2, 3, 5) */
241 #define ADD_INSN1(seq, line_node, insn, op1) \
242 ADD_ELEM((seq), (LINK_ELEMENT *) \
243 new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 1, (VALUE)(op1)))
245 /* insert an instruction with some operands (1, 2, 3, 5) before next */
246 #define INSERT_BEFORE_INSN1(next, line_no, node_id, insn, op1) \
247 ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) \
248 new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1)))
250 /* insert an instruction with some operands (1, 2, 3, 5) after prev */
251 #define INSERT_AFTER_INSN1(prev, line_no, node_id, insn, op1) \
252 ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) \
253 new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1)))
255 #define LABEL_REF(label) ((label)->refcnt++)
257 /* add an instruction with label operand (alias of ADD_INSN1) */
258 #define ADD_INSNL(seq, line_node, insn, label) (ADD_INSN1(seq, line_node, insn, label), LABEL_REF(label))
260 #define ADD_INSN2(seq, line_node, insn, op1, op2) \
261 ADD_ELEM((seq), (LINK_ELEMENT *) \
262 new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
264 #define ADD_INSN3(seq, line_node, insn, op1, op2, op3) \
265 ADD_ELEM((seq), (LINK_ELEMENT *) \
266 new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
268 /* Specific Insn factory */
269 #define ADD_SEND(seq, line_node, id, argc) \
270 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
272 #define ADD_SEND_WITH_FLAG(seq, line_node, id, argc, flag) \
273 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)(flag), NULL)
275 #define ADD_SEND_WITH_BLOCK(seq, line_node, id, argc, block) \
276 ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
278 #define ADD_CALL_RECEIVER(seq, line_node) \
279 ADD_INSN((seq), (line_node), putself)
281 #define ADD_CALL(seq, line_node, id, argc) \
282 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
284 #define ADD_CALL_WITH_BLOCK(seq, line_node, id, argc, block) \
285 ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
287 #define ADD_SEND_R(seq, line_node, id, argc, block, flag, keywords) \
288 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
290 #define ADD_TRACE(seq, event) \
291 ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), 0))
292 #define ADD_TRACE_WITH_DATA(seq, event, data) \
293 ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), (data)))
295 static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level);
296 static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level);
298 #define ADD_GETLOCAL(seq, line_node, idx, level) iseq_add_getlocal(iseq, (seq), (line_node), (idx), (level))
299 #define ADD_SETLOCAL(seq, line_node, idx, level) iseq_add_setlocal(iseq, (seq), (line_node), (idx), (level))
301 /* add label */
302 #define ADD_LABEL(seq, label) \
303 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
305 #define APPEND_LABEL(seq, before, label) \
306 APPEND_ELEM((seq), (before), (LINK_ELEMENT *) (label))
308 #define ADD_ADJUST(seq, line_node, label) \
309 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), nd_line(line_node)))
311 #define ADD_ADJUST_RESTORE(seq, label) \
312 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
314 #define LABEL_UNREMOVABLE(label) \
315 ((label) ? (LABEL_REF(label), (label)->unremovable=1) : 0)
316 #define ADD_CATCH_ENTRY(type, ls, le, iseqv, lc) do { \
317 VALUE _e = rb_ary_new3(5, (type), \
318 (VALUE)(ls) | 1, (VALUE)(le) | 1, \
319 (VALUE)(iseqv), (VALUE)(lc) | 1); \
320 LABEL_UNREMOVABLE(ls); \
321 LABEL_REF(le); \
322 LABEL_REF(lc); \
323 if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) \
324 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, rb_ary_hidden_new(3)); \
325 rb_ary_push(ISEQ_COMPILE_DATA(iseq)->catch_table_ary, freeze_hide_obj(_e)); \
326 } while (0)
328 /* compile node */
329 #define COMPILE(anchor, desc, node) \
330 (debug_compile("== " desc "\n", \
331 iseq_compile_each(iseq, (anchor), (node), 0)))
333 /* compile node, this node's value will be popped */
334 #define COMPILE_POPPED(anchor, desc, node) \
335 (debug_compile("== " desc "\n", \
336 iseq_compile_each(iseq, (anchor), (node), 1)))
338 /* compile node, which is popped when 'popped' is true */
339 #define COMPILE_(anchor, desc, node, popped) \
340 (debug_compile("== " desc "\n", \
341 iseq_compile_each(iseq, (anchor), (node), (popped))))
343 #define COMPILE_RECV(anchor, desc, node, recv) \
344 (private_recv_p(node) ? \
345 (ADD_INSN(anchor, node, putself), VM_CALL_FCALL) : \
346 COMPILE(anchor, desc, recv) ? 0 : -1)
348 #define OPERAND_AT(insn, idx) \
349 (((INSN*)(insn))->operands[(idx)])
351 #define INSN_OF(insn) \
352 (((INSN*)(insn))->insn_id)
354 #define IS_INSN(link) ((link)->type == ISEQ_ELEMENT_INSN)
355 #define IS_LABEL(link) ((link)->type == ISEQ_ELEMENT_LABEL)
356 #define IS_ADJUST(link) ((link)->type == ISEQ_ELEMENT_ADJUST)
357 #define IS_TRACE(link) ((link)->type == ISEQ_ELEMENT_TRACE)
358 #define IS_INSN_ID(iobj, insn) (INSN_OF(iobj) == BIN(insn))
359 #define IS_NEXT_INSN_ID(link, insn) \
360 ((link)->next && IS_INSN((link)->next) && IS_INSN_ID((link)->next, insn))
362 /* error */
363 #if CPDEBUG > 0
364 RBIMPL_ATTR_NORETURN()
365 #endif
366 RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
367 static void
368 append_compile_error(const rb_iseq_t *iseq, int line, const char *fmt, ...)
370 VALUE err_info = ISEQ_COMPILE_DATA(iseq)->err_info;
371 VALUE file = rb_iseq_path(iseq);
372 VALUE err = err_info == Qtrue ? Qfalse : err_info;
373 va_list args;
375 va_start(args, fmt);
376 err = rb_syntax_error_append(err, file, line, -1, NULL, fmt, args);
377 va_end(args);
378 if (NIL_P(err_info)) {
379 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err);
380 rb_set_errinfo(err);
382 else if (!err_info) {
383 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, Qtrue);
385 if (compile_debug) {
386 if (SPECIAL_CONST_P(err)) err = rb_eSyntaxError;
387 rb_exc_fatal(err);
391 #if 0
392 static void
393 compile_bug(rb_iseq_t *iseq, int line, const char *fmt, ...)
395 va_list args;
396 va_start(args, fmt);
397 rb_report_bug_valist(rb_iseq_path(iseq), line, fmt, args);
398 va_end(args);
399 abort();
401 #endif
403 #define COMPILE_ERROR append_compile_error
405 #define ERROR_ARGS_AT(n) iseq, nd_line(n),
406 #define ERROR_ARGS ERROR_ARGS_AT(node)
408 #define EXPECT_NODE(prefix, node, ndtype, errval) \
409 do { \
410 const NODE *error_node = (node); \
411 enum node_type error_type = nd_type(error_node); \
412 if (error_type != (ndtype)) { \
413 COMPILE_ERROR(ERROR_ARGS_AT(error_node) \
414 prefix ": " #ndtype " is expected, but %s", \
415 ruby_node_name(error_type)); \
416 return errval; \
418 } while (0)
420 #define EXPECT_NODE_NONULL(prefix, parent, ndtype, errval) \
421 do { \
422 COMPILE_ERROR(ERROR_ARGS_AT(parent) \
423 prefix ": must be " #ndtype ", but 0"); \
424 return errval; \
425 } while (0)
427 #define UNKNOWN_NODE(prefix, node, errval) \
428 do { \
429 const NODE *error_node = (node); \
430 COMPILE_ERROR(ERROR_ARGS_AT(error_node) prefix ": unknown node (%s)", \
431 ruby_node_name(nd_type(error_node))); \
432 return errval; \
433 } while (0)
435 #define COMPILE_OK 1
436 #define COMPILE_NG 0
438 #define CHECK(sub) if (!(sub)) {BEFORE_RETURN;return COMPILE_NG;}
439 #define NO_CHECK(sub) (void)(sub)
440 #define BEFORE_RETURN
442 /* leave name uninitialized so that compiler warn if INIT_ANCHOR is
443 * missing */
444 #define DECL_ANCHOR(name) \
445 LINK_ANCHOR name[1] = {{{ISEQ_ELEMENT_ANCHOR,},}}
446 #define INIT_ANCHOR(name) \
447 (name->last = &name->anchor)
449 static inline VALUE
450 freeze_hide_obj(VALUE obj)
452 OBJ_FREEZE(obj);
453 RBASIC_CLEAR_CLASS(obj);
454 return obj;
457 #include "optinsn.inc"
458 #if OPT_INSTRUCTIONS_UNIFICATION
459 #include "optunifs.inc"
460 #endif
462 /* for debug */
463 #if CPDEBUG < 0
464 #define ISEQ_ARG iseq,
465 #define ISEQ_ARG_DECLARE rb_iseq_t *iseq,
466 #else
467 #define ISEQ_ARG
468 #define ISEQ_ARG_DECLARE
469 #endif
471 #if CPDEBUG
472 #define gl_node_level ISEQ_COMPILE_DATA(iseq)->node_level
473 #endif
475 static void dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest);
476 static void dump_disasm_list(const LINK_ELEMENT *elem);
478 static int insn_data_length(INSN *iobj);
479 static int calc_sp_depth(int depth, INSN *iobj);
481 static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type insn_id, int argc, ...);
482 static LABEL *new_label_body(rb_iseq_t *iseq, long line);
483 static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line);
484 static TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data);
487 static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, const NODE *n, int);
488 static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
489 static int iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
490 static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
491 static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
493 static int iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl);
494 static int iseq_set_exception_local_table(rb_iseq_t *iseq);
495 static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const NODE *const node);
497 static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
498 static int iseq_set_exception_table(rb_iseq_t *iseq);
499 static int iseq_set_optargs_table(rb_iseq_t *iseq);
501 static int compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr);
502 static int compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped);
505 * To make Array to LinkedList, use link_anchor
508 static void
509 verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *const anchor)
511 #if CPDEBUG
512 int flag = 0;
513 LINK_ELEMENT *list, *plist;
515 if (!compile_debug) return;
517 list = anchor->anchor.next;
518 plist = &anchor->anchor;
519 while (list) {
520 if (plist != list->prev) {
521 flag += 1;
523 plist = list;
524 list = list->next;
527 if (anchor->last != plist && anchor->last != 0) {
528 flag |= 0x70000;
531 if (flag != 0) {
532 rb_bug("list verify error: %08x (%s)", flag, info);
534 #endif
536 #if CPDEBUG < 0
537 #define verify_list(info, anchor) verify_list(iseq, (info), (anchor))
538 #endif
540 static void
541 verify_call_cache(rb_iseq_t *iseq)
543 #if CPDEBUG
544 VALUE *original = rb_iseq_original_iseq(iseq);
545 size_t i = 0;
546 while (i < ISEQ_BODY(iseq)->iseq_size) {
547 VALUE insn = original[i];
548 const char *types = insn_op_types(insn);
550 for (int j=0; types[j]; j++) {
551 if (types[j] == TS_CALLDATA) {
552 struct rb_call_data *cd = (struct rb_call_data *)original[i+j+1];
553 const struct rb_callinfo *ci = cd->ci;
554 const struct rb_callcache *cc = cd->cc;
555 if (cc != vm_cc_empty()) {
556 vm_ci_dump(ci);
557 rb_bug("call cache is not initialized by vm_cc_empty()");
561 i += insn_len(insn);
564 for (unsigned int i=0; i<ISEQ_BODY(iseq)->ci_size; i++) {
565 struct rb_call_data *cd = &ISEQ_BODY(iseq)->call_data[i];
566 const struct rb_callinfo *ci = cd->ci;
567 const struct rb_callcache *cc = cd->cc;
568 if (cc != NULL && cc != vm_cc_empty()) {
569 vm_ci_dump(ci);
570 rb_bug("call cache is not initialized by vm_cc_empty()");
573 #endif
577 * elem1, elem2 => elem1, elem2, elem
579 static void
580 ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *elem)
582 elem->prev = anchor->last;
583 anchor->last->next = elem;
584 anchor->last = elem;
585 verify_list("add", anchor);
589 * elem1, before, elem2 => elem1, before, elem, elem2
591 static void
592 APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *before, LINK_ELEMENT *elem)
594 elem->prev = before;
595 elem->next = before->next;
596 elem->next->prev = elem;
597 before->next = elem;
598 if (before == anchor->last) anchor->last = elem;
599 verify_list("add", anchor);
601 #if CPDEBUG < 0
602 #define ADD_ELEM(anchor, elem) ADD_ELEM(iseq, (anchor), (elem))
603 #define APPEND_ELEM(anchor, before, elem) APPEND_ELEM(iseq, (anchor), (before), (elem))
604 #endif
606 static int
607 branch_coverage_valid_p(rb_iseq_t *iseq, int first_line)
609 if (!ISEQ_COVERAGE(iseq)) return 0;
610 if (!ISEQ_BRANCH_COVERAGE(iseq)) return 0;
611 if (first_line <= 0) return 0;
612 return 1;
615 #define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x)))
617 static VALUE
618 decl_branch_base(rb_iseq_t *iseq, VALUE key, const rb_code_location_t *loc, const char *type)
620 const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column;
621 const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column;
623 if (!branch_coverage_valid_p(iseq, first_lineno)) return Qundef;
626 * if !structure[node]
627 * structure[node] = [type, first_lineno, first_column, last_lineno, last_column, branches = {}]
628 * else
629 * branches = structure[node][5]
630 * end
633 VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0);
634 VALUE branch_base = rb_hash_aref(structure, key);
635 VALUE branches;
637 if (NIL_P(branch_base)) {
638 branch_base = rb_ary_hidden_new(6);
639 rb_hash_aset(structure, key, branch_base);
640 rb_ary_push(branch_base, ID2SYM(rb_intern(type)));
641 rb_ary_push(branch_base, INT2FIX(first_lineno));
642 rb_ary_push(branch_base, INT2FIX(first_column));
643 rb_ary_push(branch_base, INT2FIX(last_lineno));
644 rb_ary_push(branch_base, INT2FIX(last_column));
645 branches = rb_hash_new();
646 rb_obj_hide(branches);
647 rb_ary_push(branch_base, branches);
649 else {
650 branches = RARRAY_AREF(branch_base, 5);
653 return branches;
656 static NODE
657 generate_dummy_line_node(int lineno, int node_id)
659 NODE dummy = { 0 };
660 nd_set_line(&dummy, lineno);
661 nd_set_node_id(&dummy, node_id);
662 return dummy;
665 static void
666 add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const rb_code_location_t *loc, int node_id, int branch_id, const char *type, VALUE branches)
668 const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column;
669 const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column;
671 if (!branch_coverage_valid_p(iseq, first_lineno)) return;
674 * if !branches[branch_id]
675 * branches[branch_id] = [type, first_lineno, first_column, last_lineno, last_column, counter_idx]
676 * else
677 * counter_idx= branches[branch_id][5]
678 * end
681 VALUE key = INT2FIX(branch_id);
682 VALUE branch = rb_hash_aref(branches, key);
683 long counter_idx;
685 if (NIL_P(branch)) {
686 branch = rb_ary_hidden_new(6);
687 rb_hash_aset(branches, key, branch);
688 rb_ary_push(branch, ID2SYM(rb_intern(type)));
689 rb_ary_push(branch, INT2FIX(first_lineno));
690 rb_ary_push(branch, INT2FIX(first_column));
691 rb_ary_push(branch, INT2FIX(last_lineno));
692 rb_ary_push(branch, INT2FIX(last_column));
693 VALUE counters = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 1);
694 counter_idx = RARRAY_LEN(counters);
695 rb_ary_push(branch, LONG2FIX(counter_idx));
696 rb_ary_push(counters, INT2FIX(0));
698 else {
699 counter_idx = FIX2LONG(RARRAY_AREF(branch, 5));
702 ADD_TRACE_WITH_DATA(seq, RUBY_EVENT_COVERAGE_BRANCH, counter_idx);
703 ADD_SYNTHETIC_INSN(seq, last_lineno, node_id, nop);
706 #define ISEQ_LAST_LINE(iseq) (ISEQ_COMPILE_DATA(iseq)->last_line)
708 static int
709 validate_label(st_data_t name, st_data_t label, st_data_t arg)
711 rb_iseq_t *iseq = (rb_iseq_t *)arg;
712 LABEL *lobj = (LABEL *)label;
713 if (!lobj->link.next) {
714 do {
715 COMPILE_ERROR(iseq, lobj->position,
716 "%"PRIsVALUE": undefined label",
717 rb_sym2str((VALUE)name));
718 } while (0);
720 return ST_CONTINUE;
723 static void
724 validate_labels(rb_iseq_t *iseq, st_table *labels_table)
726 st_foreach(labels_table, validate_label, (st_data_t)iseq);
727 st_free_table(labels_table);
730 static NODE *
731 get_nd_recv(const NODE *node)
733 switch (nd_type(node)) {
734 case NODE_CALL:
735 return RNODE_CALL(node)->nd_recv;
736 case NODE_OPCALL:
737 return RNODE_OPCALL(node)->nd_recv;
738 case NODE_FCALL:
739 return 0;
740 case NODE_QCALL:
741 return RNODE_QCALL(node)->nd_recv;
742 case NODE_VCALL:
743 return 0;
744 case NODE_ATTRASGN:
745 return RNODE_ATTRASGN(node)->nd_recv;
746 case NODE_OP_ASGN1:
747 return RNODE_OP_ASGN1(node)->nd_recv;
748 case NODE_OP_ASGN2:
749 return RNODE_OP_ASGN2(node)->nd_recv;
750 default:
751 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
755 static ID
756 get_node_call_nd_mid(const NODE *node)
758 switch (nd_type(node)) {
759 case NODE_CALL:
760 return RNODE_CALL(node)->nd_mid;
761 case NODE_OPCALL:
762 return RNODE_OPCALL(node)->nd_mid;
763 case NODE_FCALL:
764 return RNODE_FCALL(node)->nd_mid;
765 case NODE_QCALL:
766 return RNODE_QCALL(node)->nd_mid;
767 case NODE_VCALL:
768 return RNODE_VCALL(node)->nd_mid;
769 case NODE_ATTRASGN:
770 return RNODE_ATTRASGN(node)->nd_mid;
771 default:
772 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
776 static NODE *
777 get_nd_args(const NODE *node)
779 switch (nd_type(node)) {
780 case NODE_CALL:
781 return RNODE_CALL(node)->nd_args;
782 case NODE_OPCALL:
783 return RNODE_OPCALL(node)->nd_args;
784 case NODE_FCALL:
785 return RNODE_FCALL(node)->nd_args;
786 case NODE_QCALL:
787 return RNODE_QCALL(node)->nd_args;
788 case NODE_VCALL:
789 return 0;
790 case NODE_ATTRASGN:
791 return RNODE_ATTRASGN(node)->nd_args;
792 default:
793 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
797 static ID
798 get_node_colon_nd_mid(const NODE *node)
800 switch (nd_type(node)) {
801 case NODE_COLON2:
802 return RNODE_COLON2(node)->nd_mid;
803 case NODE_COLON3:
804 return RNODE_COLON3(node)->nd_mid;
805 default:
806 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
810 static ID
811 get_nd_vid(const NODE *node)
813 switch (nd_type(node)) {
814 case NODE_LASGN:
815 return RNODE_LASGN(node)->nd_vid;
816 case NODE_DASGN:
817 return RNODE_DASGN(node)->nd_vid;
818 case NODE_IASGN:
819 return RNODE_IASGN(node)->nd_vid;
820 case NODE_CVASGN:
821 return RNODE_CVASGN(node)->nd_vid;
822 default:
823 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
827 static NODE *
828 get_nd_value(const NODE *node)
830 switch (nd_type(node)) {
831 case NODE_LASGN:
832 return RNODE_LASGN(node)->nd_value;
833 case NODE_DASGN:
834 return RNODE_DASGN(node)->nd_value;
835 default:
836 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
840 static VALUE
841 get_string_value(const NODE *node)
843 switch (nd_type(node)) {
844 case NODE_STR:
845 return rb_node_str_string_val(node);
846 case NODE_FILE:
847 return rb_node_file_path_val(node);
848 default:
849 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
853 VALUE
854 rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func * ifunc)
856 DECL_ANCHOR(ret);
857 INIT_ANCHOR(ret);
859 (*ifunc->func)(iseq, ret, ifunc->data);
861 ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave);
863 CHECK(iseq_setup_insn(iseq, ret));
864 return iseq_setup(iseq, ret);
867 VALUE
868 rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
870 DECL_ANCHOR(ret);
871 INIT_ANCHOR(ret);
873 if (node == 0) {
874 NO_CHECK(COMPILE(ret, "nil", node));
875 iseq_set_local_table(iseq, 0);
877 /* assume node is T_NODE */
878 else if (nd_type_p(node, NODE_SCOPE)) {
879 /* iseq type of top, method, class, block */
880 iseq_set_local_table(iseq, RNODE_SCOPE(node)->nd_tbl);
881 iseq_set_arguments(iseq, ret, (NODE *)RNODE_SCOPE(node)->nd_args);
883 switch (ISEQ_BODY(iseq)->type) {
884 case ISEQ_TYPE_BLOCK:
886 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
887 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
889 start->rescued = LABEL_RESCUE_BEG;
890 end->rescued = LABEL_RESCUE_END;
892 ADD_TRACE(ret, RUBY_EVENT_B_CALL);
893 ADD_SYNTHETIC_INSN(ret, ISEQ_BODY(iseq)->location.first_lineno, -1, nop);
894 ADD_LABEL(ret, start);
895 CHECK(COMPILE(ret, "block body", RNODE_SCOPE(node)->nd_body));
896 ADD_LABEL(ret, end);
897 ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
898 ISEQ_COMPILE_DATA(iseq)->last_line = ISEQ_BODY(iseq)->location.code_location.end_pos.lineno;
900 /* wide range catch handler must put at last */
901 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
902 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
903 break;
905 case ISEQ_TYPE_CLASS:
907 ADD_TRACE(ret, RUBY_EVENT_CLASS);
908 CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
909 ADD_TRACE(ret, RUBY_EVENT_END);
910 ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
911 break;
913 case ISEQ_TYPE_METHOD:
915 ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body;
916 ADD_TRACE(ret, RUBY_EVENT_CALL);
917 CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
918 ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body;
919 ADD_TRACE(ret, RUBY_EVENT_RETURN);
920 ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
921 break;
923 default: {
924 CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
925 break;
929 else {
930 const char *m;
931 #define INVALID_ISEQ_TYPE(type) \
932 ISEQ_TYPE_##type: m = #type; goto invalid_iseq_type
933 switch (ISEQ_BODY(iseq)->type) {
934 case INVALID_ISEQ_TYPE(METHOD);
935 case INVALID_ISEQ_TYPE(CLASS);
936 case INVALID_ISEQ_TYPE(BLOCK);
937 case INVALID_ISEQ_TYPE(EVAL);
938 case INVALID_ISEQ_TYPE(MAIN);
939 case INVALID_ISEQ_TYPE(TOP);
940 #undef INVALID_ISEQ_TYPE /* invalid iseq types end */
941 case ISEQ_TYPE_RESCUE:
942 iseq_set_exception_local_table(iseq);
943 CHECK(COMPILE(ret, "rescue", node));
944 break;
945 case ISEQ_TYPE_ENSURE:
946 iseq_set_exception_local_table(iseq);
947 CHECK(COMPILE_POPPED(ret, "ensure", node));
948 break;
949 case ISEQ_TYPE_PLAIN:
950 CHECK(COMPILE(ret, "ensure", node));
951 break;
952 default:
953 COMPILE_ERROR(ERROR_ARGS "unknown scope: %d", ISEQ_BODY(iseq)->type);
954 return COMPILE_NG;
955 invalid_iseq_type:
956 COMPILE_ERROR(ERROR_ARGS "compile/ISEQ_TYPE_%s should not be reached", m);
957 return COMPILE_NG;
961 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE || ISEQ_BODY(iseq)->type == ISEQ_TYPE_ENSURE) {
962 NODE dummy_line_node = generate_dummy_line_node(0, -1);
963 ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0);
964 ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ );
966 else {
967 ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave);
970 #if OPT_SUPPORT_JOKE
971 if (ISEQ_COMPILE_DATA(iseq)->labels_table) {
972 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
973 ISEQ_COMPILE_DATA(iseq)->labels_table = 0;
974 validate_labels(iseq, labels_table);
976 #endif
977 CHECK(iseq_setup_insn(iseq, ret));
978 return iseq_setup(iseq, ret);
981 static int
982 rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
984 #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
985 const void * const *table = rb_vm_get_insns_address_table();
986 unsigned int i;
987 VALUE *encoded = (VALUE *)ISEQ_BODY(iseq)->iseq_encoded;
989 for (i = 0; i < ISEQ_BODY(iseq)->iseq_size; /* */ ) {
990 int insn = (int)ISEQ_BODY(iseq)->iseq_encoded[i];
991 int len = insn_len(insn);
992 encoded[i] = (VALUE)table[insn];
993 i += len;
995 FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
996 #endif
998 #if USE_YJIT
999 rb_yjit_live_iseq_count++;
1000 rb_yjit_iseq_alloc_count++;
1001 #endif
1003 return COMPILE_OK;
1006 VALUE *
1007 rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */
1009 VALUE *original_code;
1011 if (ISEQ_ORIGINAL_ISEQ(iseq)) return ISEQ_ORIGINAL_ISEQ(iseq);
1012 original_code = ISEQ_ORIGINAL_ISEQ_ALLOC(iseq, ISEQ_BODY(iseq)->iseq_size);
1013 MEMCPY(original_code, ISEQ_BODY(iseq)->iseq_encoded, VALUE, ISEQ_BODY(iseq)->iseq_size);
1015 #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
1017 unsigned int i;
1019 for (i = 0; i < ISEQ_BODY(iseq)->iseq_size; /* */ ) {
1020 const void *addr = (const void *)original_code[i];
1021 const int insn = rb_vm_insn_addr2insn(addr);
1023 original_code[i] = insn;
1024 i += insn_len(insn);
1027 #endif
1028 return original_code;
1031 /*********************************************/
1032 /* definition of data structure for compiler */
1033 /*********************************************/
1036 * On 32-bit SPARC, GCC by default generates SPARC V7 code that may require
1037 * 8-byte word alignment. On the other hand, Oracle Solaris Studio seems to
1038 * generate SPARCV8PLUS code with unaligned memory access instructions.
1039 * That is why the STRICT_ALIGNMENT is defined only with GCC.
1041 #if defined(__sparc) && SIZEOF_VOIDP == 4 && defined(__GNUC__)
1042 #define STRICT_ALIGNMENT
1043 #endif
1046 * Some OpenBSD platforms (including sparc64) require strict alignment.
1048 #if defined(__OpenBSD__)
1049 #include <sys/endian.h>
1050 #ifdef __STRICT_ALIGNMENT
1051 #define STRICT_ALIGNMENT
1052 #endif
1053 #endif
1055 #ifdef STRICT_ALIGNMENT
1056 #if defined(HAVE_TRUE_LONG_LONG) && SIZEOF_LONG_LONG > SIZEOF_VALUE
1057 #define ALIGNMENT_SIZE SIZEOF_LONG_LONG
1058 #else
1059 #define ALIGNMENT_SIZE SIZEOF_VALUE
1060 #endif
1061 #define PADDING_SIZE_MAX ((size_t)((ALIGNMENT_SIZE) - 1))
1062 #define ALIGNMENT_SIZE_MASK PADDING_SIZE_MAX
1063 /* Note: ALIGNMENT_SIZE == (2 ** N) is expected. */
1064 #else
1065 #define PADDING_SIZE_MAX 0
1066 #endif /* STRICT_ALIGNMENT */
1068 #ifdef STRICT_ALIGNMENT
1069 /* calculate padding size for aligned memory access */
1070 static size_t
1071 calc_padding(void *ptr, size_t size)
1073 size_t mis;
1074 size_t padding = 0;
1076 mis = (size_t)ptr & ALIGNMENT_SIZE_MASK;
1077 if (mis > 0) {
1078 padding = ALIGNMENT_SIZE - mis;
1081 * On 32-bit sparc or equivalents, when a single VALUE is requested
1082 * and padding == sizeof(VALUE), it is clear that no padding is needed.
1084 #if ALIGNMENT_SIZE > SIZEOF_VALUE
1085 if (size == sizeof(VALUE) && padding == sizeof(VALUE)) {
1086 padding = 0;
1088 #endif
1090 return padding;
1092 #endif /* STRICT_ALIGNMENT */
1094 static void *
1095 compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t size)
1097 void *ptr = 0;
1098 struct iseq_compile_data_storage *storage = *arena;
1099 #ifdef STRICT_ALIGNMENT
1100 size_t padding = calc_padding((void *)&storage->buff[storage->pos], size);
1101 #else
1102 const size_t padding = 0; /* expected to be optimized by compiler */
1103 #endif /* STRICT_ALIGNMENT */
1105 if (size >= INT_MAX - padding) rb_memerror();
1106 if (storage->pos + size + padding > storage->size) {
1107 unsigned int alloc_size = storage->size;
1109 while (alloc_size < size + PADDING_SIZE_MAX) {
1110 if (alloc_size >= INT_MAX / 2) rb_memerror();
1111 alloc_size *= 2;
1113 storage->next = (void *)ALLOC_N(char, alloc_size +
1114 offsetof(struct iseq_compile_data_storage, buff));
1115 storage = *arena = storage->next;
1116 storage->next = 0;
1117 storage->pos = 0;
1118 storage->size = alloc_size;
1119 #ifdef STRICT_ALIGNMENT
1120 padding = calc_padding((void *)&storage->buff[storage->pos], size);
1121 #endif /* STRICT_ALIGNMENT */
1124 #ifdef STRICT_ALIGNMENT
1125 storage->pos += (int)padding;
1126 #endif /* STRICT_ALIGNMENT */
1128 ptr = (void *)&storage->buff[storage->pos];
1129 storage->pos += (int)size;
1130 return ptr;
1133 static void *
1134 compile_data_alloc(rb_iseq_t *iseq, size_t size)
1136 struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->node.storage_current;
1137 return compile_data_alloc_with_arena(arena, size);
1140 static inline void *
1141 compile_data_alloc2(rb_iseq_t *iseq, size_t x, size_t y)
1143 size_t size = rb_size_mul_or_raise(x, y, rb_eRuntimeError);
1144 return compile_data_alloc(iseq, size);
1147 static inline void *
1148 compile_data_calloc2(rb_iseq_t *iseq, size_t x, size_t y)
1150 size_t size = rb_size_mul_or_raise(x, y, rb_eRuntimeError);
1151 void *p = compile_data_alloc(iseq, size);
1152 memset(p, 0, size);
1153 return p;
1156 static INSN *
1157 compile_data_alloc_insn(rb_iseq_t *iseq)
1159 struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->insn.storage_current;
1160 return (INSN *)compile_data_alloc_with_arena(arena, sizeof(INSN));
1163 static LABEL *
1164 compile_data_alloc_label(rb_iseq_t *iseq)
1166 return (LABEL *)compile_data_alloc(iseq, sizeof(LABEL));
1169 static ADJUST *
1170 compile_data_alloc_adjust(rb_iseq_t *iseq)
1172 return (ADJUST *)compile_data_alloc(iseq, sizeof(ADJUST));
1175 static TRACE *
1176 compile_data_alloc_trace(rb_iseq_t *iseq)
1178 return (TRACE *)compile_data_alloc(iseq, sizeof(TRACE));
1182 * elem1, elemX => elem1, elem2, elemX
1184 static void
1185 ELEM_INSERT_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1187 elem2->next = elem1->next;
1188 elem2->prev = elem1;
1189 elem1->next = elem2;
1190 if (elem2->next) {
1191 elem2->next->prev = elem2;
1196 * elem1, elemX => elemX, elem2, elem1
1198 static void
1199 ELEM_INSERT_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1201 elem2->prev = elem1->prev;
1202 elem2->next = elem1;
1203 elem1->prev = elem2;
1204 if (elem2->prev) {
1205 elem2->prev->next = elem2;
1210 * elemX, elem1, elemY => elemX, elem2, elemY
1212 static void
1213 ELEM_REPLACE(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1215 elem2->prev = elem1->prev;
1216 elem2->next = elem1->next;
1217 if (elem1->prev) {
1218 elem1->prev->next = elem2;
1220 if (elem1->next) {
1221 elem1->next->prev = elem2;
1225 static void
1226 ELEM_REMOVE(LINK_ELEMENT *elem)
1228 elem->prev->next = elem->next;
1229 if (elem->next) {
1230 elem->next->prev = elem->prev;
1234 static LINK_ELEMENT *
1235 FIRST_ELEMENT(const LINK_ANCHOR *const anchor)
1237 return anchor->anchor.next;
1240 static LINK_ELEMENT *
1241 LAST_ELEMENT(LINK_ANCHOR *const anchor)
1243 return anchor->last;
1246 static LINK_ELEMENT *
1247 ELEM_FIRST_INSN(LINK_ELEMENT *elem)
1249 while (elem) {
1250 switch (elem->type) {
1251 case ISEQ_ELEMENT_INSN:
1252 case ISEQ_ELEMENT_ADJUST:
1253 return elem;
1254 default:
1255 elem = elem->next;
1258 return NULL;
1261 static int
1262 LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor)
1264 LINK_ELEMENT *first_insn = ELEM_FIRST_INSN(FIRST_ELEMENT(anchor));
1265 if (first_insn != NULL &&
1266 ELEM_FIRST_INSN(first_insn->next) == NULL) {
1267 return TRUE;
1269 else {
1270 return FALSE;
1274 static int
1275 LIST_INSN_SIZE_ZERO(const LINK_ANCHOR *const anchor)
1277 if (ELEM_FIRST_INSN(FIRST_ELEMENT(anchor)) == NULL) {
1278 return TRUE;
1280 else {
1281 return FALSE;
1286 * anc1: e1, e2, e3
1287 * anc2: e4, e5
1288 *#=>
1289 * anc1: e1, e2, e3, e4, e5
1290 * anc2: e4, e5 (broken)
1292 static void
1293 APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *const anc1, LINK_ANCHOR *const anc2)
1295 if (anc2->anchor.next) {
1296 anc1->last->next = anc2->anchor.next;
1297 anc2->anchor.next->prev = anc1->last;
1298 anc1->last = anc2->last;
1300 verify_list("append", anc1);
1302 #if CPDEBUG < 0
1303 #define APPEND_LIST(anc1, anc2) APPEND_LIST(iseq, (anc1), (anc2))
1304 #endif
1306 #if CPDEBUG && 0
1307 static void
1308 debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *cur)
1310 LINK_ELEMENT *list = FIRST_ELEMENT(anchor);
1311 printf("----\n");
1312 printf("anch: %p, frst: %p, last: %p\n", (void *)&anchor->anchor,
1313 (void *)anchor->anchor.next, (void *)anchor->last);
1314 while (list) {
1315 printf("curr: %p, next: %p, prev: %p, type: %d\n", (void *)list, (void *)list->next,
1316 (void *)list->prev, (int)list->type);
1317 list = list->next;
1319 printf("----\n");
1321 dump_disasm_list_with_cursor(anchor->anchor.next, cur, 0);
1322 verify_list("debug list", anchor);
1324 #if CPDEBUG < 0
1325 #define debug_list(anc, cur) debug_list(iseq, (anc), (cur))
1326 #endif
1327 #else
1328 #define debug_list(anc, cur) ((void)0)
1329 #endif
1331 static TRACE *
1332 new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data)
1334 TRACE *trace = compile_data_alloc_trace(iseq);
1336 trace->link.type = ISEQ_ELEMENT_TRACE;
1337 trace->link.next = NULL;
1338 trace->event = event;
1339 trace->data = data;
1341 return trace;
1344 static LABEL *
1345 new_label_body(rb_iseq_t *iseq, long line)
1347 LABEL *labelobj = compile_data_alloc_label(iseq);
1349 labelobj->link.type = ISEQ_ELEMENT_LABEL;
1350 labelobj->link.next = 0;
1352 labelobj->label_no = ISEQ_COMPILE_DATA(iseq)->label_no++;
1353 labelobj->sc_state = 0;
1354 labelobj->sp = -1;
1355 labelobj->refcnt = 0;
1356 labelobj->set = 0;
1357 labelobj->rescued = LABEL_RESCUE_NONE;
1358 labelobj->unremovable = 0;
1359 return labelobj;
1362 static ADJUST *
1363 new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
1365 ADJUST *adjust = compile_data_alloc_adjust(iseq);
1366 adjust->link.type = ISEQ_ELEMENT_ADJUST;
1367 adjust->link.next = 0;
1368 adjust->label = label;
1369 adjust->line_no = line;
1370 LABEL_UNREMOVABLE(label);
1371 return adjust;
1374 static void
1375 iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE, VALUE), VALUE data)
1377 const char *types = insn_op_types(insn->insn_id);
1378 for (int j = 0; types[j]; j++) {
1379 char type = types[j];
1380 switch (type) {
1381 case TS_CDHASH:
1382 case TS_ISEQ:
1383 case TS_VALUE:
1384 case TS_IC: // constant path array
1385 case TS_CALLDATA: // ci is stored.
1386 func(OPERAND_AT(insn, j), data);
1387 break;
1388 default:
1389 break;
1394 static void
1395 iseq_insn_each_object_write_barrier(VALUE obj, VALUE iseq)
1397 RB_OBJ_WRITTEN(iseq, Qundef, obj);
1400 static INSN *
1401 new_insn_core(rb_iseq_t *iseq, int line_no, int node_id, int insn_id, int argc, VALUE *argv)
1403 INSN *iobj = compile_data_alloc_insn(iseq);
1405 /* printf("insn_id: %d, line: %d\n", insn_id, nd_line(line_node)); */
1407 iobj->link.type = ISEQ_ELEMENT_INSN;
1408 iobj->link.next = 0;
1409 iobj->insn_id = insn_id;
1410 iobj->insn_info.line_no = line_no;
1411 iobj->insn_info.node_id = node_id;
1412 iobj->insn_info.events = 0;
1413 iobj->operands = argv;
1414 iobj->operand_size = argc;
1415 iobj->sc_state = 0;
1417 iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq);
1419 return iobj;
1422 static INSN *
1423 new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type insn_id, int argc, ...)
1425 VALUE *operands = 0;
1426 va_list argv;
1427 if (argc > 0) {
1428 int i;
1429 va_start(argv, argc);
1430 operands = compile_data_alloc2(iseq, sizeof(VALUE), argc);
1431 for (i = 0; i < argc; i++) {
1432 VALUE v = va_arg(argv, VALUE);
1433 operands[i] = v;
1435 va_end(argv);
1437 return new_insn_core(iseq, line_no, node_id, insn_id, argc, operands);
1440 static const struct rb_callinfo *
1441 new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_callinfo_kwarg *kw_arg, int has_blockiseq)
1443 VM_ASSERT(argc >= 0);
1445 if (!(flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT)) &&
1446 kw_arg == NULL && !has_blockiseq) {
1447 flag |= VM_CALL_ARGS_SIMPLE;
1450 if (kw_arg) {
1451 flag |= VM_CALL_KWARG;
1452 argc += kw_arg->keyword_len;
1455 ISEQ_BODY(iseq)->ci_size++;
1456 const struct rb_callinfo *ci = vm_ci_new(mid, flag, argc, kw_arg);
1457 RB_OBJ_WRITTEN(iseq, Qundef, ci);
1458 return ci;
1461 static INSN *
1462 new_insn_send(rb_iseq_t *iseq, int line_no, int node_id, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords)
1464 VALUE *operands = compile_data_calloc2(iseq, sizeof(VALUE), 2);
1465 VALUE ci = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL);
1466 operands[0] = ci;
1467 operands[1] = (VALUE)blockiseq;
1468 if (blockiseq) {
1469 RB_OBJ_WRITTEN(iseq, Qundef, blockiseq);
1471 INSN *insn = new_insn_core(iseq, line_no, node_id, BIN(send), 2, operands);
1472 RB_OBJ_WRITTEN(iseq, Qundef, ci);
1473 RB_GC_GUARD(ci);
1474 return insn;
1477 static rb_iseq_t *
1478 new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
1479 VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1481 rb_iseq_t *ret_iseq;
1482 VALUE ast_value = rb_ruby_ast_new(node);
1484 debugs("[new_child_iseq]> ---------------------------------------\n");
1485 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1486 ret_iseq = rb_iseq_new_with_opt(ast_value, name,
1487 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1488 line_no, parent,
1489 isolated_depth ? isolated_depth + 1 : 0,
1490 type, ISEQ_COMPILE_DATA(iseq)->option,
1491 ISEQ_BODY(iseq)->variable.script_lines);
1492 debugs("[new_child_iseq]< ---------------------------------------\n");
1493 return ret_iseq;
1496 static rb_iseq_t *
1497 new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func *ifunc,
1498 VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1500 rb_iseq_t *ret_iseq;
1502 debugs("[new_child_iseq_with_callback]> ---------------------------------------\n");
1503 ret_iseq = rb_iseq_new_with_callback(ifunc, name,
1504 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1505 line_no, parent, type, ISEQ_COMPILE_DATA(iseq)->option);
1506 debugs("[new_child_iseq_with_callback]< ---------------------------------------\n");
1507 return ret_iseq;
1510 static void
1511 set_catch_except_p(rb_iseq_t *iseq)
1513 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1514 ISEQ_COMPILE_DATA(iseq)->catch_except_p = true;
1515 if (ISEQ_BODY(iseq)->parent_iseq != NULL) {
1516 if (ISEQ_COMPILE_DATA(ISEQ_BODY(iseq)->parent_iseq)) {
1517 set_catch_except_p((rb_iseq_t *) ISEQ_BODY(iseq)->parent_iseq);
1522 /* Set body->catch_except_p to true if the ISeq may catch an exception. If it is false,
1523 JIT-ed code may be optimized. If we are extremely conservative, we should set true
1524 if catch table exists. But we want to optimize while loop, which always has catch
1525 table entries for break/next/redo.
1527 So this function sets true for limited ISeqs with break/next/redo catch table entries
1528 whose child ISeq would really raise an exception. */
1529 static void
1530 update_catch_except_flags(rb_iseq_t *iseq, struct rb_iseq_constant_body *body)
1532 unsigned int pos;
1533 size_t i;
1534 int insn;
1535 const struct iseq_catch_table *ct = body->catch_table;
1537 /* This assumes that a block has parent_iseq which may catch an exception from the block, and that
1538 BREAK/NEXT/REDO catch table entries are used only when `throw` insn is used in the block. */
1539 pos = 0;
1540 while (pos < body->iseq_size) {
1541 insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
1542 if (insn == BIN(throw)) {
1543 set_catch_except_p(iseq);
1544 break;
1546 pos += insn_len(insn);
1549 if (ct == NULL)
1550 return;
1552 for (i = 0; i < ct->size; i++) {
1553 const struct iseq_catch_table_entry *entry =
1554 UNALIGNED_MEMBER_PTR(ct, entries[i]);
1555 if (entry->type != CATCH_TYPE_BREAK
1556 && entry->type != CATCH_TYPE_NEXT
1557 && entry->type != CATCH_TYPE_REDO) {
1558 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1559 ISEQ_COMPILE_DATA(iseq)->catch_except_p = true;
1560 break;
1565 static void
1566 iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq)
1568 VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
1569 if (NIL_P(catch_table_ary)) return;
1570 unsigned int i, tlen = (unsigned int)RARRAY_LEN(catch_table_ary);
1571 const VALUE *tptr = RARRAY_CONST_PTR(catch_table_ary);
1572 for (i = 0; i < tlen; i++) {
1573 const VALUE *ptr = RARRAY_CONST_PTR(tptr[i]);
1574 LINK_ELEMENT *end = (LINK_ELEMENT *)(ptr[2] & ~1);
1575 LINK_ELEMENT *cont = (LINK_ELEMENT *)(ptr[4] & ~1);
1576 LINK_ELEMENT *e;
1578 enum rb_catch_type ct = (enum rb_catch_type)(ptr[0] & 0xffff);
1580 if (ct != CATCH_TYPE_BREAK
1581 && ct != CATCH_TYPE_NEXT
1582 && ct != CATCH_TYPE_REDO) {
1584 for (e = end; e && (IS_LABEL(e) || IS_TRACE(e)); e = e->next) {
1585 if (e == cont) {
1586 INSN *nop = new_insn_core(iseq, 0, -1, BIN(nop), 0, 0);
1587 ELEM_INSERT_NEXT(end, &nop->link);
1588 break;
1594 RB_GC_GUARD(catch_table_ary);
1597 static int
1598 iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
1600 if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
1601 return COMPILE_NG;
1603 /* debugs("[compile step 2] (iseq_array_to_linkedlist)\n"); */
1605 if (compile_debug > 5)
1606 dump_disasm_list(FIRST_ELEMENT(anchor));
1608 debugs("[compile step 3.1 (iseq_optimize)]\n");
1609 iseq_optimize(iseq, anchor);
1611 if (compile_debug > 5)
1612 dump_disasm_list(FIRST_ELEMENT(anchor));
1614 if (ISEQ_COMPILE_DATA(iseq)->option->instructions_unification) {
1615 debugs("[compile step 3.2 (iseq_insns_unification)]\n");
1616 iseq_insns_unification(iseq, anchor);
1617 if (compile_debug > 5)
1618 dump_disasm_list(FIRST_ELEMENT(anchor));
1621 debugs("[compile step 3.4 (iseq_insert_nop_between_end_and_cont)]\n");
1622 iseq_insert_nop_between_end_and_cont(iseq);
1623 if (compile_debug > 5)
1624 dump_disasm_list(FIRST_ELEMENT(anchor));
1626 return COMPILE_OK;
1629 static int
1630 iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
1632 if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
1633 return COMPILE_NG;
1635 debugs("[compile step 4.1 (iseq_set_sequence)]\n");
1636 if (!iseq_set_sequence(iseq, anchor)) return COMPILE_NG;
1637 if (compile_debug > 5)
1638 dump_disasm_list(FIRST_ELEMENT(anchor));
1640 debugs("[compile step 4.2 (iseq_set_exception_table)]\n");
1641 if (!iseq_set_exception_table(iseq)) return COMPILE_NG;
1643 debugs("[compile step 4.3 (set_optargs_table)] \n");
1644 if (!iseq_set_optargs_table(iseq)) return COMPILE_NG;
1646 debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
1647 if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG;
1649 debugs("[compile step 6 (update_catch_except_flags)] \n");
1650 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1651 update_catch_except_flags(iseq, ISEQ_BODY(iseq));
1653 debugs("[compile step 6.1 (remove unused catch tables)] \n");
1654 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1655 if (!ISEQ_COMPILE_DATA(iseq)->catch_except_p && ISEQ_BODY(iseq)->catch_table) {
1656 xfree(ISEQ_BODY(iseq)->catch_table);
1657 ISEQ_BODY(iseq)->catch_table = NULL;
1660 #if VM_INSN_INFO_TABLE_IMPL == 2
1661 if (ISEQ_BODY(iseq)->insns_info.succ_index_table == NULL) {
1662 debugs("[compile step 7 (rb_iseq_insns_info_encode_positions)] \n");
1663 rb_iseq_insns_info_encode_positions(iseq);
1665 #endif
1667 if (compile_debug > 1) {
1668 VALUE str = rb_iseq_disasm(iseq);
1669 printf("%s\n", StringValueCStr(str));
1671 verify_call_cache(iseq);
1672 debugs("[compile step: finish]\n");
1674 return COMPILE_OK;
1677 static int
1678 iseq_set_exception_local_table(rb_iseq_t *iseq)
1680 ISEQ_BODY(iseq)->local_table_size = numberof(rb_iseq_shared_exc_local_tbl);
1681 ISEQ_BODY(iseq)->local_table = rb_iseq_shared_exc_local_tbl;
1682 return COMPILE_OK;
1685 static int
1686 get_lvar_level(const rb_iseq_t *iseq)
1688 int lev = 0;
1689 while (iseq != ISEQ_BODY(iseq)->local_iseq) {
1690 lev++;
1691 iseq = ISEQ_BODY(iseq)->parent_iseq;
1693 return lev;
1696 static int
1697 get_dyna_var_idx_at_raw(const rb_iseq_t *iseq, ID id)
1699 unsigned int i;
1701 for (i = 0; i < ISEQ_BODY(iseq)->local_table_size; i++) {
1702 if (ISEQ_BODY(iseq)->local_table[i] == id) {
1703 return (int)i;
1706 return -1;
1709 static int
1710 get_local_var_idx(const rb_iseq_t *iseq, ID id)
1712 int idx = get_dyna_var_idx_at_raw(ISEQ_BODY(iseq)->local_iseq, id);
1714 if (idx < 0) {
1715 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq),
1716 "get_local_var_idx: %d", idx);
1719 return idx;
1722 static int
1723 get_dyna_var_idx(const rb_iseq_t *iseq, ID id, int *level, int *ls)
1725 int lv = 0, idx = -1;
1726 const rb_iseq_t *const topmost_iseq = iseq;
1728 while (iseq) {
1729 idx = get_dyna_var_idx_at_raw(iseq, id);
1730 if (idx >= 0) {
1731 break;
1733 iseq = ISEQ_BODY(iseq)->parent_iseq;
1734 lv++;
1737 if (idx < 0) {
1738 COMPILE_ERROR(topmost_iseq, ISEQ_LAST_LINE(topmost_iseq),
1739 "get_dyna_var_idx: -1");
1742 *level = lv;
1743 *ls = ISEQ_BODY(iseq)->local_table_size;
1744 return idx;
1747 static int
1748 iseq_local_block_param_p(const rb_iseq_t *iseq, unsigned int idx, unsigned int level)
1750 const struct rb_iseq_constant_body *body;
1751 while (level > 0) {
1752 iseq = ISEQ_BODY(iseq)->parent_iseq;
1753 level--;
1755 body = ISEQ_BODY(iseq);
1756 if (body->local_iseq == iseq && /* local variables */
1757 body->param.flags.has_block &&
1758 body->local_table_size - body->param.block_start == idx) {
1759 return TRUE;
1761 else {
1762 return FALSE;
1766 static int
1767 iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel)
1769 int level, ls;
1770 int idx = get_dyna_var_idx(iseq, id, &level, &ls);
1771 if (iseq_local_block_param_p(iseq, ls - idx, level)) {
1772 *pidx = ls - idx;
1773 *plevel = level;
1774 return TRUE;
1776 else {
1777 return FALSE;
1781 static void
1782 access_outer_variables(const rb_iseq_t *iseq, int level, ID id, bool write)
1784 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1786 if (isolated_depth && level >= isolated_depth) {
1787 if (id == rb_intern("yield")) {
1788 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not yield from isolated Proc");
1790 else {
1791 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable '%s' from isolated Proc", rb_id2name(id));
1795 for (int i=0; i<level; i++) {
1796 VALUE val;
1797 struct rb_id_table *ovs = ISEQ_BODY(iseq)->outer_variables;
1799 if (!ovs) {
1800 ovs = ISEQ_BODY(iseq)->outer_variables = rb_id_table_create(8);
1803 if (rb_id_table_lookup(ISEQ_BODY(iseq)->outer_variables, id, &val)) {
1804 if (write && !val) {
1805 rb_id_table_insert(ISEQ_BODY(iseq)->outer_variables, id, Qtrue);
1808 else {
1809 rb_id_table_insert(ISEQ_BODY(iseq)->outer_variables, id, RBOOL(write));
1812 iseq = ISEQ_BODY(iseq)->parent_iseq;
1816 static ID
1817 iseq_lvar_id(const rb_iseq_t *iseq, int idx, int level)
1819 for (int i=0; i<level; i++) {
1820 iseq = ISEQ_BODY(iseq)->parent_iseq;
1823 ID id = ISEQ_BODY(iseq)->local_table[ISEQ_BODY(iseq)->local_table_size - idx];
1824 // fprintf(stderr, "idx:%d level:%d ID:%s\n", idx, level, rb_id2name(id));
1825 return id;
1828 static void
1829 iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
1831 if (iseq_local_block_param_p(iseq, idx, level)) {
1832 ADD_INSN2(seq, line_node, getblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1834 else {
1835 ADD_INSN2(seq, line_node, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1837 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
1840 static void
1841 iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
1843 if (iseq_local_block_param_p(iseq, idx, level)) {
1844 ADD_INSN2(seq, line_node, setblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1846 else {
1847 ADD_INSN2(seq, line_node, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1849 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
1854 static void
1855 iseq_calc_param_size(rb_iseq_t *iseq)
1857 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
1858 if (body->param.flags.has_opt ||
1859 body->param.flags.has_post ||
1860 body->param.flags.has_rest ||
1861 body->param.flags.has_block ||
1862 body->param.flags.has_kw ||
1863 body->param.flags.has_kwrest) {
1865 if (body->param.flags.has_block) {
1866 body->param.size = body->param.block_start + 1;
1868 else if (body->param.flags.has_kwrest) {
1869 body->param.size = body->param.keyword->rest_start + 1;
1871 else if (body->param.flags.has_kw) {
1872 body->param.size = body->param.keyword->bits_start + 1;
1874 else if (body->param.flags.has_post) {
1875 body->param.size = body->param.post_start + body->param.post_num;
1877 else if (body->param.flags.has_rest) {
1878 body->param.size = body->param.rest_start + 1;
1880 else if (body->param.flags.has_opt) {
1881 body->param.size = body->param.lead_num + body->param.opt_num;
1883 else {
1884 UNREACHABLE;
1887 else {
1888 body->param.size = body->param.lead_num;
1892 static int
1893 iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
1894 const struct rb_args_info *args, int arg_size)
1896 const rb_node_kw_arg_t *node = args->kw_args;
1897 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
1898 struct rb_iseq_param_keyword *keyword;
1899 const VALUE default_values = rb_ary_hidden_new(1);
1900 const VALUE complex_mark = rb_str_tmp_new(0);
1901 int kw = 0, rkw = 0, di = 0, i;
1903 body->param.flags.has_kw = TRUE;
1904 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
1906 while (node) {
1907 kw++;
1908 node = node->nd_next;
1910 arg_size += kw;
1911 keyword->bits_start = arg_size++;
1913 node = args->kw_args;
1914 while (node) {
1915 const NODE *val_node = get_nd_value(node->nd_body);
1916 VALUE dv;
1918 if (val_node == NODE_SPECIAL_REQUIRED_KEYWORD) {
1919 ++rkw;
1921 else {
1922 switch (nd_type(val_node)) {
1923 case NODE_SYM:
1924 dv = rb_node_sym_string_val(val_node);
1925 break;
1926 case NODE_REGX:
1927 dv = rb_node_regx_string_val(val_node);
1928 break;
1929 case NODE_LINE:
1930 dv = rb_node_line_lineno_val(val_node);
1931 break;
1932 case NODE_INTEGER:
1933 dv = rb_node_integer_literal_val(val_node);
1934 break;
1935 case NODE_FLOAT:
1936 dv = rb_node_float_literal_val(val_node);
1937 break;
1938 case NODE_RATIONAL:
1939 dv = rb_node_rational_literal_val(val_node);
1940 break;
1941 case NODE_IMAGINARY:
1942 dv = rb_node_imaginary_literal_val(val_node);
1943 break;
1944 case NODE_ENCODING:
1945 dv = rb_node_encoding_val(val_node);
1946 break;
1947 case NODE_NIL:
1948 dv = Qnil;
1949 break;
1950 case NODE_TRUE:
1951 dv = Qtrue;
1952 break;
1953 case NODE_FALSE:
1954 dv = Qfalse;
1955 break;
1956 default:
1957 NO_CHECK(COMPILE_POPPED(optargs, "kwarg", RNODE(node))); /* nd_type_p(node, NODE_KW_ARG) */
1958 dv = complex_mark;
1961 keyword->num = ++di;
1962 rb_ary_push(default_values, dv);
1965 node = node->nd_next;
1968 keyword->num = kw;
1970 if (RNODE_DVAR(args->kw_rest_arg)->nd_vid != 0) {
1971 ID kw_id = iseq->body->local_table[arg_size];
1972 keyword->rest_start = arg_size++;
1973 body->param.flags.has_kwrest = TRUE;
1975 if (kw_id == idPow) body->param.flags.anon_kwrest = TRUE;
1977 keyword->required_num = rkw;
1978 keyword->table = &body->local_table[keyword->bits_start - keyword->num];
1981 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
1983 for (i = 0; i < RARRAY_LEN(default_values); i++) {
1984 VALUE dv = RARRAY_AREF(default_values, i);
1985 if (dv == complex_mark) dv = Qundef;
1986 if (!SPECIAL_CONST_P(dv)) {
1987 RB_OBJ_WRITTEN(iseq, Qundef, dv);
1989 dvs[i] = dv;
1992 keyword->default_values = dvs;
1994 return arg_size;
1997 static void
1998 iseq_set_use_block(rb_iseq_t *iseq)
2000 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2001 if (!body->param.flags.use_block) {
2002 body->param.flags.use_block = 1;
2004 rb_vm_t *vm = GET_VM();
2006 if (!vm->unused_block_warning_strict) {
2007 st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID
2008 st_insert(vm->unused_block_warning_table, key, 1);
2013 static int
2014 iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const node_args)
2016 debugs("iseq_set_arguments: %s\n", node_args ? "" : "0");
2018 if (node_args) {
2019 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2020 struct rb_args_info *args = &RNODE_ARGS(node_args)->nd_ainfo;
2021 ID rest_id = 0;
2022 int last_comma = 0;
2023 ID block_id = 0;
2024 int arg_size;
2026 EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG);
2028 body->param.flags.ruby2_keywords = args->ruby2_keywords;
2029 body->param.lead_num = arg_size = (int)args->pre_args_num;
2030 if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE;
2031 debugs(" - argc: %d\n", body->param.lead_num);
2033 rest_id = args->rest_arg;
2034 if (rest_id == NODE_SPECIAL_EXCESSIVE_COMMA) {
2035 last_comma = 1;
2036 rest_id = 0;
2038 block_id = args->block_arg;
2040 if (args->opt_args) {
2041 const rb_node_opt_arg_t *node = args->opt_args;
2042 LABEL *label;
2043 VALUE labels = rb_ary_hidden_new(1);
2044 VALUE *opt_table;
2045 int i = 0, j;
2047 while (node) {
2048 label = NEW_LABEL(nd_line(RNODE(node)));
2049 rb_ary_push(labels, (VALUE)label | 1);
2050 ADD_LABEL(optargs, label);
2051 NO_CHECK(COMPILE_POPPED(optargs, "optarg", node->nd_body));
2052 node = node->nd_next;
2053 i += 1;
2056 /* last label */
2057 label = NEW_LABEL(nd_line(node_args));
2058 rb_ary_push(labels, (VALUE)label | 1);
2059 ADD_LABEL(optargs, label);
2061 opt_table = ALLOC_N(VALUE, i+1);
2063 MEMCPY(opt_table, RARRAY_CONST_PTR(labels), VALUE, i+1);
2064 for (j = 0; j < i+1; j++) {
2065 opt_table[j] &= ~1;
2067 rb_ary_clear(labels);
2069 body->param.flags.has_opt = TRUE;
2070 body->param.opt_num = i;
2071 body->param.opt_table = opt_table;
2072 arg_size += i;
2075 if (rest_id) {
2076 body->param.rest_start = arg_size++;
2077 body->param.flags.has_rest = TRUE;
2078 if (rest_id == '*') body->param.flags.anon_rest = TRUE;
2079 RUBY_ASSERT(body->param.rest_start != -1);
2082 if (args->first_post_arg) {
2083 body->param.post_start = arg_size;
2084 body->param.post_num = args->post_args_num;
2085 body->param.flags.has_post = TRUE;
2086 arg_size += args->post_args_num;
2088 if (body->param.flags.has_rest) { /* TODO: why that? */
2089 body->param.post_start = body->param.rest_start + 1;
2093 if (args->kw_args) {
2094 arg_size = iseq_set_arguments_keywords(iseq, optargs, args, arg_size);
2096 else if (args->kw_rest_arg) {
2097 ID kw_id = iseq->body->local_table[arg_size];
2098 struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
2099 keyword->rest_start = arg_size++;
2100 body->param.keyword = keyword;
2101 body->param.flags.has_kwrest = TRUE;
2103 static ID anon_kwrest = 0;
2104 if (!anon_kwrest) anon_kwrest = rb_intern("**");
2105 if (kw_id == anon_kwrest) body->param.flags.anon_kwrest = TRUE;
2107 else if (args->no_kwarg) {
2108 body->param.flags.accepts_no_kwarg = TRUE;
2111 if (block_id) {
2112 body->param.block_start = arg_size++;
2113 body->param.flags.has_block = TRUE;
2114 iseq_set_use_block(iseq);
2117 iseq_calc_param_size(iseq);
2118 body->param.size = arg_size;
2120 if (args->pre_init) { /* m_init */
2121 NO_CHECK(COMPILE_POPPED(optargs, "init arguments (m)", args->pre_init));
2123 if (args->post_init) { /* p_init */
2124 NO_CHECK(COMPILE_POPPED(optargs, "init arguments (p)", args->post_init));
2127 if (body->type == ISEQ_TYPE_BLOCK) {
2128 if (body->param.flags.has_opt == FALSE &&
2129 body->param.flags.has_post == FALSE &&
2130 body->param.flags.has_rest == FALSE &&
2131 body->param.flags.has_kw == FALSE &&
2132 body->param.flags.has_kwrest == FALSE) {
2134 if (body->param.lead_num == 1 && last_comma == 0) {
2135 /* {|a|} */
2136 body->param.flags.ambiguous_param0 = TRUE;
2142 return COMPILE_OK;
2145 static int
2146 iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl)
2148 unsigned int size = tbl ? tbl->size : 0;
2150 if (size > 0) {
2151 ID *ids = (ID *)ALLOC_N(ID, size);
2152 MEMCPY(ids, tbl->ids, ID, size);
2153 ISEQ_BODY(iseq)->local_table = ids;
2155 ISEQ_BODY(iseq)->local_table_size = size;
2157 debugs("iseq_set_local_table: %u\n", ISEQ_BODY(iseq)->local_table_size);
2158 return COMPILE_OK;
2162 rb_iseq_cdhash_cmp(VALUE val, VALUE lit)
2164 int tval, tlit;
2166 if (val == lit) {
2167 return 0;
2169 else if ((tlit = OBJ_BUILTIN_TYPE(lit)) == -1) {
2170 return val != lit;
2172 else if ((tval = OBJ_BUILTIN_TYPE(val)) == -1) {
2173 return -1;
2175 else if (tlit != tval) {
2176 return -1;
2178 else if (tlit == T_SYMBOL) {
2179 return val != lit;
2181 else if (tlit == T_STRING) {
2182 return rb_str_hash_cmp(lit, val);
2184 else if (tlit == T_BIGNUM) {
2185 long x = FIX2LONG(rb_big_cmp(lit, val));
2187 /* Given lit and val are both Bignum, x must be -1, 0, 1.
2188 * There is no need to call rb_fix2int here. */
2189 RUBY_ASSERT((x == 1) || (x == 0) || (x == -1));
2190 return (int)x;
2192 else if (tlit == T_FLOAT) {
2193 return rb_float_cmp(lit, val);
2195 else if (tlit == T_RATIONAL) {
2196 const struct RRational *rat1 = RRATIONAL(val);
2197 const struct RRational *rat2 = RRATIONAL(lit);
2198 return rb_iseq_cdhash_cmp(rat1->num, rat2->num) || rb_iseq_cdhash_cmp(rat1->den, rat2->den);
2200 else if (tlit == T_COMPLEX) {
2201 const struct RComplex *comp1 = RCOMPLEX(val);
2202 const struct RComplex *comp2 = RCOMPLEX(lit);
2203 return rb_iseq_cdhash_cmp(comp1->real, comp2->real) || rb_iseq_cdhash_cmp(comp1->imag, comp2->imag);
2205 else if (tlit == T_REGEXP) {
2206 return rb_reg_equal(val, lit) ? 0 : -1;
2208 else {
2209 UNREACHABLE_RETURN(-1);
2213 st_index_t
2214 rb_iseq_cdhash_hash(VALUE a)
2216 switch (OBJ_BUILTIN_TYPE(a)) {
2217 case -1:
2218 case T_SYMBOL:
2219 return (st_index_t)a;
2220 case T_STRING:
2221 return rb_str_hash(a);
2222 case T_BIGNUM:
2223 return FIX2LONG(rb_big_hash(a));
2224 case T_FLOAT:
2225 return rb_dbl_long_hash(RFLOAT_VALUE(a));
2226 case T_RATIONAL:
2227 return rb_rational_hash(a);
2228 case T_COMPLEX:
2229 return rb_complex_hash(a);
2230 case T_REGEXP:
2231 return NUM2LONG(rb_reg_hash(a));
2232 default:
2233 UNREACHABLE_RETURN(0);
2237 static const struct st_hash_type cdhash_type = {
2238 rb_iseq_cdhash_cmp,
2239 rb_iseq_cdhash_hash,
2242 struct cdhash_set_label_struct {
2243 VALUE hash;
2244 int pos;
2245 int len;
2248 static int
2249 cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr)
2251 struct cdhash_set_label_struct *data = (struct cdhash_set_label_struct *)ptr;
2252 LABEL *lobj = (LABEL *)(val & ~1);
2253 rb_hash_aset(data->hash, key, INT2FIX(lobj->position - (data->pos+data->len)));
2254 return ST_CONTINUE;
2258 static inline VALUE
2259 get_ivar_ic_value(rb_iseq_t *iseq,ID id)
2261 return INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
2264 static inline VALUE
2265 get_cvar_ic_value(rb_iseq_t *iseq,ID id)
2267 VALUE val;
2268 struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
2269 if (tbl) {
2270 if (rb_id_table_lookup(tbl,id,&val)) {
2271 return val;
2274 else {
2275 tbl = rb_id_table_create(1);
2276 ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
2278 val = INT2FIX(ISEQ_BODY(iseq)->icvarc_size++);
2279 rb_id_table_insert(tbl,id,val);
2280 return val;
2283 #define BADINSN_DUMP(anchor, list, dest) \
2284 dump_disasm_list_with_cursor(FIRST_ELEMENT(anchor), list, dest)
2286 #define BADINSN_ERROR \
2287 (xfree(generated_iseq), \
2288 xfree(insns_info), \
2289 BADINSN_DUMP(anchor, list, NULL), \
2290 COMPILE_ERROR)
2292 static int
2293 fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
2295 int stack_max = 0, sp = 0, line = 0;
2296 LINK_ELEMENT *list;
2298 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2299 if (IS_LABEL(list)) {
2300 LABEL *lobj = (LABEL *)list;
2301 lobj->set = TRUE;
2305 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2306 switch (list->type) {
2307 case ISEQ_ELEMENT_INSN:
2309 int j, len, insn;
2310 const char *types;
2311 VALUE *operands;
2312 INSN *iobj = (INSN *)list;
2314 /* update sp */
2315 sp = calc_sp_depth(sp, iobj);
2316 if (sp < 0) {
2317 BADINSN_DUMP(anchor, list, NULL);
2318 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2319 "argument stack underflow (%d)", sp);
2320 return -1;
2322 if (sp > stack_max) {
2323 stack_max = sp;
2326 line = iobj->insn_info.line_no;
2327 /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
2328 operands = iobj->operands;
2329 insn = iobj->insn_id;
2330 types = insn_op_types(insn);
2331 len = insn_len(insn);
2333 /* operand check */
2334 if (iobj->operand_size != len - 1) {
2335 /* printf("operand size miss! (%d, %d)\n", iobj->operand_size, len); */
2336 BADINSN_DUMP(anchor, list, NULL);
2337 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2338 "operand size miss! (%d for %d)",
2339 iobj->operand_size, len - 1);
2340 return -1;
2343 for (j = 0; types[j]; j++) {
2344 if (types[j] == TS_OFFSET) {
2345 /* label(destination position) */
2346 LABEL *lobj = (LABEL *)operands[j];
2347 if (!lobj->set) {
2348 BADINSN_DUMP(anchor, list, NULL);
2349 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2350 "unknown label: "LABEL_FORMAT, lobj->label_no);
2351 return -1;
2353 if (lobj->sp == -1) {
2354 lobj->sp = sp;
2356 else if (lobj->sp != sp) {
2357 debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2358 RSTRING_PTR(rb_iseq_path(iseq)), line,
2359 lobj->label_no, lobj->sp, sp);
2363 break;
2365 case ISEQ_ELEMENT_LABEL:
2367 LABEL *lobj = (LABEL *)list;
2368 if (lobj->sp == -1) {
2369 lobj->sp = sp;
2371 else {
2372 if (lobj->sp != sp) {
2373 debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2374 RSTRING_PTR(rb_iseq_path(iseq)), line,
2375 lobj->label_no, lobj->sp, sp);
2377 sp = lobj->sp;
2379 break;
2381 case ISEQ_ELEMENT_TRACE:
2383 /* ignore */
2384 break;
2386 case ISEQ_ELEMENT_ADJUST:
2388 ADJUST *adjust = (ADJUST *)list;
2389 int orig_sp = sp;
2391 sp = adjust->label ? adjust->label->sp : 0;
2392 if (adjust->line_no != -1 && orig_sp - sp < 0) {
2393 BADINSN_DUMP(anchor, list, NULL);
2394 COMPILE_ERROR(iseq, adjust->line_no,
2395 "iseq_set_sequence: adjust bug %d < %d",
2396 orig_sp, sp);
2397 return -1;
2399 break;
2401 default:
2402 BADINSN_DUMP(anchor, list, NULL);
2403 COMPILE_ERROR(iseq, line, "unknown list type: %d", list->type);
2404 return -1;
2407 return stack_max;
2410 static int
2411 add_insn_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
2412 int insns_info_index, int code_index, const INSN *iobj)
2414 if (insns_info_index == 0 ||
2415 insns_info[insns_info_index-1].line_no != iobj->insn_info.line_no ||
2416 #ifdef USE_ISEQ_NODE_ID
2417 insns_info[insns_info_index-1].node_id != iobj->insn_info.node_id ||
2418 #endif
2419 insns_info[insns_info_index-1].events != iobj->insn_info.events) {
2420 insns_info[insns_info_index].line_no = iobj->insn_info.line_no;
2421 #ifdef USE_ISEQ_NODE_ID
2422 insns_info[insns_info_index].node_id = iobj->insn_info.node_id;
2423 #endif
2424 insns_info[insns_info_index].events = iobj->insn_info.events;
2425 positions[insns_info_index] = code_index;
2426 return TRUE;
2428 return FALSE;
2431 static int
2432 add_adjust_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
2433 int insns_info_index, int code_index, const ADJUST *adjust)
2435 insns_info[insns_info_index].line_no = adjust->line_no;
2436 insns_info[insns_info_index].events = 0;
2437 positions[insns_info_index] = code_index;
2438 return TRUE;
2441 static ID *
2442 array_to_idlist(VALUE arr)
2444 RUBY_ASSERT(RB_TYPE_P(arr, T_ARRAY));
2445 long size = RARRAY_LEN(arr);
2446 ID *ids = (ID *)ALLOC_N(ID, size + 1);
2447 for (int i = 0; i < size; i++) {
2448 VALUE sym = RARRAY_AREF(arr, i);
2449 ids[i] = SYM2ID(sym);
2451 ids[size] = 0;
2452 return ids;
2455 static VALUE
2456 idlist_to_array(const ID *ids)
2458 VALUE arr = rb_ary_new();
2459 while (*ids) {
2460 rb_ary_push(arr, ID2SYM(*ids++));
2462 return arr;
2466 ruby insn object list -> raw instruction sequence
2468 static int
2469 iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
2471 struct iseq_insn_info_entry *insns_info;
2472 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2473 unsigned int *positions;
2474 LINK_ELEMENT *list;
2475 VALUE *generated_iseq;
2476 rb_event_flag_t events = 0;
2477 long data = 0;
2479 int insn_num, code_index, insns_info_index, sp = 0;
2480 int stack_max = fix_sp_depth(iseq, anchor);
2482 if (stack_max < 0) return COMPILE_NG;
2484 /* fix label position */
2485 insn_num = code_index = 0;
2486 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2487 switch (list->type) {
2488 case ISEQ_ELEMENT_INSN:
2490 INSN *iobj = (INSN *)list;
2491 /* update sp */
2492 sp = calc_sp_depth(sp, iobj);
2493 insn_num++;
2494 events = iobj->insn_info.events |= events;
2495 if (ISEQ_COVERAGE(iseq)) {
2496 if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) &&
2497 !(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) {
2498 int line = iobj->insn_info.line_no - 1;
2499 if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) {
2500 RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0));
2503 if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) {
2504 while (RARRAY_LEN(ISEQ_PC2BRANCHINDEX(iseq)) <= code_index) {
2505 rb_ary_push(ISEQ_PC2BRANCHINDEX(iseq), Qnil);
2507 RARRAY_ASET(ISEQ_PC2BRANCHINDEX(iseq), code_index, INT2FIX(data));
2510 code_index += insn_data_length(iobj);
2511 events = 0;
2512 data = 0;
2513 break;
2515 case ISEQ_ELEMENT_LABEL:
2517 LABEL *lobj = (LABEL *)list;
2518 lobj->position = code_index;
2519 if (lobj->sp != sp) {
2520 debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2521 RSTRING_PTR(rb_iseq_path(iseq)),
2522 lobj->label_no, lobj->sp, sp);
2524 sp = lobj->sp;
2525 break;
2527 case ISEQ_ELEMENT_TRACE:
2529 TRACE *trace = (TRACE *)list;
2530 events |= trace->event;
2531 if (trace->event & RUBY_EVENT_COVERAGE_BRANCH) data = trace->data;
2532 break;
2534 case ISEQ_ELEMENT_ADJUST:
2536 ADJUST *adjust = (ADJUST *)list;
2537 if (adjust->line_no != -1) {
2538 int orig_sp = sp;
2539 sp = adjust->label ? adjust->label->sp : 0;
2540 if (orig_sp - sp > 0) {
2541 if (orig_sp - sp > 1) code_index++; /* 1 operand */
2542 code_index++; /* insn */
2543 insn_num++;
2546 break;
2548 default: break;
2552 /* make instruction sequence */
2553 generated_iseq = ALLOC_N(VALUE, code_index);
2554 insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num);
2555 positions = ALLOC_N(unsigned int, insn_num);
2556 if (ISEQ_IS_SIZE(body)) {
2557 body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body));
2559 else {
2560 body->is_entries = NULL;
2562 body->call_data = ZALLOC_N(struct rb_call_data, body->ci_size);
2563 ISEQ_COMPILE_DATA(iseq)->ci_index = 0;
2565 // Calculate the bitmask buffer size.
2566 // Round the generated_iseq size up to the nearest multiple
2567 // of the number of bits in an unsigned long.
2569 // Allocate enough room for the bitmask list
2570 iseq_bits_t * mark_offset_bits;
2571 int code_size = code_index;
2573 iseq_bits_t tmp[1] = {0};
2574 bool needs_bitmap = false;
2576 if (ISEQ_MBITS_BUFLEN(code_index) == 1) {
2577 mark_offset_bits = tmp;
2579 else {
2580 mark_offset_bits = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(code_index));
2583 list = FIRST_ELEMENT(anchor);
2584 insns_info_index = code_index = sp = 0;
2586 while (list) {
2587 switch (list->type) {
2588 case ISEQ_ELEMENT_INSN:
2590 int j, len, insn;
2591 const char *types;
2592 VALUE *operands;
2593 INSN *iobj = (INSN *)list;
2595 /* update sp */
2596 sp = calc_sp_depth(sp, iobj);
2597 /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
2598 operands = iobj->operands;
2599 insn = iobj->insn_id;
2600 generated_iseq[code_index] = insn;
2601 types = insn_op_types(insn);
2602 len = insn_len(insn);
2604 for (j = 0; types[j]; j++) {
2605 char type = types[j];
2607 /* printf("--> [%c - (%d-%d)]\n", type, k, j); */
2608 switch (type) {
2609 case TS_OFFSET:
2611 /* label(destination position) */
2612 LABEL *lobj = (LABEL *)operands[j];
2613 generated_iseq[code_index + 1 + j] = lobj->position - (code_index + len);
2614 break;
2616 case TS_CDHASH:
2618 VALUE map = operands[j];
2619 struct cdhash_set_label_struct data;
2620 data.hash = map;
2621 data.pos = code_index;
2622 data.len = len;
2623 rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
2625 rb_hash_rehash(map);
2626 freeze_hide_obj(map);
2627 generated_iseq[code_index + 1 + j] = map;
2628 ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
2629 RB_OBJ_WRITTEN(iseq, Qundef, map);
2630 needs_bitmap = true;
2631 break;
2633 case TS_LINDEX:
2634 case TS_NUM: /* ulong */
2635 generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
2636 break;
2637 case TS_ISEQ: /* iseq */
2638 case TS_VALUE: /* VALUE */
2640 VALUE v = operands[j];
2641 generated_iseq[code_index + 1 + j] = v;
2642 /* to mark ruby object */
2643 if (!SPECIAL_CONST_P(v)) {
2644 RB_OBJ_WRITTEN(iseq, Qundef, v);
2645 ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
2646 needs_bitmap = true;
2648 break;
2650 /* [ TS_IVC | TS_ICVARC | TS_ISE | TS_IC ] */
2651 case TS_IC: /* inline cache: constants */
2653 unsigned int ic_index = ISEQ_COMPILE_DATA(iseq)->ic_index++;
2654 IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
2655 if (UNLIKELY(ic_index >= body->ic_size)) {
2656 BADINSN_DUMP(anchor, &iobj->link, 0);
2657 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2658 "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
2659 ic_index, ISEQ_IS_SIZE(body));
2662 ic->segments = array_to_idlist(operands[j]);
2664 generated_iseq[code_index + 1 + j] = (VALUE)ic;
2666 break;
2667 case TS_IVC: /* inline ivar cache */
2669 unsigned int ic_index = FIX2UINT(operands[j]);
2671 IVC cache = ((IVC)&body->is_entries[ic_index]);
2673 if (insn == BIN(setinstancevariable)) {
2674 cache->iv_set_name = SYM2ID(operands[j - 1]);
2676 else {
2677 cache->iv_set_name = 0;
2680 vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
2682 case TS_ISE: /* inline storage entry: `once` insn */
2683 case TS_ICVARC: /* inline cvar cache */
2685 unsigned int ic_index = FIX2UINT(operands[j]);
2686 IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
2687 if (UNLIKELY(ic_index >= ISEQ_IS_SIZE(body))) {
2688 BADINSN_DUMP(anchor, &iobj->link, 0);
2689 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2690 "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
2691 ic_index, ISEQ_IS_SIZE(body));
2693 generated_iseq[code_index + 1 + j] = (VALUE)ic;
2695 break;
2697 case TS_CALLDATA:
2699 const struct rb_callinfo *source_ci = (const struct rb_callinfo *)operands[j];
2700 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
2701 struct rb_call_data *cd = &body->call_data[ISEQ_COMPILE_DATA(iseq)->ci_index++];
2702 cd->ci = source_ci;
2703 cd->cc = vm_cc_empty();
2704 generated_iseq[code_index + 1 + j] = (VALUE)cd;
2705 break;
2707 case TS_ID: /* ID */
2708 generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
2709 break;
2710 case TS_FUNCPTR:
2711 generated_iseq[code_index + 1 + j] = operands[j];
2712 break;
2713 case TS_BUILTIN:
2714 generated_iseq[code_index + 1 + j] = operands[j];
2715 break;
2716 default:
2717 BADINSN_ERROR(iseq, iobj->insn_info.line_no,
2718 "unknown operand type: %c", type);
2719 return COMPILE_NG;
2722 if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
2723 code_index += len;
2724 break;
2726 case ISEQ_ELEMENT_LABEL:
2728 LABEL *lobj = (LABEL *)list;
2729 if (lobj->sp != sp) {
2730 debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2731 RSTRING_PTR(rb_iseq_path(iseq)),
2732 lobj->label_no, lobj->sp, sp);
2734 sp = lobj->sp;
2735 break;
2737 case ISEQ_ELEMENT_ADJUST:
2739 ADJUST *adjust = (ADJUST *)list;
2740 int orig_sp = sp;
2742 if (adjust->label) {
2743 sp = adjust->label->sp;
2745 else {
2746 sp = 0;
2749 if (adjust->line_no != -1) {
2750 const int diff = orig_sp - sp;
2751 if (diff > 0) {
2752 if (insns_info_index == 0) {
2753 COMPILE_ERROR(iseq, adjust->line_no,
2754 "iseq_set_sequence: adjust bug (ISEQ_ELEMENT_ADJUST must not be the first in iseq)");
2756 if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
2758 if (diff > 1) {
2759 generated_iseq[code_index++] = BIN(adjuststack);
2760 generated_iseq[code_index++] = orig_sp - sp;
2762 else if (diff == 1) {
2763 generated_iseq[code_index++] = BIN(pop);
2765 else if (diff < 0) {
2766 int label_no = adjust->label ? adjust->label->label_no : -1;
2767 xfree(generated_iseq);
2768 xfree(insns_info);
2769 xfree(positions);
2770 if (ISEQ_MBITS_BUFLEN(code_size) > 1) {
2771 xfree(mark_offset_bits);
2773 debug_list(anchor, list);
2774 COMPILE_ERROR(iseq, adjust->line_no,
2775 "iseq_set_sequence: adjust bug to %d %d < %d",
2776 label_no, orig_sp, sp);
2777 return COMPILE_NG;
2780 break;
2782 default:
2783 /* ignore */
2784 break;
2786 list = list->next;
2789 body->iseq_encoded = (void *)generated_iseq;
2790 body->iseq_size = code_index;
2791 body->stack_max = stack_max;
2793 if (ISEQ_MBITS_BUFLEN(body->iseq_size) == 1) {
2794 body->mark_bits.single = mark_offset_bits[0];
2796 else {
2797 if (needs_bitmap) {
2798 body->mark_bits.list = mark_offset_bits;
2800 else {
2801 body->mark_bits.list = 0;
2802 ruby_xfree(mark_offset_bits);
2806 /* get rid of memory leak when REALLOC failed */
2807 body->insns_info.body = insns_info;
2808 body->insns_info.positions = positions;
2810 REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index);
2811 body->insns_info.body = insns_info;
2812 REALLOC_N(positions, unsigned int, insns_info_index);
2813 body->insns_info.positions = positions;
2814 body->insns_info.size = insns_info_index;
2816 return COMPILE_OK;
2819 static int
2820 label_get_position(LABEL *lobj)
2822 return lobj->position;
2825 static int
2826 label_get_sp(LABEL *lobj)
2828 return lobj->sp;
2831 static int
2832 iseq_set_exception_table(rb_iseq_t *iseq)
2834 const VALUE *tptr, *ptr;
2835 unsigned int tlen, i;
2836 struct iseq_catch_table_entry *entry;
2838 ISEQ_BODY(iseq)->catch_table = NULL;
2840 VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
2841 if (NIL_P(catch_table_ary)) return COMPILE_OK;
2842 tlen = (int)RARRAY_LEN(catch_table_ary);
2843 tptr = RARRAY_CONST_PTR(catch_table_ary);
2845 if (tlen > 0) {
2846 struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
2847 table->size = tlen;
2849 for (i = 0; i < table->size; i++) {
2850 ptr = RARRAY_CONST_PTR(tptr[i]);
2851 entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
2852 entry->type = (enum rb_catch_type)(ptr[0] & 0xffff);
2853 entry->start = label_get_position((LABEL *)(ptr[1] & ~1));
2854 entry->end = label_get_position((LABEL *)(ptr[2] & ~1));
2855 entry->iseq = (rb_iseq_t *)ptr[3];
2856 RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq);
2858 /* stack depth */
2859 if (ptr[4]) {
2860 LABEL *lobj = (LABEL *)(ptr[4] & ~1);
2861 entry->cont = label_get_position(lobj);
2862 entry->sp = label_get_sp(lobj);
2864 /* TODO: Dirty Hack! Fix me */
2865 if (entry->type == CATCH_TYPE_RESCUE ||
2866 entry->type == CATCH_TYPE_BREAK ||
2867 entry->type == CATCH_TYPE_NEXT) {
2868 RUBY_ASSERT(entry->sp > 0);
2869 entry->sp--;
2872 else {
2873 entry->cont = 0;
2876 ISEQ_BODY(iseq)->catch_table = table;
2877 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
2880 RB_GC_GUARD(catch_table_ary);
2882 return COMPILE_OK;
2886 * set optional argument table
2887 * def foo(a, b=expr1, c=expr2)
2888 * =>
2889 * b:
2890 * expr1
2891 * c:
2892 * expr2
2894 static int
2895 iseq_set_optargs_table(rb_iseq_t *iseq)
2897 int i;
2898 VALUE *opt_table = (VALUE *)ISEQ_BODY(iseq)->param.opt_table;
2900 if (ISEQ_BODY(iseq)->param.flags.has_opt) {
2901 for (i = 0; i < ISEQ_BODY(iseq)->param.opt_num + 1; i++) {
2902 opt_table[i] = label_get_position((LABEL *)opt_table[i]);
2905 return COMPILE_OK;
2908 static LINK_ELEMENT *
2909 get_destination_insn(INSN *iobj)
2911 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
2912 LINK_ELEMENT *list;
2913 rb_event_flag_t events = 0;
2915 list = lobj->link.next;
2916 while (list) {
2917 switch (list->type) {
2918 case ISEQ_ELEMENT_INSN:
2919 case ISEQ_ELEMENT_ADJUST:
2920 goto found;
2921 case ISEQ_ELEMENT_LABEL:
2922 /* ignore */
2923 break;
2924 case ISEQ_ELEMENT_TRACE:
2926 TRACE *trace = (TRACE *)list;
2927 events |= trace->event;
2929 break;
2930 default: break;
2932 list = list->next;
2934 found:
2935 if (list && IS_INSN(list)) {
2936 INSN *iobj = (INSN *)list;
2937 iobj->insn_info.events |= events;
2939 return list;
2942 static LINK_ELEMENT *
2943 get_next_insn(INSN *iobj)
2945 LINK_ELEMENT *list = iobj->link.next;
2947 while (list) {
2948 if (IS_INSN(list) || IS_ADJUST(list)) {
2949 return list;
2951 list = list->next;
2953 return 0;
2956 static LINK_ELEMENT *
2957 get_prev_insn(INSN *iobj)
2959 LINK_ELEMENT *list = iobj->link.prev;
2961 while (list) {
2962 if (IS_INSN(list) || IS_ADJUST(list)) {
2963 return list;
2965 list = list->prev;
2967 return 0;
2970 static void
2971 unref_destination(INSN *iobj, int pos)
2973 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, pos);
2974 --lobj->refcnt;
2975 if (!lobj->refcnt) ELEM_REMOVE(&lobj->link);
2978 static void
2979 replace_destination(INSN *dobj, INSN *nobj)
2981 VALUE n = OPERAND_AT(nobj, 0);
2982 LABEL *dl = (LABEL *)OPERAND_AT(dobj, 0);
2983 LABEL *nl = (LABEL *)n;
2984 --dl->refcnt;
2985 ++nl->refcnt;
2986 OPERAND_AT(dobj, 0) = n;
2987 if (!dl->refcnt) ELEM_REMOVE(&dl->link);
2990 static LABEL*
2991 find_destination(INSN *i)
2993 int pos, len = insn_len(i->insn_id);
2994 for (pos = 0; pos < len; ++pos) {
2995 if (insn_op_types(i->insn_id)[pos] == TS_OFFSET) {
2996 return (LABEL *)OPERAND_AT(i, pos);
2999 return 0;
3002 static int
3003 remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
3005 LINK_ELEMENT *first = i, *end;
3006 int *unref_counts = 0, nlabels = ISEQ_COMPILE_DATA(iseq)->label_no;
3008 if (!i) return 0;
3009 unref_counts = ALLOCA_N(int, nlabels);
3010 MEMZERO(unref_counts, int, nlabels);
3011 end = i;
3012 do {
3013 LABEL *lab;
3014 if (IS_INSN(i)) {
3015 if (IS_INSN_ID(i, leave)) {
3016 end = i;
3017 break;
3019 else if ((lab = find_destination((INSN *)i)) != 0) {
3020 if (lab->unremovable) break;
3021 unref_counts[lab->label_no]++;
3024 else if (IS_LABEL(i)) {
3025 lab = (LABEL *)i;
3026 if (lab->unremovable) return 0;
3027 if (lab->refcnt > unref_counts[lab->label_no]) {
3028 if (i == first) return 0;
3029 break;
3031 continue;
3033 else if (IS_TRACE(i)) {
3034 /* do nothing */
3036 else if (IS_ADJUST(i)) {
3037 LABEL *dest = ((ADJUST *)i)->label;
3038 if (dest && dest->unremovable) return 0;
3040 end = i;
3041 } while ((i = i->next) != 0);
3042 i = first;
3043 do {
3044 if (IS_INSN(i)) {
3045 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
3046 VALUE insn = INSN_OF(i);
3047 int pos, len = insn_len(insn);
3048 for (pos = 0; pos < len; ++pos) {
3049 switch (insn_op_types(insn)[pos]) {
3050 case TS_OFFSET:
3051 unref_destination((INSN *)i, pos);
3052 break;
3053 case TS_CALLDATA:
3054 --(body->ci_size);
3055 break;
3059 ELEM_REMOVE(i);
3060 } while ((i != end) && (i = i->next) != 0);
3061 return 1;
3064 static int
3065 iseq_pop_newarray(rb_iseq_t *iseq, INSN *iobj)
3067 switch (OPERAND_AT(iobj, 0)) {
3068 case INT2FIX(0): /* empty array */
3069 ELEM_REMOVE(&iobj->link);
3070 return TRUE;
3071 case INT2FIX(1): /* single element array */
3072 ELEM_REMOVE(&iobj->link);
3073 return FALSE;
3074 default:
3075 iobj->insn_id = BIN(adjuststack);
3076 return TRUE;
3080 static int
3081 is_frozen_putstring(INSN *insn, VALUE *op)
3083 if (IS_INSN_ID(insn, putstring)) {
3084 *op = OPERAND_AT(insn, 0);
3085 return 1;
3087 else if (IS_INSN_ID(insn, putobject)) { /* frozen_string_literal */
3088 *op = OPERAND_AT(insn, 0);
3089 return RB_TYPE_P(*op, T_STRING);
3091 return 0;
3094 static int
3095 optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
3098 * putobject obj
3099 * dup
3100 * checktype T_XXX
3101 * branchif l1
3102 * l2:
3103 * ...
3104 * l1:
3106 * => obj is a T_XXX
3108 * putobject obj (T_XXX)
3109 * jump L1
3110 * L1:
3112 * => obj is not a T_XXX
3114 * putobject obj (T_XXX)
3115 * jump L2
3116 * L2:
3118 int line, node_id;
3119 INSN *niobj, *ciobj, *dup = 0;
3120 LABEL *dest = 0;
3121 VALUE type;
3123 switch (INSN_OF(iobj)) {
3124 case BIN(putstring):
3125 type = INT2FIX(T_STRING);
3126 break;
3127 case BIN(putnil):
3128 type = INT2FIX(T_NIL);
3129 break;
3130 case BIN(putobject):
3131 type = INT2FIX(TYPE(OPERAND_AT(iobj, 0)));
3132 break;
3133 default: return FALSE;
3136 ciobj = (INSN *)get_next_insn(iobj);
3137 if (IS_INSN_ID(ciobj, jump)) {
3138 ciobj = (INSN *)get_next_insn((INSN*)OPERAND_AT(ciobj, 0));
3140 if (IS_INSN_ID(ciobj, dup)) {
3141 ciobj = (INSN *)get_next_insn(dup = ciobj);
3143 if (!ciobj || !IS_INSN_ID(ciobj, checktype)) return FALSE;
3144 niobj = (INSN *)get_next_insn(ciobj);
3145 if (!niobj) {
3146 /* TODO: putobject true/false */
3147 return FALSE;
3149 switch (INSN_OF(niobj)) {
3150 case BIN(branchif):
3151 if (OPERAND_AT(ciobj, 0) == type) {
3152 dest = (LABEL *)OPERAND_AT(niobj, 0);
3154 break;
3155 case BIN(branchunless):
3156 if (OPERAND_AT(ciobj, 0) != type) {
3157 dest = (LABEL *)OPERAND_AT(niobj, 0);
3159 break;
3160 default:
3161 return FALSE;
3163 line = ciobj->insn_info.line_no;
3164 node_id = ciobj->insn_info.node_id;
3165 if (!dest) {
3166 if (niobj->link.next && IS_LABEL(niobj->link.next)) {
3167 dest = (LABEL *)niobj->link.next; /* reuse label */
3169 else {
3170 dest = NEW_LABEL(line);
3171 ELEM_INSERT_NEXT(&niobj->link, &dest->link);
3174 INSERT_AFTER_INSN1(iobj, line, node_id, jump, dest);
3175 LABEL_REF(dest);
3176 if (!dup) INSERT_AFTER_INSN(iobj, line, node_id, pop);
3177 return TRUE;
3180 static const struct rb_callinfo *
3181 ci_flag_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, unsigned int add)
3183 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
3184 vm_ci_flag(ci) | add,
3185 vm_ci_argc(ci),
3186 vm_ci_kwarg(ci));
3187 RB_OBJ_WRITTEN(iseq, ci, nci);
3188 return nci;
3191 static const struct rb_callinfo *
3192 ci_argc_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, int argc)
3194 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
3195 vm_ci_flag(ci),
3196 argc,
3197 vm_ci_kwarg(ci));
3198 RB_OBJ_WRITTEN(iseq, ci, nci);
3199 return nci;
3202 static bool
3203 optimize_args_splat_no_copy(rb_iseq_t *iseq, INSN *insn, LINK_ELEMENT *niobj,
3204 unsigned int set_flags, unsigned int unset_flags, unsigned int remove_flags)
3206 LINK_ELEMENT *iobj = (LINK_ELEMENT *)insn;
3207 if ((set_flags & VM_CALL_ARGS_BLOCKARG) && (set_flags & VM_CALL_KW_SPLAT) &&
3208 IS_NEXT_INSN_ID(niobj, splatkw)) {
3209 niobj = niobj->next;
3211 if (!IS_NEXT_INSN_ID(niobj, send) && !IS_NEXT_INSN_ID(niobj, invokesuper)) {
3212 return false;
3214 niobj = niobj->next;
3216 const struct rb_callinfo *ci = (const struct rb_callinfo *)OPERAND_AT(niobj, 0);
3217 unsigned int flags = vm_ci_flag(ci);
3218 if ((flags & set_flags) == set_flags && !(flags & unset_flags)) {
3219 RUBY_ASSERT(flags & VM_CALL_ARGS_SPLAT_MUT);
3220 OPERAND_AT(iobj, 0) = Qfalse;
3221 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
3222 flags & ~(VM_CALL_ARGS_SPLAT_MUT|remove_flags), vm_ci_argc(ci), vm_ci_kwarg(ci));
3223 RB_OBJ_WRITTEN(iseq, ci, nci);
3224 OPERAND_AT(niobj, 0) = (VALUE)nci;
3225 return true;
3227 return false;
3230 static int
3231 iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
3233 INSN *const iobj = (INSN *)list;
3235 again:
3236 optimize_checktype(iseq, iobj);
3238 if (IS_INSN_ID(iobj, jump)) {
3239 INSN *niobj, *diobj, *piobj;
3240 diobj = (INSN *)get_destination_insn(iobj);
3241 niobj = (INSN *)get_next_insn(iobj);
3243 if (diobj == niobj) {
3245 * jump LABEL
3246 * LABEL:
3247 * =>
3248 * LABEL:
3250 unref_destination(iobj, 0);
3251 ELEM_REMOVE(&iobj->link);
3252 return COMPILE_OK;
3254 else if (iobj != diobj && IS_INSN(&diobj->link) &&
3255 IS_INSN_ID(diobj, jump) &&
3256 OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0) &&
3257 diobj->insn_info.events == 0) {
3259 * useless jump elimination:
3260 * jump LABEL1
3261 * ...
3262 * LABEL1:
3263 * jump LABEL2
3265 * => in this case, first jump instruction should jump to
3266 * LABEL2 directly
3268 replace_destination(iobj, diobj);
3269 remove_unreachable_chunk(iseq, iobj->link.next);
3270 goto again;
3272 else if (IS_INSN_ID(diobj, leave)) {
3274 * jump LABEL
3275 * ...
3276 * LABEL:
3277 * leave
3278 * =>
3279 * leave
3280 * ...
3281 * LABEL:
3282 * leave
3284 /* replace */
3285 unref_destination(iobj, 0);
3286 iobj->insn_id = BIN(leave);
3287 iobj->operand_size = 0;
3288 iobj->insn_info = diobj->insn_info;
3289 goto again;
3291 else if (IS_INSN(iobj->link.prev) &&
3292 (piobj = (INSN *)iobj->link.prev) &&
3293 (IS_INSN_ID(piobj, branchif) ||
3294 IS_INSN_ID(piobj, branchunless))) {
3295 INSN *pdiobj = (INSN *)get_destination_insn(piobj);
3296 if (niobj == pdiobj) {
3297 int refcnt = IS_LABEL(piobj->link.next) ?
3298 ((LABEL *)piobj->link.next)->refcnt : 0;
3300 * useless jump elimination (if/unless destination):
3301 * if L1
3302 * jump L2
3303 * L1:
3304 * ...
3305 * L2:
3307 * ==>
3308 * unless L2
3309 * L1:
3310 * ...
3311 * L2:
3313 piobj->insn_id = (IS_INSN_ID(piobj, branchif))
3314 ? BIN(branchunless) : BIN(branchif);
3315 replace_destination(piobj, iobj);
3316 if (refcnt <= 1) {
3317 ELEM_REMOVE(&iobj->link);
3319 else {
3320 /* TODO: replace other branch destinations too */
3322 return COMPILE_OK;
3324 else if (diobj == pdiobj) {
3326 * useless jump elimination (if/unless before jump):
3327 * L1:
3328 * ...
3329 * if L1
3330 * jump L1
3332 * ==>
3333 * L1:
3334 * ...
3335 * pop
3336 * jump L1
3338 INSN *popiobj = new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, BIN(pop), 0, 0);
3339 ELEM_REPLACE(&piobj->link, &popiobj->link);
3342 if (remove_unreachable_chunk(iseq, iobj->link.next)) {
3343 goto again;
3348 * putstring "beg"
3349 * putstring "end"
3350 * newrange excl
3352 * ==>
3354 * putobject "beg".."end"
3356 if (IS_INSN_ID(iobj, newrange)) {
3357 INSN *const range = iobj;
3358 INSN *beg, *end;
3359 VALUE str_beg, str_end;
3361 if ((end = (INSN *)get_prev_insn(range)) != 0 &&
3362 is_frozen_putstring(end, &str_end) &&
3363 (beg = (INSN *)get_prev_insn(end)) != 0 &&
3364 is_frozen_putstring(beg, &str_beg)) {
3365 int excl = FIX2INT(OPERAND_AT(range, 0));
3366 VALUE lit_range = rb_range_new(str_beg, str_end, excl);
3368 ELEM_REMOVE(&beg->link);
3369 ELEM_REMOVE(&end->link);
3370 range->insn_id = BIN(putobject);
3371 OPERAND_AT(range, 0) = lit_range;
3372 RB_OBJ_WRITTEN(iseq, Qundef, lit_range);
3376 if (IS_INSN_ID(iobj, leave)) {
3377 remove_unreachable_chunk(iseq, iobj->link.next);
3381 * ...
3382 * duparray [...]
3383 * concatarray | concattoarray
3384 * =>
3385 * ...
3386 * putobject [...]
3387 * concatarray | concattoarray
3389 if (IS_INSN_ID(iobj, duparray)) {
3390 LINK_ELEMENT *next = iobj->link.next;
3391 if (IS_INSN(next) && (IS_INSN_ID(next, concatarray) || IS_INSN_ID(next, concattoarray))) {
3392 iobj->insn_id = BIN(putobject);
3396 if (IS_INSN_ID(iobj, branchif) ||
3397 IS_INSN_ID(iobj, branchnil) ||
3398 IS_INSN_ID(iobj, branchunless)) {
3400 * if L1
3401 * ...
3402 * L1:
3403 * jump L2
3404 * =>
3405 * if L2
3407 INSN *nobj = (INSN *)get_destination_insn(iobj);
3409 /* This is super nasty hack!!!
3411 * This jump-jump optimization may ignore event flags of the jump
3412 * instruction being skipped. Actually, Line 2 TracePoint event
3413 * is never fired in the following code:
3415 * 1: raise if 1 == 2
3416 * 2: while true
3417 * 3: break
3418 * 4: end
3420 * This is critical for coverage measurement. [Bug #15980]
3422 * This is a stopgap measure: stop the jump-jump optimization if
3423 * coverage measurement is enabled and if the skipped instruction
3424 * has any event flag.
3426 * Note that, still, TracePoint Line event does not occur on Line 2.
3427 * This should be fixed in future.
3429 int stop_optimization =
3430 ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
3431 nobj->link.type == ISEQ_ELEMENT_INSN &&
3432 nobj->insn_info.events;
3433 if (!stop_optimization) {
3434 INSN *pobj = (INSN *)iobj->link.prev;
3435 int prev_dup = 0;
3436 if (pobj) {
3437 if (!IS_INSN(&pobj->link))
3438 pobj = 0;
3439 else if (IS_INSN_ID(pobj, dup))
3440 prev_dup = 1;
3443 for (;;) {
3444 if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) {
3445 replace_destination(iobj, nobj);
3447 else if (prev_dup && IS_INSN_ID(nobj, dup) &&
3448 !!(nobj = (INSN *)nobj->link.next) &&
3449 /* basic blocks, with no labels in the middle */
3450 nobj->insn_id == iobj->insn_id) {
3452 * dup
3453 * if L1
3454 * ...
3455 * L1:
3456 * dup
3457 * if L2
3458 * =>
3459 * dup
3460 * if L2
3461 * ...
3462 * L1:
3463 * dup
3464 * if L2
3466 replace_destination(iobj, nobj);
3468 else if (pobj) {
3470 * putnil
3471 * if L1
3472 * =>
3473 * # nothing
3475 * putobject true
3476 * if L1
3477 * =>
3478 * jump L1
3480 * putstring ".."
3481 * if L1
3482 * =>
3483 * jump L1
3485 * putstring ".."
3486 * dup
3487 * if L1
3488 * =>
3489 * putstring ".."
3490 * jump L1
3493 int cond;
3494 if (prev_dup && IS_INSN(pobj->link.prev)) {
3495 pobj = (INSN *)pobj->link.prev;
3497 if (IS_INSN_ID(pobj, putobject)) {
3498 cond = (IS_INSN_ID(iobj, branchif) ?
3499 OPERAND_AT(pobj, 0) != Qfalse :
3500 IS_INSN_ID(iobj, branchunless) ?
3501 OPERAND_AT(pobj, 0) == Qfalse :
3502 FALSE);
3504 else if (IS_INSN_ID(pobj, putstring) ||
3505 IS_INSN_ID(pobj, duparray) ||
3506 IS_INSN_ID(pobj, newarray)) {
3507 cond = IS_INSN_ID(iobj, branchif);
3509 else if (IS_INSN_ID(pobj, putnil)) {
3510 cond = !IS_INSN_ID(iobj, branchif);
3512 else break;
3513 if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
3514 ELEM_REMOVE(iobj->link.prev);
3516 else if (!iseq_pop_newarray(iseq, pobj)) {
3517 pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(pop), 0, NULL);
3518 ELEM_INSERT_PREV(&iobj->link, &pobj->link);
3520 if (cond) {
3521 if (prev_dup) {
3522 pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(putnil), 0, NULL);
3523 ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
3525 iobj->insn_id = BIN(jump);
3526 goto again;
3528 else {
3529 unref_destination(iobj, 0);
3530 ELEM_REMOVE(&iobj->link);
3532 break;
3534 else break;
3535 nobj = (INSN *)get_destination_insn(nobj);
3540 if (IS_INSN_ID(iobj, pop)) {
3542 * putself / putnil / putobject obj / putstring "..."
3543 * pop
3544 * =>
3545 * # do nothing
3547 LINK_ELEMENT *prev = iobj->link.prev;
3548 if (IS_INSN(prev)) {
3549 enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id;
3550 if (previ == BIN(putobject) || previ == BIN(putnil) ||
3551 previ == BIN(putself) || previ == BIN(putstring) ||
3552 previ == BIN(dup) ||
3553 previ == BIN(getlocal) ||
3554 previ == BIN(getblockparam) ||
3555 previ == BIN(getblockparamproxy) ||
3556 previ == BIN(getinstancevariable) ||
3557 previ == BIN(duparray)) {
3558 /* just push operand or static value and pop soon, no
3559 * side effects */
3560 ELEM_REMOVE(prev);
3561 ELEM_REMOVE(&iobj->link);
3563 else if (previ == BIN(newarray) && iseq_pop_newarray(iseq, (INSN*)prev)) {
3564 ELEM_REMOVE(&iobj->link);
3566 else if (previ == BIN(concatarray)) {
3567 INSN *piobj = (INSN *)prev;
3568 INSERT_BEFORE_INSN1(piobj, piobj->insn_info.line_no, piobj->insn_info.node_id, splatarray, Qfalse);
3569 INSN_OF(piobj) = BIN(pop);
3571 else if (previ == BIN(concatstrings)) {
3572 if (OPERAND_AT(prev, 0) == INT2FIX(1)) {
3573 ELEM_REMOVE(prev);
3575 else {
3576 ELEM_REMOVE(&iobj->link);
3577 INSN_OF(prev) = BIN(adjuststack);
3583 if (IS_INSN_ID(iobj, newarray) ||
3584 IS_INSN_ID(iobj, duparray) ||
3585 IS_INSN_ID(iobj, concatarray) ||
3586 IS_INSN_ID(iobj, splatarray) ||
3587 0) {
3589 * newarray N
3590 * splatarray
3591 * =>
3592 * newarray N
3593 * newarray always puts an array
3595 LINK_ELEMENT *next = iobj->link.next;
3596 if (IS_INSN(next) && IS_INSN_ID(next, splatarray)) {
3597 /* remove splatarray following always-array insn */
3598 ELEM_REMOVE(next);
3602 if (IS_INSN_ID(iobj, newarray)) {
3603 LINK_ELEMENT *next = iobj->link.next;
3604 if (IS_INSN(next) && IS_INSN_ID(next, expandarray) &&
3605 OPERAND_AT(next, 1) == INT2FIX(0)) {
3606 VALUE op1, op2;
3607 op1 = OPERAND_AT(iobj, 0);
3608 op2 = OPERAND_AT(next, 0);
3609 ELEM_REMOVE(next);
3611 if (op1 == op2) {
3613 * newarray 2
3614 * expandarray 2, 0
3615 * =>
3616 * swap
3618 if (op1 == INT2FIX(2)) {
3619 INSN_OF(iobj) = BIN(swap);
3620 iobj->operand_size = 0;
3623 * newarray X
3624 * expandarray X, 0
3625 * =>
3626 * opt_reverse X
3628 else {
3629 INSN_OF(iobj) = BIN(opt_reverse);
3632 else {
3633 long diff = FIX2LONG(op1) - FIX2LONG(op2);
3634 INSN_OF(iobj) = BIN(opt_reverse);
3635 OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0);
3637 if (op1 > op2) {
3638 /* X > Y
3639 * newarray X
3640 * expandarray Y, 0
3641 * =>
3642 * pop * (Y-X)
3643 * opt_reverse Y
3645 for (; diff > 0; diff--) {
3646 INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, pop);
3649 else { /* (op1 < op2) */
3650 /* X < Y
3651 * newarray X
3652 * expandarray Y, 0
3653 * =>
3654 * putnil * (Y-X)
3655 * opt_reverse Y
3657 for (; diff < 0; diff++) {
3658 INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, putnil);
3665 if (IS_INSN_ID(iobj, duparray)) {
3666 LINK_ELEMENT *next = iobj->link.next;
3668 * duparray obj
3669 * expandarray X, 0
3670 * =>
3671 * putobject obj
3672 * expandarray X, 0
3674 if (IS_INSN(next) && IS_INSN_ID(next, expandarray)) {
3675 INSN_OF(iobj) = BIN(putobject);
3679 if (IS_INSN_ID(iobj, anytostring)) {
3680 LINK_ELEMENT *next = iobj->link.next;
3682 * anytostring
3683 * concatstrings 1
3684 * =>
3685 * anytostring
3687 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings) &&
3688 OPERAND_AT(next, 0) == INT2FIX(1)) {
3689 ELEM_REMOVE(next);
3693 if (IS_INSN_ID(iobj, putstring) ||
3694 (IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) {
3696 * putstring ""
3697 * concatstrings N
3698 * =>
3699 * concatstrings N-1
3701 if (IS_NEXT_INSN_ID(&iobj->link, concatstrings) &&
3702 RSTRING_LEN(OPERAND_AT(iobj, 0)) == 0) {
3703 INSN *next = (INSN *)iobj->link.next;
3704 if ((OPERAND_AT(next, 0) = FIXNUM_INC(OPERAND_AT(next, 0), -1)) == INT2FIX(1)) {
3705 ELEM_REMOVE(&next->link);
3707 ELEM_REMOVE(&iobj->link);
3711 if (IS_INSN_ID(iobj, concatstrings)) {
3713 * concatstrings N
3714 * concatstrings M
3715 * =>
3716 * concatstrings N+M-1
3718 LINK_ELEMENT *next = iobj->link.next;
3719 INSN *jump = 0;
3720 if (IS_INSN(next) && IS_INSN_ID(next, jump))
3721 next = get_destination_insn(jump = (INSN *)next);
3722 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings)) {
3723 int n = FIX2INT(OPERAND_AT(iobj, 0)) + FIX2INT(OPERAND_AT(next, 0)) - 1;
3724 OPERAND_AT(iobj, 0) = INT2FIX(n);
3725 if (jump) {
3726 LABEL *label = ((LABEL *)OPERAND_AT(jump, 0));
3727 if (!--label->refcnt) {
3728 ELEM_REMOVE(&label->link);
3730 else {
3731 label = NEW_LABEL(0);
3732 OPERAND_AT(jump, 0) = (VALUE)label;
3734 label->refcnt++;
3735 ELEM_INSERT_NEXT(next, &label->link);
3736 CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
3738 else {
3739 ELEM_REMOVE(next);
3744 if (do_tailcallopt &&
3745 (IS_INSN_ID(iobj, send) ||
3746 IS_INSN_ID(iobj, opt_aref_with) ||
3747 IS_INSN_ID(iobj, opt_aset_with) ||
3748 IS_INSN_ID(iobj, invokesuper))) {
3750 * send ...
3751 * leave
3752 * =>
3753 * send ..., ... | VM_CALL_TAILCALL, ...
3754 * leave # unreachable
3756 INSN *piobj = NULL;
3757 if (iobj->link.next) {
3758 LINK_ELEMENT *next = iobj->link.next;
3759 do {
3760 if (!IS_INSN(next)) {
3761 next = next->next;
3762 continue;
3764 switch (INSN_OF(next)) {
3765 case BIN(nop):
3766 next = next->next;
3767 break;
3768 case BIN(jump):
3769 /* if cond
3770 * return tailcall
3771 * end
3773 next = get_destination_insn((INSN *)next);
3774 break;
3775 case BIN(leave):
3776 piobj = iobj;
3777 /* fall through */
3778 default:
3779 next = NULL;
3780 break;
3782 } while (next);
3785 if (piobj) {
3786 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(piobj, 0);
3787 if (IS_INSN_ID(piobj, send) ||
3788 IS_INSN_ID(piobj, invokesuper)) {
3789 if (OPERAND_AT(piobj, 1) == 0) { /* no blockiseq */
3790 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
3791 OPERAND_AT(piobj, 0) = (VALUE)ci;
3792 RB_OBJ_WRITTEN(iseq, Qundef, ci);
3795 else {
3796 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
3797 OPERAND_AT(piobj, 0) = (VALUE)ci;
3798 RB_OBJ_WRITTEN(iseq, Qundef, ci);
3803 if (IS_INSN_ID(iobj, dup)) {
3804 if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
3805 LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
3808 * dup
3809 * setlocal x, y
3810 * setlocal x, y
3811 * =>
3812 * dup
3813 * setlocal x, y
3815 if (IS_NEXT_INSN_ID(set1, setlocal)) {
3816 set2 = set1->next;
3817 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
3818 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
3819 ELEM_REMOVE(set1);
3820 ELEM_REMOVE(&iobj->link);
3825 * dup
3826 * setlocal x, y
3827 * dup
3828 * setlocal x, y
3829 * =>
3830 * dup
3831 * setlocal x, y
3833 else if (IS_NEXT_INSN_ID(set1, dup) &&
3834 IS_NEXT_INSN_ID(set1->next, setlocal)) {
3835 set2 = set1->next->next;
3836 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
3837 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
3838 ELEM_REMOVE(set1->next);
3839 ELEM_REMOVE(set2);
3846 * getlocal x, y
3847 * dup
3848 * setlocal x, y
3849 * =>
3850 * dup
3852 if (IS_INSN_ID(iobj, getlocal)) {
3853 LINK_ELEMENT *niobj = &iobj->link;
3854 if (IS_NEXT_INSN_ID(niobj, dup)) {
3855 niobj = niobj->next;
3857 if (IS_NEXT_INSN_ID(niobj, setlocal)) {
3858 LINK_ELEMENT *set1 = niobj->next;
3859 if (OPERAND_AT(iobj, 0) == OPERAND_AT(set1, 0) &&
3860 OPERAND_AT(iobj, 1) == OPERAND_AT(set1, 1)) {
3861 ELEM_REMOVE(set1);
3862 ELEM_REMOVE(niobj);
3868 * opt_invokebuiltin_delegate
3869 * trace
3870 * leave
3871 * =>
3872 * opt_invokebuiltin_delegate_leave
3873 * trace
3874 * leave
3876 if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) {
3877 if (IS_TRACE(iobj->link.next)) {
3878 if (IS_NEXT_INSN_ID(iobj->link.next, leave)) {
3879 iobj->insn_id = BIN(opt_invokebuiltin_delegate_leave);
3880 const struct rb_builtin_function *bf = (const struct rb_builtin_function *)iobj->operands[0];
3881 if (iobj == (INSN *)list && bf->argc == 0 && (iseq->body->builtin_attrs & BUILTIN_ATTR_LEAF)) {
3882 iseq->body->builtin_attrs |= BUILTIN_ATTR_SINGLE_NOARG_LEAF;
3889 * getblockparam
3890 * branchif / branchunless
3891 * =>
3892 * getblockparamproxy
3893 * branchif / branchunless
3895 if (IS_INSN_ID(iobj, getblockparam)) {
3896 if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) {
3897 iobj->insn_id = BIN(getblockparamproxy);
3901 if (IS_INSN_ID(iobj, splatarray) && OPERAND_AT(iobj, 0) == Qtrue) {
3902 LINK_ELEMENT *niobj = &iobj->link;
3905 * Eliminate array allocation for f(1, *a)
3907 * splatarray true
3908 * send ARGS_SPLAT and not KW_SPLAT|ARGS_BLOCKARG
3909 * =>
3910 * splatarray false
3911 * send
3913 if (optimize_args_splat_no_copy(iseq, iobj, niobj,
3914 VM_CALL_ARGS_SPLAT, VM_CALL_KW_SPLAT|VM_CALL_ARGS_BLOCKARG, 0)) goto optimized_splat;
3916 if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable)) {
3917 niobj = niobj->next;
3920 * Eliminate array allocation for f(1, *a, &lvar) and f(1, *a, &@iv)
3922 * splatarray true
3923 * getlocal / getinstancevariable
3924 * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT
3925 * =>
3926 * splatarray false
3927 * getlocal / getinstancevariable
3928 * send
3930 if (optimize_args_splat_no_copy(iseq, iobj, niobj,
3931 VM_CALL_ARGS_SPLAT|VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT, 0)) goto optimized_splat;
3934 * Eliminate array allocation for f(*a, **lvar) and f(*a, **@iv)
3936 * splatarray true
3937 * getlocal / getinstancevariable
3938 * send ARGS_SPLAT|KW_SPLAT and not ARGS_BLOCKARG
3939 * =>
3940 * splatarray false
3941 * getlocal / getinstancevariable
3942 * send
3944 if (optimize_args_splat_no_copy(iseq, iobj, niobj,
3945 VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT, VM_CALL_ARGS_BLOCKARG, 0)) goto optimized_splat;
3947 if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) ||
3948 IS_NEXT_INSN_ID(niobj, getblockparamproxy)) {
3949 niobj = niobj->next;
3952 * Eliminate array allocation for f(*a, **lvar, &{arg,lvar,@iv})
3954 * splatarray true
3955 * getlocal / getinstancevariable
3956 * getlocal / getinstancevariable / getblockparamproxy
3957 * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG
3958 * =>
3959 * splatarray false
3960 * getlocal / getinstancevariable
3961 * getlocal / getinstancevariable / getblockparamproxy
3962 * send
3964 optimize_args_splat_no_copy(iseq, iobj, niobj,
3965 VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_ARGS_BLOCKARG, 0, 0);
3967 } else if (IS_NEXT_INSN_ID(niobj, getblockparamproxy)) {
3969 * Eliminate array allocation for f(1, *a, &arg)
3971 * splatarray true
3972 * getblockparamproxy
3973 * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT
3974 * =>
3975 * splatarray false
3976 * getblockparamproxy
3977 * send
3979 optimize_args_splat_no_copy(iseq, iobj, niobj,
3980 VM_CALL_ARGS_SPLAT|VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT, 0);
3981 } else if (IS_NEXT_INSN_ID(niobj, duphash)) {
3982 niobj = niobj->next;
3985 * Eliminate array and hash allocation for f(*a, kw: 1)
3987 * splatarray true
3988 * duphash
3989 * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT and not ARGS_BLOCKARG
3990 * =>
3991 * splatarray false
3992 * putobject
3993 * send ARGS_SPLAT|KW_SPLAT
3995 if (optimize_args_splat_no_copy(iseq, iobj, niobj,
3996 VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT, VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT_MUT)) {
3998 ((INSN*)niobj)->insn_id = BIN(putobject);
3999 OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0)));
4001 goto optimized_splat;
4004 if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) ||
4005 IS_NEXT_INSN_ID(niobj, getblockparamproxy)) {
4007 * Eliminate array and hash allocation for f(*a, kw: 1, &{arg,lvar,@iv})
4009 * splatarray true
4010 * duphash
4011 * getlocal / getinstancevariable / getblockparamproxy
4012 * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT|ARGS_BLOCKARG
4013 * =>
4014 * splatarray false
4015 * putobject
4016 * getlocal / getinstancevariable / getblockparamproxy
4017 * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG
4019 if (optimize_args_splat_no_copy(iseq, iobj, niobj->next,
4020 VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG, 0, VM_CALL_KW_SPLAT_MUT)) {
4022 ((INSN*)niobj)->insn_id = BIN(putobject);
4023 OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0)));
4028 optimized_splat:
4030 return COMPILE_OK;
4033 static int
4034 insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
4036 iobj->insn_id = insn_id;
4037 iobj->operand_size = insn_len(insn_id) - 1;
4038 iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
4040 if (insn_id == BIN(opt_neq)) {
4041 VALUE original_ci = iobj->operands[0];
4042 iobj->operand_size = 2;
4043 iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE));
4044 iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
4045 iobj->operands[1] = original_ci;
4048 return COMPILE_OK;
4051 static int
4052 iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
4054 if (IS_INSN_ID(iobj, newarray) && iobj->link.next &&
4055 IS_INSN(iobj->link.next)) {
4057 * [a, b, ...].max/min -> a, b, c, opt_newarray_max/min
4059 INSN *niobj = (INSN *)iobj->link.next;
4060 if (IS_INSN_ID(niobj, send)) {
4061 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
4062 if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) {
4063 switch (vm_ci_mid(ci)) {
4064 case idMax:
4065 case idMin:
4066 case idHash:
4068 VALUE num = iobj->operands[0];
4069 iobj->insn_id = BIN(opt_newarray_send);
4070 iobj->operands = compile_data_calloc2(iseq, insn_len(iobj->insn_id) - 1, sizeof(VALUE));
4071 iobj->operands[0] = num;
4072 iobj->operands[1] = rb_id2sym(vm_ci_mid(ci));
4073 iobj->operand_size = insn_len(iobj->insn_id) - 1;
4074 ELEM_REMOVE(&niobj->link);
4075 return COMPILE_OK;
4082 if (IS_INSN_ID(iobj, send)) {
4083 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
4084 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1);
4086 #define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
4087 if (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) {
4088 switch (vm_ci_argc(ci)) {
4089 case 0:
4090 switch (vm_ci_mid(ci)) {
4091 case idLength: SP_INSN(length); return COMPILE_OK;
4092 case idSize: SP_INSN(size); return COMPILE_OK;
4093 case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
4094 case idNilP: SP_INSN(nil_p); return COMPILE_OK;
4095 case idSucc: SP_INSN(succ); return COMPILE_OK;
4096 case idNot: SP_INSN(not); return COMPILE_OK;
4098 break;
4099 case 1:
4100 switch (vm_ci_mid(ci)) {
4101 case idPLUS: SP_INSN(plus); return COMPILE_OK;
4102 case idMINUS: SP_INSN(minus); return COMPILE_OK;
4103 case idMULT: SP_INSN(mult); return COMPILE_OK;
4104 case idDIV: SP_INSN(div); return COMPILE_OK;
4105 case idMOD: SP_INSN(mod); return COMPILE_OK;
4106 case idEq: SP_INSN(eq); return COMPILE_OK;
4107 case idNeq: SP_INSN(neq); return COMPILE_OK;
4108 case idEqTilde:SP_INSN(regexpmatch2);return COMPILE_OK;
4109 case idLT: SP_INSN(lt); return COMPILE_OK;
4110 case idLE: SP_INSN(le); return COMPILE_OK;
4111 case idGT: SP_INSN(gt); return COMPILE_OK;
4112 case idGE: SP_INSN(ge); return COMPILE_OK;
4113 case idLTLT: SP_INSN(ltlt); return COMPILE_OK;
4114 case idAREF: SP_INSN(aref); return COMPILE_OK;
4115 case idAnd: SP_INSN(and); return COMPILE_OK;
4116 case idOr: SP_INSN(or); return COMPILE_OK;
4118 break;
4119 case 2:
4120 switch (vm_ci_mid(ci)) {
4121 case idASET: SP_INSN(aset); return COMPILE_OK;
4123 break;
4127 if ((vm_ci_flag(ci) & VM_CALL_ARGS_BLOCKARG) == 0 && blockiseq == NULL) {
4128 iobj->insn_id = BIN(opt_send_without_block);
4129 iobj->operand_size = insn_len(iobj->insn_id) - 1;
4132 #undef SP_INSN
4134 return COMPILE_OK;
4137 static inline int
4138 tailcallable_p(rb_iseq_t *iseq)
4140 switch (ISEQ_BODY(iseq)->type) {
4141 case ISEQ_TYPE_TOP:
4142 case ISEQ_TYPE_EVAL:
4143 case ISEQ_TYPE_MAIN:
4144 /* not tail callable because cfp will be over popped */
4145 case ISEQ_TYPE_RESCUE:
4146 case ISEQ_TYPE_ENSURE:
4147 /* rescue block can't tail call because of errinfo */
4148 return FALSE;
4149 default:
4150 return TRUE;
4154 static int
4155 iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
4157 LINK_ELEMENT *list;
4158 const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
4159 const int do_tailcallopt = tailcallable_p(iseq) &&
4160 ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
4161 const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
4162 const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
4163 int rescue_level = 0;
4164 int tailcallopt = do_tailcallopt;
4166 list = FIRST_ELEMENT(anchor);
4168 int do_block_optimization = 0;
4170 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK && !ISEQ_COMPILE_DATA(iseq)->catch_except_p) {
4171 do_block_optimization = 1;
4174 while (list) {
4175 if (IS_INSN(list)) {
4176 if (do_peepholeopt) {
4177 iseq_peephole_optimize(iseq, list, tailcallopt);
4179 if (do_si) {
4180 iseq_specialized_instruction(iseq, (INSN *)list);
4182 if (do_ou) {
4183 insn_operands_unification((INSN *)list);
4186 if (do_block_optimization) {
4187 INSN * item = (INSN *)list;
4188 if (IS_INSN_ID(item, jump)) {
4189 do_block_optimization = 0;
4193 if (IS_LABEL(list)) {
4194 switch (((LABEL *)list)->rescued) {
4195 case LABEL_RESCUE_BEG:
4196 rescue_level++;
4197 tailcallopt = FALSE;
4198 break;
4199 case LABEL_RESCUE_END:
4200 if (!--rescue_level) tailcallopt = do_tailcallopt;
4201 break;
4204 list = list->next;
4207 if (do_block_optimization) {
4208 LINK_ELEMENT * le = FIRST_ELEMENT(anchor)->next;
4209 if (IS_INSN(le) && IS_INSN_ID((INSN *)le, nop)) {
4210 ELEM_REMOVE(le);
4213 return COMPILE_OK;
4216 #if OPT_INSTRUCTIONS_UNIFICATION
4217 static INSN *
4218 new_unified_insn(rb_iseq_t *iseq,
4219 int insn_id, int size, LINK_ELEMENT *seq_list)
4221 INSN *iobj = 0;
4222 LINK_ELEMENT *list = seq_list;
4223 int i, argc = 0;
4224 VALUE *operands = 0, *ptr = 0;
4227 /* count argc */
4228 for (i = 0; i < size; i++) {
4229 iobj = (INSN *)list;
4230 argc += iobj->operand_size;
4231 list = list->next;
4234 if (argc > 0) {
4235 ptr = operands = compile_data_alloc2(iseq, sizeof(VALUE), argc);
4238 /* copy operands */
4239 list = seq_list;
4240 for (i = 0; i < size; i++) {
4241 iobj = (INSN *)list;
4242 MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
4243 ptr += iobj->operand_size;
4244 list = list->next;
4247 return new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, insn_id, argc, operands);
4249 #endif
4252 * This scheme can get more performance if do this optimize with
4253 * label address resolving.
4254 * It's future work (if compile time was bottle neck).
4256 static int
4257 iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
4259 #if OPT_INSTRUCTIONS_UNIFICATION
4260 LINK_ELEMENT *list;
4261 INSN *iobj, *niobj;
4262 int id, k;
4263 intptr_t j;
4265 list = FIRST_ELEMENT(anchor);
4266 while (list) {
4267 if (IS_INSN(list)) {
4268 iobj = (INSN *)list;
4269 id = iobj->insn_id;
4270 if (unified_insns_data[id] != 0) {
4271 const int *const *entry = unified_insns_data[id];
4272 for (j = 1; j < (intptr_t)entry[0]; j++) {
4273 const int *unified = entry[j];
4274 LINK_ELEMENT *li = list->next;
4275 for (k = 2; k < unified[1]; k++) {
4276 if (!IS_INSN(li) ||
4277 ((INSN *)li)->insn_id != unified[k]) {
4278 goto miss;
4280 li = li->next;
4282 /* matched */
4283 niobj =
4284 new_unified_insn(iseq, unified[0], unified[1] - 1,
4285 list);
4287 /* insert to list */
4288 niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
4289 niobj->link.next = li;
4290 if (li) {
4291 li->prev = (LINK_ELEMENT *)niobj;
4294 list->prev->next = (LINK_ELEMENT *)niobj;
4295 list = (LINK_ELEMENT *)niobj;
4296 break;
4297 miss:;
4301 list = list->next;
4303 #endif
4304 return COMPILE_OK;
4307 static int
4308 all_string_result_p(const NODE *node)
4310 if (!node) return FALSE;
4311 switch (nd_type(node)) {
4312 case NODE_STR: case NODE_DSTR: case NODE_FILE:
4313 return TRUE;
4314 case NODE_IF: case NODE_UNLESS:
4315 if (!RNODE_IF(node)->nd_body || !RNODE_IF(node)->nd_else) return FALSE;
4316 if (all_string_result_p(RNODE_IF(node)->nd_body))
4317 return all_string_result_p(RNODE_IF(node)->nd_else);
4318 return FALSE;
4319 case NODE_AND: case NODE_OR:
4320 if (!RNODE_AND(node)->nd_2nd)
4321 return all_string_result_p(RNODE_AND(node)->nd_1st);
4322 if (!all_string_result_p(RNODE_AND(node)->nd_1st))
4323 return FALSE;
4324 return all_string_result_p(RNODE_AND(node)->nd_2nd);
4325 default:
4326 return FALSE;
4330 static int
4331 compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp)
4333 const struct RNode_LIST *list = RNODE_DSTR(node)->nd_next;
4334 VALUE lit = rb_node_dstr_string_val(node);
4335 LINK_ELEMENT *first_lit = 0;
4336 int cnt = 0;
4338 debugp_param("nd_lit", lit);
4339 if (!NIL_P(lit)) {
4340 cnt++;
4341 if (!RB_TYPE_P(lit, T_STRING)) {
4342 COMPILE_ERROR(ERROR_ARGS "dstr: must be string: %s",
4343 rb_builtin_type_name(TYPE(lit)));
4344 return COMPILE_NG;
4346 lit = rb_fstring(lit);
4347 ADD_INSN1(ret, node, putobject, lit);
4348 RB_OBJ_WRITTEN(iseq, Qundef, lit);
4349 if (RSTRING_LEN(lit) == 0) first_lit = LAST_ELEMENT(ret);
4352 while (list) {
4353 const NODE *const head = list->nd_head;
4354 if (nd_type_p(head, NODE_STR)) {
4355 lit = rb_node_str_string_val(head);
4356 ADD_INSN1(ret, head, putobject, lit);
4357 RB_OBJ_WRITTEN(iseq, Qundef, lit);
4358 lit = Qnil;
4360 else {
4361 CHECK(COMPILE(ret, "each string", head));
4363 cnt++;
4364 list = (struct RNode_LIST *)list->nd_next;
4366 if (NIL_P(lit) && first_lit) {
4367 ELEM_REMOVE(first_lit);
4368 --cnt;
4370 *cntp = cnt;
4372 return COMPILE_OK;
4375 static int
4376 compile_block(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
4378 while (node && nd_type_p(node, NODE_BLOCK)) {
4379 CHECK(COMPILE_(ret, "BLOCK body", RNODE_BLOCK(node)->nd_head,
4380 (RNODE_BLOCK(node)->nd_next ? 1 : popped)));
4381 node = RNODE_BLOCK(node)->nd_next;
4383 if (node) {
4384 CHECK(COMPILE_(ret, "BLOCK next", RNODE_BLOCK(node)->nd_next, popped));
4386 return COMPILE_OK;
4389 static int
4390 compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
4392 int cnt;
4393 if (!RNODE_DSTR(node)->nd_next) {
4394 VALUE lit = rb_node_dstr_string_val(node);
4395 ADD_INSN1(ret, node, putstring, lit);
4396 RB_OBJ_WRITTEN(iseq, Qundef, lit);
4398 else {
4399 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
4400 ADD_INSN1(ret, node, concatstrings, INT2FIX(cnt));
4402 return COMPILE_OK;
4405 static int
4406 compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
4408 int cnt;
4410 if (!RNODE_DREGX(node)->nd_next) {
4411 if (!popped) {
4412 VALUE src = rb_node_dregx_string_val(node);
4413 VALUE match = rb_reg_compile(src, (int)RNODE_DREGX(node)->nd_cflag, NULL, 0);
4414 ADD_INSN1(ret, node, putobject, match);
4415 RB_OBJ_WRITTEN(iseq, Qundef, match);
4417 return COMPILE_OK;
4420 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
4421 ADD_INSN2(ret, node, toregexp, INT2FIX(RNODE_DREGX(node)->nd_cflag), INT2FIX(cnt));
4423 if (popped) {
4424 ADD_INSN(ret, node, pop);
4427 return COMPILE_OK;
4430 static int
4431 compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int again,
4432 LABEL *then_label, LABEL *else_label)
4434 const int line = nd_line(node);
4435 LABEL *lend = NEW_LABEL(line);
4436 rb_num_t cnt = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq)
4437 + VM_SVAR_FLIPFLOP_START;
4438 VALUE key = INT2FIX(cnt);
4440 ADD_INSN2(ret, node, getspecial, key, INT2FIX(0));
4441 ADD_INSNL(ret, node, branchif, lend);
4443 /* *flip == 0 */
4444 CHECK(COMPILE(ret, "flip2 beg", RNODE_FLIP2(node)->nd_beg));
4445 ADD_INSNL(ret, node, branchunless, else_label);
4446 ADD_INSN1(ret, node, putobject, Qtrue);
4447 ADD_INSN1(ret, node, setspecial, key);
4448 if (!again) {
4449 ADD_INSNL(ret, node, jump, then_label);
4452 /* *flip == 1 */
4453 ADD_LABEL(ret, lend);
4454 CHECK(COMPILE(ret, "flip2 end", RNODE_FLIP2(node)->nd_end));
4455 ADD_INSNL(ret, node, branchunless, then_label);
4456 ADD_INSN1(ret, node, putobject, Qfalse);
4457 ADD_INSN1(ret, node, setspecial, key);
4458 ADD_INSNL(ret, node, jump, then_label);
4460 return COMPILE_OK;
4463 static int
4464 compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
4465 LABEL *then_label, LABEL *else_label);
4467 #define COMPILE_SINGLE 2
4468 static int
4469 compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
4470 LABEL *then_label, LABEL *else_label)
4472 DECL_ANCHOR(seq);
4473 INIT_ANCHOR(seq);
4474 LABEL *label = NEW_LABEL(nd_line(cond));
4475 if (!then_label) then_label = label;
4476 else if (!else_label) else_label = label;
4478 CHECK(compile_branch_condition(iseq, seq, cond, then_label, else_label));
4480 if (LIST_INSN_SIZE_ONE(seq)) {
4481 INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
4482 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label)
4483 return COMPILE_OK;
4485 if (!label->refcnt) {
4486 return COMPILE_SINGLE;
4488 ADD_LABEL(seq, label);
4489 ADD_SEQ(ret, seq);
4490 return COMPILE_OK;
4493 static int
4494 compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
4495 LABEL *then_label, LABEL *else_label)
4497 int ok;
4498 DECL_ANCHOR(ignore);
4500 again:
4501 switch (nd_type(cond)) {
4502 case NODE_AND:
4503 CHECK(ok = compile_logical(iseq, ret, RNODE_AND(cond)->nd_1st, NULL, else_label));
4504 cond = RNODE_AND(cond)->nd_2nd;
4505 if (ok == COMPILE_SINGLE) {
4506 INIT_ANCHOR(ignore);
4507 ret = ignore;
4508 then_label = NEW_LABEL(nd_line(cond));
4510 goto again;
4511 case NODE_OR:
4512 CHECK(ok = compile_logical(iseq, ret, RNODE_OR(cond)->nd_1st, then_label, NULL));
4513 cond = RNODE_OR(cond)->nd_2nd;
4514 if (ok == COMPILE_SINGLE) {
4515 INIT_ANCHOR(ignore);
4516 ret = ignore;
4517 else_label = NEW_LABEL(nd_line(cond));
4519 goto again;
4520 case NODE_SYM:
4521 case NODE_LINE:
4522 case NODE_FILE:
4523 case NODE_ENCODING:
4524 case NODE_INTEGER: /* NODE_INTEGER is always true */
4525 case NODE_FLOAT: /* NODE_FLOAT is always true */
4526 case NODE_RATIONAL: /* NODE_RATIONAL is always true */
4527 case NODE_IMAGINARY: /* NODE_IMAGINARY is always true */
4528 case NODE_TRUE:
4529 case NODE_STR:
4530 case NODE_REGX:
4531 case NODE_ZLIST:
4532 case NODE_LAMBDA:
4533 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4534 ADD_INSNL(ret, cond, jump, then_label);
4535 return COMPILE_OK;
4536 case NODE_FALSE:
4537 case NODE_NIL:
4538 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4539 ADD_INSNL(ret, cond, jump, else_label);
4540 return COMPILE_OK;
4541 case NODE_LIST:
4542 case NODE_ARGSCAT:
4543 case NODE_DREGX:
4544 case NODE_DSTR:
4545 CHECK(COMPILE_POPPED(ret, "branch condition", cond));
4546 ADD_INSNL(ret, cond, jump, then_label);
4547 return COMPILE_OK;
4548 case NODE_FLIP2:
4549 CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
4550 return COMPILE_OK;
4551 case NODE_FLIP3:
4552 CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
4553 return COMPILE_OK;
4554 case NODE_DEFINED:
4555 CHECK(compile_defined_expr(iseq, ret, cond, Qfalse));
4556 break;
4557 default:
4559 DECL_ANCHOR(cond_seq);
4560 INIT_ANCHOR(cond_seq);
4562 CHECK(COMPILE(cond_seq, "branch condition", cond));
4564 if (LIST_INSN_SIZE_ONE(cond_seq)) {
4565 INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
4566 if (insn->insn_id == BIN(putobject)) {
4567 if (RTEST(insn->operands[0])) {
4568 ADD_INSNL(ret, cond, jump, then_label);
4569 // maybe unreachable
4570 return COMPILE_OK;
4572 else {
4573 ADD_INSNL(ret, cond, jump, else_label);
4574 return COMPILE_OK;
4578 ADD_SEQ(ret, cond_seq);
4580 break;
4583 ADD_INSNL(ret, cond, branchunless, else_label);
4584 ADD_INSNL(ret, cond, jump, then_label);
4585 return COMPILE_OK;
4588 #define HASH_BRACE 1
4590 static int
4591 keyword_node_p(const NODE *const node)
4593 return nd_type_p(node, NODE_HASH) && (RNODE_HASH(node)->nd_brace & HASH_BRACE) != HASH_BRACE;
4596 static VALUE
4597 get_symbol_value(rb_iseq_t *iseq, const NODE *node)
4599 switch (nd_type(node)) {
4600 case NODE_SYM:
4601 return rb_node_sym_string_val(node);
4602 default:
4603 UNKNOWN_NODE("get_symbol_value", node, Qnil);
4607 static VALUE
4608 node_hash_unique_key_index(rb_iseq_t *iseq, rb_node_hash_t *node_hash, int *count_ptr)
4610 NODE *node = node_hash->nd_head;
4611 VALUE hash = rb_hash_new();
4612 VALUE ary = rb_ary_new();
4614 for (int i = 0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
4615 VALUE key = get_symbol_value(iseq, RNODE_LIST(node)->nd_head);
4616 VALUE idx = rb_hash_aref(hash, key);
4617 if (!NIL_P(idx)) {
4618 rb_ary_store(ary, FIX2INT(idx), Qfalse);
4619 (*count_ptr)--;
4621 rb_hash_aset(hash, key, INT2FIX(i));
4622 rb_ary_store(ary, i, Qtrue);
4623 (*count_ptr)++;
4626 return ary;
4629 static int
4630 compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
4631 const NODE *const root_node,
4632 struct rb_callinfo_kwarg **const kw_arg_ptr,
4633 unsigned int *flag)
4635 RUBY_ASSERT(nd_type_p(root_node, NODE_HASH));
4636 RUBY_ASSERT(kw_arg_ptr != NULL);
4637 RUBY_ASSERT(flag != NULL);
4639 if (RNODE_HASH(root_node)->nd_head && nd_type_p(RNODE_HASH(root_node)->nd_head, NODE_LIST)) {
4640 const NODE *node = RNODE_HASH(root_node)->nd_head;
4641 int seen_nodes = 0;
4643 while (node) {
4644 const NODE *key_node = RNODE_LIST(node)->nd_head;
4645 seen_nodes++;
4647 RUBY_ASSERT(nd_type_p(node, NODE_LIST));
4648 if (key_node && nd_type_p(key_node, NODE_SYM)) {
4649 /* can be keywords */
4651 else {
4652 if (flag) {
4653 *flag |= VM_CALL_KW_SPLAT;
4654 if (seen_nodes > 1 || RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
4655 /* A new hash will be created for the keyword arguments
4656 * in this case, so mark the method as passing mutable
4657 * keyword splat.
4659 *flag |= VM_CALL_KW_SPLAT_MUT;
4662 return FALSE;
4664 node = RNODE_LIST(node)->nd_next; /* skip value node */
4665 node = RNODE_LIST(node)->nd_next;
4668 /* may be keywords */
4669 node = RNODE_HASH(root_node)->nd_head;
4671 int len = 0;
4672 VALUE key_index = node_hash_unique_key_index(iseq, RNODE_HASH(root_node), &len);
4673 struct rb_callinfo_kwarg *kw_arg =
4674 rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
4675 VALUE *keywords = kw_arg->keywords;
4676 int i = 0;
4677 int j = 0;
4678 kw_arg->references = 0;
4679 kw_arg->keyword_len = len;
4681 *kw_arg_ptr = kw_arg;
4683 for (i=0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
4684 const NODE *key_node = RNODE_LIST(node)->nd_head;
4685 const NODE *val_node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
4686 int popped = TRUE;
4687 if (rb_ary_entry(key_index, i)) {
4688 keywords[j] = get_symbol_value(iseq, key_node);
4689 j++;
4690 popped = FALSE;
4692 NO_CHECK(COMPILE_(ret, "keyword values", val_node, popped));
4694 RUBY_ASSERT(j == len);
4695 return TRUE;
4698 return FALSE;
4701 static int
4702 compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, NODE **kwnode_ptr)
4704 int len = 0;
4706 for (; node; len++, node = RNODE_LIST(node)->nd_next) {
4707 if (CPDEBUG > 0) {
4708 EXPECT_NODE("compile_args", node, NODE_LIST, -1);
4711 if (RNODE_LIST(node)->nd_next == NULL && keyword_node_p(RNODE_LIST(node)->nd_head)) { /* last node is kwnode */
4712 *kwnode_ptr = RNODE_LIST(node)->nd_head;
4714 else {
4715 RUBY_ASSERT(!keyword_node_p(RNODE_LIST(node)->nd_head));
4716 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, FALSE));
4720 return len;
4723 static inline bool
4724 frozen_string_literal_p(const rb_iseq_t *iseq)
4726 return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal > 0;
4729 static inline bool
4730 static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key)
4732 switch (nd_type(node)) {
4733 case NODE_SYM:
4734 case NODE_REGX:
4735 case NODE_LINE:
4736 case NODE_ENCODING:
4737 case NODE_INTEGER:
4738 case NODE_FLOAT:
4739 case NODE_RATIONAL:
4740 case NODE_IMAGINARY:
4741 case NODE_NIL:
4742 case NODE_TRUE:
4743 case NODE_FALSE:
4744 return TRUE;
4745 case NODE_STR:
4746 case NODE_FILE:
4747 return hash_key || frozen_string_literal_p(iseq);
4748 default:
4749 return FALSE;
4753 static inline VALUE
4754 static_literal_value(const NODE *node, rb_iseq_t *iseq)
4756 switch (nd_type(node)) {
4757 case NODE_INTEGER:
4758 return rb_node_integer_literal_val(node);
4759 case NODE_FLOAT:
4760 return rb_node_float_literal_val(node);
4761 case NODE_RATIONAL:
4762 return rb_node_rational_literal_val(node);
4763 case NODE_IMAGINARY:
4764 return rb_node_imaginary_literal_val(node);
4765 case NODE_NIL:
4766 return Qnil;
4767 case NODE_TRUE:
4768 return Qtrue;
4769 case NODE_FALSE:
4770 return Qfalse;
4771 case NODE_SYM:
4772 return rb_node_sym_string_val(node);
4773 case NODE_REGX:
4774 return rb_node_regx_string_val(node);
4775 case NODE_LINE:
4776 return rb_node_line_lineno_val(node);
4777 case NODE_ENCODING:
4778 return rb_node_encoding_val(node);
4779 case NODE_FILE:
4780 case NODE_STR:
4781 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
4782 VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX((int)nd_line(node)));
4783 VALUE lit = rb_str_dup(get_string_value(node));
4784 rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
4785 return rb_str_freeze(lit);
4787 else {
4788 return get_string_value(node);
4790 default:
4791 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
4795 static int
4796 compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped, bool first_chunk)
4798 const NODE *line_node = node;
4800 if (nd_type_p(node, NODE_ZLIST)) {
4801 if (!popped) {
4802 ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
4804 return 0;
4807 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
4809 if (popped) {
4810 for (; node; node = RNODE_LIST(node)->nd_next) {
4811 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, popped));
4813 return 1;
4816 /* Compilation of an array literal.
4817 * The following code is essentially the same as:
4819 * for (int count = 0; node; count++; node->nd_next) {
4820 * compile(node->nd_head);
4822 * ADD_INSN(newarray, count);
4824 * However, there are three points.
4826 * - The code above causes stack overflow for a big string literal.
4827 * The following limits the stack length up to max_stack_len.
4829 * [x1,x2,...,x10000] =>
4830 * push x1 ; push x2 ; ...; push x256; newarray 256;
4831 * push x257; push x258; ...; push x512; pushtoarray 256;
4832 * push x513; push x514; ...; push x768; pushtoarray 256;
4833 * ...
4835 * - Long subarray can be optimized by pre-allocating a hidden array.
4837 * [1,2,3,...,100] =>
4838 * duparray [1,2,3,...,100]
4840 * [x, 1,2,3,...,100, z] =>
4841 * push x; newarray 1;
4842 * putobject [1,2,3,...,100] (<- hidden array); concattoarray;
4843 * push z; pushtoarray 1;
4845 * - If the last element is a keyword, pushtoarraykwsplat should be emitted
4846 * to only push it onto the array if it is not empty
4847 * (Note: a keyword is NODE_HASH which is not static_literal_node_p.)
4849 * [1,2,3,**kw] =>
4850 * putobject 1; putobject 2; putobject 3; newarray 3; ...; pushtoarraykwsplat kw
4853 const int max_stack_len = 0x100;
4854 const int min_tmp_ary_len = 0x40;
4855 int stack_len = 0;
4857 /* Either create a new array, or push to the existing array */
4858 #define FLUSH_CHUNK \
4859 if (stack_len) { \
4860 if (first_chunk) ADD_INSN1(ret, line_node, newarray, INT2FIX(stack_len)); \
4861 else ADD_INSN1(ret, line_node, pushtoarray, INT2FIX(stack_len)); \
4862 first_chunk = FALSE; \
4863 stack_len = 0; \
4866 while (node) {
4867 int count = 1;
4869 /* pre-allocation check (this branch can be omittable) */
4870 if (static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, false)) {
4871 /* count the elements that are optimizable */
4872 const NODE *node_tmp = RNODE_LIST(node)->nd_next;
4873 for (; node_tmp && static_literal_node_p(RNODE_LIST(node_tmp)->nd_head, iseq, false); node_tmp = RNODE_LIST(node_tmp)->nd_next)
4874 count++;
4876 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) {
4877 /* The literal contains only optimizable elements, or the subarray is long enough */
4878 VALUE ary = rb_ary_hidden_new(count);
4880 /* Create a hidden array */
4881 for (; count; count--, node = RNODE_LIST(node)->nd_next)
4882 rb_ary_push(ary, static_literal_value(RNODE_LIST(node)->nd_head, iseq));
4883 OBJ_FREEZE(ary);
4885 /* Emit optimized code */
4886 FLUSH_CHUNK;
4887 if (first_chunk) {
4888 ADD_INSN1(ret, line_node, duparray, ary);
4889 first_chunk = FALSE;
4891 else {
4892 ADD_INSN1(ret, line_node, putobject, ary);
4893 ADD_INSN(ret, line_node, concattoarray);
4895 RB_OBJ_WRITTEN(iseq, Qundef, ary);
4899 /* Base case: Compile "count" elements */
4900 for (; count; count--, node = RNODE_LIST(node)->nd_next) {
4901 if (CPDEBUG > 0) {
4902 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
4905 if (!RNODE_LIST(node)->nd_next && keyword_node_p(RNODE_LIST(node)->nd_head)) {
4906 /* Create array or push existing non-keyword elements onto array */
4907 if (stack_len == 0 && first_chunk) {
4908 ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
4910 else {
4911 FLUSH_CHUNK;
4913 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
4914 ADD_INSN(ret, line_node, pushtoarraykwsplat);
4915 return 1;
4917 else {
4918 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
4919 stack_len++;
4922 /* If there are many pushed elements, flush them to avoid stack overflow */
4923 if (stack_len >= max_stack_len) FLUSH_CHUNK;
4927 FLUSH_CHUNK;
4928 #undef FLUSH_CHUNK
4929 return 1;
4932 static inline int
4933 static_literal_node_pair_p(const NODE *node, const rb_iseq_t *iseq)
4935 return RNODE_LIST(node)->nd_head && static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, true) && static_literal_node_p(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq, false);
4938 static int
4939 compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped)
4941 const NODE *line_node = node;
4943 node = RNODE_HASH(node)->nd_head;
4945 if (!node || nd_type_p(node, NODE_ZLIST)) {
4946 if (!popped) {
4947 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
4949 return 0;
4952 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
4954 if (popped) {
4955 for (; node; node = RNODE_LIST(node)->nd_next) {
4956 NO_CHECK(COMPILE_(ret, "hash element", RNODE_LIST(node)->nd_head, popped));
4958 return 1;
4961 /* Compilation of a hash literal (or keyword arguments).
4962 * This is very similar to compile_array, but there are some differences:
4964 * - It contains key-value pairs. So we need to take every two elements.
4965 * We can assume that the length is always even.
4967 * - Merging is done by a method call (id_core_hash_merge_ptr).
4968 * Sometimes we need to insert the receiver, so "anchor" is needed.
4969 * In addition, a method call is much slower than concatarray.
4970 * So it pays only when the subsequence is really long.
4971 * (min_tmp_hash_len must be much larger than min_tmp_ary_len.)
4973 * - We need to handle keyword splat: **kw.
4974 * For **kw, the key part (node->nd_head) is NULL, and the value part
4975 * (node->nd_next->nd_head) is "kw".
4976 * The code is a bit difficult to avoid hash allocation for **{}.
4979 const int max_stack_len = 0x100;
4980 const int min_tmp_hash_len = 0x800;
4981 int stack_len = 0;
4982 int first_chunk = 1;
4983 DECL_ANCHOR(anchor);
4984 INIT_ANCHOR(anchor);
4986 /* Convert pushed elements to a hash, and merge if needed */
4987 #define FLUSH_CHUNK() \
4988 if (stack_len) { \
4989 if (first_chunk) { \
4990 APPEND_LIST(ret, anchor); \
4991 ADD_INSN1(ret, line_node, newhash, INT2FIX(stack_len)); \
4993 else { \
4994 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
4995 ADD_INSN(ret, line_node, swap); \
4996 APPEND_LIST(ret, anchor); \
4997 ADD_SEND(ret, line_node, id_core_hash_merge_ptr, INT2FIX(stack_len + 1)); \
4999 INIT_ANCHOR(anchor); \
5000 first_chunk = stack_len = 0; \
5003 while (node) {
5004 int count = 1;
5006 /* pre-allocation check (this branch can be omittable) */
5007 if (static_literal_node_pair_p(node, iseq)) {
5008 /* count the elements that are optimizable */
5009 const NODE *node_tmp = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next;
5010 for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = RNODE_LIST(RNODE_LIST(node_tmp)->nd_next)->nd_next)
5011 count++;
5013 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_hash_len) {
5014 /* The literal contains only optimizable elements, or the subsequence is long enough */
5015 VALUE ary = rb_ary_hidden_new(count);
5017 /* Create a hidden hash */
5018 for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5019 VALUE elem[2];
5020 elem[0] = static_literal_value(RNODE_LIST(node)->nd_head, iseq);
5021 elem[1] = static_literal_value(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq);
5022 rb_ary_cat(ary, elem, 2);
5024 VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
5025 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
5026 hash = rb_obj_hide(hash);
5027 OBJ_FREEZE(hash);
5029 /* Emit optimized code */
5030 FLUSH_CHUNK();
5031 if (first_chunk) {
5032 ADD_INSN1(ret, line_node, duphash, hash);
5033 first_chunk = 0;
5035 else {
5036 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5037 ADD_INSN(ret, line_node, swap);
5039 ADD_INSN1(ret, line_node, putobject, hash);
5041 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5043 RB_OBJ_WRITTEN(iseq, Qundef, hash);
5047 /* Base case: Compile "count" elements */
5048 for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5050 if (CPDEBUG > 0) {
5051 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
5054 if (RNODE_LIST(node)->nd_head) {
5055 /* Normal key-value pair */
5056 NO_CHECK(COMPILE_(anchor, "hash key element", RNODE_LIST(node)->nd_head, 0));
5057 NO_CHECK(COMPILE_(anchor, "hash value element", RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, 0));
5058 stack_len += 2;
5060 /* If there are many pushed elements, flush them to avoid stack overflow */
5061 if (stack_len >= max_stack_len) FLUSH_CHUNK();
5063 else {
5064 /* kwsplat case: foo(..., **kw, ...) */
5065 FLUSH_CHUNK();
5067 const NODE *kw = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
5068 int empty_kw = nd_type_p(kw, NODE_HASH) && (!RNODE_HASH(kw)->nd_head); /* foo( ..., **{}, ...) */
5069 int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */
5070 int last_kw = !RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; /* foo( ..., **kw) */
5071 int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
5073 empty_kw = empty_kw || nd_type_p(kw, NODE_NIL); /* foo( ..., **nil, ...) */
5074 if (empty_kw) {
5075 if (only_kw && method_call_keywords) {
5076 /* **{} appears at the only keyword argument in method call,
5077 * so it won't be modified.
5078 * kw is a special NODE_LIT that contains a special empty hash,
5079 * so this emits: putobject {}.
5080 * This is only done for method calls and not for literal hashes,
5081 * because literal hashes should always result in a new hash.
5083 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5085 else if (first_kw) {
5086 /* **{} appears as the first keyword argument, so it may be modified.
5087 * We need to create a fresh hash object.
5089 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5091 /* Any empty keyword splats that are not the first can be ignored.
5092 * since merging an empty hash into the existing hash is the same
5093 * as not merging it. */
5095 else {
5096 if (only_kw && method_call_keywords) {
5097 /* **kw is only keyword argument in method call.
5098 * Use directly. This will be not be flagged as mutable.
5099 * This is only done for method calls and not for literal hashes,
5100 * because literal hashes should always result in a new hash.
5102 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5104 else {
5105 /* There is more than one keyword argument, or this is not a method
5106 * call. In that case, we need to add an empty hash (if first keyword),
5107 * or merge the hash to the accumulated hash (if not the first keyword).
5109 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5110 if (first_kw) ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5111 else ADD_INSN(ret, line_node, swap);
5113 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5115 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5119 first_chunk = 0;
5124 FLUSH_CHUNK();
5125 #undef FLUSH_CHUNK
5126 return 1;
5129 VALUE
5130 rb_node_case_when_optimizable_literal(const NODE *const node)
5132 switch (nd_type(node)) {
5133 case NODE_INTEGER:
5134 return rb_node_integer_literal_val(node);
5135 case NODE_FLOAT: {
5136 VALUE v = rb_node_float_literal_val(node);
5137 double ival;
5139 if (modf(RFLOAT_VALUE(v), &ival) == 0.0) {
5140 return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
5142 return v;
5144 case NODE_RATIONAL:
5145 case NODE_IMAGINARY:
5146 return Qundef;
5147 case NODE_NIL:
5148 return Qnil;
5149 case NODE_TRUE:
5150 return Qtrue;
5151 case NODE_FALSE:
5152 return Qfalse;
5153 case NODE_SYM:
5154 return rb_node_sym_string_val(node);
5155 case NODE_LINE:
5156 return rb_node_line_lineno_val(node);
5157 case NODE_STR:
5158 return rb_node_str_string_val(node);
5159 case NODE_FILE:
5160 return rb_node_file_path_val(node);
5162 return Qundef;
5165 static int
5166 when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5167 LABEL *l1, int only_special_literals, VALUE literals)
5169 while (vals) {
5170 const NODE *val = RNODE_LIST(vals)->nd_head;
5171 VALUE lit = rb_node_case_when_optimizable_literal(val);
5173 if (UNDEF_P(lit)) {
5174 only_special_literals = 0;
5176 else if (NIL_P(rb_hash_lookup(literals, lit))) {
5177 rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
5180 if (nd_type_p(val, NODE_STR) || nd_type_p(val, NODE_FILE)) {
5181 debugp_param("nd_lit", get_string_value(val));
5182 lit = get_string_value(val);
5183 ADD_INSN1(cond_seq, val, putobject, lit);
5184 RB_OBJ_WRITTEN(iseq, Qundef, lit);
5186 else {
5187 if (!COMPILE(cond_seq, "when cond", val)) return -1;
5190 // Emit pattern === target
5191 ADD_INSN1(cond_seq, vals, topn, INT2FIX(1));
5192 ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1));
5193 ADD_INSNL(cond_seq, val, branchif, l1);
5194 vals = RNODE_LIST(vals)->nd_next;
5196 return only_special_literals;
5199 static int
5200 when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5201 LABEL *l1, int only_special_literals, VALUE literals)
5203 const NODE *line_node = vals;
5205 switch (nd_type(vals)) {
5206 case NODE_LIST:
5207 if (when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals) < 0)
5208 return COMPILE_NG;
5209 break;
5210 case NODE_SPLAT:
5211 ADD_INSN (cond_seq, line_node, dup);
5212 CHECK(COMPILE(cond_seq, "when splat", RNODE_SPLAT(vals)->nd_head));
5213 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5214 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5215 ADD_INSNL(cond_seq, line_node, branchif, l1);
5216 break;
5217 case NODE_ARGSCAT:
5218 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_head, l1, only_special_literals, literals));
5219 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_body, l1, only_special_literals, literals));
5220 break;
5221 case NODE_ARGSPUSH:
5222 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSPUSH(vals)->nd_head, l1, only_special_literals, literals));
5223 ADD_INSN (cond_seq, line_node, dup);
5224 CHECK(COMPILE(cond_seq, "when argspush body", RNODE_ARGSPUSH(vals)->nd_body));
5225 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
5226 ADD_INSNL(cond_seq, line_node, branchif, l1);
5227 break;
5228 default:
5229 ADD_INSN (cond_seq, line_node, dup);
5230 CHECK(COMPILE(cond_seq, "when val", vals));
5231 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5232 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5233 ADD_INSNL(cond_seq, line_node, branchif, l1);
5234 break;
5236 return COMPILE_OK;
5239 /* Multiple Assignment Handling
5241 * In order to handle evaluation of multiple assignment such that the left hand side
5242 * is evaluated before the right hand side, we need to process the left hand side
5243 * and see if there are any attributes that need to be assigned, or constants set
5244 * on explicit objects. If so, we add instructions to evaluate the receiver of
5245 * any assigned attributes or constants before we process the right hand side.
5247 * For a multiple assignment such as:
5249 * l1.m1, l2[0] = r3, r4
5251 * We start off evaluating l1 and l2, then we evaluate r3 and r4, then we
5252 * assign the result of r3 to l1.m1, and then the result of r4 to l2.m2.
5253 * On the VM stack, this looks like:
5255 * self # putself
5256 * l1 # send
5257 * l1, self # putself
5258 * l1, l2 # send
5259 * l1, l2, 0 # putobject 0
5260 * l1, l2, 0, [r3, r4] # after evaluation of RHS
5261 * l1, l2, 0, [r3, r4], r4, r3 # expandarray
5262 * l1, l2, 0, [r3, r4], r4, r3, l1 # topn 5
5263 * l1, l2, 0, [r3, r4], r4, l1, r3 # swap
5264 * l1, l2, 0, [r3, r4], r4, m1= # send
5265 * l1, l2, 0, [r3, r4], r4 # pop
5266 * l1, l2, 0, [r3, r4], r4, l2 # topn 3
5267 * l1, l2, 0, [r3, r4], r4, l2, 0 # topn 3
5268 * l1, l2, 0, [r3, r4], r4, l2, 0, r4 # topn 2
5269 * l1, l2, 0, [r3, r4], r4, []= # send
5270 * l1, l2, 0, [r3, r4], r4 # pop
5271 * l1, l2, 0, [r3, r4] # pop
5272 * [r3, r4], l2, 0, [r3, r4] # setn 3
5273 * [r3, r4], l2, 0 # pop
5274 * [r3, r4], l2 # pop
5275 * [r3, r4] # pop
5277 * This is made more complex when you have to handle splats, post args,
5278 * and arbitrary levels of nesting. You need to keep track of the total
5279 * number of attributes to set, and for each attribute, how many entries
5280 * are on the stack before the final attribute, in order to correctly
5281 * calculate the topn value to use to get the receiver of the attribute
5282 * setter method.
5284 * A brief description of the VM stack for simple multiple assignment
5285 * with no splat (rhs_array will not be present if the return value of
5286 * the multiple assignment is not needed):
5288 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., rhs_arg2, rhs_arg1
5290 * For multiple assignment with splats, while processing the part before
5291 * the splat (splat+post here is an array of the splat and the post arguments):
5293 * lhs_attr1, lhs_attr2, ..., rhs_array, splat+post, ..., rhs_arg2, rhs_arg1
5295 * When processing the splat and post arguments:
5297 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., post_arg2, post_arg1, splat
5299 * When processing nested multiple assignment, existing values on the stack
5300 * are kept. So for:
5302 * (l1.m1, l2.m2), l3.m3, l4* = [r1, r2], r3, r4
5304 * The stack layout would be the following before processing the nested
5305 * multiple assignment:
5307 * l1, l2, [[r1, r2], r3, r4], [r4], r3, [r1, r2]
5309 * In order to handle this correctly, we need to keep track of the nesting
5310 * level for each attribute assignment, as well as the attribute number
5311 * (left hand side attributes are processed left to right) and number of
5312 * arguments to pass to the setter method. struct masgn_lhs_node tracks
5313 * this information.
5315 * We also need to track information for the entire multiple assignment, such
5316 * as the total number of arguments, and the current nesting level, to
5317 * handle both nested multiple assignment as well as cases where the
5318 * rhs is not needed. We also need to keep track of all attribute
5319 * assignments in this, which we do using a linked listed. struct masgn_state
5320 * tracks this information.
5323 struct masgn_lhs_node {
5324 INSN *before_insn;
5325 struct masgn_lhs_node *next;
5326 const NODE *line_node;
5327 int argn;
5328 int num_args;
5329 int lhs_pos;
5332 struct masgn_state {
5333 struct masgn_lhs_node *first_memo;
5334 struct masgn_lhs_node *last_memo;
5335 int lhs_level;
5336 int num_args;
5337 bool nested;
5340 static int
5341 add_masgn_lhs_node(struct masgn_state *state, int lhs_pos, const NODE *line_node, int argc, INSN *before_insn)
5343 if (!state) {
5344 rb_bug("no masgn_state");
5347 struct masgn_lhs_node *memo;
5348 memo = malloc(sizeof(struct masgn_lhs_node));
5349 if (!memo) {
5350 return COMPILE_NG;
5353 memo->before_insn = before_insn;
5354 memo->line_node = line_node;
5355 memo->argn = state->num_args + 1;
5356 memo->num_args = argc;
5357 state->num_args += argc;
5358 memo->lhs_pos = lhs_pos;
5359 memo->next = NULL;
5360 if (!state->first_memo) {
5361 state->first_memo = memo;
5363 else {
5364 state->last_memo->next = memo;
5366 state->last_memo = memo;
5368 return COMPILE_OK;
5371 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);
5373 static int
5374 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)
5376 switch (nd_type(node)) {
5377 case NODE_ATTRASGN: {
5378 INSN *iobj;
5379 const NODE *line_node = node;
5381 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
5383 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5384 iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
5385 ASSUME(iobj);
5386 ELEM_REMOVE(LAST_ELEMENT(pre));
5387 ELEM_REMOVE((LINK_ELEMENT *)iobj);
5388 pre->last = iobj->link.prev;
5390 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
5391 int argc = vm_ci_argc(ci) + 1;
5392 ci = ci_argc_set(iseq, ci, argc);
5393 OPERAND_AT(iobj, 0) = (VALUE)ci;
5394 RB_OBJ_WRITTEN(iseq, Qundef, ci);
5396 if (argc == 1) {
5397 ADD_INSN(lhs, line_node, swap);
5399 else {
5400 ADD_INSN1(lhs, line_node, topn, INT2FIX(argc));
5403 if (!add_masgn_lhs_node(state, lhs_pos, line_node, argc, (INSN *)LAST_ELEMENT(lhs))) {
5404 return COMPILE_NG;
5407 ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
5408 if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
5409 int argc = vm_ci_argc(ci);
5410 bool dupsplat = false;
5411 ci = ci_argc_set(iseq, ci, argc - 1);
5412 if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT_MUT)) {
5413 /* Given h[*a], _ = ary
5414 * setup_args sets VM_CALL_ARGS_SPLAT and not VM_CALL_ARGS_SPLAT_MUT
5415 * `a` must be dupped, because it will be appended with ary[0]
5416 * Since you are dupping `a`, you can set VM_CALL_ARGS_SPLAT_MUT
5418 dupsplat = true;
5419 ci = ci_flag_set(iseq, ci, VM_CALL_ARGS_SPLAT_MUT);
5421 OPERAND_AT(iobj, 0) = (VALUE)ci;
5422 RB_OBJ_WRITTEN(iseq, Qundef, iobj);
5424 /* Given: h[*a], h[*b, 1] = ary
5425 * h[*a] uses splatarray false and does not set VM_CALL_ARGS_SPLAT_MUT,
5426 * so this uses splatarray true on a to dup it before using pushtoarray
5427 * h[*b, 1] uses splatarray true and sets VM_CALL_ARGS_SPLAT_MUT,
5428 * so you can use pushtoarray directly
5430 int line_no = nd_line(line_node);
5431 int node_id = nd_node_id(line_node);
5433 if (dupsplat) {
5434 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5435 INSERT_BEFORE_INSN1(iobj, line_no, node_id, splatarray, Qtrue);
5436 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5438 INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1));
5440 ADD_INSN(lhs, line_node, pop);
5441 if (argc != 1) {
5442 ADD_INSN(lhs, line_node, pop);
5444 for (int i=0; i < argc; i++) {
5445 ADD_INSN(post, line_node, pop);
5447 break;
5449 case NODE_MASGN: {
5450 DECL_ANCHOR(nest_rhs);
5451 INIT_ANCHOR(nest_rhs);
5452 DECL_ANCHOR(nest_lhs);
5453 INIT_ANCHOR(nest_lhs);
5455 int prev_level = state->lhs_level;
5456 bool prev_nested = state->nested;
5457 state->nested = 1;
5458 state->lhs_level = lhs_pos - 1;
5459 CHECK(compile_massign0(iseq, pre, nest_rhs, nest_lhs, post, node, state, 1));
5460 state->lhs_level = prev_level;
5461 state->nested = prev_nested;
5463 ADD_SEQ(lhs, nest_rhs);
5464 ADD_SEQ(lhs, nest_lhs);
5465 break;
5467 case NODE_CDECL:
5468 if (!RNODE_CDECL(node)->nd_vid) {
5469 /* Special handling only needed for expr::C, not for C */
5470 INSN *iobj;
5472 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_CDECL)", node));
5474 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5475 iobj = (INSN *)insn_element; /* setconstant insn */
5476 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn((INSN *)get_prev_insn(iobj)));
5477 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn(iobj));
5478 ELEM_REMOVE(insn_element);
5479 pre->last = iobj->link.prev;
5480 ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
5482 if (!add_masgn_lhs_node(state, lhs_pos, node, 1, (INSN *)LAST_ELEMENT(lhs))) {
5483 return COMPILE_NG;
5486 ADD_INSN(post, node, pop);
5487 break;
5489 /* Fallthrough */
5490 default: {
5491 DECL_ANCHOR(anchor);
5492 INIT_ANCHOR(anchor);
5493 CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
5494 ELEM_REMOVE(FIRST_ELEMENT(anchor));
5495 ADD_SEQ(lhs, anchor);
5499 return COMPILE_OK;
5502 static int
5503 compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
5505 if (lhsn) {
5506 CHECK(compile_massign_opt_lhs(iseq, ret, RNODE_LIST(lhsn)->nd_next));
5507 CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, RNODE_LIST(lhsn)->nd_head, NULL, 0));
5509 return COMPILE_OK;
5512 static int
5513 compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5514 const NODE *rhsn, const NODE *orig_lhsn)
5516 VALUE mem[64];
5517 const int memsize = numberof(mem);
5518 int memindex = 0;
5519 int llen = 0, rlen = 0;
5520 int i;
5521 const NODE *lhsn = orig_lhsn;
5523 #define MEMORY(v) { \
5524 int i; \
5525 if (memindex == memsize) return 0; \
5526 for (i=0; i<memindex; i++) { \
5527 if (mem[i] == (v)) return 0; \
5529 mem[memindex++] = (v); \
5532 if (rhsn == 0 || !nd_type_p(rhsn, NODE_LIST)) {
5533 return 0;
5536 while (lhsn) {
5537 const NODE *ln = RNODE_LIST(lhsn)->nd_head;
5538 switch (nd_type(ln)) {
5539 case NODE_LASGN:
5540 case NODE_DASGN:
5541 case NODE_IASGN:
5542 case NODE_CVASGN:
5543 MEMORY(get_nd_vid(ln));
5544 break;
5545 default:
5546 return 0;
5548 lhsn = RNODE_LIST(lhsn)->nd_next;
5549 llen++;
5552 while (rhsn) {
5553 if (llen <= rlen) {
5554 NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", RNODE_LIST(rhsn)->nd_head));
5556 else {
5557 NO_CHECK(COMPILE(ret, "masgn val", RNODE_LIST(rhsn)->nd_head));
5559 rhsn = RNODE_LIST(rhsn)->nd_next;
5560 rlen++;
5563 if (llen > rlen) {
5564 for (i=0; i<llen-rlen; i++) {
5565 ADD_INSN(ret, orig_lhsn, putnil);
5569 compile_massign_opt_lhs(iseq, ret, orig_lhsn);
5570 return 1;
5573 static int
5574 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)
5576 const NODE *rhsn = RNODE_MASGN(node)->nd_value;
5577 const NODE *splatn = RNODE_MASGN(node)->nd_args;
5578 const NODE *lhsn = RNODE_MASGN(node)->nd_head;
5579 const NODE *lhsn_count = lhsn;
5580 int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0;
5582 int llen = 0;
5583 int lpos = 0;
5585 while (lhsn_count) {
5586 llen++;
5587 lhsn_count = RNODE_LIST(lhsn_count)->nd_next;
5589 while (lhsn) {
5590 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(lhsn)->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level));
5591 lpos++;
5592 lhsn = RNODE_LIST(lhsn)->nd_next;
5595 if (lhs_splat) {
5596 if (nd_type_p(splatn, NODE_POSTARG)) {
5597 /*a, b, *r, p1, p2 */
5598 const NODE *postn = RNODE_POSTARG(splatn)->nd_2nd;
5599 const NODE *restn = RNODE_POSTARG(splatn)->nd_1st;
5600 int plen = (int)RNODE_LIST(postn)->as.nd_alen;
5601 int ppos = 0;
5602 int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00);
5604 ADD_INSN2(lhs, splatn, expandarray, INT2FIX(plen), INT2FIX(flag));
5606 if (NODE_NAMED_REST_P(restn)) {
5607 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, restn, state, 1 + plen + state->lhs_level));
5609 while (postn) {
5610 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(postn)->nd_head, state, (plen - ppos) + state->lhs_level));
5611 ppos++;
5612 postn = RNODE_LIST(postn)->nd_next;
5615 else {
5616 /* a, b, *r */
5617 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, splatn, state, 1 + state->lhs_level));
5621 if (!state->nested) {
5622 NO_CHECK(COMPILE(rhs, "normal masgn rhs", rhsn));
5625 if (!popped) {
5626 ADD_INSN(rhs, node, dup);
5628 ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
5629 return COMPILE_OK;
5632 static int
5633 compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
5635 if (!popped || RNODE_MASGN(node)->nd_args || !compile_massign_opt(iseq, ret, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head)) {
5636 struct masgn_state state;
5637 state.lhs_level = popped ? 0 : 1;
5638 state.nested = 0;
5639 state.num_args = 0;
5640 state.first_memo = NULL;
5641 state.last_memo = NULL;
5643 DECL_ANCHOR(pre);
5644 INIT_ANCHOR(pre);
5645 DECL_ANCHOR(rhs);
5646 INIT_ANCHOR(rhs);
5647 DECL_ANCHOR(lhs);
5648 INIT_ANCHOR(lhs);
5649 DECL_ANCHOR(post);
5650 INIT_ANCHOR(post);
5651 int ok = compile_massign0(iseq, pre, rhs, lhs, post, node, &state, popped);
5653 struct masgn_lhs_node *memo = state.first_memo, *tmp_memo;
5654 while (memo) {
5655 VALUE topn_arg = INT2FIX((state.num_args - memo->argn) + memo->lhs_pos);
5656 for (int i = 0; i < memo->num_args; i++) {
5657 INSERT_BEFORE_INSN1(memo->before_insn, nd_line(memo->line_node), nd_node_id(memo->line_node), topn, topn_arg);
5659 tmp_memo = memo->next;
5660 free(memo);
5661 memo = tmp_memo;
5663 CHECK(ok);
5665 ADD_SEQ(ret, pre);
5666 ADD_SEQ(ret, rhs);
5667 ADD_SEQ(ret, lhs);
5668 if (!popped && state.num_args >= 1) {
5669 /* make sure rhs array is returned before popping */
5670 ADD_INSN1(ret, node, setn, INT2FIX(state.num_args));
5672 ADD_SEQ(ret, post);
5674 return COMPILE_OK;
5677 static VALUE
5678 collect_const_segments(rb_iseq_t *iseq, const NODE *node)
5680 VALUE arr = rb_ary_new();
5681 for (;;) {
5682 switch (nd_type(node)) {
5683 case NODE_CONST:
5684 rb_ary_unshift(arr, ID2SYM(RNODE_CONST(node)->nd_vid));
5685 return arr;
5686 case NODE_COLON3:
5687 rb_ary_unshift(arr, ID2SYM(RNODE_COLON3(node)->nd_mid));
5688 rb_ary_unshift(arr, ID2SYM(idNULL));
5689 return arr;
5690 case NODE_COLON2:
5691 rb_ary_unshift(arr, ID2SYM(RNODE_COLON2(node)->nd_mid));
5692 node = RNODE_COLON2(node)->nd_head;
5693 break;
5694 default:
5695 return Qfalse;
5700 static int
5701 compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
5702 LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
5704 switch (nd_type(node)) {
5705 case NODE_CONST:
5706 debugi("compile_const_prefix - colon", RNODE_CONST(node)->nd_vid);
5707 ADD_INSN1(body, node, putobject, Qtrue);
5708 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
5709 break;
5710 case NODE_COLON3:
5711 debugi("compile_const_prefix - colon3", RNODE_COLON3(node)->nd_mid);
5712 ADD_INSN(body, node, pop);
5713 ADD_INSN1(body, node, putobject, rb_cObject);
5714 ADD_INSN1(body, node, putobject, Qtrue);
5715 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
5716 break;
5717 case NODE_COLON2:
5718 CHECK(compile_const_prefix(iseq, RNODE_COLON2(node)->nd_head, pref, body));
5719 debugi("compile_const_prefix - colon2", RNODE_COLON2(node)->nd_mid);
5720 ADD_INSN1(body, node, putobject, Qfalse);
5721 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON2(node)->nd_mid));
5722 break;
5723 default:
5724 CHECK(COMPILE(pref, "const colon2 prefix", node));
5725 break;
5727 return COMPILE_OK;
5730 static int
5731 compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
5733 if (nd_type_p(cpath, NODE_COLON3)) {
5734 /* toplevel class ::Foo */
5735 ADD_INSN1(ret, cpath, putobject, rb_cObject);
5736 return VM_DEFINECLASS_FLAG_SCOPED;
5738 else if (nd_type_p(cpath, NODE_COLON2) && RNODE_COLON2(cpath)->nd_head) {
5739 /* Bar::Foo */
5740 NO_CHECK(COMPILE(ret, "nd_else->nd_head", RNODE_COLON2(cpath)->nd_head));
5741 return VM_DEFINECLASS_FLAG_SCOPED;
5743 else {
5744 /* class at cbase Foo */
5745 ADD_INSN1(ret, cpath, putspecialobject,
5746 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5747 return 0;
5751 static inline int
5752 private_recv_p(const NODE *node)
5754 NODE *recv = get_nd_recv(node);
5755 if (recv && nd_type_p(recv, NODE_SELF)) {
5756 return RNODE_SELF(recv)->nd_state != 0;
5758 return 0;
5761 static void
5762 defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5763 const NODE *const node, LABEL **lfinish, VALUE needstr);
5765 static int
5766 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);
5768 static void
5769 defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5770 const NODE *const node, LABEL **lfinish, VALUE needstr,
5771 bool keep_result)
5773 enum defined_type expr_type = DEFINED_NOT_DEFINED;
5774 enum node_type type;
5775 const int line = nd_line(node);
5776 const NODE *line_node = node;
5778 switch (type = nd_type(node)) {
5780 /* easy literals */
5781 case NODE_NIL:
5782 expr_type = DEFINED_NIL;
5783 break;
5784 case NODE_SELF:
5785 expr_type = DEFINED_SELF;
5786 break;
5787 case NODE_TRUE:
5788 expr_type = DEFINED_TRUE;
5789 break;
5790 case NODE_FALSE:
5791 expr_type = DEFINED_FALSE;
5792 break;
5794 case NODE_LIST:{
5795 const NODE *vals = node;
5797 do {
5798 defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false);
5800 if (!lfinish[1]) {
5801 lfinish[1] = NEW_LABEL(line);
5803 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5804 } while ((vals = RNODE_LIST(vals)->nd_next) != NULL);
5806 /* fall through */
5807 case NODE_STR:
5808 case NODE_SYM:
5809 case NODE_REGX:
5810 case NODE_LINE:
5811 case NODE_FILE:
5812 case NODE_ENCODING:
5813 case NODE_INTEGER:
5814 case NODE_FLOAT:
5815 case NODE_RATIONAL:
5816 case NODE_IMAGINARY:
5817 case NODE_ZLIST:
5818 case NODE_AND:
5819 case NODE_OR:
5820 default:
5821 expr_type = DEFINED_EXPR;
5822 break;
5824 /* variables */
5825 case NODE_LVAR:
5826 case NODE_DVAR:
5827 expr_type = DEFINED_LVAR;
5828 break;
5830 #define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type))
5831 case NODE_IVAR:
5832 ADD_INSN3(ret, line_node, definedivar,
5833 ID2SYM(RNODE_IVAR(node)->nd_vid), get_ivar_ic_value(iseq,RNODE_IVAR(node)->nd_vid), PUSH_VAL(DEFINED_IVAR));
5834 return;
5836 case NODE_GVAR:
5837 ADD_INSN(ret, line_node, putnil);
5838 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR),
5839 ID2SYM(RNODE_GVAR(node)->nd_vid), PUSH_VAL(DEFINED_GVAR));
5840 return;
5842 case NODE_CVAR:
5843 ADD_INSN(ret, line_node, putnil);
5844 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR),
5845 ID2SYM(RNODE_CVAR(node)->nd_vid), PUSH_VAL(DEFINED_CVAR));
5846 return;
5848 case NODE_CONST:
5849 ADD_INSN(ret, line_node, putnil);
5850 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST),
5851 ID2SYM(RNODE_CONST(node)->nd_vid), PUSH_VAL(DEFINED_CONST));
5852 return;
5853 case NODE_COLON2:
5854 if (!lfinish[1]) {
5855 lfinish[1] = NEW_LABEL(line);
5857 defined_expr0(iseq, ret, RNODE_COLON2(node)->nd_head, lfinish, Qfalse, false);
5858 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5859 NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", RNODE_COLON2(node)->nd_head));
5861 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
5862 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST_FROM),
5863 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
5865 else {
5866 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
5867 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_METHOD));
5869 return;
5870 case NODE_COLON3:
5871 ADD_INSN1(ret, line_node, putobject, rb_cObject);
5872 ADD_INSN3(ret, line_node, defined,
5873 INT2FIX(DEFINED_CONST_FROM), ID2SYM(RNODE_COLON3(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
5874 return;
5876 /* method dispatch */
5877 case NODE_CALL:
5878 case NODE_OPCALL:
5879 case NODE_VCALL:
5880 case NODE_FCALL:
5881 case NODE_ATTRASGN:{
5882 const int explicit_receiver =
5883 (type == NODE_CALL || type == NODE_OPCALL ||
5884 (type == NODE_ATTRASGN && !private_recv_p(node)));
5886 if (get_nd_args(node) || explicit_receiver) {
5887 if (!lfinish[1]) {
5888 lfinish[1] = NEW_LABEL(line);
5890 if (!lfinish[2]) {
5891 lfinish[2] = NEW_LABEL(line);
5894 if (get_nd_args(node)) {
5895 defined_expr0(iseq, ret, get_nd_args(node), lfinish, Qfalse, false);
5896 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5898 if (explicit_receiver) {
5899 defined_expr0(iseq, ret, get_nd_recv(node), lfinish, Qfalse, true);
5900 switch (nd_type(get_nd_recv(node))) {
5901 case NODE_CALL:
5902 case NODE_OPCALL:
5903 case NODE_VCALL:
5904 case NODE_FCALL:
5905 case NODE_ATTRASGN:
5906 ADD_INSNL(ret, line_node, branchunless, lfinish[2]);
5907 compile_call(iseq, ret, get_nd_recv(node), nd_type(get_nd_recv(node)), line_node, 0, true);
5908 break;
5909 default:
5910 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
5911 NO_CHECK(COMPILE(ret, "defined/recv", get_nd_recv(node)));
5912 break;
5914 if (keep_result) {
5915 ADD_INSN(ret, line_node, dup);
5917 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
5918 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
5920 else {
5921 ADD_INSN(ret, line_node, putself);
5922 if (keep_result) {
5923 ADD_INSN(ret, line_node, dup);
5925 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC),
5926 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
5928 return;
5931 case NODE_YIELD:
5932 ADD_INSN(ret, line_node, putnil);
5933 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
5934 PUSH_VAL(DEFINED_YIELD));
5935 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
5936 return;
5938 case NODE_BACK_REF:
5939 case NODE_NTH_REF:
5940 ADD_INSN(ret, line_node, putnil);
5941 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF),
5942 INT2FIX((RNODE_BACK_REF(node)->nd_nth << 1) | (type == NODE_BACK_REF)),
5943 PUSH_VAL(DEFINED_GVAR));
5944 return;
5946 case NODE_SUPER:
5947 case NODE_ZSUPER:
5948 ADD_INSN(ret, line_node, putnil);
5949 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_ZSUPER), 0,
5950 PUSH_VAL(DEFINED_ZSUPER));
5951 return;
5953 #undef PUSH_VAL
5954 case NODE_OP_ASGN1:
5955 case NODE_OP_ASGN2:
5956 case NODE_OP_ASGN_OR:
5957 case NODE_OP_ASGN_AND:
5958 case NODE_MASGN:
5959 case NODE_LASGN:
5960 case NODE_DASGN:
5961 case NODE_GASGN:
5962 case NODE_IASGN:
5963 case NODE_CDECL:
5964 case NODE_CVASGN:
5965 case NODE_OP_CDECL:
5966 expr_type = DEFINED_ASGN;
5967 break;
5970 RUBY_ASSERT(expr_type != DEFINED_NOT_DEFINED);
5972 if (needstr != Qfalse) {
5973 VALUE str = rb_iseq_defined_string(expr_type);
5974 ADD_INSN1(ret, line_node, putobject, str);
5976 else {
5977 ADD_INSN1(ret, line_node, putobject, Qtrue);
5981 static void
5982 build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *unused)
5984 ADD_SYNTHETIC_INSN(ret, 0, -1, putnil);
5985 iseq_set_exception_local_table(iseq);
5988 static void
5989 defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5990 const NODE *const node, LABEL **lfinish, VALUE needstr)
5992 LINK_ELEMENT *lcur = ret->last;
5993 defined_expr0(iseq, ret, node, lfinish, needstr, false);
5994 if (lfinish[1]) {
5995 int line = nd_line(node);
5996 LABEL *lstart = NEW_LABEL(line);
5997 LABEL *lend = NEW_LABEL(line);
5998 const rb_iseq_t *rescue;
5999 struct rb_iseq_new_with_callback_callback_func *ifunc =
6000 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
6001 rescue = new_child_iseq_with_callback(iseq, ifunc,
6002 rb_str_concat(rb_str_new2("defined guard in "),
6003 ISEQ_BODY(iseq)->location.label),
6004 iseq, ISEQ_TYPE_RESCUE, 0);
6005 lstart->rescued = LABEL_RESCUE_BEG;
6006 lend->rescued = LABEL_RESCUE_END;
6007 APPEND_LABEL(ret, lcur, lstart);
6008 ADD_LABEL(ret, lend);
6009 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
6013 static int
6014 compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr)
6016 const int line = nd_line(node);
6017 const NODE *line_node = node;
6018 if (!RNODE_DEFINED(node)->nd_head) {
6019 VALUE str = rb_iseq_defined_string(DEFINED_NIL);
6020 ADD_INSN1(ret, line_node, putobject, str);
6022 else {
6023 LABEL *lfinish[3];
6024 LINK_ELEMENT *last = ret->last;
6025 lfinish[0] = NEW_LABEL(line);
6026 lfinish[1] = 0;
6027 lfinish[2] = 0;
6028 defined_expr(iseq, ret, RNODE_DEFINED(node)->nd_head, lfinish, needstr);
6029 if (lfinish[1]) {
6030 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(putnil), 0)->link);
6031 ADD_INSN(ret, line_node, swap);
6032 if (lfinish[2]) {
6033 ADD_LABEL(ret, lfinish[2]);
6035 ADD_INSN(ret, line_node, pop);
6036 ADD_LABEL(ret, lfinish[1]);
6038 ADD_LABEL(ret, lfinish[0]);
6040 return COMPILE_OK;
6043 static VALUE
6044 make_name_for_block(const rb_iseq_t *orig_iseq)
6046 int level = 1;
6047 const rb_iseq_t *iseq = orig_iseq;
6049 if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
6050 while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
6051 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
6052 level++;
6054 iseq = ISEQ_BODY(iseq)->parent_iseq;
6058 if (level == 1) {
6059 return rb_sprintf("block in %"PRIsVALUE, ISEQ_BODY(iseq)->location.label);
6061 else {
6062 return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, ISEQ_BODY(iseq)->location.label);
6066 static void
6067 push_ensure_entry(rb_iseq_t *iseq,
6068 struct iseq_compile_data_ensure_node_stack *enl,
6069 struct ensure_range *er, const void *const node)
6071 enl->ensure_node = node;
6072 enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
6073 enl->erange = er;
6074 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl;
6077 static void
6078 add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
6079 LABEL *lstart, LABEL *lend)
6081 struct ensure_range *ne =
6082 compile_data_alloc(iseq, sizeof(struct ensure_range));
6084 while (erange->next != 0) {
6085 erange = erange->next;
6087 ne->next = 0;
6088 ne->begin = lend;
6089 ne->end = erange->end;
6090 erange->end = lstart;
6092 erange->next = ne;
6095 static bool
6096 can_add_ensure_iseq(const rb_iseq_t *iseq)
6098 struct iseq_compile_data_ensure_node_stack *e;
6099 if (ISEQ_COMPILE_DATA(iseq)->in_rescue && (e = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack) != NULL) {
6100 while (e) {
6101 if (e->ensure_node) return false;
6102 e = e->prev;
6105 return true;
6108 static void
6109 add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
6111 RUBY_ASSERT(can_add_ensure_iseq(iseq));
6113 struct iseq_compile_data_ensure_node_stack *enlp =
6114 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
6115 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
6116 DECL_ANCHOR(ensure);
6118 INIT_ANCHOR(ensure);
6119 while (enlp) {
6120 if (enlp->erange != NULL) {
6121 DECL_ANCHOR(ensure_part);
6122 LABEL *lstart = NEW_LABEL(0);
6123 LABEL *lend = NEW_LABEL(0);
6124 INIT_ANCHOR(ensure_part);
6126 add_ensure_range(iseq, enlp->erange, lstart, lend);
6128 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
6129 ADD_LABEL(ensure_part, lstart);
6130 NO_CHECK(COMPILE_POPPED(ensure_part, "ensure part", enlp->ensure_node));
6131 ADD_LABEL(ensure_part, lend);
6132 ADD_SEQ(ensure, ensure_part);
6134 else {
6135 if (!is_return) {
6136 break;
6139 enlp = enlp->prev;
6141 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
6142 ADD_SEQ(ret, ensure);
6145 #if RUBY_DEBUG
6146 static int
6147 check_keyword(const NODE *node)
6149 /* This check is essentially a code clone of compile_keyword_arg. */
6151 if (nd_type_p(node, NODE_LIST)) {
6152 while (RNODE_LIST(node)->nd_next) {
6153 node = RNODE_LIST(node)->nd_next;
6155 node = RNODE_LIST(node)->nd_head;
6158 return keyword_node_p(node);
6160 #endif
6162 static bool
6163 keyword_node_single_splat_p(NODE *kwnode)
6165 RUBY_ASSERT(keyword_node_p(kwnode));
6167 NODE *node = RNODE_HASH(kwnode)->nd_head;
6168 return RNODE_LIST(node)->nd_head == NULL &&
6169 RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next == NULL;
6172 static int
6173 setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6174 int dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr)
6176 if (!argn) return 0;
6178 NODE *kwnode = NULL;
6180 switch (nd_type(argn)) {
6181 case NODE_LIST: {
6182 // f(x, y, z)
6183 int len = compile_args(iseq, args, argn, &kwnode);
6184 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0);
6186 if (kwnode) {
6187 if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) {
6188 len -= 1;
6190 else {
6191 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6195 return len;
6197 case NODE_SPLAT: {
6198 // f(*a)
6199 NO_CHECK(COMPILE(args, "args (splat)", RNODE_SPLAT(argn)->nd_head));
6200 ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest));
6201 if (flag_ptr) {
6202 *flag_ptr |= VM_CALL_ARGS_SPLAT;
6203 if (dup_rest) *flag_ptr |= VM_CALL_ARGS_SPLAT_MUT;
6205 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0);
6206 return 1;
6208 case NODE_ARGSCAT: {
6209 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT;
6210 int argc = setup_args_core(iseq, args, RNODE_ARGSCAT(argn)->nd_head, 1, NULL, NULL);
6211 bool args_pushed = false;
6213 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_body, NODE_LIST)) {
6214 int rest_len = compile_args(iseq, args, RNODE_ARGSCAT(argn)->nd_body, &kwnode);
6215 if (kwnode) rest_len--;
6216 ADD_INSN1(args, argn, pushtoarray, INT2FIX(rest_len));
6217 args_pushed = true;
6219 else {
6220 RUBY_ASSERT(!check_keyword(RNODE_ARGSCAT(argn)->nd_body));
6221 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSCAT(argn)->nd_body));
6224 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_head, NODE_LIST)) {
6225 ADD_INSN1(args, argn, splatarray, Qtrue);
6226 argc += 1;
6228 else if (!args_pushed) {
6229 ADD_INSN(args, argn, concattoarray);
6232 // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...})
6233 if (kwnode) {
6234 // kwsplat
6235 *flag_ptr |= VM_CALL_KW_SPLAT;
6236 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6237 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6238 argc += 1;
6241 return argc;
6243 case NODE_ARGSPUSH: {
6244 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT;
6245 int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, 1, NULL, NULL);
6247 if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) {
6248 int rest_len = compile_args(iseq, args, RNODE_ARGSPUSH(argn)->nd_body, &kwnode);
6249 if (kwnode) rest_len--;
6250 ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
6251 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6253 else {
6254 if (keyword_node_p(RNODE_ARGSPUSH(argn)->nd_body)) {
6255 kwnode = RNODE_ARGSPUSH(argn)->nd_body;
6257 else {
6258 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSPUSH(argn)->nd_body));
6259 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6263 if (kwnode) {
6264 // f(*a, k:1)
6265 *flag_ptr |= VM_CALL_KW_SPLAT;
6266 if (!keyword_node_single_splat_p(kwnode)) {
6267 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6269 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6270 argc += 1;
6273 return argc;
6275 default: {
6276 UNKNOWN_NODE("setup_arg", argn, Qnil);
6281 static VALUE
6282 setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6283 unsigned int *flag, struct rb_callinfo_kwarg **keywords)
6285 VALUE ret;
6286 if (argn && nd_type_p(argn, NODE_BLOCK_PASS)) {
6287 unsigned int dup_rest = 1;
6288 DECL_ANCHOR(arg_block);
6289 INIT_ANCHOR(arg_block);
6290 NO_CHECK(COMPILE(arg_block, "block", RNODE_BLOCK_PASS(argn)->nd_body));
6292 *flag |= VM_CALL_ARGS_BLOCKARG;
6294 if (LIST_INSN_SIZE_ONE(arg_block)) {
6295 LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
6296 if (IS_INSN(elem)) {
6297 INSN *iobj = (INSN *)elem;
6298 if (iobj->insn_id == BIN(getblockparam)) {
6299 iobj->insn_id = BIN(getblockparamproxy);
6301 dup_rest = 0;
6304 ret = INT2FIX(setup_args_core(iseq, args, RNODE_BLOCK_PASS(argn)->nd_head, dup_rest, flag, keywords));
6305 ADD_SEQ(args, arg_block);
6307 else {
6308 ret = INT2FIX(setup_args_core(iseq, args, argn, 0, flag, keywords));
6310 return ret;
6313 static void
6314 build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *ptr)
6316 const NODE *body = ptr;
6317 int line = nd_line(body);
6318 VALUE argc = INT2FIX(0);
6319 const rb_iseq_t *block = NEW_CHILD_ISEQ(body, make_name_for_block(ISEQ_BODY(iseq)->parent_iseq), ISEQ_TYPE_BLOCK, line);
6321 ADD_INSN1(ret, body, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6322 ADD_CALL_WITH_BLOCK(ret, body, id_core_set_postexe, argc, block);
6323 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
6324 iseq_set_local_table(iseq, 0);
6327 static void
6328 compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
6330 const NODE *vars;
6331 LINK_ELEMENT *last;
6332 int line = nd_line(node);
6333 const NODE *line_node = node;
6334 LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line);
6336 #if !(defined(NAMED_CAPTURE_BY_SVAR) && NAMED_CAPTURE_BY_SVAR-0)
6337 ADD_INSN1(ret, line_node, getglobal, ID2SYM(idBACKREF));
6338 #else
6339 ADD_INSN2(ret, line_node, getspecial, INT2FIX(1) /* '~' */, INT2FIX(0));
6340 #endif
6341 ADD_INSN(ret, line_node, dup);
6342 ADD_INSNL(ret, line_node, branchunless, fail_label);
6344 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6345 INSN *cap;
6346 if (RNODE_BLOCK(vars)->nd_next) {
6347 ADD_INSN(ret, line_node, dup);
6349 last = ret->last;
6350 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6351 last = last->next; /* putobject :var */
6352 cap = new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), idAREF, INT2FIX(1),
6353 NULL, INT2FIX(0), NULL);
6354 ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
6355 #if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
6356 if (!RNODE_BLOCK(vars)->nd_next && vars == node) {
6357 /* only one name */
6358 DECL_ANCHOR(nom);
6360 INIT_ANCHOR(nom);
6361 ADD_INSNL(nom, line_node, jump, end_label);
6362 ADD_LABEL(nom, fail_label);
6363 # if 0 /* $~ must be MatchData or nil */
6364 ADD_INSN(nom, line_node, pop);
6365 ADD_INSN(nom, line_node, putnil);
6366 # endif
6367 ADD_LABEL(nom, end_label);
6368 (nom->last->next = cap->link.next)->prev = nom->last;
6369 (cap->link.next = nom->anchor.next)->prev = &cap->link;
6370 return;
6372 #endif
6374 ADD_INSNL(ret, line_node, jump, end_label);
6375 ADD_LABEL(ret, fail_label);
6376 ADD_INSN(ret, line_node, pop);
6377 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6378 last = ret->last;
6379 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6380 last = last->next; /* putobject :var */
6381 ((INSN*)last)->insn_id = BIN(putnil);
6382 ((INSN*)last)->operand_size = 0;
6384 ADD_LABEL(ret, end_label);
6387 static int
6388 optimizable_range_item_p(const NODE *n)
6390 if (!n) return FALSE;
6391 switch (nd_type(n)) {
6392 case NODE_LINE:
6393 return TRUE;
6394 case NODE_INTEGER:
6395 return TRUE;
6396 case NODE_NIL:
6397 return TRUE;
6398 default:
6399 return FALSE;
6403 static VALUE
6404 optimized_range_item(const NODE *n)
6406 switch (nd_type(n)) {
6407 case NODE_LINE:
6408 return rb_node_line_lineno_val(n);
6409 case NODE_INTEGER:
6410 return rb_node_integer_literal_val(n);
6411 case NODE_FLOAT:
6412 return rb_node_float_literal_val(n);
6413 case NODE_RATIONAL:
6414 return rb_node_rational_literal_val(n);
6415 case NODE_IMAGINARY:
6416 return rb_node_imaginary_literal_val(n);
6417 case NODE_NIL:
6418 return Qnil;
6419 default:
6420 rb_bug("unexpected node: %s", ruby_node_name(nd_type(n)));
6424 static int
6425 compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
6427 const NODE *const node_body = type == NODE_IF ? RNODE_IF(node)->nd_body : RNODE_UNLESS(node)->nd_else;
6428 const NODE *const node_else = type == NODE_IF ? RNODE_IF(node)->nd_else : RNODE_UNLESS(node)->nd_body;
6430 const int line = nd_line(node);
6431 const NODE *line_node = node;
6432 DECL_ANCHOR(cond_seq);
6433 LABEL *then_label, *else_label, *end_label;
6434 VALUE branches = Qfalse;
6436 INIT_ANCHOR(cond_seq);
6437 then_label = NEW_LABEL(line);
6438 else_label = NEW_LABEL(line);
6439 end_label = 0;
6441 compile_branch_condition(iseq, cond_seq, RNODE_IF(node)->nd_cond, then_label, else_label);
6442 ADD_SEQ(ret, cond_seq);
6444 if (then_label->refcnt && else_label->refcnt) {
6445 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless");
6448 if (then_label->refcnt) {
6449 ADD_LABEL(ret, then_label);
6451 DECL_ANCHOR(then_seq);
6452 INIT_ANCHOR(then_seq);
6453 CHECK(COMPILE_(then_seq, "then", node_body, popped));
6455 if (else_label->refcnt) {
6456 const NODE *const coverage_node = node_body ? node_body : node;
6457 add_trace_branch_coverage(
6458 iseq,
6459 ret,
6460 nd_code_loc(coverage_node),
6461 nd_node_id(coverage_node),
6463 type == NODE_IF ? "then" : "else",
6464 branches);
6465 end_label = NEW_LABEL(line);
6466 ADD_INSNL(then_seq, line_node, jump, end_label);
6467 if (!popped) {
6468 ADD_INSN(then_seq, line_node, pop);
6471 ADD_SEQ(ret, then_seq);
6474 if (else_label->refcnt) {
6475 ADD_LABEL(ret, else_label);
6477 DECL_ANCHOR(else_seq);
6478 INIT_ANCHOR(else_seq);
6479 CHECK(COMPILE_(else_seq, "else", node_else, popped));
6481 if (then_label->refcnt) {
6482 const NODE *const coverage_node = node_else ? node_else : node;
6483 add_trace_branch_coverage(
6484 iseq,
6485 ret,
6486 nd_code_loc(coverage_node),
6487 nd_node_id(coverage_node),
6489 type == NODE_IF ? "else" : "then",
6490 branches);
6492 ADD_SEQ(ret, else_seq);
6495 if (end_label) {
6496 ADD_LABEL(ret, end_label);
6499 return COMPILE_OK;
6502 static int
6503 compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
6505 const NODE *vals;
6506 const NODE *node = orig_node;
6507 LABEL *endlabel, *elselabel;
6508 DECL_ANCHOR(head);
6509 DECL_ANCHOR(body_seq);
6510 DECL_ANCHOR(cond_seq);
6511 int only_special_literals = 1;
6512 VALUE literals = rb_hash_new();
6513 int line;
6514 enum node_type type;
6515 const NODE *line_node;
6516 VALUE branches = Qfalse;
6517 int branch_id = 0;
6519 INIT_ANCHOR(head);
6520 INIT_ANCHOR(body_seq);
6521 INIT_ANCHOR(cond_seq);
6523 RHASH_TBL_RAW(literals)->type = &cdhash_type;
6525 CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head));
6527 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
6529 node = RNODE_CASE(node)->nd_body;
6530 EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG);
6531 type = nd_type(node);
6532 line = nd_line(node);
6533 line_node = node;
6535 endlabel = NEW_LABEL(line);
6536 elselabel = NEW_LABEL(line);
6538 ADD_SEQ(ret, head); /* case VAL */
6540 while (type == NODE_WHEN) {
6541 LABEL *l1;
6543 l1 = NEW_LABEL(line);
6544 ADD_LABEL(body_seq, l1);
6545 ADD_INSN(body_seq, line_node, pop);
6547 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
6548 add_trace_branch_coverage(
6549 iseq,
6550 body_seq,
6551 nd_code_loc(coverage_node),
6552 nd_node_id(coverage_node),
6553 branch_id++,
6554 "when",
6555 branches);
6557 CHECK(COMPILE_(body_seq, "when body", RNODE_WHEN(node)->nd_body, popped));
6558 ADD_INSNL(body_seq, line_node, jump, endlabel);
6560 vals = RNODE_WHEN(node)->nd_head;
6561 if (vals) {
6562 switch (nd_type(vals)) {
6563 case NODE_LIST:
6564 only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
6565 if (only_special_literals < 0) return COMPILE_NG;
6566 break;
6567 case NODE_SPLAT:
6568 case NODE_ARGSCAT:
6569 case NODE_ARGSPUSH:
6570 only_special_literals = 0;
6571 CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
6572 break;
6573 default:
6574 UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
6577 else {
6578 EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
6581 node = RNODE_WHEN(node)->nd_next;
6582 if (!node) {
6583 break;
6585 type = nd_type(node);
6586 line = nd_line(node);
6587 line_node = node;
6589 /* else */
6590 if (node) {
6591 ADD_LABEL(cond_seq, elselabel);
6592 ADD_INSN(cond_seq, line_node, pop);
6593 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
6594 CHECK(COMPILE_(cond_seq, "else", node, popped));
6595 ADD_INSNL(cond_seq, line_node, jump, endlabel);
6597 else {
6598 debugs("== else (implicit)\n");
6599 ADD_LABEL(cond_seq, elselabel);
6600 ADD_INSN(cond_seq, orig_node, pop);
6601 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
6602 if (!popped) {
6603 ADD_INSN(cond_seq, orig_node, putnil);
6605 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
6608 if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
6609 ADD_INSN(ret, orig_node, dup);
6610 ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
6611 RB_OBJ_WRITTEN(iseq, Qundef, literals);
6612 LABEL_REF(elselabel);
6615 ADD_SEQ(ret, cond_seq);
6616 ADD_SEQ(ret, body_seq);
6617 ADD_LABEL(ret, endlabel);
6618 return COMPILE_OK;
6621 static int
6622 compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
6624 const NODE *vals;
6625 const NODE *val;
6626 const NODE *node = RNODE_CASE2(orig_node)->nd_body;
6627 LABEL *endlabel;
6628 DECL_ANCHOR(body_seq);
6629 VALUE branches = Qfalse;
6630 int branch_id = 0;
6632 branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case");
6634 INIT_ANCHOR(body_seq);
6635 endlabel = NEW_LABEL(nd_line(node));
6637 while (node && nd_type_p(node, NODE_WHEN)) {
6638 const int line = nd_line(node);
6639 LABEL *l1 = NEW_LABEL(line);
6640 ADD_LABEL(body_seq, l1);
6642 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
6643 add_trace_branch_coverage(
6644 iseq,
6645 body_seq,
6646 nd_code_loc(coverage_node),
6647 nd_node_id(coverage_node),
6648 branch_id++,
6649 "when",
6650 branches);
6652 CHECK(COMPILE_(body_seq, "when", RNODE_WHEN(node)->nd_body, popped));
6653 ADD_INSNL(body_seq, node, jump, endlabel);
6655 vals = RNODE_WHEN(node)->nd_head;
6656 if (!vals) {
6657 EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG);
6659 switch (nd_type(vals)) {
6660 case NODE_LIST:
6661 while (vals) {
6662 LABEL *lnext;
6663 val = RNODE_LIST(vals)->nd_head;
6664 lnext = NEW_LABEL(nd_line(val));
6665 debug_compile("== when2\n", (void)0);
6666 CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
6667 ADD_LABEL(ret, lnext);
6668 vals = RNODE_LIST(vals)->nd_next;
6670 break;
6671 case NODE_SPLAT:
6672 case NODE_ARGSCAT:
6673 case NODE_ARGSPUSH:
6674 ADD_INSN(ret, vals, putnil);
6675 CHECK(COMPILE(ret, "when2/cond splat", vals));
6676 ADD_INSN1(ret, vals, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
6677 ADD_INSNL(ret, vals, branchif, l1);
6678 break;
6679 default:
6680 UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
6682 node = RNODE_WHEN(node)->nd_next;
6684 /* else */
6685 const NODE *const coverage_node = node ? node : orig_node;
6686 add_trace_branch_coverage(
6687 iseq,
6688 ret,
6689 nd_code_loc(coverage_node),
6690 nd_node_id(coverage_node),
6691 branch_id,
6692 "else",
6693 branches);
6694 CHECK(COMPILE_(ret, "else", node, popped));
6695 ADD_INSNL(ret, orig_node, jump, endlabel);
6697 ADD_SEQ(ret, body_seq);
6698 ADD_LABEL(ret, endlabel);
6699 return COMPILE_OK;
6702 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);
6704 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);
6705 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);
6706 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);
6707 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);
6708 static int iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index);
6710 #define CASE3_BI_OFFSET_DECONSTRUCTED_CACHE 0
6711 #define CASE3_BI_OFFSET_ERROR_STRING 1
6712 #define CASE3_BI_OFFSET_KEY_ERROR_P 2
6713 #define CASE3_BI_OFFSET_KEY_ERROR_MATCHEE 3
6714 #define CASE3_BI_OFFSET_KEY_ERROR_KEY 4
6716 static int
6717 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)
6719 const int line = nd_line(node);
6720 const NODE *line_node = node;
6722 switch (nd_type(node)) {
6723 case NODE_ARYPTN: {
6725 * if pattern.use_rest_num?
6726 * rest_num = 0
6727 * end
6728 * if pattern.has_constant_node?
6729 * unless pattern.constant === obj
6730 * goto match_failed
6731 * end
6732 * end
6733 * unless obj.respond_to?(:deconstruct)
6734 * goto match_failed
6735 * end
6736 * d = obj.deconstruct
6737 * unless Array === d
6738 * goto type_error
6739 * end
6740 * min_argc = pattern.pre_args_num + pattern.post_args_num
6741 * if pattern.has_rest_arg?
6742 * unless d.length >= min_argc
6743 * goto match_failed
6744 * end
6745 * else
6746 * unless d.length == min_argc
6747 * goto match_failed
6748 * end
6749 * end
6750 * pattern.pre_args_num.each do |i|
6751 * unless pattern.pre_args[i].match?(d[i])
6752 * goto match_failed
6753 * end
6754 * end
6755 * if pattern.use_rest_num?
6756 * rest_num = d.length - min_argc
6757 * if pattern.has_rest_arg? && pattern.has_rest_arg_id # not `*`, but `*rest`
6758 * unless pattern.rest_arg.match?(d[pattern.pre_args_num, rest_num])
6759 * goto match_failed
6760 * end
6761 * end
6762 * end
6763 * pattern.post_args_num.each do |i|
6764 * j = pattern.pre_args_num + i
6765 * j += rest_num
6766 * unless pattern.post_args[i].match?(d[j])
6767 * goto match_failed
6768 * end
6769 * end
6770 * goto matched
6771 * type_error:
6772 * FrozenCore.raise TypeError
6773 * match_failed:
6774 * goto unmatched
6776 const NODE *args = RNODE_ARYPTN(node)->pre_args;
6777 const int pre_args_num = RNODE_ARYPTN(node)->pre_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->pre_args)->as.nd_alen) : 0;
6778 const int post_args_num = RNODE_ARYPTN(node)->post_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->post_args)->as.nd_alen) : 0;
6780 const int min_argc = pre_args_num + post_args_num;
6781 const int use_rest_num = RNODE_ARYPTN(node)->rest_arg && (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) ||
6782 (!NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) && post_args_num > 0));
6784 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
6785 int i;
6786 match_failed = NEW_LABEL(line);
6787 type_error = NEW_LABEL(line);
6788 deconstruct = NEW_LABEL(line);
6789 deconstructed = NEW_LABEL(line);
6791 if (use_rest_num) {
6792 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for rest_num */
6793 ADD_INSN(ret, line_node, swap);
6794 if (base_index) {
6795 base_index++;
6799 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
6801 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
6803 ADD_INSN(ret, line_node, dup);
6804 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6805 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
6806 ADD_SEND(ret, line_node, RNODE_ARYPTN(node)->rest_arg ? idGE : idEq, INT2FIX(1)); // (1)
6807 if (in_single_pattern) {
6808 CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node,
6809 RNODE_ARYPTN(node)->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") :
6810 rb_fstring_lit("%p length mismatch (given %p, expected %p)"),
6811 INT2FIX(min_argc), base_index + 1 /* (1) */));
6813 ADD_INSNL(ret, line_node, branchunless, match_failed);
6815 for (i = 0; i < pre_args_num; i++) {
6816 ADD_INSN(ret, line_node, dup);
6817 ADD_INSN1(ret, line_node, putobject, INT2FIX(i));
6818 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (2)
6819 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (2) */, false));
6820 args = RNODE_LIST(args)->nd_next;
6823 if (RNODE_ARYPTN(node)->rest_arg) {
6824 if (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg)) {
6825 ADD_INSN(ret, line_node, dup);
6826 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num));
6827 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
6828 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6829 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
6830 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
6831 ADD_INSN1(ret, line_node, setn, INT2FIX(4));
6832 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (3)
6834 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_ARYPTN(node)->rest_arg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (3) */, false));
6836 else {
6837 if (post_args_num > 0) {
6838 ADD_INSN(ret, line_node, dup);
6839 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6840 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
6841 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
6842 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
6843 ADD_INSN(ret, line_node, pop);
6848 args = RNODE_ARYPTN(node)->post_args;
6849 for (i = 0; i < post_args_num; i++) {
6850 ADD_INSN(ret, line_node, dup);
6852 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num + i));
6853 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6854 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
6856 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (4)
6857 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (4) */, false));
6858 args = RNODE_LIST(args)->nd_next;
6861 ADD_INSN(ret, line_node, pop);
6862 if (use_rest_num) {
6863 ADD_INSN(ret, line_node, pop);
6865 ADD_INSNL(ret, line_node, jump, matched);
6866 ADD_INSN(ret, line_node, putnil);
6867 if (use_rest_num) {
6868 ADD_INSN(ret, line_node, putnil);
6871 ADD_LABEL(ret, type_error);
6872 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6873 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
6874 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
6875 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
6876 ADD_INSN(ret, line_node, pop);
6878 ADD_LABEL(ret, match_failed);
6879 ADD_INSN(ret, line_node, pop);
6880 if (use_rest_num) {
6881 ADD_INSN(ret, line_node, pop);
6883 ADD_INSNL(ret, line_node, jump, unmatched);
6885 break;
6887 case NODE_FNDPTN: {
6889 * if pattern.has_constant_node?
6890 * unless pattern.constant === obj
6891 * goto match_failed
6892 * end
6893 * end
6894 * unless obj.respond_to?(:deconstruct)
6895 * goto match_failed
6896 * end
6897 * d = obj.deconstruct
6898 * unless Array === d
6899 * goto type_error
6900 * end
6901 * unless d.length >= pattern.args_num
6902 * goto match_failed
6903 * end
6905 * begin
6906 * len = d.length
6907 * limit = d.length - pattern.args_num
6908 * i = 0
6909 * while i <= limit
6910 * if pattern.args_num.times.all? {|j| pattern.args[j].match?(d[i+j]) }
6911 * if pattern.has_pre_rest_arg_id
6912 * unless pattern.pre_rest_arg.match?(d[0, i])
6913 * goto find_failed
6914 * end
6915 * end
6916 * if pattern.has_post_rest_arg_id
6917 * unless pattern.post_rest_arg.match?(d[i+pattern.args_num, len])
6918 * goto find_failed
6919 * end
6920 * end
6921 * goto find_succeeded
6922 * end
6923 * i+=1
6924 * end
6925 * find_failed:
6926 * goto match_failed
6927 * find_succeeded:
6928 * end
6930 * goto matched
6931 * type_error:
6932 * FrozenCore.raise TypeError
6933 * match_failed:
6934 * goto unmatched
6936 const NODE *args = RNODE_FNDPTN(node)->args;
6937 const int args_num = RNODE_FNDPTN(node)->args ? rb_long2int(RNODE_LIST(RNODE_FNDPTN(node)->args)->as.nd_alen) : 0;
6939 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
6940 match_failed = NEW_LABEL(line);
6941 type_error = NEW_LABEL(line);
6942 deconstruct = NEW_LABEL(line);
6943 deconstructed = NEW_LABEL(line);
6945 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
6947 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
6949 ADD_INSN(ret, line_node, dup);
6950 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
6951 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
6952 ADD_SEND(ret, line_node, idGE, INT2FIX(1)); // (1)
6953 if (in_single_pattern) {
6954 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) */));
6956 ADD_INSNL(ret, line_node, branchunless, match_failed);
6959 LABEL *while_begin = NEW_LABEL(nd_line(node));
6960 LABEL *next_loop = NEW_LABEL(nd_line(node));
6961 LABEL *find_succeeded = NEW_LABEL(line);
6962 LABEL *find_failed = NEW_LABEL(nd_line(node));
6963 int j;
6965 ADD_INSN(ret, line_node, dup); /* allocate stack for len */
6966 ADD_SEND(ret, line_node, idLength, INT2FIX(0)); // (2)
6968 ADD_INSN(ret, line_node, dup); /* allocate stack for limit */
6969 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
6970 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1)); // (3)
6972 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for i */ // (4)
6974 ADD_LABEL(ret, while_begin);
6976 ADD_INSN(ret, line_node, dup);
6977 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
6978 ADD_SEND(ret, line_node, idLE, INT2FIX(1));
6979 ADD_INSNL(ret, line_node, branchunless, find_failed);
6981 for (j = 0; j < args_num; j++) {
6982 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6983 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
6984 if (j != 0) {
6985 ADD_INSN1(ret, line_node, putobject, INT2FIX(j));
6986 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
6988 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (5)
6990 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, next_loop, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (5) */, false));
6991 args = RNODE_LIST(args)->nd_next;
6994 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->pre_rest_arg)) {
6995 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
6996 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
6997 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
6998 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (6)
6999 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->pre_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (6) */, false));
7001 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->post_rest_arg)) {
7002 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7003 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7004 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7005 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7006 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7007 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (7)
7008 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->post_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3),(4), (7) */, false));
7010 ADD_INSNL(ret, line_node, jump, find_succeeded);
7012 ADD_LABEL(ret, next_loop);
7013 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
7014 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7015 ADD_INSNL(ret, line_node, jump, while_begin);
7017 ADD_LABEL(ret, find_failed);
7018 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7019 if (in_single_pattern) {
7020 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7021 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p does not match to find pattern"));
7022 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7023 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (8)
7024 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (8) */)); // (9)
7026 ADD_INSN1(ret, line_node, putobject, Qfalse);
7027 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (8), (9) */));
7029 ADD_INSN(ret, line_node, pop);
7030 ADD_INSN(ret, line_node, pop);
7032 ADD_INSNL(ret, line_node, jump, match_failed);
7033 ADD_INSN1(ret, line_node, dupn, INT2FIX(3));
7035 ADD_LABEL(ret, find_succeeded);
7036 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7039 ADD_INSN(ret, line_node, pop);
7040 ADD_INSNL(ret, line_node, jump, matched);
7041 ADD_INSN(ret, line_node, putnil);
7043 ADD_LABEL(ret, type_error);
7044 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7045 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7046 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
7047 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7048 ADD_INSN(ret, line_node, pop);
7050 ADD_LABEL(ret, match_failed);
7051 ADD_INSN(ret, line_node, pop);
7052 ADD_INSNL(ret, line_node, jump, unmatched);
7054 break;
7056 case NODE_HSHPTN: {
7058 * keys = nil
7059 * if pattern.has_kw_args_node? && !pattern.has_kw_rest_arg_node?
7060 * keys = pattern.kw_args_node.keys
7061 * end
7062 * if pattern.has_constant_node?
7063 * unless pattern.constant === obj
7064 * goto match_failed
7065 * end
7066 * end
7067 * unless obj.respond_to?(:deconstruct_keys)
7068 * goto match_failed
7069 * end
7070 * d = obj.deconstruct_keys(keys)
7071 * unless Hash === d
7072 * goto type_error
7073 * end
7074 * if pattern.has_kw_rest_arg_node?
7075 * d = d.dup
7076 * end
7077 * if pattern.has_kw_args_node?
7078 * pattern.kw_args_node.each |k,|
7079 * unless d.key?(k)
7080 * goto match_failed
7081 * end
7082 * end
7083 * pattern.kw_args_node.each |k, pat|
7084 * if pattern.has_kw_rest_arg_node?
7085 * unless pat.match?(d.delete(k))
7086 * goto match_failed
7087 * end
7088 * else
7089 * unless pat.match?(d[k])
7090 * goto match_failed
7091 * end
7092 * end
7093 * end
7094 * else
7095 * unless d.empty?
7096 * goto match_failed
7097 * end
7098 * end
7099 * if pattern.has_kw_rest_arg_node?
7100 * if pattern.no_rest_keyword?
7101 * unless d.empty?
7102 * goto match_failed
7103 * end
7104 * else
7105 * unless pattern.kw_rest_arg_node.match?(d)
7106 * goto match_failed
7107 * end
7108 * end
7109 * end
7110 * goto matched
7111 * type_error:
7112 * FrozenCore.raise TypeError
7113 * match_failed:
7114 * goto unmatched
7116 LABEL *match_failed, *type_error;
7117 VALUE keys = Qnil;
7119 match_failed = NEW_LABEL(line);
7120 type_error = NEW_LABEL(line);
7122 if (RNODE_HSHPTN(node)->nd_pkwargs && !RNODE_HSHPTN(node)->nd_pkwrestarg) {
7123 const NODE *kw_args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7124 keys = rb_ary_new_capa(kw_args ? RNODE_LIST(kw_args)->as.nd_alen/2 : 0);
7125 while (kw_args) {
7126 rb_ary_push(keys, get_symbol_value(iseq, RNODE_LIST(kw_args)->nd_head));
7127 kw_args = RNODE_LIST(RNODE_LIST(kw_args)->nd_next)->nd_next;
7131 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7133 ADD_INSN(ret, line_node, dup);
7134 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct_keys")));
7135 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (1)
7136 if (in_single_pattern) {
7137 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1 /* (1) */));
7139 ADD_INSNL(ret, line_node, branchunless, match_failed);
7141 if (NIL_P(keys)) {
7142 ADD_INSN(ret, line_node, putnil);
7144 else {
7145 ADD_INSN1(ret, line_node, duparray, keys);
7146 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
7148 ADD_SEND(ret, line_node, rb_intern("deconstruct_keys"), INT2FIX(1)); // (2)
7150 ADD_INSN(ret, line_node, dup);
7151 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_HASH));
7152 ADD_INSNL(ret, line_node, branchunless, type_error);
7154 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7155 ADD_SEND(ret, line_node, rb_intern("dup"), INT2FIX(0));
7158 if (RNODE_HSHPTN(node)->nd_pkwargs) {
7159 int i;
7160 int keys_num;
7161 const NODE *args;
7162 args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7163 if (args) {
7164 DECL_ANCHOR(match_values);
7165 INIT_ANCHOR(match_values);
7166 keys_num = rb_long2int(RNODE_LIST(args)->as.nd_alen) / 2;
7167 for (i = 0; i < keys_num; i++) {
7168 NODE *key_node = RNODE_LIST(args)->nd_head;
7169 NODE *value_node = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_head;
7170 VALUE key = get_symbol_value(iseq, key_node);
7172 ADD_INSN(ret, line_node, dup);
7173 ADD_INSN1(ret, line_node, putobject, key);
7174 ADD_SEND(ret, line_node, rb_intern("key?"), INT2FIX(1)); // (3)
7175 if (in_single_pattern) {
7176 LABEL *match_succeeded;
7177 match_succeeded = NEW_LABEL(line);
7179 ADD_INSN(ret, line_node, dup);
7180 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7182 ADD_INSN1(ret, line_node, putobject, rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, key))); // (4)
7183 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 2 /* (3), (4) */));
7184 ADD_INSN1(ret, line_node, putobject, Qtrue); // (5)
7185 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 3 /* (3), (4), (5) */));
7186 ADD_INSN1(ret, line_node, topn, INT2FIX(3)); // (6)
7187 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4 /* (3), (4), (5), (6) */));
7188 ADD_INSN1(ret, line_node, putobject, key); // (7)
7189 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_KEY + 5 /* (3), (4), (5), (6), (7) */));
7191 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(4));
7193 ADD_LABEL(ret, match_succeeded);
7195 ADD_INSNL(ret, line_node, branchunless, match_failed);
7197 ADD_INSN(match_values, line_node, dup);
7198 ADD_INSN1(match_values, line_node, putobject, key);
7199 ADD_SEND(match_values, line_node, RNODE_HSHPTN(node)->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8)
7200 CHECK(iseq_compile_pattern_match(iseq, match_values, value_node, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (8) */, false));
7201 args = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_next;
7203 ADD_SEQ(ret, match_values);
7206 else {
7207 ADD_INSN(ret, line_node, dup);
7208 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (9)
7209 if (in_single_pattern) {
7210 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p is not empty"), base_index + 1 /* (9) */));
7212 ADD_INSNL(ret, line_node, branchunless, match_failed);
7215 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7216 if (RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
7217 ADD_INSN(ret, line_node, dup);
7218 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (10)
7219 if (in_single_pattern) {
7220 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("rest of %p is not empty"), base_index + 1 /* (10) */));
7222 ADD_INSNL(ret, line_node, branchunless, match_failed);
7224 else {
7225 ADD_INSN(ret, line_node, dup); // (11)
7226 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_HSHPTN(node)->nd_pkwrestarg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (11) */, false));
7230 ADD_INSN(ret, line_node, pop);
7231 ADD_INSNL(ret, line_node, jump, matched);
7232 ADD_INSN(ret, line_node, putnil);
7234 ADD_LABEL(ret, type_error);
7235 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7236 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7237 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct_keys must return Hash"));
7238 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7239 ADD_INSN(ret, line_node, pop);
7241 ADD_LABEL(ret, match_failed);
7242 ADD_INSN(ret, line_node, pop);
7243 ADD_INSNL(ret, line_node, jump, unmatched);
7244 break;
7246 case NODE_SYM:
7247 case NODE_REGX:
7248 case NODE_LINE:
7249 case NODE_INTEGER:
7250 case NODE_FLOAT:
7251 case NODE_RATIONAL:
7252 case NODE_IMAGINARY:
7253 case NODE_FILE:
7254 case NODE_ENCODING:
7255 case NODE_STR:
7256 case NODE_XSTR:
7257 case NODE_DSTR:
7258 case NODE_DSYM:
7259 case NODE_DREGX:
7260 case NODE_LIST:
7261 case NODE_ZLIST:
7262 case NODE_LAMBDA:
7263 case NODE_DOT2:
7264 case NODE_DOT3:
7265 case NODE_CONST:
7266 case NODE_LVAR:
7267 case NODE_DVAR:
7268 case NODE_IVAR:
7269 case NODE_CVAR:
7270 case NODE_GVAR:
7271 case NODE_TRUE:
7272 case NODE_FALSE:
7273 case NODE_SELF:
7274 case NODE_NIL:
7275 case NODE_COLON2:
7276 case NODE_COLON3:
7277 case NODE_BEGIN:
7278 case NODE_BLOCK:
7279 case NODE_ONCE:
7280 CHECK(COMPILE(ret, "case in literal", node)); // (1)
7281 if (in_single_pattern) {
7282 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7284 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (2)
7285 if (in_single_pattern) {
7286 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 2 /* (1), (2) */));
7288 ADD_INSNL(ret, line_node, branchif, matched);
7289 ADD_INSNL(ret, line_node, jump, unmatched);
7290 break;
7291 case NODE_LASGN: {
7292 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
7293 ID id = RNODE_LASGN(node)->nd_vid;
7294 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
7296 if (in_alt_pattern) {
7297 const char *name = rb_id2name(id);
7298 if (name && strlen(name) > 0 && name[0] != '_') {
7299 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7300 rb_id2str(id));
7301 return COMPILE_NG;
7305 ADD_SETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
7306 ADD_INSNL(ret, line_node, jump, matched);
7307 break;
7309 case NODE_DASGN: {
7310 int idx, lv, ls;
7311 ID id = RNODE_DASGN(node)->nd_vid;
7313 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
7315 if (in_alt_pattern) {
7316 const char *name = rb_id2name(id);
7317 if (name && strlen(name) > 0 && name[0] != '_') {
7318 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7319 rb_id2str(id));
7320 return COMPILE_NG;
7324 if (idx < 0) {
7325 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
7326 rb_id2str(id));
7327 return COMPILE_NG;
7329 ADD_SETLOCAL(ret, line_node, ls - idx, lv);
7330 ADD_INSNL(ret, line_node, jump, matched);
7331 break;
7333 case NODE_IF:
7334 case NODE_UNLESS: {
7335 LABEL *match_failed;
7336 match_failed = unmatched;
7337 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_IF(node)->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7338 CHECK(COMPILE(ret, "case in if", RNODE_IF(node)->nd_cond));
7339 if (in_single_pattern) {
7340 LABEL *match_succeeded;
7341 match_succeeded = NEW_LABEL(line);
7343 ADD_INSN(ret, line_node, dup);
7344 if (nd_type_p(node, NODE_IF)) {
7345 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7347 else {
7348 ADD_INSNL(ret, line_node, branchunless, match_succeeded);
7351 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("guard clause does not return true")); // (1)
7352 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
7353 ADD_INSN1(ret, line_node, putobject, Qfalse);
7354 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
7356 ADD_INSN(ret, line_node, pop);
7357 ADD_INSN(ret, line_node, pop);
7359 ADD_LABEL(ret, match_succeeded);
7361 if (nd_type_p(node, NODE_IF)) {
7362 ADD_INSNL(ret, line_node, branchunless, match_failed);
7364 else {
7365 ADD_INSNL(ret, line_node, branchif, match_failed);
7367 ADD_INSNL(ret, line_node, jump, matched);
7368 break;
7370 case NODE_HASH: {
7371 NODE *n;
7372 LABEL *match_failed;
7373 match_failed = NEW_LABEL(line);
7375 n = RNODE_HASH(node)->nd_head;
7376 if (! (nd_type_p(n, NODE_LIST) && RNODE_LIST(n)->as.nd_alen == 2)) {
7377 COMPILE_ERROR(ERROR_ARGS "unexpected node");
7378 return COMPILE_NG;
7381 ADD_INSN(ret, line_node, dup); // (1)
7382 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(n)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (1) */, use_deconstructed_cache));
7383 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head, matched, match_failed, in_single_pattern, in_alt_pattern, base_index, false));
7384 ADD_INSN(ret, line_node, putnil);
7386 ADD_LABEL(ret, match_failed);
7387 ADD_INSN(ret, line_node, pop);
7388 ADD_INSNL(ret, line_node, jump, unmatched);
7389 break;
7391 case NODE_OR: {
7392 LABEL *match_succeeded, *fin;
7393 match_succeeded = NEW_LABEL(line);
7394 fin = NEW_LABEL(line);
7396 ADD_INSN(ret, line_node, dup); // (1)
7397 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_1st, match_succeeded, fin, in_single_pattern, true, base_index + 1 /* (1) */, use_deconstructed_cache));
7398 ADD_LABEL(ret, match_succeeded);
7399 ADD_INSN(ret, line_node, pop);
7400 ADD_INSNL(ret, line_node, jump, matched);
7401 ADD_INSN(ret, line_node, putnil);
7402 ADD_LABEL(ret, fin);
7403 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache));
7404 break;
7406 default:
7407 UNKNOWN_NODE("NODE_IN", node, COMPILE_NG);
7409 return COMPILE_OK;
7412 static int
7413 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)
7415 LABEL *fin = NEW_LABEL(nd_line(node));
7416 CHECK(iseq_compile_pattern_each(iseq, ret, node, fin, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7417 ADD_LABEL(ret, fin);
7418 return COMPILE_OK;
7421 static int
7422 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)
7424 const NODE *line_node = node;
7426 if (RNODE_ARYPTN(node)->nd_pconst) {
7427 ADD_INSN(ret, line_node, dup); // (1)
7428 CHECK(COMPILE(ret, "constant", RNODE_ARYPTN(node)->nd_pconst)); // (2)
7429 if (in_single_pattern) {
7430 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7432 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (3)
7433 if (in_single_pattern) {
7434 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 3 /* (1), (2), (3) */));
7436 ADD_INSNL(ret, line_node, branchunless, match_failed);
7438 return COMPILE_OK;
7442 static int
7443 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)
7445 const NODE *line_node = node;
7447 // NOTE: this optimization allows us to re-use the #deconstruct value
7448 // (or its absence).
7449 if (use_deconstructed_cache) {
7450 // If value is nil then we haven't tried to deconstruct
7451 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
7452 ADD_INSNL(ret, line_node, branchnil, deconstruct);
7454 // If false then the value is not deconstructable
7455 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
7456 ADD_INSNL(ret, line_node, branchunless, match_failed);
7458 // Drop value, add deconstructed to the stack and jump
7459 ADD_INSN(ret, line_node, pop); // (1)
7460 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE - 1 /* (1) */));
7461 ADD_INSNL(ret, line_node, jump, deconstructed);
7463 else {
7464 ADD_INSNL(ret, line_node, jump, deconstruct);
7467 ADD_LABEL(ret, deconstruct);
7468 ADD_INSN(ret, line_node, dup);
7469 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct")));
7470 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (2)
7472 // Cache the result of respond_to? (in case it's false is stays there, if true - it's overwritten after #deconstruct)
7473 if (use_deconstructed_cache) {
7474 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE + 1 /* (2) */));
7477 if (in_single_pattern) {
7478 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1 /* (2) */));
7481 ADD_INSNL(ret, line_node, branchunless, match_failed);
7483 ADD_SEND(ret, line_node, rb_intern("deconstruct"), INT2FIX(0));
7485 // Cache the result (if it's cacheable - currently, only top-level array patterns)
7486 if (use_deconstructed_cache) {
7487 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
7490 ADD_INSN(ret, line_node, dup);
7491 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_ARRAY));
7492 ADD_INSNL(ret, line_node, branchunless, type_error);
7494 ADD_LABEL(ret, deconstructed);
7496 return COMPILE_OK;
7499 static int
7500 iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index)
7503 * if match_succeeded?
7504 * goto match_succeeded
7505 * end
7506 * error_string = FrozenCore.sprintf(errmsg, matchee)
7507 * key_error_p = false
7508 * match_succeeded:
7510 const int line = nd_line(node);
7511 const NODE *line_node = node;
7512 LABEL *match_succeeded = NEW_LABEL(line);
7514 ADD_INSN(ret, line_node, dup);
7515 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7517 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7518 ADD_INSN1(ret, line_node, putobject, errmsg);
7519 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7520 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (1)
7521 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
7523 ADD_INSN1(ret, line_node, putobject, Qfalse);
7524 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
7526 ADD_INSN(ret, line_node, pop);
7527 ADD_INSN(ret, line_node, pop);
7528 ADD_LABEL(ret, match_succeeded);
7530 return COMPILE_OK;
7533 static int
7534 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)
7537 * if match_succeeded?
7538 * goto match_succeeded
7539 * end
7540 * error_string = FrozenCore.sprintf(errmsg, matchee, matchee.length, pat.length)
7541 * key_error_p = false
7542 * match_succeeded:
7544 const int line = nd_line(node);
7545 const NODE *line_node = node;
7546 LABEL *match_succeeded = NEW_LABEL(line);
7548 ADD_INSN(ret, line_node, dup);
7549 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7551 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7552 ADD_INSN1(ret, line_node, putobject, errmsg);
7553 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7554 ADD_INSN(ret, line_node, dup);
7555 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7556 ADD_INSN1(ret, line_node, putobject, pattern_length);
7557 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(4)); // (1)
7558 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
7560 ADD_INSN1(ret, line_node, putobject, Qfalse);
7561 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2/* (1), (2) */));
7563 ADD_INSN(ret, line_node, pop);
7564 ADD_INSN(ret, line_node, pop);
7565 ADD_LABEL(ret, match_succeeded);
7567 return COMPILE_OK;
7570 static int
7571 iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index)
7574 * if match_succeeded?
7575 * goto match_succeeded
7576 * end
7577 * error_string = FrozenCore.sprintf("%p === %p does not return true", pat, matchee)
7578 * key_error_p = false
7579 * match_succeeded:
7581 const int line = nd_line(node);
7582 const NODE *line_node = node;
7583 LABEL *match_succeeded = NEW_LABEL(line);
7585 ADD_INSN(ret, line_node, dup);
7586 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7588 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7589 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p === %p does not return true"));
7590 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7591 ADD_INSN1(ret, line_node, topn, INT2FIX(5));
7592 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(3)); // (1)
7593 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
7595 ADD_INSN1(ret, line_node, putobject, Qfalse);
7596 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
7598 ADD_INSN(ret, line_node, pop);
7599 ADD_INSN(ret, line_node, pop);
7601 ADD_LABEL(ret, match_succeeded);
7602 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
7603 ADD_INSN(ret, line_node, pop);
7604 ADD_INSN(ret, line_node, pop);
7606 return COMPILE_OK;
7609 static int
7610 compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
7612 const NODE *pattern;
7613 const NODE *node = orig_node;
7614 LABEL *endlabel, *elselabel;
7615 DECL_ANCHOR(head);
7616 DECL_ANCHOR(body_seq);
7617 DECL_ANCHOR(cond_seq);
7618 int line;
7619 enum node_type type;
7620 const NODE *line_node;
7621 VALUE branches = 0;
7622 int branch_id = 0;
7623 bool single_pattern;
7625 INIT_ANCHOR(head);
7626 INIT_ANCHOR(body_seq);
7627 INIT_ANCHOR(cond_seq);
7629 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
7631 node = RNODE_CASE3(node)->nd_body;
7632 EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG);
7633 type = nd_type(node);
7634 line = nd_line(node);
7635 line_node = node;
7636 single_pattern = !RNODE_IN(node)->nd_next;
7638 endlabel = NEW_LABEL(line);
7639 elselabel = NEW_LABEL(line);
7641 if (single_pattern) {
7642 /* allocate stack for ... */
7643 ADD_INSN(head, line_node, putnil); /* key_error_key */
7644 ADD_INSN(head, line_node, putnil); /* key_error_matchee */
7645 ADD_INSN1(head, line_node, putobject, Qfalse); /* key_error_p */
7646 ADD_INSN(head, line_node, putnil); /* error_string */
7648 ADD_INSN(head, line_node, putnil); /* allocate stack for cached #deconstruct value */
7650 CHECK(COMPILE(head, "case base", RNODE_CASE3(orig_node)->nd_head));
7652 ADD_SEQ(ret, head); /* case VAL */
7654 while (type == NODE_IN) {
7655 LABEL *l1;
7657 if (branch_id) {
7658 ADD_INSN(body_seq, line_node, putnil);
7660 l1 = NEW_LABEL(line);
7661 ADD_LABEL(body_seq, l1);
7662 ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2));
7664 const NODE *const coverage_node = RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node;
7665 add_trace_branch_coverage(
7666 iseq,
7667 body_seq,
7668 nd_code_loc(coverage_node),
7669 nd_node_id(coverage_node),
7670 branch_id++,
7671 "in",
7672 branches);
7674 CHECK(COMPILE_(body_seq, "in body", RNODE_IN(node)->nd_body, popped));
7675 ADD_INSNL(body_seq, line_node, jump, endlabel);
7677 pattern = RNODE_IN(node)->nd_head;
7678 if (pattern) {
7679 int pat_line = nd_line(pattern);
7680 LABEL *next_pat = NEW_LABEL(pat_line);
7681 ADD_INSN (cond_seq, pattern, dup); /* dup case VAL */
7682 // NOTE: set base_index (it's "under" the matchee value, so it's position is 2)
7683 CHECK(iseq_compile_pattern_each(iseq, cond_seq, pattern, l1, next_pat, single_pattern, false, 2, true));
7684 ADD_LABEL(cond_seq, next_pat);
7685 LABEL_UNREMOVABLE(next_pat);
7687 else {
7688 COMPILE_ERROR(ERROR_ARGS "unexpected node");
7689 return COMPILE_NG;
7692 node = RNODE_IN(node)->nd_next;
7693 if (!node) {
7694 break;
7696 type = nd_type(node);
7697 line = nd_line(node);
7698 line_node = node;
7700 /* else */
7701 if (node) {
7702 ADD_LABEL(cond_seq, elselabel);
7703 ADD_INSN(cond_seq, line_node, pop);
7704 ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */
7705 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
7706 CHECK(COMPILE_(cond_seq, "else", node, popped));
7707 ADD_INSNL(cond_seq, line_node, jump, endlabel);
7708 ADD_INSN(cond_seq, line_node, putnil);
7709 if (popped) {
7710 ADD_INSN(cond_seq, line_node, putnil);
7713 else {
7714 debugs("== else (implicit)\n");
7715 ADD_LABEL(cond_seq, elselabel);
7716 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
7717 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7719 if (single_pattern) {
7721 * if key_error_p
7722 * FrozenCore.raise NoMatchingPatternKeyError.new(FrozenCore.sprintf("%p: %s", case_val, error_string), matchee: key_error_matchee, key: key_error_key)
7723 * else
7724 * FrozenCore.raise NoMatchingPatternError, FrozenCore.sprintf("%p: %s", case_val, error_string)
7725 * end
7727 LABEL *key_error, *fin;
7728 struct rb_callinfo_kwarg *kw_arg;
7730 key_error = NEW_LABEL(line);
7731 fin = NEW_LABEL(line);
7733 kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
7734 kw_arg->references = 0;
7735 kw_arg->keyword_len = 2;
7736 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
7737 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
7739 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_P + 2));
7740 ADD_INSNL(cond_seq, orig_node, branchif, key_error);
7741 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
7742 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7743 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
7744 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
7745 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
7746 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
7747 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
7748 ADD_INSNL(cond_seq, orig_node, jump, fin);
7750 ADD_LABEL(cond_seq, key_error);
7751 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternKeyError);
7752 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7753 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
7754 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
7755 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
7756 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
7757 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4));
7758 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_KEY + 5));
7759 ADD_SEND_R(cond_seq, orig_node, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
7760 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(1));
7762 ADD_LABEL(cond_seq, fin);
7764 else {
7765 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
7766 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(2));
7767 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
7769 ADD_INSN1(cond_seq, orig_node, adjuststack, INT2FIX(single_pattern ? 7 : 3));
7770 if (!popped) {
7771 ADD_INSN(cond_seq, orig_node, putnil);
7773 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
7774 ADD_INSN1(cond_seq, orig_node, dupn, INT2FIX(single_pattern ? 5 : 1));
7775 if (popped) {
7776 ADD_INSN(cond_seq, line_node, putnil);
7780 ADD_SEQ(ret, cond_seq);
7781 ADD_SEQ(ret, body_seq);
7782 ADD_LABEL(ret, endlabel);
7783 return COMPILE_OK;
7786 #undef CASE3_BI_OFFSET_DECONSTRUCTED_CACHE
7787 #undef CASE3_BI_OFFSET_ERROR_STRING
7788 #undef CASE3_BI_OFFSET_KEY_ERROR_P
7789 #undef CASE3_BI_OFFSET_KEY_ERROR_MATCHEE
7790 #undef CASE3_BI_OFFSET_KEY_ERROR_KEY
7792 static int
7793 compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
7795 const int line = (int)nd_line(node);
7796 const NODE *line_node = node;
7798 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
7799 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
7800 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
7801 int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
7802 VALUE branches = Qfalse;
7804 struct iseq_compile_data_ensure_node_stack enl;
7806 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line); /* next */
7807 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line); /* redo */
7808 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line); /* break */
7809 LABEL *end_label = NEW_LABEL(line);
7810 LABEL *adjust_label = NEW_LABEL(line);
7812 LABEL *next_catch_label = NEW_LABEL(line);
7813 LABEL *tmp_label = NULL;
7815 ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
7816 push_ensure_entry(iseq, &enl, NULL, NULL);
7818 if (RNODE_WHILE(node)->nd_state == 1) {
7819 ADD_INSNL(ret, line_node, jump, next_label);
7821 else {
7822 tmp_label = NEW_LABEL(line);
7823 ADD_INSNL(ret, line_node, jump, tmp_label);
7825 ADD_LABEL(ret, adjust_label);
7826 ADD_INSN(ret, line_node, putnil);
7827 ADD_LABEL(ret, next_catch_label);
7828 ADD_INSN(ret, line_node, pop);
7829 ADD_INSNL(ret, line_node, jump, next_label);
7830 if (tmp_label) ADD_LABEL(ret, tmp_label);
7832 ADD_LABEL(ret, redo_label);
7833 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until");
7835 const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node;
7836 add_trace_branch_coverage(
7837 iseq,
7838 ret,
7839 nd_code_loc(coverage_node),
7840 nd_node_id(coverage_node),
7842 "body",
7843 branches);
7845 CHECK(COMPILE_POPPED(ret, "while body", RNODE_WHILE(node)->nd_body));
7846 ADD_LABEL(ret, next_label); /* next */
7848 if (type == NODE_WHILE) {
7849 compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
7850 redo_label, end_label);
7852 else {
7853 /* until */
7854 compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
7855 end_label, redo_label);
7858 ADD_LABEL(ret, end_label);
7859 ADD_ADJUST_RESTORE(ret, adjust_label);
7861 if (UNDEF_P(RNODE_WHILE(node)->nd_state)) {
7862 /* ADD_INSN(ret, line_node, putundef); */
7863 COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
7864 return COMPILE_NG;
7866 else {
7867 ADD_INSN(ret, line_node, putnil);
7870 ADD_LABEL(ret, break_label); /* break */
7872 if (popped) {
7873 ADD_INSN(ret, line_node, pop);
7876 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
7877 break_label);
7878 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
7879 next_catch_label);
7880 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
7881 ISEQ_COMPILE_DATA(iseq)->redo_label);
7883 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
7884 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
7885 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
7886 ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
7887 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
7888 return COMPILE_OK;
7891 static int
7892 compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7894 const int line = nd_line(node);
7895 const NODE *line_node = node;
7896 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
7897 LABEL *retry_label = NEW_LABEL(line);
7898 LABEL *retry_end_l = NEW_LABEL(line);
7899 const rb_iseq_t *child_iseq;
7901 ADD_LABEL(ret, retry_label);
7902 if (nd_type_p(node, NODE_FOR)) {
7903 CHECK(COMPILE(ret, "iter caller (for)", RNODE_FOR(node)->nd_iter));
7905 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
7906 NEW_CHILD_ISEQ(RNODE_FOR(node)->nd_body, make_name_for_block(iseq),
7907 ISEQ_TYPE_BLOCK, line);
7908 ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
7910 else {
7911 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
7912 NEW_CHILD_ISEQ(RNODE_ITER(node)->nd_body, make_name_for_block(iseq),
7913 ISEQ_TYPE_BLOCK, line);
7914 CHECK(COMPILE(ret, "iter caller", RNODE_ITER(node)->nd_iter));
7918 // We need to put the label "retry_end_l" immediately after the last "send" instruction.
7919 // This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
7920 // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
7922 // Normally, "send" instruction is at the last.
7923 // However, qcall under branch coverage measurement adds some instructions after the "send".
7925 // Note that "invokesuper" appears instead of "send".
7926 INSN *iobj;
7927 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
7928 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
7929 while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) {
7930 iobj = (INSN*) get_prev_insn(iobj);
7932 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
7934 // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
7935 // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
7936 if (&iobj->link == LAST_ELEMENT(ret)) {
7937 ret->last = (LINK_ELEMENT*) retry_end_l;
7941 if (popped) {
7942 ADD_INSN(ret, line_node, pop);
7945 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
7947 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
7948 return COMPILE_OK;
7951 static int
7952 compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7954 /* massign to var in "for"
7955 * (args.length == 1 && Array.try_convert(args[0])) || args
7957 const NODE *line_node = node;
7958 const NODE *var = RNODE_FOR_MASGN(node)->nd_var;
7959 LABEL *not_single = NEW_LABEL(nd_line(var));
7960 LABEL *not_ary = NEW_LABEL(nd_line(var));
7961 CHECK(COMPILE(ret, "for var", var));
7962 ADD_INSN(ret, line_node, dup);
7963 ADD_CALL(ret, line_node, idLength, INT2FIX(0));
7964 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
7965 ADD_CALL(ret, line_node, idEq, INT2FIX(1));
7966 ADD_INSNL(ret, line_node, branchunless, not_single);
7967 ADD_INSN(ret, line_node, dup);
7968 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
7969 ADD_CALL(ret, line_node, idAREF, INT2FIX(1));
7970 ADD_INSN1(ret, line_node, putobject, rb_cArray);
7971 ADD_INSN(ret, line_node, swap);
7972 ADD_CALL(ret, line_node, rb_intern("try_convert"), INT2FIX(1));
7973 ADD_INSN(ret, line_node, dup);
7974 ADD_INSNL(ret, line_node, branchunless, not_ary);
7975 ADD_INSN(ret, line_node, swap);
7976 ADD_LABEL(ret, not_ary);
7977 ADD_INSN(ret, line_node, pop);
7978 ADD_LABEL(ret, not_single);
7979 return COMPILE_OK;
7982 static int
7983 compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
7985 const NODE *line_node = node;
7986 unsigned long throw_flag = 0;
7988 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7989 /* while/until */
7990 LABEL *splabel = NEW_LABEL(0);
7991 ADD_LABEL(ret, splabel);
7992 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
7993 CHECK(COMPILE_(ret, "break val (while/until)", RNODE_BREAK(node)->nd_stts,
7994 ISEQ_COMPILE_DATA(iseq)->loopval_popped));
7995 add_ensure_iseq(ret, iseq, 0);
7996 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7997 ADD_ADJUST_RESTORE(ret, splabel);
7999 if (!popped) {
8000 ADD_INSN(ret, line_node, putnil);
8003 else {
8004 const rb_iseq_t *ip = iseq;
8006 while (ip) {
8007 if (!ISEQ_COMPILE_DATA(ip)) {
8008 ip = 0;
8009 break;
8012 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8013 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8015 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8016 throw_flag = 0;
8018 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8019 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
8020 return COMPILE_NG;
8022 else {
8023 ip = ISEQ_BODY(ip)->parent_iseq;
8024 continue;
8027 /* escape from block */
8028 CHECK(COMPILE(ret, "break val (block)", RNODE_BREAK(node)->nd_stts));
8029 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_BREAK));
8030 if (popped) {
8031 ADD_INSN(ret, line_node, pop);
8033 return COMPILE_OK;
8035 COMPILE_ERROR(ERROR_ARGS "Invalid break");
8036 return COMPILE_NG;
8038 return COMPILE_OK;
8041 static int
8042 compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8044 const NODE *line_node = node;
8045 unsigned long throw_flag = 0;
8047 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8048 LABEL *splabel = NEW_LABEL(0);
8049 debugs("next in while loop\n");
8050 ADD_LABEL(ret, splabel);
8051 CHECK(COMPILE(ret, "next val/valid syntax?", RNODE_NEXT(node)->nd_stts));
8052 add_ensure_iseq(ret, iseq, 0);
8053 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8054 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8055 ADD_ADJUST_RESTORE(ret, splabel);
8056 if (!popped) {
8057 ADD_INSN(ret, line_node, putnil);
8060 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8061 LABEL *splabel = NEW_LABEL(0);
8062 debugs("next in block\n");
8063 ADD_LABEL(ret, splabel);
8064 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8065 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8066 add_ensure_iseq(ret, iseq, 0);
8067 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8068 ADD_ADJUST_RESTORE(ret, splabel);
8069 splabel->unremovable = FALSE;
8071 if (!popped) {
8072 ADD_INSN(ret, line_node, putnil);
8075 else {
8076 const rb_iseq_t *ip = iseq;
8078 while (ip) {
8079 if (!ISEQ_COMPILE_DATA(ip)) {
8080 ip = 0;
8081 break;
8084 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8085 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8086 /* while loop */
8087 break;
8089 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8090 break;
8092 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8093 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
8094 return COMPILE_NG;
8097 ip = ISEQ_BODY(ip)->parent_iseq;
8099 if (ip != 0) {
8100 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8101 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT));
8103 if (popped) {
8104 ADD_INSN(ret, line_node, pop);
8107 else {
8108 COMPILE_ERROR(ERROR_ARGS "Invalid next");
8109 return COMPILE_NG;
8112 return COMPILE_OK;
8115 static int
8116 compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8118 const NODE *line_node = node;
8120 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8121 LABEL *splabel = NEW_LABEL(0);
8122 debugs("redo in while");
8123 ADD_LABEL(ret, splabel);
8124 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8125 add_ensure_iseq(ret, iseq, 0);
8126 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8127 ADD_ADJUST_RESTORE(ret, splabel);
8128 if (!popped) {
8129 ADD_INSN(ret, line_node, putnil);
8132 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8133 LABEL *splabel = NEW_LABEL(0);
8135 debugs("redo in block");
8136 ADD_LABEL(ret, splabel);
8137 add_ensure_iseq(ret, iseq, 0);
8138 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8139 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8140 ADD_ADJUST_RESTORE(ret, splabel);
8142 if (!popped) {
8143 ADD_INSN(ret, line_node, putnil);
8146 else {
8147 const rb_iseq_t *ip = iseq;
8149 while (ip) {
8150 if (!ISEQ_COMPILE_DATA(ip)) {
8151 ip = 0;
8152 break;
8155 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8156 break;
8158 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8159 break;
8161 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8162 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
8163 return COMPILE_NG;
8166 ip = ISEQ_BODY(ip)->parent_iseq;
8168 if (ip != 0) {
8169 ADD_INSN(ret, line_node, putnil);
8170 ADD_INSN1(ret, line_node, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8172 if (popped) {
8173 ADD_INSN(ret, line_node, pop);
8176 else {
8177 COMPILE_ERROR(ERROR_ARGS "Invalid redo");
8178 return COMPILE_NG;
8181 return COMPILE_OK;
8184 static int
8185 compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8187 const NODE *line_node = node;
8189 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
8190 ADD_INSN(ret, line_node, putnil);
8191 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETRY));
8193 if (popped) {
8194 ADD_INSN(ret, line_node, pop);
8197 else {
8198 COMPILE_ERROR(ERROR_ARGS "Invalid retry");
8199 return COMPILE_NG;
8201 return COMPILE_OK;
8204 static int
8205 compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8207 const int line = nd_line(node);
8208 const NODE *line_node = node;
8209 LABEL *lstart = NEW_LABEL(line);
8210 LABEL *lend = NEW_LABEL(line);
8211 LABEL *lcont = NEW_LABEL(line);
8212 const rb_iseq_t *rescue = NEW_CHILD_ISEQ(RNODE_RESCUE(node)->nd_resq,
8213 rb_str_concat(rb_str_new2("rescue in "),
8214 ISEQ_BODY(iseq)->location.label),
8215 ISEQ_TYPE_RESCUE, line);
8217 lstart->rescued = LABEL_RESCUE_BEG;
8218 lend->rescued = LABEL_RESCUE_END;
8219 ADD_LABEL(ret, lstart);
8221 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
8222 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
8224 CHECK(COMPILE(ret, "rescue head", RNODE_RESCUE(node)->nd_head));
8226 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
8228 ADD_LABEL(ret, lend);
8229 if (RNODE_RESCUE(node)->nd_else) {
8230 ADD_INSN(ret, line_node, pop);
8231 CHECK(COMPILE(ret, "rescue else", RNODE_RESCUE(node)->nd_else));
8233 ADD_INSN(ret, line_node, nop);
8234 ADD_LABEL(ret, lcont);
8236 if (popped) {
8237 ADD_INSN(ret, line_node, pop);
8240 /* register catch entry */
8241 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
8242 ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
8243 return COMPILE_OK;
8246 static int
8247 compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8249 const int line = nd_line(node);
8250 const NODE *line_node = node;
8251 const NODE *resq = node;
8252 const NODE *narg;
8253 LABEL *label_miss, *label_hit;
8255 while (resq) {
8256 label_miss = NEW_LABEL(line);
8257 label_hit = NEW_LABEL(line);
8259 narg = RNODE_RESBODY(resq)->nd_args;
8260 if (narg) {
8261 switch (nd_type(narg)) {
8262 case NODE_LIST:
8263 while (narg) {
8264 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8265 CHECK(COMPILE(ret, "rescue arg", RNODE_LIST(narg)->nd_head));
8266 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8267 ADD_INSNL(ret, line_node, branchif, label_hit);
8268 narg = RNODE_LIST(narg)->nd_next;
8270 break;
8271 case NODE_SPLAT:
8272 case NODE_ARGSCAT:
8273 case NODE_ARGSPUSH:
8274 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8275 CHECK(COMPILE(ret, "rescue/cond splat", narg));
8276 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
8277 ADD_INSNL(ret, line_node, branchif, label_hit);
8278 break;
8279 default:
8280 UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
8283 else {
8284 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8285 ADD_INSN1(ret, line_node, putobject, rb_eStandardError);
8286 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8287 ADD_INSNL(ret, line_node, branchif, label_hit);
8289 ADD_INSNL(ret, line_node, jump, label_miss);
8290 ADD_LABEL(ret, label_hit);
8291 ADD_TRACE(ret, RUBY_EVENT_RESCUE);
8293 if (nd_type(RNODE_RESBODY(resq)->nd_body) == NODE_BEGIN && RNODE_BEGIN(RNODE_RESBODY(resq)->nd_body)->nd_body == NULL) {
8294 // empty body
8295 ADD_SYNTHETIC_INSN(ret, nd_line(RNODE_RESBODY(resq)->nd_body), -1, putnil);
8297 else {
8298 CHECK(COMPILE(ret, "resbody body", RNODE_RESBODY(resq)->nd_body));
8301 if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
8302 ADD_INSN(ret, line_node, nop);
8304 ADD_INSN(ret, line_node, leave);
8305 ADD_LABEL(ret, label_miss);
8306 resq = RNODE_RESBODY(resq)->nd_next;
8308 return COMPILE_OK;
8311 static int
8312 compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8314 const int line = nd_line(node);
8315 const NODE *line_node = node;
8316 DECL_ANCHOR(ensr);
8317 const rb_iseq_t *ensure = NEW_CHILD_ISEQ(RNODE_ENSURE(node)->nd_ensr,
8318 rb_str_concat(rb_str_new2 ("ensure in "), ISEQ_BODY(iseq)->location.label),
8319 ISEQ_TYPE_ENSURE, line);
8320 LABEL *lstart = NEW_LABEL(line);
8321 LABEL *lend = NEW_LABEL(line);
8322 LABEL *lcont = NEW_LABEL(line);
8323 LINK_ELEMENT *last;
8324 int last_leave = 0;
8325 struct ensure_range er;
8326 struct iseq_compile_data_ensure_node_stack enl;
8327 struct ensure_range *erange;
8329 INIT_ANCHOR(ensr);
8330 CHECK(COMPILE_POPPED(ensr, "ensure ensr", RNODE_ENSURE(node)->nd_ensr));
8331 last = ensr->last;
8332 last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
8334 er.begin = lstart;
8335 er.end = lend;
8336 er.next = 0;
8337 push_ensure_entry(iseq, &enl, &er, RNODE_ENSURE(node)->nd_ensr);
8339 ADD_LABEL(ret, lstart);
8340 CHECK(COMPILE_(ret, "ensure head", RNODE_ENSURE(node)->nd_head, (popped | last_leave)));
8341 ADD_LABEL(ret, lend);
8342 ADD_SEQ(ret, ensr);
8343 if (!popped && last_leave) ADD_INSN(ret, line_node, putnil);
8344 ADD_LABEL(ret, lcont);
8345 if (last_leave) ADD_INSN(ret, line_node, pop);
8347 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
8348 if (lstart->link.next != &lend->link) {
8349 while (erange) {
8350 ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
8351 ensure, lcont);
8352 erange = erange->next;
8356 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
8357 return COMPILE_OK;
8360 static int
8361 compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8363 const NODE *line_node = node;
8365 if (iseq) {
8366 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8367 const rb_iseq_t *is = iseq;
8368 enum rb_iseq_type t = type;
8369 const NODE *retval = RNODE_RETURN(node)->nd_stts;
8370 LABEL *splabel = 0;
8372 while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
8373 if (!(is = ISEQ_BODY(is)->parent_iseq)) break;
8374 t = ISEQ_BODY(is)->type;
8376 switch (t) {
8377 case ISEQ_TYPE_TOP:
8378 case ISEQ_TYPE_MAIN:
8379 if (retval) {
8380 rb_warn("argument of top-level return is ignored");
8382 if (is == iseq) {
8383 /* plain top-level, leave directly */
8384 type = ISEQ_TYPE_METHOD;
8386 break;
8387 default:
8388 break;
8391 if (type == ISEQ_TYPE_METHOD) {
8392 splabel = NEW_LABEL(0);
8393 ADD_LABEL(ret, splabel);
8394 ADD_ADJUST(ret, line_node, 0);
8397 CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
8399 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8400 add_ensure_iseq(ret, iseq, 1);
8401 ADD_TRACE(ret, RUBY_EVENT_RETURN);
8402 ADD_INSN(ret, line_node, leave);
8403 ADD_ADJUST_RESTORE(ret, splabel);
8405 if (!popped) {
8406 ADD_INSN(ret, line_node, putnil);
8409 else {
8410 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETURN));
8411 if (popped) {
8412 ADD_INSN(ret, line_node, pop);
8416 return COMPILE_OK;
8419 static int
8420 compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8422 CHECK(COMPILE_(ret, "nd_body", node, popped));
8424 if (!popped && !all_string_result_p(node)) {
8425 const NODE *line_node = node;
8426 const unsigned int flag = VM_CALL_FCALL;
8428 // Note, this dup could be removed if we are willing to change anytostring. It pops
8429 // two VALUEs off the stack when it could work by replacing the top most VALUE.
8430 ADD_INSN(ret, line_node, dup);
8431 ADD_INSN1(ret, line_node, objtostring, new_callinfo(iseq, idTo_s, 0, flag, NULL, FALSE));
8432 ADD_INSN(ret, line_node, anytostring);
8434 return COMPILE_OK;
8437 static void
8438 compile_lvar(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *line_node, ID id)
8440 int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
8442 debugs("id: %s idx: %d\n", rb_id2name(id), idx);
8443 ADD_GETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
8446 static LABEL *
8447 qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, const NODE *node, const NODE *line_node)
8449 LABEL *else_label = NEW_LABEL(nd_line(line_node));
8450 VALUE br = 0;
8452 br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&.");
8453 *branches = br;
8454 ADD_INSN(recv, line_node, dup);
8455 ADD_INSNL(recv, line_node, branchnil, else_label);
8456 add_trace_branch_coverage(iseq, recv, nd_code_loc(node), nd_node_id(node), 0, "then", br);
8457 return else_label;
8460 static void
8461 qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VALUE branches, const NODE *node, const NODE *line_node)
8463 LABEL *end_label;
8464 if (!else_label) return;
8465 end_label = NEW_LABEL(nd_line(line_node));
8466 ADD_INSNL(ret, line_node, jump, end_label);
8467 ADD_LABEL(ret, else_label);
8468 add_trace_branch_coverage(iseq, ret, nd_code_loc(node), nd_node_id(node), 1, "else", branches);
8469 ADD_LABEL(ret, end_label);
8472 static int
8473 compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped)
8475 /* optimization shortcut
8476 * "literal".freeze -> opt_str_freeze("literal")
8478 if (get_nd_recv(node) &&
8479 (nd_type_p(get_nd_recv(node), NODE_STR) || nd_type_p(get_nd_recv(node), NODE_FILE)) &&
8480 (get_node_call_nd_mid(node) == idFreeze || get_node_call_nd_mid(node) == idUMinus) &&
8481 get_nd_args(node) == NULL &&
8482 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
8483 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
8484 VALUE str = get_string_value(get_nd_recv(node));
8485 if (get_node_call_nd_mid(node) == idUMinus) {
8486 ADD_INSN2(ret, line_node, opt_str_uminus, str,
8487 new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
8489 else {
8490 ADD_INSN2(ret, line_node, opt_str_freeze, str,
8491 new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
8493 RB_OBJ_WRITTEN(iseq, Qundef, str);
8494 if (popped) {
8495 ADD_INSN(ret, line_node, pop);
8497 return TRUE;
8499 /* optimization shortcut
8500 * obj["literal"] -> opt_aref_with(obj, "literal")
8502 if (get_node_call_nd_mid(node) == idAREF && !private_recv_p(node) && get_nd_args(node) &&
8503 nd_type_p(get_nd_args(node), NODE_LIST) && RNODE_LIST(get_nd_args(node))->as.nd_alen == 1 &&
8504 (nd_type_p(RNODE_LIST(get_nd_args(node))->nd_head, NODE_STR) || nd_type_p(RNODE_LIST(get_nd_args(node))->nd_head, NODE_FILE)) &&
8505 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
8506 !frozen_string_literal_p(iseq) &&
8507 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
8508 VALUE str = get_string_value(RNODE_LIST(get_nd_args(node))->nd_head);
8509 CHECK(COMPILE(ret, "recv", get_nd_recv(node)));
8510 ADD_INSN2(ret, line_node, opt_aref_with, str,
8511 new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE));
8512 RB_OBJ_WRITTEN(iseq, Qundef, str);
8513 if (popped) {
8514 ADD_INSN(ret, line_node, pop);
8516 return TRUE;
8518 return FALSE;
8521 static int
8522 iseq_has_builtin_function_table(const rb_iseq_t *iseq)
8524 return ISEQ_COMPILE_DATA(iseq)->builtin_function_table != NULL;
8527 static const struct rb_builtin_function *
8528 iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name)
8530 int i;
8531 const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
8532 for (i=0; table[i].index != -1; i++) {
8533 if (strcmp(table[i].name, name) == 0) {
8534 return &table[i];
8537 return NULL;
8540 static const char *
8541 iseq_builtin_function_name(const enum node_type type, const NODE *recv, ID mid)
8543 const char *name = rb_id2name(mid);
8544 static const char prefix[] = "__builtin_";
8545 const size_t prefix_len = sizeof(prefix) - 1;
8547 switch (type) {
8548 case NODE_CALL:
8549 if (recv) {
8550 switch (nd_type(recv)) {
8551 case NODE_VCALL:
8552 if (RNODE_VCALL(recv)->nd_mid == rb_intern("__builtin")) {
8553 return name;
8555 break;
8556 case NODE_CONST:
8557 if (RNODE_CONST(recv)->nd_vid == rb_intern("Primitive")) {
8558 return name;
8560 break;
8561 default: break;
8564 break;
8565 case NODE_VCALL:
8566 case NODE_FCALL:
8567 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
8568 return &name[prefix_len];
8570 break;
8571 default: break;
8573 return NULL;
8576 static int
8577 delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args, unsigned int *pstart_index)
8580 if (argc == 0) {
8581 *pstart_index = 0;
8582 return TRUE;
8584 else if (argc <= ISEQ_BODY(iseq)->local_table_size) {
8585 unsigned int start=0;
8587 // local_table: [p1, p2, p3, l1, l2, l3]
8588 // arguments: [p3, l1, l2] -> 2
8589 for (start = 0;
8590 argc + start <= ISEQ_BODY(iseq)->local_table_size;
8591 start++) {
8592 const LINK_ELEMENT *elem = FIRST_ELEMENT(args);
8594 for (unsigned int i=start; i-start<argc; i++) {
8595 if (IS_INSN(elem) &&
8596 INSN_OF(elem) == BIN(getlocal)) {
8597 int local_index = FIX2INT(OPERAND_AT(elem, 0));
8598 int local_level = FIX2INT(OPERAND_AT(elem, 1));
8600 if (local_level == 0) {
8601 unsigned int index = ISEQ_BODY(iseq)->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1);
8602 if (0) { // for debug
8603 fprintf(stderr, "lvar:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n",
8604 rb_id2name(ISEQ_BODY(iseq)->local_table[i]), i,
8605 rb_id2name(ISEQ_BODY(iseq)->local_table[index]), index,
8606 local_index, (int)ISEQ_BODY(iseq)->local_table_size);
8608 if (i == index) {
8609 elem = elem->next;
8610 continue; /* for */
8612 else {
8613 goto next;
8616 else {
8617 goto fail; // level != 0 is unsupported
8620 else {
8621 goto fail; // insn is not a getlocal
8624 goto success;
8625 next:;
8627 fail:
8628 return FALSE;
8629 success:
8630 *pstart_index = start;
8631 return TRUE;
8633 else {
8634 return FALSE;
8638 // Compile Primitive.attr! :leaf, ...
8639 static int
8640 compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
8642 VALUE symbol;
8643 VALUE string;
8644 if (!node) goto no_arg;
8645 while (node) {
8646 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
8647 const NODE *next = RNODE_LIST(node)->nd_next;
8649 node = RNODE_LIST(node)->nd_head;
8650 if (!node) goto no_arg;
8651 switch (nd_type(node)) {
8652 case NODE_SYM:
8653 symbol = rb_node_sym_string_val(node);
8654 break;
8655 default:
8656 goto bad_arg;
8659 if (!SYMBOL_P(symbol)) goto non_symbol_arg;
8661 string = rb_sym_to_s(symbol);
8662 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
8663 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
8665 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
8666 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
8668 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
8669 iseq_set_use_block(iseq);
8671 else {
8672 goto unknown_arg;
8674 node = next;
8676 return COMPILE_OK;
8677 no_arg:
8678 COMPILE_ERROR(ERROR_ARGS "attr!: no argument");
8679 return COMPILE_NG;
8680 non_symbol_arg:
8681 COMPILE_ERROR(ERROR_ARGS "non symbol argument to attr!: %s", rb_builtin_class_name(symbol));
8682 return COMPILE_NG;
8683 unknown_arg:
8684 COMPILE_ERROR(ERROR_ARGS "unknown argument to attr!: %s", RSTRING_PTR(string));
8685 return COMPILE_NG;
8686 bad_arg:
8687 UNKNOWN_NODE("attr!", node, COMPILE_NG);
8690 static int
8691 compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, const NODE *line_node, int popped)
8693 VALUE name;
8695 if (!node) goto no_arg;
8696 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
8697 if (RNODE_LIST(node)->nd_next) goto too_many_arg;
8698 node = RNODE_LIST(node)->nd_head;
8699 if (!node) goto no_arg;
8700 switch (nd_type(node)) {
8701 case NODE_SYM:
8702 name = rb_node_sym_string_val(node);
8703 break;
8704 default:
8705 goto bad_arg;
8707 if (!SYMBOL_P(name)) goto non_symbol_arg;
8708 if (!popped) {
8709 compile_lvar(iseq, ret, line_node, SYM2ID(name));
8711 return COMPILE_OK;
8712 no_arg:
8713 COMPILE_ERROR(ERROR_ARGS "arg!: no argument");
8714 return COMPILE_NG;
8715 too_many_arg:
8716 COMPILE_ERROR(ERROR_ARGS "arg!: too many argument");
8717 return COMPILE_NG;
8718 non_symbol_arg:
8719 COMPILE_ERROR(ERROR_ARGS "non symbol argument to arg!: %s",
8720 rb_builtin_class_name(name));
8721 return COMPILE_NG;
8722 bad_arg:
8723 UNKNOWN_NODE("arg!", node, COMPILE_NG);
8726 static NODE *
8727 mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node)
8729 const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node;
8730 if (nd_type(node) == NODE_IF && RNODE_IF(node)->nd_cond == cond_node) {
8731 return RNODE_IF(node)->nd_body;
8733 else {
8734 rb_bug("mandatory_node: can't find mandatory node");
8738 static int
8739 compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const NODE *line_node)
8741 // arguments
8742 struct rb_args_info args = {
8743 .pre_args_num = ISEQ_BODY(iseq)->param.lead_num,
8745 rb_node_args_t args_node;
8746 rb_node_init(RNODE(&args_node), NODE_ARGS);
8747 args_node.nd_ainfo = args;
8749 // local table without non-mandatory parameters
8750 const int skip_local_size = ISEQ_BODY(iseq)->param.size - ISEQ_BODY(iseq)->param.lead_num;
8751 const int table_size = ISEQ_BODY(iseq)->local_table_size - skip_local_size;
8753 VALUE idtmp = 0;
8754 rb_ast_id_table_t *tbl = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
8755 tbl->size = table_size;
8757 int i;
8759 // lead parameters
8760 for (i=0; i<ISEQ_BODY(iseq)->param.lead_num; i++) {
8761 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i];
8763 // local variables
8764 for (; i<table_size; i++) {
8765 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i + skip_local_size];
8768 rb_node_scope_t scope_node;
8769 rb_node_init(RNODE(&scope_node), NODE_SCOPE);
8770 scope_node.nd_tbl = tbl;
8771 scope_node.nd_body = mandatory_node(iseq, node);
8772 scope_node.nd_args = &args_node;
8774 VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node));
8776 ISEQ_BODY(iseq)->mandatory_only_iseq =
8777 rb_iseq_new_with_opt(ast_value, rb_iseq_base_label(iseq),
8778 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
8779 nd_line(line_node), NULL, 0,
8780 ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option,
8781 ISEQ_BODY(iseq)->variable.script_lines);
8783 ALLOCV_END(idtmp);
8784 return COMPILE_OK;
8787 static int
8788 compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped,
8789 const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func)
8791 NODE *args_node = get_nd_args(node);
8793 if (parent_block != NULL) {
8794 COMPILE_ERROR(ERROR_ARGS_AT(line_node) "should not call builtins here.");
8795 return COMPILE_NG;
8797 else {
8798 # define BUILTIN_INLINE_PREFIX "_bi"
8799 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
8800 bool cconst = false;
8801 retry:;
8802 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
8804 if (bf == NULL) {
8805 if (strcmp("cstmt!", builtin_func) == 0 ||
8806 strcmp("cexpr!", builtin_func) == 0) {
8807 // ok
8809 else if (strcmp("cconst!", builtin_func) == 0) {
8810 cconst = true;
8812 else if (strcmp("cinit!", builtin_func) == 0) {
8813 // ignore
8814 return COMPILE_OK;
8816 else if (strcmp("attr!", builtin_func) == 0) {
8817 return compile_builtin_attr(iseq, args_node);
8819 else if (strcmp("arg!", builtin_func) == 0) {
8820 return compile_builtin_arg(iseq, ret, args_node, line_node, popped);
8822 else if (strcmp("mandatory_only?", builtin_func) == 0) {
8823 if (popped) {
8824 rb_bug("mandatory_only? should be in if condition");
8826 else if (!LIST_INSN_SIZE_ZERO(ret)) {
8827 rb_bug("mandatory_only? should be put on top");
8830 ADD_INSN1(ret, line_node, putobject, Qfalse);
8831 return compile_builtin_mandatory_only_method(iseq, node, line_node);
8833 else if (1) {
8834 rb_bug("can't find builtin function:%s", builtin_func);
8836 else {
8837 COMPILE_ERROR(ERROR_ARGS "can't find builtin function:%s", builtin_func);
8838 return COMPILE_NG;
8841 int inline_index = nd_line(node);
8842 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
8843 builtin_func = inline_func;
8844 args_node = NULL;
8845 goto retry;
8848 if (cconst) {
8849 typedef VALUE(*builtin_func0)(void *, VALUE);
8850 VALUE const_val = (*(builtin_func0)bf->func_ptr)(NULL, Qnil);
8851 ADD_INSN1(ret, line_node, putobject, const_val);
8852 return COMPILE_OK;
8855 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
8857 unsigned int flag = 0;
8858 struct rb_callinfo_kwarg *keywords = NULL;
8859 VALUE argc = setup_args(iseq, args, args_node, &flag, &keywords);
8861 if (FIX2INT(argc) != bf->argc) {
8862 COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
8863 builtin_func, bf->argc, FIX2INT(argc));
8864 return COMPILE_NG;
8867 unsigned int start_index;
8868 if (delegate_call_p(iseq, FIX2INT(argc), args, &start_index)) {
8869 ADD_INSN2(ret, line_node, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
8871 else {
8872 ADD_SEQ(ret, args);
8873 ADD_INSN1(ret, line_node, invokebuiltin, bf);
8876 if (popped) ADD_INSN(ret, line_node, pop);
8877 return COMPILE_OK;
8881 static int
8882 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)
8884 /* call: obj.method(...)
8885 * fcall: func(...)
8886 * vcall: func
8888 DECL_ANCHOR(recv);
8889 DECL_ANCHOR(args);
8890 ID mid = get_node_call_nd_mid(node);
8891 VALUE argc;
8892 unsigned int flag = 0;
8893 struct rb_callinfo_kwarg *keywords = NULL;
8894 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8895 LABEL *else_label = NULL;
8896 VALUE branches = Qfalse;
8898 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
8900 INIT_ANCHOR(recv);
8901 INIT_ANCHOR(args);
8902 #if OPT_SUPPORT_JOKE
8903 if (nd_type_p(node, NODE_VCALL)) {
8904 ID id_bitblt;
8905 ID id_answer;
8907 CONST_ID(id_bitblt, "bitblt");
8908 CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
8910 if (mid == id_bitblt) {
8911 ADD_INSN(ret, line_node, bitblt);
8912 return COMPILE_OK;
8914 else if (mid == id_answer) {
8915 ADD_INSN(ret, line_node, answer);
8916 return COMPILE_OK;
8919 /* only joke */
8921 ID goto_id;
8922 ID label_id;
8924 CONST_ID(goto_id, "__goto__");
8925 CONST_ID(label_id, "__label__");
8927 if (nd_type_p(node, NODE_FCALL) &&
8928 (mid == goto_id || mid == label_id)) {
8929 LABEL *label;
8930 st_data_t data;
8931 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
8932 VALUE label_name;
8934 if (!labels_table) {
8935 labels_table = st_init_numtable();
8936 ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
8939 COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
8940 return COMPILE_NG;
8943 if (mid == goto_id) {
8944 ADD_INSNL(ret, line_node, jump, label);
8946 else {
8947 ADD_LABEL(ret, label);
8949 return COMPILE_OK;
8952 #endif
8954 const char *builtin_func;
8955 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
8956 (builtin_func = iseq_builtin_function_name(type, get_nd_recv(node), mid)) != NULL) {
8957 return compile_builtin_function_call(iseq, ret, node, line_node, popped, parent_block, args, builtin_func);
8960 /* receiver */
8961 if (!assume_receiver) {
8962 if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
8963 int idx, level;
8965 if (mid == idCall &&
8966 nd_type_p(get_nd_recv(node), NODE_LVAR) &&
8967 iseq_block_param_id_p(iseq, RNODE_LVAR(get_nd_recv(node))->nd_vid, &idx, &level)) {
8968 ADD_INSN2(recv, get_nd_recv(node), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
8970 else if (private_recv_p(node)) {
8971 ADD_INSN(recv, node, putself);
8972 flag |= VM_CALL_FCALL;
8974 else {
8975 CHECK(COMPILE(recv, "recv", get_nd_recv(node)));
8978 if (type == NODE_QCALL) {
8979 else_label = qcall_branch_start(iseq, recv, &branches, node, line_node);
8982 else if (type == NODE_FCALL || type == NODE_VCALL) {
8983 ADD_CALL_RECEIVER(recv, line_node);
8987 /* args */
8988 if (type != NODE_VCALL) {
8989 argc = setup_args(iseq, args, get_nd_args(node), &flag, &keywords);
8990 CHECK(!NIL_P(argc));
8992 else {
8993 argc = INT2FIX(0);
8996 ADD_SEQ(ret, recv);
8997 ADD_SEQ(ret, args);
8999 debugp_param("call args argc", argc);
9000 debugp_param("call method", ID2SYM(mid));
9002 switch ((int)type) {
9003 case NODE_VCALL:
9004 flag |= VM_CALL_VCALL;
9005 /* VCALL is funcall, so fall through */
9006 case NODE_FCALL:
9007 flag |= VM_CALL_FCALL;
9010 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
9011 ADD_INSN(ret, line_node, splatkw);
9013 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
9015 qcall_branch_end(iseq, ret, else_label, branches, node, line_node);
9016 if (popped) {
9017 ADD_INSN(ret, line_node, pop);
9019 return COMPILE_OK;
9022 static int
9023 compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9025 const int line = nd_line(node);
9026 VALUE argc;
9027 unsigned int flag = 0;
9028 int asgnflag = 0;
9029 ID id = RNODE_OP_ASGN1(node)->nd_mid;
9032 * a[x] (op)= y
9034 * nil # nil
9035 * eval a # nil a
9036 * eval x # nil a x
9037 * dupn 2 # nil a x a x
9038 * send :[] # nil a x a[x]
9039 * eval y # nil a x a[x] y
9040 * send op # nil a x ret
9041 * setn 3 # ret a x ret
9042 * send []= # ret ?
9043 * pop # ret
9047 * nd_recv[nd_args->nd_body] (nd_mid)= nd_args->nd_head;
9048 * NODE_OP_ASGN nd_recv
9049 * nd_args->nd_head
9050 * nd_args->nd_body
9051 * nd_mid
9054 if (!popped) {
9055 ADD_INSN(ret, node, putnil);
9057 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node, RNODE_OP_ASGN1(node)->nd_recv);
9058 CHECK(asgnflag != -1);
9059 switch (nd_type(RNODE_OP_ASGN1(node)->nd_index)) {
9060 case NODE_ZLIST:
9061 argc = INT2FIX(0);
9062 break;
9063 default:
9064 argc = setup_args(iseq, ret, RNODE_OP_ASGN1(node)->nd_index, &flag, NULL);
9065 CHECK(!NIL_P(argc));
9067 int dup_argn = FIX2INT(argc) + 1;
9068 ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn));
9069 flag |= asgnflag;
9070 ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_ARGS_SPLAT_MUT), NULL);
9072 if (id == idOROP || id == idANDOP) {
9073 /* a[x] ||= y or a[x] &&= y
9075 unless/if a[x]
9076 a[x]= y
9077 else
9081 LABEL *label = NEW_LABEL(line);
9082 LABEL *lfin = NEW_LABEL(line);
9084 ADD_INSN(ret, node, dup);
9085 if (id == idOROP) {
9086 ADD_INSNL(ret, node, branchif, label);
9088 else { /* idANDOP */
9089 ADD_INSNL(ret, node, branchunless, label);
9091 ADD_INSN(ret, node, pop);
9093 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9094 if (!popped) {
9095 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9097 if (flag & VM_CALL_ARGS_SPLAT) {
9098 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9099 ADD_INSN(ret, node, swap);
9100 ADD_INSN1(ret, node, splatarray, Qtrue);
9101 ADD_INSN(ret, node, swap);
9102 flag |= VM_CALL_ARGS_SPLAT_MUT;
9104 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9105 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9107 else {
9108 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9110 ADD_INSN(ret, node, pop);
9111 ADD_INSNL(ret, node, jump, lfin);
9112 ADD_LABEL(ret, label);
9113 if (!popped) {
9114 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9116 ADD_INSN1(ret, node, adjuststack, INT2FIX(dup_argn+1));
9117 ADD_LABEL(ret, lfin);
9119 else {
9120 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9121 ADD_SEND(ret, node, id, INT2FIX(1));
9122 if (!popped) {
9123 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9125 if (flag & VM_CALL_ARGS_SPLAT) {
9126 if (flag & VM_CALL_KW_SPLAT) {
9127 ADD_INSN1(ret, node, topn, INT2FIX(2));
9128 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9129 ADD_INSN1(ret, node, splatarray, Qtrue);
9130 flag |= VM_CALL_ARGS_SPLAT_MUT;
9132 ADD_INSN(ret, node, swap);
9133 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9134 ADD_INSN1(ret, node, setn, INT2FIX(2));
9135 ADD_INSN(ret, node, pop);
9137 else {
9138 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9139 ADD_INSN(ret, node, swap);
9140 ADD_INSN1(ret, node, splatarray, Qtrue);
9141 ADD_INSN(ret, node, swap);
9142 flag |= VM_CALL_ARGS_SPLAT_MUT;
9144 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9146 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9148 else {
9149 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9151 ADD_INSN(ret, node, pop);
9153 return COMPILE_OK;
9156 static int
9157 compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9159 const int line = nd_line(node);
9160 ID atype = RNODE_OP_ASGN2(node)->nd_mid;
9161 ID vid = RNODE_OP_ASGN2(node)->nd_vid, aid = rb_id_attrset(vid);
9162 int asgnflag;
9163 LABEL *lfin = NEW_LABEL(line);
9164 LABEL *lcfin = NEW_LABEL(line);
9165 LABEL *lskip = 0;
9167 class C; attr_accessor :c; end
9168 r = C.new
9169 r.a &&= v # asgn2
9171 eval r # r
9172 dup # r r
9173 eval r.a # r o
9175 # or
9176 dup # r o o
9177 if lcfin # r o
9178 pop # r
9179 eval v # r v
9180 swap # v r
9181 topn 1 # v r v
9182 send a= # v ?
9183 jump lfin # v ?
9185 lcfin: # r o
9186 swap # o r
9188 lfin: # o ?
9189 pop # o
9191 # or (popped)
9192 if lcfin # r
9193 eval v # r v
9194 send a= # ?
9195 jump lfin # ?
9197 lcfin: # r
9199 lfin: # ?
9200 pop #
9202 # and
9203 dup # r o o
9204 unless lcfin
9205 pop # r
9206 eval v # r v
9207 swap # v r
9208 topn 1 # v r v
9209 send a= # v ?
9210 jump lfin # v ?
9212 # others
9213 eval v # r o v
9214 send ?? # r w
9215 send a= # w
9219 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node, RNODE_OP_ASGN2(node)->nd_recv);
9220 CHECK(asgnflag != -1);
9221 if (RNODE_OP_ASGN2(node)->nd_aid) {
9222 lskip = NEW_LABEL(line);
9223 ADD_INSN(ret, node, dup);
9224 ADD_INSNL(ret, node, branchnil, lskip);
9226 ADD_INSN(ret, node, dup);
9227 ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag));
9229 if (atype == idOROP || atype == idANDOP) {
9230 if (!popped) {
9231 ADD_INSN(ret, node, dup);
9233 if (atype == idOROP) {
9234 ADD_INSNL(ret, node, branchif, lcfin);
9236 else { /* idANDOP */
9237 ADD_INSNL(ret, node, branchunless, lcfin);
9239 if (!popped) {
9240 ADD_INSN(ret, node, pop);
9242 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9243 if (!popped) {
9244 ADD_INSN(ret, node, swap);
9245 ADD_INSN1(ret, node, topn, INT2FIX(1));
9247 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9248 ADD_INSNL(ret, node, jump, lfin);
9250 ADD_LABEL(ret, lcfin);
9251 if (!popped) {
9252 ADD_INSN(ret, node, swap);
9255 ADD_LABEL(ret, lfin);
9257 else {
9258 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9259 ADD_SEND(ret, node, atype, INT2FIX(1));
9260 if (!popped) {
9261 ADD_INSN(ret, node, swap);
9262 ADD_INSN1(ret, node, topn, INT2FIX(1));
9264 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9266 if (lskip && popped) {
9267 ADD_LABEL(ret, lskip);
9269 ADD_INSN(ret, node, pop);
9270 if (lskip && !popped) {
9271 ADD_LABEL(ret, lskip);
9273 return COMPILE_OK;
9276 static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value);
9278 static int
9279 compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9281 const int line = nd_line(node);
9282 LABEL *lfin = 0;
9283 LABEL *lassign = 0;
9284 ID mid;
9286 switch (nd_type(RNODE_OP_CDECL(node)->nd_head)) {
9287 case NODE_COLON3:
9288 ADD_INSN1(ret, node, putobject, rb_cObject);
9289 break;
9290 case NODE_COLON2:
9291 CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", RNODE_COLON2(RNODE_OP_CDECL(node)->nd_head)->nd_head));
9292 break;
9293 default:
9294 COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
9295 ruby_node_name(nd_type(RNODE_OP_CDECL(node)->nd_head)));
9296 return COMPILE_NG;
9298 mid = get_node_colon_nd_mid(RNODE_OP_CDECL(node)->nd_head);
9299 /* cref */
9300 if (RNODE_OP_CDECL(node)->nd_aid == idOROP) {
9301 lassign = NEW_LABEL(line);
9302 ADD_INSN(ret, node, dup); /* cref cref */
9303 ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
9304 ID2SYM(mid), Qtrue); /* cref bool */
9305 ADD_INSNL(ret, node, branchunless, lassign); /* cref */
9307 ADD_INSN(ret, node, dup); /* cref cref */
9308 ADD_INSN1(ret, node, putobject, Qtrue);
9309 ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */
9311 if (RNODE_OP_CDECL(node)->nd_aid == idOROP || RNODE_OP_CDECL(node)->nd_aid == idANDOP) {
9312 lfin = NEW_LABEL(line);
9313 if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
9314 if (RNODE_OP_CDECL(node)->nd_aid == idOROP)
9315 ADD_INSNL(ret, node, branchif, lfin);
9316 else /* idANDOP */
9317 ADD_INSNL(ret, node, branchunless, lfin);
9318 /* cref [obj] */
9319 if (!popped) ADD_INSN(ret, node, pop); /* cref */
9320 if (lassign) ADD_LABEL(ret, lassign);
9321 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9322 /* cref value */
9323 if (popped)
9324 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
9325 else {
9326 ADD_INSN1(ret, node, dupn, INT2FIX(2)); /* cref value cref value */
9327 ADD_INSN(ret, node, swap); /* cref value value cref */
9329 ADD_INSN1(ret, node, setconstant, ID2SYM(mid)); /* cref [value] */
9330 ADD_LABEL(ret, lfin); /* cref [value] */
9331 if (!popped) ADD_INSN(ret, node, swap); /* [value] cref */
9332 ADD_INSN(ret, node, pop); /* [value] */
9334 else {
9335 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9336 /* cref obj value */
9337 ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1));
9338 /* cref value */
9339 ADD_INSN(ret, node, swap); /* value cref */
9340 if (!popped) {
9341 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* value cref value */
9342 ADD_INSN(ret, node, swap); /* value value cref */
9344 ADD_INSN1(ret, node, setconstant, ID2SYM(mid));
9346 return COMPILE_OK;
9349 static int
9350 compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
9352 const int line = nd_line(node);
9353 LABEL *lfin = NEW_LABEL(line);
9354 LABEL *lassign;
9356 if (type == NODE_OP_ASGN_OR && !nd_type_p(RNODE_OP_ASGN_OR(node)->nd_head, NODE_IVAR)) {
9357 LABEL *lfinish[2];
9358 lfinish[0] = lfin;
9359 lfinish[1] = 0;
9360 defined_expr(iseq, ret, RNODE_OP_ASGN_OR(node)->nd_head, lfinish, Qfalse);
9361 lassign = lfinish[1];
9362 if (!lassign) {
9363 lassign = NEW_LABEL(line);
9365 ADD_INSNL(ret, node, branchunless, lassign);
9367 else {
9368 lassign = NEW_LABEL(line);
9371 CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", RNODE_OP_ASGN_OR(node)->nd_head));
9373 if (!popped) {
9374 ADD_INSN(ret, node, dup);
9377 if (type == NODE_OP_ASGN_AND) {
9378 ADD_INSNL(ret, node, branchunless, lfin);
9380 else {
9381 ADD_INSNL(ret, node, branchif, lfin);
9384 if (!popped) {
9385 ADD_INSN(ret, node, pop);
9388 ADD_LABEL(ret, lassign);
9389 CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", RNODE_OP_ASGN_OR(node)->nd_value, popped));
9390 ADD_LABEL(ret, lfin);
9391 return COMPILE_OK;
9394 static int
9395 compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
9397 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
9398 DECL_ANCHOR(args);
9399 int argc;
9400 unsigned int flag = 0;
9401 struct rb_callinfo_kwarg *keywords = NULL;
9402 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9403 int use_block = 1;
9405 INIT_ANCHOR(args);
9406 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
9408 if (type == NODE_SUPER) {
9409 VALUE vargc = setup_args(iseq, args, RNODE_SUPER(node)->nd_args, &flag, &keywords);
9410 CHECK(!NIL_P(vargc));
9411 argc = FIX2INT(vargc);
9412 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
9413 ADD_INSN(args, node, splatkw);
9416 if (flag & VM_CALL_ARGS_BLOCKARG) {
9417 use_block = 0;
9420 else {
9421 /* NODE_ZSUPER */
9422 int i;
9423 const rb_iseq_t *liseq = body->local_iseq;
9424 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(liseq);
9425 const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
9426 int lvar_level = get_lvar_level(iseq);
9428 argc = local_body->param.lead_num;
9430 /* normal arguments */
9431 for (i = 0; i < local_body->param.lead_num; i++) {
9432 int idx = local_body->local_table_size - i;
9433 ADD_GETLOCAL(args, node, idx, lvar_level);
9436 if (local_body->param.flags.has_opt) {
9437 /* optional arguments */
9438 int j;
9439 for (j = 0; j < local_body->param.opt_num; j++) {
9440 int idx = local_body->local_table_size - (i + j);
9441 ADD_GETLOCAL(args, node, idx, lvar_level);
9443 i += j;
9444 argc = i;
9446 if (local_body->param.flags.has_rest) {
9447 /* rest argument */
9448 int idx = local_body->local_table_size - local_body->param.rest_start;
9449 ADD_GETLOCAL(args, node, idx, lvar_level);
9450 ADD_INSN1(args, node, splatarray, RBOOL(local_body->param.flags.has_post));
9452 argc = local_body->param.rest_start + 1;
9453 flag |= VM_CALL_ARGS_SPLAT;
9455 if (local_body->param.flags.has_post) {
9456 /* post arguments */
9457 int post_len = local_body->param.post_num;
9458 int post_start = local_body->param.post_start;
9460 if (local_body->param.flags.has_rest) {
9461 int j;
9462 for (j=0; j<post_len; j++) {
9463 int idx = local_body->local_table_size - (post_start + j);
9464 ADD_GETLOCAL(args, node, idx, lvar_level);
9466 ADD_INSN1(args, node, pushtoarray, INT2FIX(j));
9467 flag |= VM_CALL_ARGS_SPLAT_MUT;
9468 /* argc is settled at above */
9470 else {
9471 int j;
9472 for (j=0; j<post_len; j++) {
9473 int idx = local_body->local_table_size - (post_start + j);
9474 ADD_GETLOCAL(args, node, idx, lvar_level);
9476 argc = post_len + post_start;
9480 if (local_body->param.flags.has_kw) { /* TODO: support keywords */
9481 int local_size = local_body->local_table_size;
9482 argc++;
9484 ADD_INSN1(args, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9486 if (local_body->param.flags.has_kwrest) {
9487 int idx = local_body->local_table_size - local_kwd->rest_start;
9488 ADD_GETLOCAL(args, node, idx, lvar_level);
9489 RUBY_ASSERT(local_kwd->num > 0);
9490 ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
9492 else {
9493 ADD_INSN1(args, node, newhash, INT2FIX(0));
9495 for (i = 0; i < local_kwd->num; ++i) {
9496 ID id = local_kwd->table[i];
9497 int idx = local_size - get_local_var_idx(liseq, id);
9498 ADD_INSN1(args, node, putobject, ID2SYM(id));
9499 ADD_GETLOCAL(args, node, idx, lvar_level);
9501 ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
9502 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
9504 else if (local_body->param.flags.has_kwrest) {
9505 int idx = local_body->local_table_size - local_kwd->rest_start;
9506 ADD_GETLOCAL(args, node, idx, lvar_level);
9507 argc++;
9508 flag |= VM_CALL_KW_SPLAT;
9512 if (use_block && parent_block == NULL) {
9513 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
9516 flag |= VM_CALL_SUPER | VM_CALL_FCALL;
9517 if (type == NODE_ZSUPER) flag |= VM_CALL_ZSUPER;
9518 ADD_INSN(ret, node, putself);
9519 ADD_SEQ(ret, args);
9520 ADD_INSN2(ret, node, invokesuper,
9521 new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL),
9522 parent_block);
9524 if (popped) {
9525 ADD_INSN(ret, node, pop);
9527 return COMPILE_OK;
9530 static int
9531 compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9533 DECL_ANCHOR(args);
9534 VALUE argc;
9535 unsigned int flag = 0;
9536 struct rb_callinfo_kwarg *keywords = NULL;
9538 INIT_ANCHOR(args);
9540 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
9541 case ISEQ_TYPE_TOP:
9542 case ISEQ_TYPE_MAIN:
9543 case ISEQ_TYPE_CLASS:
9544 COMPILE_ERROR(ERROR_ARGS "Invalid yield");
9545 return COMPILE_NG;
9546 default: /* valid */;
9549 if (RNODE_YIELD(node)->nd_head) {
9550 argc = setup_args(iseq, args, RNODE_YIELD(node)->nd_head, &flag, &keywords);
9551 CHECK(!NIL_P(argc));
9553 else {
9554 argc = INT2FIX(0);
9557 ADD_SEQ(ret, args);
9558 ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
9559 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
9561 if (popped) {
9562 ADD_INSN(ret, node, pop);
9565 int level = 0;
9566 const rb_iseq_t *tmp_iseq = iseq;
9567 for (; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++ ) {
9568 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
9570 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
9572 return COMPILE_OK;
9575 static int
9576 compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
9578 DECL_ANCHOR(recv);
9579 DECL_ANCHOR(val);
9581 INIT_ANCHOR(recv);
9582 INIT_ANCHOR(val);
9583 switch ((int)type) {
9584 case NODE_MATCH:
9585 ADD_INSN1(recv, node, putobject, rb_node_regx_string_val(node));
9586 ADD_INSN2(val, node, getspecial, INT2FIX(0),
9587 INT2FIX(0));
9588 break;
9589 case NODE_MATCH2:
9590 CHECK(COMPILE(recv, "receiver", RNODE_MATCH2(node)->nd_recv));
9591 CHECK(COMPILE(val, "value", RNODE_MATCH2(node)->nd_value));
9592 break;
9593 case NODE_MATCH3:
9594 CHECK(COMPILE(recv, "receiver", RNODE_MATCH3(node)->nd_value));
9595 CHECK(COMPILE(val, "value", RNODE_MATCH3(node)->nd_recv));
9596 break;
9599 ADD_SEQ(ret, recv);
9600 ADD_SEQ(ret, val);
9601 ADD_SEND(ret, node, idEqTilde, INT2FIX(1));
9603 if (nd_type_p(node, NODE_MATCH2) && RNODE_MATCH2(node)->nd_args) {
9604 compile_named_capture_assign(iseq, ret, RNODE_MATCH2(node)->nd_args);
9607 if (popped) {
9608 ADD_INSN(ret, node, pop);
9610 return COMPILE_OK;
9613 static int
9614 compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9616 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
9617 /* constant */
9618 VALUE segments;
9619 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache &&
9620 (segments = collect_const_segments(iseq, node))) {
9621 ISEQ_BODY(iseq)->ic_size++;
9622 ADD_INSN1(ret, node, opt_getconstant_path, segments);
9623 RB_OBJ_WRITTEN(iseq, Qundef, segments);
9625 else {
9626 /* constant */
9627 DECL_ANCHOR(pref);
9628 DECL_ANCHOR(body);
9630 INIT_ANCHOR(pref);
9631 INIT_ANCHOR(body);
9632 CHECK(compile_const_prefix(iseq, node, pref, body));
9633 if (LIST_INSN_SIZE_ZERO(pref)) {
9634 ADD_INSN(ret, node, putnil);
9635 ADD_SEQ(ret, body);
9637 else {
9638 ADD_SEQ(ret, pref);
9639 ADD_SEQ(ret, body);
9643 else {
9644 /* function call */
9645 ADD_CALL_RECEIVER(ret, node);
9646 CHECK(COMPILE(ret, "colon2#nd_head", RNODE_COLON2(node)->nd_head));
9647 ADD_CALL(ret, node, RNODE_COLON2(node)->nd_mid, INT2FIX(1));
9649 if (popped) {
9650 ADD_INSN(ret, node, pop);
9652 return COMPILE_OK;
9655 static int
9656 compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9658 debugi("colon3#nd_mid", RNODE_COLON3(node)->nd_mid);
9660 /* add cache insn */
9661 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
9662 ISEQ_BODY(iseq)->ic_size++;
9663 VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(RNODE_COLON3(node)->nd_mid));
9664 ADD_INSN1(ret, node, opt_getconstant_path, segments);
9665 RB_OBJ_WRITTEN(iseq, Qundef, segments);
9667 else {
9668 ADD_INSN1(ret, node, putobject, rb_cObject);
9669 ADD_INSN1(ret, node, putobject, Qtrue);
9670 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
9673 if (popped) {
9674 ADD_INSN(ret, node, pop);
9676 return COMPILE_OK;
9679 static int
9680 compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const int excl)
9682 VALUE flag = INT2FIX(excl);
9683 const NODE *b = RNODE_DOT2(node)->nd_beg;
9684 const NODE *e = RNODE_DOT2(node)->nd_end;
9686 if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
9687 if (!popped) {
9688 VALUE bv = optimized_range_item(b);
9689 VALUE ev = optimized_range_item(e);
9690 VALUE val = rb_range_new(bv, ev, excl);
9691 ADD_INSN1(ret, node, putobject, val);
9692 RB_OBJ_WRITTEN(iseq, Qundef, val);
9695 else {
9696 CHECK(COMPILE_(ret, "min", b, popped));
9697 CHECK(COMPILE_(ret, "max", e, popped));
9698 if (!popped) {
9699 ADD_INSN1(ret, node, newrange, flag);
9702 return COMPILE_OK;
9705 static int
9706 compile_errinfo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9708 if (!popped) {
9709 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
9710 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, 0);
9712 else {
9713 const rb_iseq_t *ip = iseq;
9714 int level = 0;
9715 while (ip) {
9716 if (ISEQ_BODY(ip)->type == ISEQ_TYPE_RESCUE) {
9717 break;
9719 ip = ISEQ_BODY(ip)->parent_iseq;
9720 level++;
9722 if (ip) {
9723 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, level);
9725 else {
9726 ADD_INSN(ret, node, putnil);
9730 return COMPILE_OK;
9733 static int
9734 compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9736 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
9737 LABEL *end_label = NEW_LABEL(nd_line(node));
9738 const NODE *default_value = get_nd_value(RNODE_KW_ARG(node)->nd_body);
9740 if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) {
9741 /* required argument. do nothing */
9742 COMPILE_ERROR(ERROR_ARGS "unreachable");
9743 return COMPILE_NG;
9745 else if (nd_type_p(default_value, NODE_SYM) ||
9746 nd_type_p(default_value, NODE_REGX) ||
9747 nd_type_p(default_value, NODE_LINE) ||
9748 nd_type_p(default_value, NODE_INTEGER) ||
9749 nd_type_p(default_value, NODE_FLOAT) ||
9750 nd_type_p(default_value, NODE_RATIONAL) ||
9751 nd_type_p(default_value, NODE_IMAGINARY) ||
9752 nd_type_p(default_value, NODE_NIL) ||
9753 nd_type_p(default_value, NODE_TRUE) ||
9754 nd_type_p(default_value, NODE_FALSE)) {
9755 COMPILE_ERROR(ERROR_ARGS "unreachable");
9756 return COMPILE_NG;
9758 else {
9759 /* if keywordcheck(_kw_bits, nth_keyword)
9760 * kw = default_value
9761 * end
9763 int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
9764 int keyword_idx = body->param.keyword->num;
9766 ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
9767 ADD_INSNL(ret, node, branchif, end_label);
9768 CHECK(COMPILE_POPPED(ret, "keyword default argument", RNODE_KW_ARG(node)->nd_body));
9769 ADD_LABEL(ret, end_label);
9771 return COMPILE_OK;
9774 static int
9775 compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9777 DECL_ANCHOR(recv);
9778 DECL_ANCHOR(args);
9779 unsigned int flag = 0;
9780 ID mid = RNODE_ATTRASGN(node)->nd_mid;
9781 VALUE argc;
9782 LABEL *else_label = NULL;
9783 VALUE branches = Qfalse;
9785 /* optimization shortcut
9786 * obj["literal"] = value -> opt_aset_with(obj, "literal", value)
9788 if (mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args &&
9789 nd_type_p(RNODE_ATTRASGN(node)->nd_args, NODE_LIST) && RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->as.nd_alen == 2 &&
9790 (nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_STR) || nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_FILE)) &&
9791 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
9792 !frozen_string_literal_p(iseq) &&
9793 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction)
9795 VALUE str = get_string_value(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head);
9796 CHECK(COMPILE(ret, "recv", RNODE_ATTRASGN(node)->nd_recv));
9797 CHECK(COMPILE(ret, "value", RNODE_LIST(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_next)->nd_head));
9798 if (!popped) {
9799 ADD_INSN(ret, node, swap);
9800 ADD_INSN1(ret, node, topn, INT2FIX(1));
9802 ADD_INSN2(ret, node, opt_aset_with, str,
9803 new_callinfo(iseq, idASET, 2, 0, NULL, FALSE));
9804 RB_OBJ_WRITTEN(iseq, Qundef, str);
9805 ADD_INSN(ret, node, pop);
9806 return COMPILE_OK;
9809 INIT_ANCHOR(recv);
9810 INIT_ANCHOR(args);
9811 argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, NULL);
9812 CHECK(!NIL_P(argc));
9814 int asgnflag = COMPILE_RECV(recv, "recv", node, RNODE_ATTRASGN(node)->nd_recv);
9815 CHECK(asgnflag != -1);
9816 flag |= (unsigned int)asgnflag;
9818 debugp_param("argc", argc);
9819 debugp_param("nd_mid", ID2SYM(mid));
9821 if (!rb_is_attrset_id(mid)) {
9822 /* safe nav attr */
9823 mid = rb_id_attrset(mid);
9824 else_label = qcall_branch_start(iseq, recv, &branches, node, node);
9826 if (!popped) {
9827 ADD_INSN(ret, node, putnil);
9828 ADD_SEQ(ret, recv);
9829 ADD_SEQ(ret, args);
9831 if (flag & VM_CALL_ARGS_SPLAT) {
9832 ADD_INSN(ret, node, dup);
9833 ADD_INSN1(ret, node, putobject, INT2FIX(-1));
9834 ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
9835 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2));
9836 ADD_INSN (ret, node, pop);
9838 else {
9839 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 1));
9842 else {
9843 ADD_SEQ(ret, recv);
9844 ADD_SEQ(ret, args);
9846 ADD_SEND_WITH_FLAG(ret, node, mid, argc, INT2FIX(flag));
9847 qcall_branch_end(iseq, ret, else_label, branches, node, node);
9848 ADD_INSN(ret, node, pop);
9849 return COMPILE_OK;
9852 static int
9853 compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy)
9855 ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore);
9856 ADD_SEQ(ret, sub);
9858 if (copy) {
9860 * NEW_CALL(fcore, rb_intern("make_shareable_copy"),
9861 * NEW_LIST(value, loc), loc);
9863 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
9865 else {
9867 * NEW_CALL(fcore, rb_intern("make_shareable"),
9868 * NEW_LIST(value, loc), loc);
9870 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
9873 return COMPILE_OK;
9876 static VALUE
9877 node_const_decl_val(const NODE *node)
9879 VALUE path;
9880 switch (nd_type(node)) {
9881 case NODE_CDECL:
9882 if (RNODE_CDECL(node)->nd_vid) {
9883 path = rb_id2str(RNODE_CDECL(node)->nd_vid);
9884 goto end;
9886 else {
9887 node = RNODE_CDECL(node)->nd_else;
9889 break;
9890 case NODE_COLON2:
9891 break;
9892 case NODE_COLON3:
9893 // ::Const
9894 path = rb_str_new_cstr("::");
9895 rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
9896 goto end;
9897 default:
9898 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
9899 UNREACHABLE_RETURN(0);
9902 path = rb_ary_new();
9903 if (node) {
9904 for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) {
9905 rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid));
9907 if (node && nd_type_p(node, NODE_CONST)) {
9908 // Const::Name
9909 rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid));
9911 else if (node && nd_type_p(node, NODE_COLON3)) {
9912 // ::Const::Name
9913 rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
9914 rb_ary_push(path, rb_str_new(0, 0));
9916 else {
9917 // expression::Name
9918 rb_ary_push(path, rb_str_new_cstr("..."));
9920 path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
9922 end:
9923 path = rb_fstring(path);
9924 return path;
9927 static VALUE
9928 const_decl_path(NODE *dest)
9930 VALUE path = Qnil;
9931 if (!nd_type_p(dest, NODE_CALL)) {
9932 path = node_const_decl_val(dest);
9934 return path;
9937 static int
9938 compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value)
9941 *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest))
9943 VALUE path = const_decl_path(dest);
9944 ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore);
9945 CHECK(COMPILE(ret, "compile_ensure_shareable_node", value));
9946 ADD_INSN1(ret, value, putobject, path);
9947 RB_OBJ_WRITTEN(iseq, Qundef, path);
9948 ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
9950 return COMPILE_OK;
9953 #ifndef SHAREABLE_BARE_EXPRESSION
9954 #define SHAREABLE_BARE_EXPRESSION 1
9955 #endif
9957 static int
9958 compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, NODE *dest, const NODE *node, size_t level, VALUE *value_p, int *shareable_literal_p)
9960 # define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \
9961 compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p)
9962 VALUE lit = Qnil;
9963 DECL_ANCHOR(anchor);
9965 enum node_type type = nd_type(node);
9966 switch (type) {
9967 case NODE_TRUE:
9968 *value_p = Qtrue;
9969 goto compile;
9970 case NODE_FALSE:
9971 *value_p = Qfalse;
9972 goto compile;
9973 case NODE_NIL:
9974 *value_p = Qnil;
9975 goto compile;
9976 case NODE_SYM:
9977 *value_p = rb_node_sym_string_val(node);
9978 goto compile;
9979 case NODE_REGX:
9980 *value_p = rb_node_regx_string_val(node);
9981 goto compile;
9982 case NODE_LINE:
9983 *value_p = rb_node_line_lineno_val(node);
9984 goto compile;
9985 case NODE_INTEGER:
9986 *value_p = rb_node_integer_literal_val(node);
9987 goto compile;
9988 case NODE_FLOAT:
9989 *value_p = rb_node_float_literal_val(node);
9990 goto compile;
9991 case NODE_RATIONAL:
9992 *value_p = rb_node_rational_literal_val(node);
9993 goto compile;
9994 case NODE_IMAGINARY:
9995 *value_p = rb_node_imaginary_literal_val(node);
9996 goto compile;
9997 case NODE_ENCODING:
9998 *value_p = rb_node_encoding_val(node);
10000 compile:
10001 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10002 *shareable_literal_p = 1;
10003 return COMPILE_OK;
10005 case NODE_DSTR:
10006 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10007 if (shareable == rb_parser_shareable_literal) {
10009 * NEW_CALL(node, idUMinus, 0, loc);
10011 * -"#{var}"
10013 ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
10015 *value_p = Qundef;
10016 *shareable_literal_p = 1;
10017 return COMPILE_OK;
10019 case NODE_STR:{
10020 VALUE lit = rb_node_str_string_val(node);
10021 ADD_INSN1(ret, node, putobject, lit);
10022 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10023 *value_p = lit;
10024 *shareable_literal_p = 1;
10026 return COMPILE_OK;
10029 case NODE_FILE:{
10030 VALUE lit = rb_node_file_path_val(node);
10031 ADD_INSN1(ret, node, putobject, lit);
10032 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10033 *value_p = lit;
10034 *shareable_literal_p = 1;
10036 return COMPILE_OK;
10039 case NODE_ZLIST:{
10040 VALUE lit = rb_ary_new();
10041 OBJ_FREEZE(lit);
10042 ADD_INSN1(ret, node, putobject, lit);
10043 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10044 *value_p = lit;
10045 *shareable_literal_p = 1;
10047 return COMPILE_OK;
10050 case NODE_LIST:{
10051 INIT_ANCHOR(anchor);
10052 lit = rb_ary_new();
10053 for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) {
10054 VALUE val;
10055 int shareable_literal_p2;
10056 NODE *elt = RNODE_LIST(n)->nd_head;
10057 if (elt) {
10058 CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2));
10059 if (shareable_literal_p2) {
10060 /* noop */
10062 else if (RTEST(lit)) {
10063 rb_ary_clear(lit);
10064 lit = Qfalse;
10067 if (RTEST(lit)) {
10068 if (!UNDEF_P(val)) {
10069 rb_ary_push(lit, val);
10071 else {
10072 rb_ary_clear(lit);
10073 lit = Qnil; /* make shareable at runtime */
10077 break;
10079 case NODE_HASH:{
10080 if (!RNODE_HASH(node)->nd_brace) {
10081 *value_p = Qundef;
10082 *shareable_literal_p = 0;
10083 return COMPILE_OK;
10086 INIT_ANCHOR(anchor);
10087 lit = rb_hash_new();
10088 for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
10089 VALUE key_val;
10090 VALUE value_val;
10091 int shareable_literal_p2;
10092 NODE *key = RNODE_LIST(n)->nd_head;
10093 NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head;
10094 if (key) {
10095 CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2));
10096 if (shareable_literal_p2) {
10097 /* noop */
10099 else if (RTEST(lit)) {
10100 rb_hash_clear(lit);
10101 lit = Qfalse;
10104 if (val) {
10105 CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2));
10106 if (shareable_literal_p2) {
10107 /* noop */
10109 else if (RTEST(lit)) {
10110 rb_hash_clear(lit);
10111 lit = Qfalse;
10114 if (RTEST(lit)) {
10115 if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) {
10116 rb_hash_aset(lit, key_val, value_val);
10118 else {
10119 rb_hash_clear(lit);
10120 lit = Qnil; /* make shareable at runtime */
10124 break;
10127 default:
10128 if (shareable == rb_parser_shareable_literal &&
10129 (SHAREABLE_BARE_EXPRESSION || level > 0)) {
10130 CHECK(compile_ensure_shareable_node(iseq, ret, dest, node));
10131 *value_p = Qundef;
10132 *shareable_literal_p = 1;
10133 return COMPILE_OK;
10135 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10136 *value_p = Qundef;
10137 *shareable_literal_p = 0;
10138 return COMPILE_OK;
10141 /* Array or Hash */
10142 if (!lit) {
10143 if (nd_type(node) == NODE_LIST) {
10144 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10146 else if (nd_type(node) == NODE_HASH) {
10147 int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10148 ADD_INSN1(anchor, node, newhash, INT2FIX(len));
10150 *value_p = Qundef;
10151 *shareable_literal_p = 0;
10152 ADD_SEQ(ret, anchor);
10153 return COMPILE_OK;
10155 if (NIL_P(lit)) {
10156 // if shareable_literal, all elements should have been ensured
10157 // as shareable
10158 if (nd_type(node) == NODE_LIST) {
10159 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10161 else if (nd_type(node) == NODE_HASH) {
10162 int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10163 ADD_INSN1(anchor, node, newhash, INT2FIX(len));
10165 CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false));
10166 *value_p = Qundef;
10167 *shareable_literal_p = 1;
10169 else {
10170 VALUE val = rb_ractor_make_shareable(lit);
10171 ADD_INSN1(ret, node, putobject, val);
10172 RB_OBJ_WRITTEN(iseq, Qundef, val);
10173 *value_p = val;
10174 *shareable_literal_p = 1;
10177 return COMPILE_OK;
10180 static int
10181 compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value)
10183 int literal_p = 0;
10184 VALUE val;
10185 DECL_ANCHOR(anchor);
10186 INIT_ANCHOR(anchor);
10188 switch (shareable) {
10189 case rb_parser_shareable_none:
10190 CHECK(COMPILE(ret, "compile_shareable_constant_value", value));
10191 return COMPILE_OK;
10193 case rb_parser_shareable_literal:
10194 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10195 ADD_SEQ(ret, anchor);
10196 return COMPILE_OK;
10198 case rb_parser_shareable_copy:
10199 case rb_parser_shareable_everything:
10200 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10201 if (!literal_p) {
10202 CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy));
10204 else {
10205 ADD_SEQ(ret, anchor);
10207 return COMPILE_OK;
10208 default:
10209 rb_bug("unexpected rb_parser_shareability: %d", shareable);
10213 static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped);
10215 compile each node
10217 self: InstructionSequence
10218 node: Ruby compiled node
10219 popped: This node will be popped
10221 static int
10222 iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
10224 if (node == 0) {
10225 if (!popped) {
10226 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
10227 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
10228 debugs("node: NODE_NIL(implicit)\n");
10229 ADD_SYNTHETIC_INSN(ret, lineno, -1, putnil);
10231 return COMPILE_OK;
10233 return iseq_compile_each0(iseq, ret, node, popped);
10236 static int
10237 iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10239 const int line = (int)nd_line(node);
10240 const enum node_type type = nd_type(node);
10241 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10243 if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
10244 /* ignore */
10246 else {
10247 if (nd_fl_newline(node)) {
10248 int event = RUBY_EVENT_LINE;
10249 ISEQ_COMPILE_DATA(iseq)->last_line = line;
10250 if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
10251 event |= RUBY_EVENT_COVERAGE_LINE;
10253 ADD_TRACE(ret, event);
10257 debug_node_start(node);
10258 #undef BEFORE_RETURN
10259 #define BEFORE_RETURN debug_node_end()
10261 switch (type) {
10262 case NODE_BLOCK:
10263 CHECK(compile_block(iseq, ret, node, popped));
10264 break;
10265 case NODE_IF:
10266 case NODE_UNLESS:
10267 CHECK(compile_if(iseq, ret, node, popped, type));
10268 break;
10269 case NODE_CASE:
10270 CHECK(compile_case(iseq, ret, node, popped));
10271 break;
10272 case NODE_CASE2:
10273 CHECK(compile_case2(iseq, ret, node, popped));
10274 break;
10275 case NODE_CASE3:
10276 CHECK(compile_case3(iseq, ret, node, popped));
10277 break;
10278 case NODE_WHILE:
10279 case NODE_UNTIL:
10280 CHECK(compile_loop(iseq, ret, node, popped, type));
10281 break;
10282 case NODE_FOR:
10283 case NODE_ITER:
10284 CHECK(compile_iter(iseq, ret, node, popped));
10285 break;
10286 case NODE_FOR_MASGN:
10287 CHECK(compile_for_masgn(iseq, ret, node, popped));
10288 break;
10289 case NODE_BREAK:
10290 CHECK(compile_break(iseq, ret, node, popped));
10291 break;
10292 case NODE_NEXT:
10293 CHECK(compile_next(iseq, ret, node, popped));
10294 break;
10295 case NODE_REDO:
10296 CHECK(compile_redo(iseq, ret, node, popped));
10297 break;
10298 case NODE_RETRY:
10299 CHECK(compile_retry(iseq, ret, node, popped));
10300 break;
10301 case NODE_BEGIN:{
10302 CHECK(COMPILE_(ret, "NODE_BEGIN", RNODE_BEGIN(node)->nd_body, popped));
10303 break;
10305 case NODE_RESCUE:
10306 CHECK(compile_rescue(iseq, ret, node, popped));
10307 break;
10308 case NODE_RESBODY:
10309 CHECK(compile_resbody(iseq, ret, node, popped));
10310 break;
10311 case NODE_ENSURE:
10312 CHECK(compile_ensure(iseq, ret, node, popped));
10313 break;
10315 case NODE_AND:
10316 case NODE_OR:{
10317 LABEL *end_label = NEW_LABEL(line);
10318 CHECK(COMPILE(ret, "nd_1st", RNODE_OR(node)->nd_1st));
10319 if (!popped) {
10320 ADD_INSN(ret, node, dup);
10322 if (type == NODE_AND) {
10323 ADD_INSNL(ret, node, branchunless, end_label);
10325 else {
10326 ADD_INSNL(ret, node, branchif, end_label);
10328 if (!popped) {
10329 ADD_INSN(ret, node, pop);
10331 CHECK(COMPILE_(ret, "nd_2nd", RNODE_OR(node)->nd_2nd, popped));
10332 ADD_LABEL(ret, end_label);
10333 break;
10336 case NODE_MASGN:{
10337 compile_massign(iseq, ret, node, popped);
10338 break;
10341 case NODE_LASGN:{
10342 ID id = RNODE_LASGN(node)->nd_vid;
10343 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
10345 debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
10346 CHECK(COMPILE(ret, "rvalue", RNODE_LASGN(node)->nd_value));
10348 if (!popped) {
10349 ADD_INSN(ret, node, dup);
10351 ADD_SETLOCAL(ret, node, idx, get_lvar_level(iseq));
10352 break;
10354 case NODE_DASGN: {
10355 int idx, lv, ls;
10356 ID id = RNODE_DASGN(node)->nd_vid;
10357 CHECK(COMPILE(ret, "dvalue", RNODE_DASGN(node)->nd_value));
10358 debugi("dassn id", rb_id2str(id) ? id : '*');
10360 if (!popped) {
10361 ADD_INSN(ret, node, dup);
10364 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
10366 if (idx < 0) {
10367 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
10368 rb_id2str(id));
10369 goto ng;
10371 ADD_SETLOCAL(ret, node, ls - idx, lv);
10372 break;
10374 case NODE_GASGN:{
10375 CHECK(COMPILE(ret, "lvalue", RNODE_GASGN(node)->nd_value));
10377 if (!popped) {
10378 ADD_INSN(ret, node, dup);
10380 ADD_INSN1(ret, node, setglobal, ID2SYM(RNODE_GASGN(node)->nd_vid));
10381 break;
10383 case NODE_IASGN:{
10384 CHECK(COMPILE(ret, "lvalue", RNODE_IASGN(node)->nd_value));
10385 if (!popped) {
10386 ADD_INSN(ret, node, dup);
10388 ADD_INSN2(ret, node, setinstancevariable,
10389 ID2SYM(RNODE_IASGN(node)->nd_vid),
10390 get_ivar_ic_value(iseq,RNODE_IASGN(node)->nd_vid));
10391 break;
10393 case NODE_CDECL:{
10394 if (RNODE_CDECL(node)->nd_vid) {
10395 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
10397 if (!popped) {
10398 ADD_INSN(ret, node, dup);
10401 ADD_INSN1(ret, node, putspecialobject,
10402 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
10403 ADD_INSN1(ret, node, setconstant, ID2SYM(RNODE_CDECL(node)->nd_vid));
10405 else {
10406 compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else);
10407 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
10408 ADD_INSN(ret, node, swap);
10410 if (!popped) {
10411 ADD_INSN1(ret, node, topn, INT2FIX(1));
10412 ADD_INSN(ret, node, swap);
10415 ADD_INSN1(ret, node, setconstant, ID2SYM(get_node_colon_nd_mid(RNODE_CDECL(node)->nd_else)));
10417 break;
10419 case NODE_CVASGN:{
10420 CHECK(COMPILE(ret, "cvasgn val", RNODE_CVASGN(node)->nd_value));
10421 if (!popped) {
10422 ADD_INSN(ret, node, dup);
10424 ADD_INSN2(ret, node, setclassvariable,
10425 ID2SYM(RNODE_CVASGN(node)->nd_vid),
10426 get_cvar_ic_value(iseq, RNODE_CVASGN(node)->nd_vid));
10427 break;
10429 case NODE_OP_ASGN1:
10430 CHECK(compile_op_asgn1(iseq, ret, node, popped));
10431 break;
10432 case NODE_OP_ASGN2:
10433 CHECK(compile_op_asgn2(iseq, ret, node, popped));
10434 break;
10435 case NODE_OP_CDECL:
10436 CHECK(compile_op_cdecl(iseq, ret, node, popped));
10437 break;
10438 case NODE_OP_ASGN_AND:
10439 case NODE_OP_ASGN_OR:
10440 CHECK(compile_op_log(iseq, ret, node, popped, type));
10441 break;
10442 case NODE_CALL: /* obj.foo */
10443 case NODE_OPCALL: /* foo[] */
10444 if (compile_call_precheck_freeze(iseq, ret, node, node, popped) == TRUE) {
10445 break;
10447 case NODE_QCALL: /* obj&.foo */
10448 case NODE_FCALL: /* foo() */
10449 case NODE_VCALL: /* foo (variable or call) */
10450 if (compile_call(iseq, ret, node, type, node, popped, false) == COMPILE_NG) {
10451 goto ng;
10453 break;
10454 case NODE_SUPER:
10455 case NODE_ZSUPER:
10456 CHECK(compile_super(iseq, ret, node, popped, type));
10457 break;
10458 case NODE_LIST:{
10459 CHECK(compile_array(iseq, ret, node, popped, TRUE) >= 0);
10460 break;
10462 case NODE_ZLIST:{
10463 if (!popped) {
10464 ADD_INSN1(ret, node, newarray, INT2FIX(0));
10466 break;
10468 case NODE_HASH:
10469 CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0);
10470 break;
10471 case NODE_RETURN:
10472 CHECK(compile_return(iseq, ret, node, popped));
10473 break;
10474 case NODE_YIELD:
10475 CHECK(compile_yield(iseq, ret, node, popped));
10476 break;
10477 case NODE_LVAR:{
10478 if (!popped) {
10479 compile_lvar(iseq, ret, node, RNODE_LVAR(node)->nd_vid);
10481 break;
10483 case NODE_DVAR:{
10484 int lv, idx, ls;
10485 debugi("nd_vid", RNODE_DVAR(node)->nd_vid);
10486 if (!popped) {
10487 idx = get_dyna_var_idx(iseq, RNODE_DVAR(node)->nd_vid, &lv, &ls);
10488 if (idx < 0) {
10489 COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
10490 rb_id2str(RNODE_DVAR(node)->nd_vid));
10491 goto ng;
10493 ADD_GETLOCAL(ret, node, ls - idx, lv);
10495 break;
10497 case NODE_GVAR:{
10498 ADD_INSN1(ret, node, getglobal, ID2SYM(RNODE_GVAR(node)->nd_vid));
10499 if (popped) {
10500 ADD_INSN(ret, node, pop);
10502 break;
10504 case NODE_IVAR:{
10505 debugi("nd_vid", RNODE_IVAR(node)->nd_vid);
10506 if (!popped) {
10507 ADD_INSN2(ret, node, getinstancevariable,
10508 ID2SYM(RNODE_IVAR(node)->nd_vid),
10509 get_ivar_ic_value(iseq, RNODE_IVAR(node)->nd_vid));
10511 break;
10513 case NODE_CONST:{
10514 debugi("nd_vid", RNODE_CONST(node)->nd_vid);
10516 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
10517 body->ic_size++;
10518 VALUE segments = rb_ary_new_from_args(1, ID2SYM(RNODE_CONST(node)->nd_vid));
10519 ADD_INSN1(ret, node, opt_getconstant_path, segments);
10520 RB_OBJ_WRITTEN(iseq, Qundef, segments);
10522 else {
10523 ADD_INSN(ret, node, putnil);
10524 ADD_INSN1(ret, node, putobject, Qtrue);
10525 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
10528 if (popped) {
10529 ADD_INSN(ret, node, pop);
10531 break;
10533 case NODE_CVAR:{
10534 if (!popped) {
10535 ADD_INSN2(ret, node, getclassvariable,
10536 ID2SYM(RNODE_CVAR(node)->nd_vid),
10537 get_cvar_ic_value(iseq, RNODE_CVAR(node)->nd_vid));
10539 break;
10541 case NODE_NTH_REF:{
10542 if (!popped) {
10543 if (!RNODE_NTH_REF(node)->nd_nth) {
10544 ADD_INSN(ret, node, putnil);
10545 break;
10547 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
10548 INT2FIX(RNODE_NTH_REF(node)->nd_nth << 1));
10550 break;
10552 case NODE_BACK_REF:{
10553 if (!popped) {
10554 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
10555 INT2FIX(0x01 | (RNODE_BACK_REF(node)->nd_nth << 1)));
10557 break;
10559 case NODE_MATCH:
10560 case NODE_MATCH2:
10561 case NODE_MATCH3:
10562 CHECK(compile_match(iseq, ret, node, popped, type));
10563 break;
10564 case NODE_SYM:{
10565 if (!popped) {
10566 ADD_INSN1(ret, node, putobject, rb_node_sym_string_val(node));
10568 break;
10570 case NODE_LINE:{
10571 if (!popped) {
10572 ADD_INSN1(ret, node, putobject, rb_node_line_lineno_val(node));
10574 break;
10576 case NODE_ENCODING:{
10577 if (!popped) {
10578 ADD_INSN1(ret, node, putobject, rb_node_encoding_val(node));
10580 break;
10582 case NODE_INTEGER:{
10583 VALUE lit = rb_node_integer_literal_val(node);
10584 debugp_param("integer", lit);
10585 if (!popped) {
10586 ADD_INSN1(ret, node, putobject, lit);
10587 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10589 break;
10591 case NODE_FLOAT:{
10592 VALUE lit = rb_node_float_literal_val(node);
10593 debugp_param("float", lit);
10594 if (!popped) {
10595 ADD_INSN1(ret, node, putobject, lit);
10596 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10598 break;
10600 case NODE_RATIONAL:{
10601 VALUE lit = rb_node_rational_literal_val(node);
10602 debugp_param("rational", lit);
10603 if (!popped) {
10604 ADD_INSN1(ret, node, putobject, lit);
10605 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10607 break;
10609 case NODE_IMAGINARY:{
10610 VALUE lit = rb_node_imaginary_literal_val(node);
10611 debugp_param("imaginary", lit);
10612 if (!popped) {
10613 ADD_INSN1(ret, node, putobject, lit);
10614 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10616 break;
10618 case NODE_FILE:
10619 case NODE_STR:{
10620 debugp_param("nd_lit", get_string_value(node));
10621 if (!popped) {
10622 VALUE lit = get_string_value(node);
10623 switch (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
10624 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
10625 ADD_INSN1(ret, node, putchilledstring, lit);
10626 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10627 break;
10628 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
10629 ADD_INSN1(ret, node, putstring, lit);
10630 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10631 break;
10632 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
10633 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
10634 VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
10635 lit = rb_str_dup(lit);
10636 rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
10637 lit = rb_str_freeze(lit);
10639 ADD_INSN1(ret, node, putobject, lit);
10640 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10641 break;
10642 default:
10643 rb_bug("invalid frozen_string_literal");
10646 break;
10648 case NODE_DSTR:{
10649 compile_dstr(iseq, ret, node);
10651 if (popped) {
10652 ADD_INSN(ret, node, pop);
10654 break;
10656 case NODE_XSTR:{
10657 ADD_CALL_RECEIVER(ret, node);
10658 VALUE str = rb_node_str_string_val(node);
10659 ADD_INSN1(ret, node, putobject, str);
10660 RB_OBJ_WRITTEN(iseq, Qundef, str);
10661 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
10663 if (popped) {
10664 ADD_INSN(ret, node, pop);
10666 break;
10668 case NODE_DXSTR:{
10669 ADD_CALL_RECEIVER(ret, node);
10670 compile_dstr(iseq, ret, node);
10671 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
10673 if (popped) {
10674 ADD_INSN(ret, node, pop);
10676 break;
10678 case NODE_EVSTR:
10679 CHECK(compile_evstr(iseq, ret, RNODE_EVSTR(node)->nd_body, popped));
10680 break;
10681 case NODE_REGX:{
10682 if (!popped) {
10683 VALUE lit = rb_node_regx_string_val(node);
10684 ADD_INSN1(ret, node, putobject, lit);
10685 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10687 break;
10689 case NODE_DREGX:
10690 compile_dregx(iseq, ret, node, popped);
10691 break;
10692 case NODE_ONCE:{
10693 int ic_index = body->ise_size++;
10694 const rb_iseq_t *block_iseq;
10695 block_iseq = NEW_CHILD_ISEQ(RNODE_ONCE(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
10697 ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
10698 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq);
10700 if (popped) {
10701 ADD_INSN(ret, node, pop);
10703 break;
10705 case NODE_ARGSCAT:{
10706 if (popped) {
10707 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
10708 ADD_INSN1(ret, node, splatarray, Qfalse);
10709 ADD_INSN(ret, node, pop);
10710 CHECK(COMPILE(ret, "argscat body", RNODE_ARGSCAT(node)->nd_body));
10711 ADD_INSN1(ret, node, splatarray, Qfalse);
10712 ADD_INSN(ret, node, pop);
10714 else {
10715 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
10716 const NODE *body_node = RNODE_ARGSCAT(node)->nd_body;
10717 if (nd_type_p(body_node, NODE_LIST)) {
10718 CHECK(compile_array(iseq, ret, body_node, popped, FALSE) >= 0);
10720 else {
10721 CHECK(COMPILE(ret, "argscat body", body_node));
10722 ADD_INSN(ret, node, concattoarray);
10725 break;
10727 case NODE_ARGSPUSH:{
10728 if (popped) {
10729 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
10730 ADD_INSN1(ret, node, splatarray, Qfalse);
10731 ADD_INSN(ret, node, pop);
10732 CHECK(COMPILE_(ret, "argspush body", RNODE_ARGSPUSH(node)->nd_body, popped));
10734 else {
10735 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
10736 const NODE *body_node = RNODE_ARGSPUSH(node)->nd_body;
10737 if (keyword_node_p(body_node)) {
10738 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
10739 ADD_INSN(ret, node, pushtoarraykwsplat);
10741 else if (static_literal_node_p(body_node, iseq, false)) {
10742 ADD_INSN1(ret, body_node, putobject, static_literal_value(body_node, iseq));
10743 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
10745 else {
10746 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
10747 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
10750 break;
10752 case NODE_SPLAT:{
10753 CHECK(COMPILE(ret, "splat", RNODE_SPLAT(node)->nd_head));
10754 ADD_INSN1(ret, node, splatarray, Qtrue);
10756 if (popped) {
10757 ADD_INSN(ret, node, pop);
10759 break;
10761 case NODE_DEFN:{
10762 ID mid = RNODE_DEFN(node)->nd_mid;
10763 const rb_iseq_t *method_iseq = NEW_ISEQ(RNODE_DEFN(node)->nd_defn,
10764 rb_id2str(mid),
10765 ISEQ_TYPE_METHOD, line);
10767 debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
10768 ADD_INSN2(ret, node, definemethod, ID2SYM(mid), method_iseq);
10769 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)method_iseq);
10771 if (!popped) {
10772 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
10775 break;
10777 case NODE_DEFS:{
10778 ID mid = RNODE_DEFS(node)->nd_mid;
10779 const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(RNODE_DEFS(node)->nd_defn,
10780 rb_id2str(mid),
10781 ISEQ_TYPE_METHOD, line);
10783 debugp_param("defs/iseq", rb_iseqw_new(singleton_method_iseq));
10784 CHECK(COMPILE(ret, "defs: recv", RNODE_DEFS(node)->nd_recv));
10785 ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq);
10786 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq);
10788 if (!popped) {
10789 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
10791 break;
10793 case NODE_ALIAS:{
10794 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10795 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
10796 CHECK(COMPILE(ret, "alias arg1", RNODE_ALIAS(node)->nd_1st));
10797 CHECK(COMPILE(ret, "alias arg2", RNODE_ALIAS(node)->nd_2nd));
10798 ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
10800 if (popped) {
10801 ADD_INSN(ret, node, pop);
10803 break;
10805 case NODE_VALIAS:{
10806 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10807 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_alias));
10808 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_orig));
10809 ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
10811 if (popped) {
10812 ADD_INSN(ret, node, pop);
10814 break;
10816 case NODE_UNDEF:{
10817 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10818 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
10819 CHECK(COMPILE(ret, "undef arg", RNODE_UNDEF(node)->nd_undef));
10820 ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
10822 if (popped) {
10823 ADD_INSN(ret, node, pop);
10825 break;
10827 case NODE_CLASS:{
10828 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(RNODE_CLASS(node)->nd_body,
10829 rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)))),
10830 ISEQ_TYPE_CLASS, line);
10831 const int flags = VM_DEFINECLASS_TYPE_CLASS |
10832 (RNODE_CLASS(node)->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
10833 compile_cpath(ret, iseq, RNODE_CLASS(node)->nd_cpath);
10835 CHECK(COMPILE(ret, "super", RNODE_CLASS(node)->nd_super));
10836 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)), class_iseq, INT2FIX(flags));
10837 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
10839 if (popped) {
10840 ADD_INSN(ret, node, pop);
10842 break;
10844 case NODE_MODULE:{
10845 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(RNODE_MODULE(node)->nd_body,
10846 rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)))),
10847 ISEQ_TYPE_CLASS, line);
10848 const int flags = VM_DEFINECLASS_TYPE_MODULE |
10849 compile_cpath(ret, iseq, RNODE_MODULE(node)->nd_cpath);
10851 ADD_INSN (ret, node, putnil); /* dummy */
10852 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)), module_iseq, INT2FIX(flags));
10853 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq);
10855 if (popped) {
10856 ADD_INSN(ret, node, pop);
10858 break;
10860 case NODE_SCLASS:{
10861 ID singletonclass;
10862 const rb_iseq_t *singleton_class = NEW_ISEQ(RNODE_SCLASS(node)->nd_body, rb_fstring_lit("singleton class"),
10863 ISEQ_TYPE_CLASS, line);
10865 CHECK(COMPILE(ret, "sclass#recv", RNODE_SCLASS(node)->nd_recv));
10866 ADD_INSN (ret, node, putnil);
10867 CONST_ID(singletonclass, "singletonclass");
10868 ADD_INSN3(ret, node, defineclass,
10869 ID2SYM(singletonclass), singleton_class,
10870 INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
10871 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_class);
10873 if (popped) {
10874 ADD_INSN(ret, node, pop);
10876 break;
10878 case NODE_COLON2:
10879 CHECK(compile_colon2(iseq, ret, node, popped));
10880 break;
10881 case NODE_COLON3:
10882 CHECK(compile_colon3(iseq, ret, node, popped));
10883 break;
10884 case NODE_DOT2:
10885 CHECK(compile_dots(iseq, ret, node, popped, FALSE));
10886 break;
10887 case NODE_DOT3:
10888 CHECK(compile_dots(iseq, ret, node, popped, TRUE));
10889 break;
10890 case NODE_FLIP2:
10891 case NODE_FLIP3:{
10892 LABEL *lend = NEW_LABEL(line);
10893 LABEL *ltrue = NEW_LABEL(line);
10894 LABEL *lfalse = NEW_LABEL(line);
10895 CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
10896 ltrue, lfalse));
10897 ADD_LABEL(ret, ltrue);
10898 ADD_INSN1(ret, node, putobject, Qtrue);
10899 ADD_INSNL(ret, node, jump, lend);
10900 ADD_LABEL(ret, lfalse);
10901 ADD_INSN1(ret, node, putobject, Qfalse);
10902 ADD_LABEL(ret, lend);
10903 break;
10905 case NODE_SELF:{
10906 if (!popped) {
10907 ADD_INSN(ret, node, putself);
10909 break;
10911 case NODE_NIL:{
10912 if (!popped) {
10913 ADD_INSN(ret, node, putnil);
10915 break;
10917 case NODE_TRUE:{
10918 if (!popped) {
10919 ADD_INSN1(ret, node, putobject, Qtrue);
10921 break;
10923 case NODE_FALSE:{
10924 if (!popped) {
10925 ADD_INSN1(ret, node, putobject, Qfalse);
10927 break;
10929 case NODE_ERRINFO:
10930 CHECK(compile_errinfo(iseq, ret, node, popped));
10931 break;
10932 case NODE_DEFINED:
10933 if (!popped) {
10934 CHECK(compile_defined_expr(iseq, ret, node, Qtrue));
10936 break;
10937 case NODE_POSTEXE:{
10938 /* compiled to:
10939 * ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
10941 int is_index = body->ise_size++;
10942 struct rb_iseq_new_with_callback_callback_func *ifunc =
10943 rb_iseq_new_with_callback_new_callback(build_postexe_iseq, RNODE_POSTEXE(node)->nd_body);
10944 const rb_iseq_t *once_iseq =
10945 new_child_iseq_with_callback(iseq, ifunc,
10946 rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line);
10948 ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index));
10949 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)once_iseq);
10951 if (popped) {
10952 ADD_INSN(ret, node, pop);
10954 break;
10956 case NODE_KW_ARG:
10957 CHECK(compile_kw_arg(iseq, ret, node, popped));
10958 break;
10959 case NODE_DSYM:{
10960 compile_dstr(iseq, ret, node);
10961 if (!popped) {
10962 ADD_INSN(ret, node, intern);
10964 else {
10965 ADD_INSN(ret, node, pop);
10967 break;
10969 case NODE_ATTRASGN:
10970 CHECK(compile_attrasgn(iseq, ret, node, popped));
10971 break;
10972 case NODE_LAMBDA:{
10973 /* compile same as lambda{...} */
10974 const rb_iseq_t *block = NEW_CHILD_ISEQ(RNODE_LAMBDA(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
10975 VALUE argc = INT2FIX(0);
10977 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10978 ADD_CALL_WITH_BLOCK(ret, node, idLambda, argc, block);
10979 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
10981 if (popped) {
10982 ADD_INSN(ret, node, pop);
10984 break;
10986 default:
10987 UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
10989 debug_node_end();
10990 return COMPILE_NG;
10993 debug_node_end();
10994 return COMPILE_OK;
10997 /***************************/
10998 /* instruction information */
10999 /***************************/
11001 static int
11002 insn_data_length(INSN *iobj)
11004 return insn_len(iobj->insn_id);
11007 static int
11008 calc_sp_depth(int depth, INSN *insn)
11010 return comptime_insn_stack_increase(depth, insn->insn_id, insn->operands);
11013 static VALUE
11014 opobj_inspect(VALUE obj)
11016 if (!SPECIAL_CONST_P(obj) && !RBASIC_CLASS(obj)) {
11017 switch (BUILTIN_TYPE(obj)) {
11018 case T_STRING:
11019 obj = rb_str_new_cstr(RSTRING_PTR(obj));
11020 break;
11021 case T_ARRAY:
11022 obj = rb_ary_dup(obj);
11023 break;
11024 default:
11025 break;
11028 return rb_inspect(obj);
11033 static VALUE
11034 insn_data_to_s_detail(INSN *iobj)
11036 VALUE str = rb_sprintf("%-20s ", insn_name(iobj->insn_id));
11038 if (iobj->operands) {
11039 const char *types = insn_op_types(iobj->insn_id);
11040 int j;
11042 for (j = 0; types[j]; j++) {
11043 char type = types[j];
11045 switch (type) {
11046 case TS_OFFSET: /* label(destination position) */
11048 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
11049 rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
11050 break;
11052 break;
11053 case TS_ISEQ: /* iseq */
11055 rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
11056 VALUE val = Qnil;
11057 if (0 && iseq) { /* TODO: invalidate now */
11058 val = (VALUE)iseq;
11060 rb_str_concat(str, opobj_inspect(val));
11062 break;
11063 case TS_LINDEX:
11064 case TS_NUM: /* ulong */
11065 case TS_VALUE: /* VALUE */
11067 VALUE v = OPERAND_AT(iobj, j);
11068 if (!CLASS_OF(v))
11069 rb_str_cat2(str, "<hidden>");
11070 else {
11071 rb_str_concat(str, opobj_inspect(v));
11073 break;
11075 case TS_ID: /* ID */
11076 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11077 break;
11078 case TS_IC: /* inline cache */
11079 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11080 break;
11081 case TS_IVC: /* inline ivar cache */
11082 rb_str_catf(str, "<ivc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11083 break;
11084 case TS_ICVARC: /* inline cvar cache */
11085 rb_str_catf(str, "<icvarc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11086 break;
11087 case TS_ISE: /* inline storage entry */
11088 rb_str_catf(str, "<ise:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11089 break;
11090 case TS_CALLDATA: /* we store these as call infos at compile time */
11092 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, j);
11093 rb_str_cat2(str, "<calldata:");
11094 if (vm_ci_mid(ci)) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(vm_ci_mid(ci)));
11095 rb_str_catf(str, ", %d>", vm_ci_argc(ci));
11096 break;
11098 case TS_CDHASH: /* case/when condition cache */
11099 rb_str_cat2(str, "<ch>");
11100 break;
11101 case TS_FUNCPTR:
11103 void *func = (void *)OPERAND_AT(iobj, j);
11104 #ifdef HAVE_DLADDR
11105 Dl_info info;
11106 if (dladdr(func, &info) && info.dli_sname) {
11107 rb_str_cat2(str, info.dli_sname);
11108 break;
11110 #endif
11111 rb_str_catf(str, "<%p>", func);
11113 break;
11114 case TS_BUILTIN:
11115 rb_str_cat2(str, "<TS_BUILTIN>");
11116 break;
11117 default:{
11118 rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
11121 if (types[j + 1]) {
11122 rb_str_cat2(str, ", ");
11126 return str;
11129 static void
11130 dump_disasm_list(const LINK_ELEMENT *link)
11132 dump_disasm_list_with_cursor(link, NULL, NULL);
11135 static void
11136 dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest)
11138 int pos = 0;
11139 INSN *iobj;
11140 LABEL *lobj;
11141 VALUE str;
11143 printf("-- raw disasm--------\n");
11145 while (link) {
11146 if (curr) printf(curr == link ? "*" : " ");
11147 switch (link->type) {
11148 case ISEQ_ELEMENT_INSN:
11150 iobj = (INSN *)link;
11151 str = insn_data_to_s_detail(iobj);
11152 printf(" %04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
11153 pos += insn_data_length(iobj);
11154 break;
11156 case ISEQ_ELEMENT_LABEL:
11158 lobj = (LABEL *)link;
11159 printf(LABEL_FORMAT" [sp: %d, unremovable: %d, refcnt: %d]%s\n", lobj->label_no, lobj->sp, lobj->unremovable, lobj->refcnt,
11160 dest == lobj ? " <---" : "");
11161 break;
11163 case ISEQ_ELEMENT_TRACE:
11165 TRACE *trace = (TRACE *)link;
11166 printf(" trace: %0x\n", trace->event);
11167 break;
11169 case ISEQ_ELEMENT_ADJUST:
11171 ADJUST *adjust = (ADJUST *)link;
11172 printf(" adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
11173 break;
11175 default:
11176 /* ignore */
11177 rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type));
11179 link = link->next;
11181 printf("---------------------\n");
11182 fflush(stdout);
11186 rb_insn_len(VALUE insn)
11188 return insn_len(insn);
11191 const char *
11192 rb_insns_name(int i)
11194 return insn_name(i);
11197 VALUE
11198 rb_insns_name_array(void)
11200 VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
11201 int i;
11202 for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
11203 rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
11205 return rb_obj_freeze(ary);
11208 static LABEL *
11209 register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
11211 LABEL *label = 0;
11212 st_data_t tmp;
11213 obj = rb_to_symbol_type(obj);
11215 if (st_lookup(labels_table, obj, &tmp) == 0) {
11216 label = NEW_LABEL(0);
11217 st_insert(labels_table, obj, (st_data_t)label);
11219 else {
11220 label = (LABEL *)tmp;
11222 LABEL_REF(label);
11223 return label;
11226 static VALUE
11227 get_exception_sym2type(VALUE sym)
11229 static VALUE symRescue, symEnsure, symRetry;
11230 static VALUE symBreak, symRedo, symNext;
11232 if (symRescue == 0) {
11233 symRescue = ID2SYM(rb_intern_const("rescue"));
11234 symEnsure = ID2SYM(rb_intern_const("ensure"));
11235 symRetry = ID2SYM(rb_intern_const("retry"));
11236 symBreak = ID2SYM(rb_intern_const("break"));
11237 symRedo = ID2SYM(rb_intern_const("redo"));
11238 symNext = ID2SYM(rb_intern_const("next"));
11241 if (sym == symRescue) return CATCH_TYPE_RESCUE;
11242 if (sym == symEnsure) return CATCH_TYPE_ENSURE;
11243 if (sym == symRetry) return CATCH_TYPE_RETRY;
11244 if (sym == symBreak) return CATCH_TYPE_BREAK;
11245 if (sym == symRedo) return CATCH_TYPE_REDO;
11246 if (sym == symNext) return CATCH_TYPE_NEXT;
11247 rb_raise(rb_eSyntaxError, "invalid exception symbol: %+"PRIsVALUE, sym);
11248 return 0;
11251 static int
11252 iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
11253 VALUE exception)
11255 int i;
11257 for (i=0; i<RARRAY_LEN(exception); i++) {
11258 const rb_iseq_t *eiseq;
11259 VALUE v, type;
11260 LABEL *lstart, *lend, *lcont;
11261 unsigned int sp;
11263 v = rb_to_array_type(RARRAY_AREF(exception, i));
11264 if (RARRAY_LEN(v) != 6) {
11265 rb_raise(rb_eSyntaxError, "wrong exception entry");
11267 type = get_exception_sym2type(RARRAY_AREF(v, 0));
11268 if (NIL_P(RARRAY_AREF(v, 1))) {
11269 eiseq = NULL;
11271 else {
11272 eiseq = rb_iseqw_to_iseq(rb_iseq_load(RARRAY_AREF(v, 1), (VALUE)iseq, Qnil));
11275 lstart = register_label(iseq, labels_table, RARRAY_AREF(v, 2));
11276 lend = register_label(iseq, labels_table, RARRAY_AREF(v, 3));
11277 lcont = register_label(iseq, labels_table, RARRAY_AREF(v, 4));
11278 sp = NUM2UINT(RARRAY_AREF(v, 5));
11280 /* TODO: Dirty Hack! Fix me */
11281 if (type == CATCH_TYPE_RESCUE ||
11282 type == CATCH_TYPE_BREAK ||
11283 type == CATCH_TYPE_NEXT) {
11284 ++sp;
11287 lcont->sp = sp;
11289 ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
11291 RB_GC_GUARD(v);
11293 return COMPILE_OK;
11296 static struct st_table *
11297 insn_make_insn_table(void)
11299 struct st_table *table;
11300 int i;
11301 table = st_init_numtable_with_size(VM_INSTRUCTION_SIZE);
11303 for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
11304 st_insert(table, ID2SYM(rb_intern_const(insn_name(i))), i);
11307 return table;
11310 static const rb_iseq_t *
11311 iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
11313 VALUE iseqw;
11314 const rb_iseq_t *loaded_iseq;
11316 if (RB_TYPE_P(op, T_ARRAY)) {
11317 iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
11319 else if (CLASS_OF(op) == rb_cISeq) {
11320 iseqw = op;
11322 else {
11323 rb_raise(rb_eSyntaxError, "ISEQ is required");
11326 loaded_iseq = rb_iseqw_to_iseq(iseqw);
11327 return loaded_iseq;
11330 static VALUE
11331 iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
11333 ID mid = 0;
11334 int orig_argc = 0;
11335 unsigned int flag = 0;
11336 struct rb_callinfo_kwarg *kw_arg = 0;
11338 if (!NIL_P(op)) {
11339 VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern_const("mid")));
11340 VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern_const("flag")));
11341 VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern_const("orig_argc")));
11342 VALUE vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern_const("kw_arg")));
11344 if (!NIL_P(vmid)) mid = SYM2ID(vmid);
11345 if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
11346 if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
11348 if (!NIL_P(vkw_arg)) {
11349 int i;
11350 int len = RARRAY_LENINT(vkw_arg);
11351 size_t n = rb_callinfo_kwarg_bytes(len);
11353 kw_arg = xmalloc(n);
11354 kw_arg->references = 0;
11355 kw_arg->keyword_len = len;
11356 for (i = 0; i < len; i++) {
11357 VALUE kw = RARRAY_AREF(vkw_arg, i);
11358 SYM2ID(kw); /* make immortal */
11359 kw_arg->keywords[i] = kw;
11364 const struct rb_callinfo *ci = new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
11365 RB_OBJ_WRITTEN(iseq, Qundef, ci);
11366 return (VALUE)ci;
11369 static rb_event_flag_t
11370 event_name_to_flag(VALUE sym)
11372 #define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern_const(#ev))) return ev;
11373 CHECK_EVENT(RUBY_EVENT_LINE);
11374 CHECK_EVENT(RUBY_EVENT_CLASS);
11375 CHECK_EVENT(RUBY_EVENT_END);
11376 CHECK_EVENT(RUBY_EVENT_CALL);
11377 CHECK_EVENT(RUBY_EVENT_RETURN);
11378 CHECK_EVENT(RUBY_EVENT_B_CALL);
11379 CHECK_EVENT(RUBY_EVENT_B_RETURN);
11380 CHECK_EVENT(RUBY_EVENT_RESCUE);
11381 #undef CHECK_EVENT
11382 return RUBY_EVENT_NONE;
11385 static int
11386 iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
11387 VALUE body, VALUE node_ids, VALUE labels_wrapper)
11389 /* TODO: body should be frozen */
11390 long i, len = RARRAY_LEN(body);
11391 struct st_table *labels_table = DATA_PTR(labels_wrapper);
11392 int j;
11393 int line_no = 0, node_id = -1, insn_idx = 0;
11394 int ret = COMPILE_OK;
11397 * index -> LABEL *label
11399 static struct st_table *insn_table;
11401 if (insn_table == 0) {
11402 insn_table = insn_make_insn_table();
11405 for (i=0; i<len; i++) {
11406 VALUE obj = RARRAY_AREF(body, i);
11408 if (SYMBOL_P(obj)) {
11409 rb_event_flag_t event;
11410 if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
11411 ADD_TRACE(anchor, event);
11413 else {
11414 LABEL *label = register_label(iseq, labels_table, obj);
11415 ADD_LABEL(anchor, label);
11418 else if (FIXNUM_P(obj)) {
11419 line_no = NUM2INT(obj);
11421 else if (RB_TYPE_P(obj, T_ARRAY)) {
11422 VALUE *argv = 0;
11423 int argc = RARRAY_LENINT(obj) - 1;
11424 st_data_t insn_id;
11425 VALUE insn;
11427 if (node_ids) {
11428 node_id = NUM2INT(rb_ary_entry(node_ids, insn_idx++));
11431 insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
11432 if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
11433 /* TODO: exception */
11434 COMPILE_ERROR(iseq, line_no,
11435 "unknown instruction: %+"PRIsVALUE, insn);
11436 ret = COMPILE_NG;
11437 break;
11440 if (argc != insn_len((VALUE)insn_id)-1) {
11441 COMPILE_ERROR(iseq, line_no,
11442 "operand size mismatch");
11443 ret = COMPILE_NG;
11444 break;
11447 if (argc > 0) {
11448 argv = compile_data_calloc2(iseq, sizeof(VALUE), argc);
11450 // add element before operand setup to make GC root
11451 ADD_ELEM(anchor,
11452 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
11453 (enum ruby_vminsn_type)insn_id, argc, argv));
11455 for (j=0; j<argc; j++) {
11456 VALUE op = rb_ary_entry(obj, j+1);
11457 switch (insn_op_type((VALUE)insn_id, j)) {
11458 case TS_OFFSET: {
11459 LABEL *label = register_label(iseq, labels_table, op);
11460 argv[j] = (VALUE)label;
11461 break;
11463 case TS_LINDEX:
11464 case TS_NUM:
11465 (void)NUM2INT(op);
11466 argv[j] = op;
11467 break;
11468 case TS_VALUE:
11469 argv[j] = op;
11470 RB_OBJ_WRITTEN(iseq, Qundef, op);
11471 break;
11472 case TS_ISEQ:
11474 if (op != Qnil) {
11475 VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
11476 argv[j] = v;
11477 RB_OBJ_WRITTEN(iseq, Qundef, v);
11479 else {
11480 argv[j] = 0;
11483 break;
11484 case TS_ISE:
11485 argv[j] = op;
11486 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ise_size) {
11487 ISEQ_BODY(iseq)->ise_size = NUM2INT(op) + 1;
11489 break;
11490 case TS_IC:
11492 VALUE segments = rb_ary_new();
11493 op = rb_to_array_type(op);
11495 for (int i = 0; i < RARRAY_LEN(op); i++) {
11496 VALUE sym = RARRAY_AREF(op, i);
11497 sym = rb_to_symbol_type(sym);
11498 rb_ary_push(segments, sym);
11501 RB_GC_GUARD(op);
11502 argv[j] = segments;
11503 RB_OBJ_WRITTEN(iseq, Qundef, segments);
11504 ISEQ_BODY(iseq)->ic_size++;
11506 break;
11507 case TS_IVC: /* inline ivar cache */
11508 argv[j] = op;
11509 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ivc_size) {
11510 ISEQ_BODY(iseq)->ivc_size = NUM2INT(op) + 1;
11512 break;
11513 case TS_ICVARC: /* inline cvar cache */
11514 argv[j] = op;
11515 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->icvarc_size) {
11516 ISEQ_BODY(iseq)->icvarc_size = NUM2INT(op) + 1;
11518 break;
11519 case TS_CALLDATA:
11520 argv[j] = iseq_build_callinfo_from_hash(iseq, op);
11521 break;
11522 case TS_ID:
11523 argv[j] = rb_to_symbol_type(op);
11524 break;
11525 case TS_CDHASH:
11527 int i;
11528 VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
11530 RHASH_TBL_RAW(map)->type = &cdhash_type;
11531 op = rb_to_array_type(op);
11532 for (i=0; i<RARRAY_LEN(op); i+=2) {
11533 VALUE key = RARRAY_AREF(op, i);
11534 VALUE sym = RARRAY_AREF(op, i+1);
11535 LABEL *label =
11536 register_label(iseq, labels_table, sym);
11537 rb_hash_aset(map, key, (VALUE)label | 1);
11539 RB_GC_GUARD(op);
11540 argv[j] = map;
11541 RB_OBJ_WRITTEN(iseq, Qundef, map);
11543 break;
11544 case TS_FUNCPTR:
11546 #if SIZEOF_VALUE <= SIZEOF_LONG
11547 long funcptr = NUM2LONG(op);
11548 #else
11549 LONG_LONG funcptr = NUM2LL(op);
11550 #endif
11551 argv[j] = (VALUE)funcptr;
11553 break;
11554 default:
11555 rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
11559 else {
11560 ADD_ELEM(anchor,
11561 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
11562 (enum ruby_vminsn_type)insn_id, argc, NULL));
11565 else {
11566 rb_raise(rb_eTypeError, "unexpected object for instruction");
11569 DATA_PTR(labels_wrapper) = 0;
11570 RB_GC_GUARD(labels_wrapper);
11571 validate_labels(iseq, labels_table);
11572 if (!ret) return ret;
11573 return iseq_setup(iseq, anchor);
11576 #define CHECK_ARRAY(v) rb_to_array_type(v)
11577 #define CHECK_SYMBOL(v) rb_to_symbol_type(v)
11579 static int
11580 int_param(int *dst, VALUE param, VALUE sym)
11582 VALUE val = rb_hash_aref(param, sym);
11583 if (FIXNUM_P(val)) {
11584 *dst = FIX2INT(val);
11585 return TRUE;
11587 else if (!NIL_P(val)) {
11588 rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
11589 sym, val);
11591 return FALSE;
11594 static const struct rb_iseq_param_keyword *
11595 iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
11597 int i, j;
11598 int len = RARRAY_LENINT(keywords);
11599 int default_len;
11600 VALUE key, sym, default_val;
11601 VALUE *dvs;
11602 ID *ids;
11603 struct rb_iseq_param_keyword *keyword = ZALLOC(struct rb_iseq_param_keyword);
11605 ISEQ_BODY(iseq)->param.flags.has_kw = TRUE;
11607 keyword->num = len;
11608 #define SYM(s) ID2SYM(rb_intern_const(#s))
11609 (void)int_param(&keyword->bits_start, params, SYM(kwbits));
11610 i = keyword->bits_start - keyword->num;
11611 ids = (ID *)&ISEQ_BODY(iseq)->local_table[i];
11612 #undef SYM
11614 /* required args */
11615 for (i = 0; i < len; i++) {
11616 VALUE val = RARRAY_AREF(keywords, i);
11618 if (!SYMBOL_P(val)) {
11619 goto default_values;
11621 ids[i] = SYM2ID(val);
11622 keyword->required_num++;
11625 default_values: /* note: we intentionally preserve `i' from previous loop */
11626 default_len = len - i;
11627 if (default_len == 0) {
11628 keyword->table = ids;
11629 return keyword;
11631 else if (default_len < 0) {
11632 UNREACHABLE;
11635 dvs = ALLOC_N(VALUE, (unsigned int)default_len);
11637 for (j = 0; i < len; i++, j++) {
11638 key = RARRAY_AREF(keywords, i);
11639 CHECK_ARRAY(key);
11641 switch (RARRAY_LEN(key)) {
11642 case 1:
11643 sym = RARRAY_AREF(key, 0);
11644 default_val = Qundef;
11645 break;
11646 case 2:
11647 sym = RARRAY_AREF(key, 0);
11648 default_val = RARRAY_AREF(key, 1);
11649 break;
11650 default:
11651 rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
11653 ids[i] = SYM2ID(sym);
11654 dvs[j] = default_val;
11657 keyword->table = ids;
11658 keyword->default_values = dvs;
11660 return keyword;
11663 static void
11664 iseq_insn_each_object_mark_and_pin(VALUE obj, VALUE _)
11666 rb_gc_mark(obj);
11669 void
11670 rb_iseq_mark_and_pin_insn_storage(struct iseq_compile_data_storage *storage)
11672 INSN *iobj = 0;
11673 size_t size = sizeof(INSN);
11674 unsigned int pos = 0;
11676 while (storage) {
11677 #ifdef STRICT_ALIGNMENT
11678 size_t padding = calc_padding((void *)&storage->buff[pos], size);
11679 #else
11680 const size_t padding = 0; /* expected to be optimized by compiler */
11681 #endif /* STRICT_ALIGNMENT */
11682 size_t offset = pos + size + padding;
11683 if (offset > storage->size || offset > storage->pos) {
11684 pos = 0;
11685 storage = storage->next;
11687 else {
11688 #ifdef STRICT_ALIGNMENT
11689 pos += (int)padding;
11690 #endif /* STRICT_ALIGNMENT */
11692 iobj = (INSN *)&storage->buff[pos];
11694 if (iobj->operands) {
11695 iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark_and_pin, (VALUE)0);
11697 pos += (int)size;
11702 void
11703 rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
11704 VALUE exception, VALUE body)
11706 #define SYM(s) ID2SYM(rb_intern_const(#s))
11707 int i, len;
11708 unsigned int arg_size, local_size, stack_max;
11709 ID *tbl;
11710 struct st_table *labels_table = st_init_numtable();
11711 VALUE labels_wrapper = Data_Wrap_Struct(0, rb_mark_set, st_free_table, labels_table);
11712 VALUE arg_opt_labels = rb_hash_aref(params, SYM(opt));
11713 VALUE keywords = rb_hash_aref(params, SYM(keyword));
11714 VALUE sym_arg_rest = ID2SYM(rb_intern_const("#arg_rest"));
11715 DECL_ANCHOR(anchor);
11716 INIT_ANCHOR(anchor);
11718 len = RARRAY_LENINT(locals);
11719 ISEQ_BODY(iseq)->local_table_size = len;
11720 ISEQ_BODY(iseq)->local_table = tbl = len > 0 ? (ID *)ALLOC_N(ID, ISEQ_BODY(iseq)->local_table_size) : NULL;
11722 for (i = 0; i < len; i++) {
11723 VALUE lv = RARRAY_AREF(locals, i);
11725 if (sym_arg_rest == lv) {
11726 tbl[i] = 0;
11728 else {
11729 tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
11733 #define INT_PARAM(F) int_param(&ISEQ_BODY(iseq)->param.F, params, SYM(F))
11734 if (INT_PARAM(lead_num)) {
11735 ISEQ_BODY(iseq)->param.flags.has_lead = TRUE;
11737 if (INT_PARAM(post_num)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
11738 if (INT_PARAM(post_start)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
11739 if (INT_PARAM(rest_start)) ISEQ_BODY(iseq)->param.flags.has_rest = TRUE;
11740 if (INT_PARAM(block_start)) ISEQ_BODY(iseq)->param.flags.has_block = TRUE;
11741 #undef INT_PARAM
11743 #define INT_PARAM(F) F = (int_param(&x, misc, SYM(F)) ? (unsigned int)x : 0)
11744 int x;
11745 INT_PARAM(arg_size);
11746 INT_PARAM(local_size);
11747 INT_PARAM(stack_max);
11748 #undef INT_PARAM
11751 VALUE node_ids = Qfalse;
11752 #ifdef USE_ISEQ_NODE_ID
11753 node_ids = rb_hash_aref(misc, ID2SYM(rb_intern("node_ids")));
11754 if (!RB_TYPE_P(node_ids, T_ARRAY)) {
11755 rb_raise(rb_eTypeError, "node_ids is not an array");
11757 #endif
11759 if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
11760 len = RARRAY_LENINT(arg_opt_labels);
11761 ISEQ_BODY(iseq)->param.flags.has_opt = !!(len - 1 >= 0);
11763 if (ISEQ_BODY(iseq)->param.flags.has_opt) {
11764 VALUE *opt_table = ALLOC_N(VALUE, len);
11766 for (i = 0; i < len; i++) {
11767 VALUE ent = RARRAY_AREF(arg_opt_labels, i);
11768 LABEL *label = register_label(iseq, labels_table, ent);
11769 opt_table[i] = (VALUE)label;
11772 ISEQ_BODY(iseq)->param.opt_num = len - 1;
11773 ISEQ_BODY(iseq)->param.opt_table = opt_table;
11776 else if (!NIL_P(arg_opt_labels)) {
11777 rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
11778 arg_opt_labels);
11781 if (RB_TYPE_P(keywords, T_ARRAY)) {
11782 ISEQ_BODY(iseq)->param.keyword = iseq_build_kw(iseq, params, keywords);
11784 else if (!NIL_P(keywords)) {
11785 rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
11786 keywords);
11789 if (Qtrue == rb_hash_aref(params, SYM(ambiguous_param0))) {
11790 ISEQ_BODY(iseq)->param.flags.ambiguous_param0 = TRUE;
11793 if (Qtrue == rb_hash_aref(params, SYM(use_block))) {
11794 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
11797 if (int_param(&i, params, SYM(kwrest))) {
11798 struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)ISEQ_BODY(iseq)->param.keyword;
11799 if (keyword == NULL) {
11800 ISEQ_BODY(iseq)->param.keyword = keyword = ZALLOC(struct rb_iseq_param_keyword);
11802 keyword->rest_start = i;
11803 ISEQ_BODY(iseq)->param.flags.has_kwrest = TRUE;
11805 #undef SYM
11806 iseq_calc_param_size(iseq);
11808 /* exception */
11809 iseq_build_from_ary_exception(iseq, labels_table, exception);
11811 /* body */
11812 iseq_build_from_ary_body(iseq, anchor, body, node_ids, labels_wrapper);
11814 ISEQ_BODY(iseq)->param.size = arg_size;
11815 ISEQ_BODY(iseq)->local_table_size = local_size;
11816 ISEQ_BODY(iseq)->stack_max = stack_max;
11819 /* for parser */
11822 rb_dvar_defined(ID id, const rb_iseq_t *iseq)
11824 if (iseq) {
11825 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
11826 while (body->type == ISEQ_TYPE_BLOCK ||
11827 body->type == ISEQ_TYPE_RESCUE ||
11828 body->type == ISEQ_TYPE_ENSURE ||
11829 body->type == ISEQ_TYPE_EVAL ||
11830 body->type == ISEQ_TYPE_MAIN
11832 unsigned int i;
11834 for (i = 0; i < body->local_table_size; i++) {
11835 if (body->local_table[i] == id) {
11836 return 1;
11839 iseq = body->parent_iseq;
11840 body = ISEQ_BODY(iseq);
11843 return 0;
11847 rb_local_defined(ID id, const rb_iseq_t *iseq)
11849 if (iseq) {
11850 unsigned int i;
11851 const struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
11853 for (i=0; i<body->local_table_size; i++) {
11854 if (body->local_table[i] == id) {
11855 return 1;
11859 return 0;
11862 /* ISeq binary format */
11864 #ifndef IBF_ISEQ_DEBUG
11865 #define IBF_ISEQ_DEBUG 0
11866 #endif
11868 #ifndef IBF_ISEQ_ENABLE_LOCAL_BUFFER
11869 #define IBF_ISEQ_ENABLE_LOCAL_BUFFER 0
11870 #endif
11872 typedef uint32_t ibf_offset_t;
11873 #define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
11875 #define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
11876 #ifdef RUBY_DEVEL
11877 #define IBF_DEVEL_VERSION 4
11878 #define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
11879 #else
11880 #define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
11881 #endif
11883 static const char IBF_ENDIAN_MARK =
11884 #ifdef WORDS_BIGENDIAN
11886 #else
11888 #endif
11891 struct ibf_header {
11892 char magic[4]; /* YARB */
11893 uint32_t major_version;
11894 uint32_t minor_version;
11895 uint32_t size;
11896 uint32_t extra_size;
11898 uint32_t iseq_list_size;
11899 uint32_t global_object_list_size;
11900 ibf_offset_t iseq_list_offset;
11901 ibf_offset_t global_object_list_offset;
11902 uint8_t endian;
11903 uint8_t wordsize; /* assume no 2048-bit CPU */
11906 struct ibf_dump_buffer {
11907 VALUE str;
11908 st_table *obj_table; /* obj -> obj number */
11911 struct ibf_dump {
11912 st_table *iseq_table; /* iseq -> iseq number */
11913 struct ibf_dump_buffer global_buffer;
11914 struct ibf_dump_buffer *current_buffer;
11917 struct ibf_load_buffer {
11918 const char *buff;
11919 ibf_offset_t size;
11921 VALUE obj_list; /* [obj0, ...] */
11922 unsigned int obj_list_size;
11923 ibf_offset_t obj_list_offset;
11926 struct ibf_load {
11927 const struct ibf_header *header;
11928 VALUE iseq_list; /* [iseq0, ...] */
11929 struct ibf_load_buffer global_buffer;
11930 VALUE loader_obj;
11931 rb_iseq_t *iseq;
11932 VALUE str;
11933 struct ibf_load_buffer *current_buffer;
11936 struct pinned_list {
11937 long size;
11938 VALUE buffer[1];
11941 static void
11942 pinned_list_mark(void *ptr)
11944 long i;
11945 struct pinned_list *list = (struct pinned_list *)ptr;
11946 for (i = 0; i < list->size; i++) {
11947 if (list->buffer[i]) {
11948 rb_gc_mark(list->buffer[i]);
11953 static const rb_data_type_t pinned_list_type = {
11954 "pinned_list",
11956 pinned_list_mark,
11957 RUBY_DEFAULT_FREE,
11958 NULL, // No external memory to report,
11960 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
11963 static VALUE
11964 pinned_list_fetch(VALUE list, long offset)
11966 struct pinned_list * ptr;
11968 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
11970 if (offset >= ptr->size) {
11971 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
11974 return ptr->buffer[offset];
11977 static void
11978 pinned_list_store(VALUE list, long offset, VALUE object)
11980 struct pinned_list * ptr;
11982 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
11984 if (offset >= ptr->size) {
11985 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
11988 RB_OBJ_WRITE(list, &ptr->buffer[offset], object);
11991 static VALUE
11992 pinned_list_new(long size)
11994 size_t memsize = offsetof(struct pinned_list, buffer) + size * sizeof(VALUE);
11995 VALUE obj_list = rb_data_typed_object_zalloc(0, memsize, &pinned_list_type);
11996 struct pinned_list * ptr = RTYPEDDATA_GET_DATA(obj_list);
11997 ptr->size = size;
11998 return obj_list;
12001 static ibf_offset_t
12002 ibf_dump_pos(struct ibf_dump *dump)
12004 long pos = RSTRING_LEN(dump->current_buffer->str);
12005 #if SIZEOF_LONG > SIZEOF_INT
12006 if (pos >= UINT_MAX) {
12007 rb_raise(rb_eRuntimeError, "dump size exceeds");
12009 #endif
12010 return (unsigned int)pos;
12013 static void
12014 ibf_dump_align(struct ibf_dump *dump, size_t align)
12016 ibf_offset_t pos = ibf_dump_pos(dump);
12017 if (pos % align) {
12018 static const char padding[sizeof(VALUE)];
12019 size_t size = align - ((size_t)pos % align);
12020 #if SIZEOF_LONG > SIZEOF_INT
12021 if (pos + size >= UINT_MAX) {
12022 rb_raise(rb_eRuntimeError, "dump size exceeds");
12024 #endif
12025 for (; size > sizeof(padding); size -= sizeof(padding)) {
12026 rb_str_cat(dump->current_buffer->str, padding, sizeof(padding));
12028 rb_str_cat(dump->current_buffer->str, padding, size);
12032 static ibf_offset_t
12033 ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size)
12035 ibf_offset_t pos = ibf_dump_pos(dump);
12036 rb_str_cat(dump->current_buffer->str, (const char *)buff, size);
12037 /* TODO: overflow check */
12038 return pos;
12041 static ibf_offset_t
12042 ibf_dump_write_byte(struct ibf_dump *dump, unsigned char byte)
12044 return ibf_dump_write(dump, &byte, sizeof(unsigned char));
12047 static void
12048 ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long offset)
12050 VALUE str = dump->current_buffer->str;
12051 char *ptr = RSTRING_PTR(str);
12052 if ((unsigned long)(size + offset) > (unsigned long)RSTRING_LEN(str))
12053 rb_bug("ibf_dump_overwrite: overflow");
12054 memcpy(ptr + offset, buff, size);
12057 static const void *
12058 ibf_load_ptr(const struct ibf_load *load, ibf_offset_t *offset, int size)
12060 ibf_offset_t beg = *offset;
12061 *offset += size;
12062 return load->current_buffer->buff + beg;
12065 static void *
12066 ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, size_t x, size_t y)
12068 void *buff = ruby_xmalloc2(x, y);
12069 size_t size = x * y;
12070 memcpy(buff, load->current_buffer->buff + offset, size);
12071 return buff;
12074 #define IBF_W_ALIGN(type) (RUBY_ALIGNOF(type) > 1 ? ibf_dump_align(dump, RUBY_ALIGNOF(type)) : (void)0)
12076 #define IBF_W(b, type, n) (IBF_W_ALIGN(type), (type *)(VALUE)IBF_WP(b, type, n))
12077 #define IBF_WV(variable) ibf_dump_write(dump, &(variable), sizeof(variable))
12078 #define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n))
12079 #define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type), (n))
12080 #define IBF_ZERO(variable) memset(&(variable), 0, sizeof(variable))
12082 static int
12083 ibf_table_lookup(struct st_table *table, st_data_t key)
12085 st_data_t val;
12087 if (st_lookup(table, key, &val)) {
12088 return (int)val;
12090 else {
12091 return -1;
12095 static int
12096 ibf_table_find_or_insert(struct st_table *table, st_data_t key)
12098 int index = ibf_table_lookup(table, key);
12100 if (index < 0) { /* not found */
12101 index = (int)table->num_entries;
12102 st_insert(table, key, (st_data_t)index);
12105 return index;
12108 /* dump/load generic */
12110 static void ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size);
12112 static VALUE ibf_load_object(const struct ibf_load *load, VALUE object_index);
12113 static rb_iseq_t *ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq);
12115 static st_table *
12116 ibf_dump_object_table_new(void)
12118 st_table *obj_table = st_init_numtable(); /* need free */
12119 st_insert(obj_table, (st_data_t)Qnil, (st_data_t)0); /* 0th is nil */
12121 return obj_table;
12124 static VALUE
12125 ibf_dump_object(struct ibf_dump *dump, VALUE obj)
12127 return ibf_table_find_or_insert(dump->current_buffer->obj_table, (st_data_t)obj);
12130 static VALUE
12131 ibf_dump_id(struct ibf_dump *dump, ID id)
12133 if (id == 0 || rb_id2name(id) == NULL) {
12134 return 0;
12136 return ibf_dump_object(dump, rb_id2sym(id));
12139 static ID
12140 ibf_load_id(const struct ibf_load *load, const ID id_index)
12142 if (id_index == 0) {
12143 return 0;
12145 VALUE sym = ibf_load_object(load, id_index);
12146 if (rb_integer_type_p(sym)) {
12147 /* Load hidden local variables as indexes */
12148 return NUM2ULONG(sym);
12150 return rb_sym2id(sym);
12153 /* dump/load: code */
12155 static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq);
12157 static int
12158 ibf_dump_iseq(struct ibf_dump *dump, const rb_iseq_t *iseq)
12160 if (iseq == NULL) {
12161 return -1;
12163 else {
12164 return ibf_table_find_or_insert(dump->iseq_table, (st_data_t)iseq);
12168 static unsigned char
12169 ibf_load_byte(const struct ibf_load *load, ibf_offset_t *offset)
12171 if (*offset >= load->current_buffer->size) { rb_raise(rb_eRuntimeError, "invalid bytecode"); }
12172 return (unsigned char)load->current_buffer->buff[(*offset)++];
12176 * Small uint serialization
12177 * 0x00000000_00000000 - 0x00000000_0000007f: 1byte | XXXX XXX1 |
12178 * 0x00000000_00000080 - 0x00000000_00003fff: 2byte | XXXX XX10 | XXXX XXXX |
12179 * 0x00000000_00004000 - 0x00000000_001fffff: 3byte | XXXX X100 | XXXX XXXX | XXXX XXXX |
12180 * 0x00000000_00020000 - 0x00000000_0fffffff: 4byte | XXXX 1000 | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12181 * ...
12182 * 0x00010000_00000000 - 0x00ffffff_ffffffff: 8byte | 1000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12183 * 0x01000000_00000000 - 0xffffffff_ffffffff: 9byte | 0000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12185 static void
12186 ibf_dump_write_small_value(struct ibf_dump *dump, VALUE x)
12188 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12189 ibf_dump_write(dump, &x, sizeof(VALUE));
12190 return;
12193 enum { max_byte_length = sizeof(VALUE) + 1 };
12195 unsigned char bytes[max_byte_length];
12196 ibf_offset_t n;
12198 for (n = 0; n < sizeof(VALUE) && (x >> (7 - n)); n++, x >>= 8) {
12199 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12202 x <<= 1;
12203 x |= 1;
12204 x <<= n;
12205 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12206 n++;
12208 ibf_dump_write(dump, bytes + max_byte_length - n, n);
12211 static VALUE
12212 ibf_load_small_value(const struct ibf_load *load, ibf_offset_t *offset)
12214 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12215 union { char s[sizeof(VALUE)]; VALUE v; } x;
12217 memcpy(x.s, load->current_buffer->buff + *offset, sizeof(VALUE));
12218 *offset += sizeof(VALUE);
12220 return x.v;
12223 enum { max_byte_length = sizeof(VALUE) + 1 };
12225 const unsigned char *buffer = (const unsigned char *)load->current_buffer->buff;
12226 const unsigned char c = buffer[*offset];
12228 ibf_offset_t n =
12229 c & 1 ? 1 :
12230 c == 0 ? 9 : ntz_int32(c) + 1;
12231 VALUE x = (VALUE)c >> n;
12233 if (*offset + n > load->current_buffer->size) {
12234 rb_raise(rb_eRuntimeError, "invalid byte sequence");
12237 ibf_offset_t i;
12238 for (i = 1; i < n; i++) {
12239 x <<= 8;
12240 x |= (VALUE)buffer[*offset + i];
12243 *offset += n;
12244 return x;
12247 static void
12248 ibf_dump_builtin(struct ibf_dump *dump, const struct rb_builtin_function *bf)
12250 // short: index
12251 // short: name.length
12252 // bytes: name
12253 // // omit argc (only verify with name)
12254 ibf_dump_write_small_value(dump, (VALUE)bf->index);
12256 size_t len = strlen(bf->name);
12257 ibf_dump_write_small_value(dump, (VALUE)len);
12258 ibf_dump_write(dump, bf->name, len);
12261 static const struct rb_builtin_function *
12262 ibf_load_builtin(const struct ibf_load *load, ibf_offset_t *offset)
12264 int i = (int)ibf_load_small_value(load, offset);
12265 int len = (int)ibf_load_small_value(load, offset);
12266 const char *name = (char *)ibf_load_ptr(load, offset, len);
12268 if (0) {
12269 fprintf(stderr, "%.*s!!\n", len, name);
12272 const struct rb_builtin_function *table = GET_VM()->builtin_function_table;
12273 if (table == NULL) rb_raise(rb_eArgError, "builtin function table is not provided");
12274 if (strncmp(table[i].name, name, len) != 0) {
12275 rb_raise(rb_eArgError, "builtin function index (%d) mismatch (expect %s but %s)", i, name, table[i].name);
12277 // fprintf(stderr, "load-builtin: name:%s(%d)\n", table[i].name, table[i].argc);
12279 return &table[i];
12282 static ibf_offset_t
12283 ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
12285 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
12286 const int iseq_size = body->iseq_size;
12287 int code_index;
12288 const VALUE *orig_code = rb_iseq_original_iseq(iseq);
12290 ibf_offset_t offset = ibf_dump_pos(dump);
12292 for (code_index=0; code_index<iseq_size;) {
12293 const VALUE insn = orig_code[code_index++];
12294 const char *types = insn_op_types(insn);
12295 int op_index;
12297 /* opcode */
12298 if (insn >= 0x100) { rb_raise(rb_eRuntimeError, "invalid instruction"); }
12299 ibf_dump_write_small_value(dump, insn);
12301 /* operands */
12302 for (op_index=0; types[op_index]; op_index++, code_index++) {
12303 VALUE op = orig_code[code_index];
12304 VALUE wv;
12306 switch (types[op_index]) {
12307 case TS_CDHASH:
12308 case TS_VALUE:
12309 wv = ibf_dump_object(dump, op);
12310 break;
12311 case TS_ISEQ:
12312 wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
12313 break;
12314 case TS_IC:
12316 IC ic = (IC)op;
12317 VALUE arr = idlist_to_array(ic->segments);
12318 wv = ibf_dump_object(dump, arr);
12320 break;
12321 case TS_ISE:
12322 case TS_IVC:
12323 case TS_ICVARC:
12325 union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)op;
12326 wv = is - ISEQ_IS_ENTRY_START(body, types[op_index]);
12328 break;
12329 case TS_CALLDATA:
12331 goto skip_wv;
12333 case TS_ID:
12334 wv = ibf_dump_id(dump, (ID)op);
12335 break;
12336 case TS_FUNCPTR:
12337 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
12338 goto skip_wv;
12339 case TS_BUILTIN:
12340 ibf_dump_builtin(dump, (const struct rb_builtin_function *)op);
12341 goto skip_wv;
12342 default:
12343 wv = op;
12344 break;
12346 ibf_dump_write_small_value(dump, wv);
12347 skip_wv:;
12349 RUBY_ASSERT(insn_len(insn) == op_index+1);
12352 return offset;
12355 static VALUE *
12356 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)
12358 VALUE iseqv = (VALUE)iseq;
12359 unsigned int code_index;
12360 ibf_offset_t reading_pos = bytecode_offset;
12361 VALUE *code = ALLOC_N(VALUE, iseq_size);
12363 struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq);
12364 struct rb_call_data *cd_entries = load_body->call_data;
12365 int ic_index = 0;
12367 iseq_bits_t * mark_offset_bits;
12369 iseq_bits_t tmp[1] = {0};
12371 if (ISEQ_MBITS_BUFLEN(iseq_size) == 1) {
12372 mark_offset_bits = tmp;
12374 else {
12375 mark_offset_bits = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(iseq_size));
12377 bool needs_bitmap = false;
12379 for (code_index=0; code_index<iseq_size;) {
12380 /* opcode */
12381 const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos);
12382 const char *types = insn_op_types(insn);
12383 int op_index;
12385 code_index++;
12387 /* operands */
12388 for (op_index=0; types[op_index]; op_index++, code_index++) {
12389 const char operand_type = types[op_index];
12390 switch (operand_type) {
12391 case TS_VALUE:
12393 VALUE op = ibf_load_small_value(load, &reading_pos);
12394 VALUE v = ibf_load_object(load, op);
12395 code[code_index] = v;
12396 if (!SPECIAL_CONST_P(v)) {
12397 RB_OBJ_WRITTEN(iseqv, Qundef, v);
12398 ISEQ_MBITS_SET(mark_offset_bits, code_index);
12399 needs_bitmap = true;
12401 break;
12403 case TS_CDHASH:
12405 VALUE op = ibf_load_small_value(load, &reading_pos);
12406 VALUE v = ibf_load_object(load, op);
12407 v = rb_hash_dup(v); // hash dumped as frozen
12408 RHASH_TBL_RAW(v)->type = &cdhash_type;
12409 rb_hash_rehash(v); // hash function changed
12410 freeze_hide_obj(v);
12412 // Overwrite the existing hash in the object list. This
12413 // is to keep the object alive during load time.
12414 // [Bug #17984] [ruby-core:104259]
12415 pinned_list_store(load->current_buffer->obj_list, (long)op, v);
12417 code[code_index] = v;
12418 ISEQ_MBITS_SET(mark_offset_bits, code_index);
12419 RB_OBJ_WRITTEN(iseqv, Qundef, v);
12420 needs_bitmap = true;
12421 break;
12423 case TS_ISEQ:
12425 VALUE op = (VALUE)ibf_load_small_value(load, &reading_pos);
12426 VALUE v = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
12427 code[code_index] = v;
12428 if (!SPECIAL_CONST_P(v)) {
12429 RB_OBJ_WRITTEN(iseqv, Qundef, v);
12430 ISEQ_MBITS_SET(mark_offset_bits, code_index);
12431 needs_bitmap = true;
12433 break;
12435 case TS_IC:
12437 VALUE op = ibf_load_small_value(load, &reading_pos);
12438 VALUE arr = ibf_load_object(load, op);
12440 IC ic = &ISEQ_IS_IC_ENTRY(load_body, ic_index++);
12441 ic->segments = array_to_idlist(arr);
12443 code[code_index] = (VALUE)ic;
12445 break;
12446 case TS_ISE:
12447 case TS_ICVARC:
12448 case TS_IVC:
12450 unsigned int op = (unsigned int)ibf_load_small_value(load, &reading_pos);
12452 ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op;
12453 code[code_index] = (VALUE)ic;
12455 if (operand_type == TS_IVC) {
12456 IVC cache = (IVC)ic;
12458 if (insn == BIN(setinstancevariable)) {
12459 ID iv_name = (ID)code[code_index - 1];
12460 cache->iv_set_name = iv_name;
12462 else {
12463 cache->iv_set_name = 0;
12466 vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
12470 break;
12471 case TS_CALLDATA:
12473 code[code_index] = (VALUE)cd_entries++;
12475 break;
12476 case TS_ID:
12478 VALUE op = ibf_load_small_value(load, &reading_pos);
12479 code[code_index] = ibf_load_id(load, (ID)(VALUE)op);
12481 break;
12482 case TS_FUNCPTR:
12483 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
12484 break;
12485 case TS_BUILTIN:
12486 code[code_index] = (VALUE)ibf_load_builtin(load, &reading_pos);
12487 break;
12488 default:
12489 code[code_index] = ibf_load_small_value(load, &reading_pos);
12490 continue;
12493 if (insn_len(insn) != op_index+1) {
12494 rb_raise(rb_eRuntimeError, "operand size mismatch");
12498 load_body->iseq_encoded = code;
12499 load_body->iseq_size = code_index;
12501 if (ISEQ_MBITS_BUFLEN(load_body->iseq_size) == 1) {
12502 load_body->mark_bits.single = mark_offset_bits[0];
12504 else {
12505 if (needs_bitmap) {
12506 load_body->mark_bits.list = mark_offset_bits;
12508 else {
12509 load_body->mark_bits.list = 0;
12510 ruby_xfree(mark_offset_bits);
12514 RUBY_ASSERT(code_index == iseq_size);
12515 RUBY_ASSERT(reading_pos == bytecode_offset + bytecode_size);
12516 return code;
12519 static ibf_offset_t
12520 ibf_dump_param_opt_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
12522 int opt_num = ISEQ_BODY(iseq)->param.opt_num;
12524 if (opt_num > 0) {
12525 IBF_W_ALIGN(VALUE);
12526 return ibf_dump_write(dump, ISEQ_BODY(iseq)->param.opt_table, sizeof(VALUE) * (opt_num + 1));
12528 else {
12529 return ibf_dump_pos(dump);
12533 static VALUE *
12534 ibf_load_param_opt_table(const struct ibf_load *load, ibf_offset_t opt_table_offset, int opt_num)
12536 if (opt_num > 0) {
12537 VALUE *table = ALLOC_N(VALUE, opt_num+1);
12538 MEMCPY(table, load->current_buffer->buff + opt_table_offset, VALUE, opt_num+1);
12539 return table;
12541 else {
12542 return NULL;
12546 static ibf_offset_t
12547 ibf_dump_param_keyword(struct ibf_dump *dump, const rb_iseq_t *iseq)
12549 const struct rb_iseq_param_keyword *kw = ISEQ_BODY(iseq)->param.keyword;
12551 if (kw) {
12552 struct rb_iseq_param_keyword dump_kw = *kw;
12553 int dv_num = kw->num - kw->required_num;
12554 ID *ids = kw->num > 0 ? ALLOCA_N(ID, kw->num) : NULL;
12555 VALUE *dvs = dv_num > 0 ? ALLOCA_N(VALUE, dv_num) : NULL;
12556 int i;
12558 for (i=0; i<kw->num; i++) ids[i] = (ID)ibf_dump_id(dump, kw->table[i]);
12559 for (i=0; i<dv_num; i++) dvs[i] = (VALUE)ibf_dump_object(dump, kw->default_values[i]);
12561 dump_kw.table = IBF_W(ids, ID, kw->num);
12562 dump_kw.default_values = IBF_W(dvs, VALUE, dv_num);
12563 IBF_W_ALIGN(struct rb_iseq_param_keyword);
12564 return ibf_dump_write(dump, &dump_kw, sizeof(struct rb_iseq_param_keyword) * 1);
12566 else {
12567 return 0;
12571 static const struct rb_iseq_param_keyword *
12572 ibf_load_param_keyword(const struct ibf_load *load, ibf_offset_t param_keyword_offset)
12574 if (param_keyword_offset) {
12575 struct rb_iseq_param_keyword *kw = IBF_R(param_keyword_offset, struct rb_iseq_param_keyword, 1);
12576 ID *ids = IBF_R(kw->table, ID, kw->num);
12577 int dv_num = kw->num - kw->required_num;
12578 VALUE *dvs = IBF_R(kw->default_values, VALUE, dv_num);
12579 int i;
12581 for (i=0; i<kw->num; i++) {
12582 ids[i] = ibf_load_id(load, ids[i]);
12584 for (i=0; i<dv_num; i++) {
12585 dvs[i] = ibf_load_object(load, dvs[i]);
12588 kw->table = ids;
12589 kw->default_values = dvs;
12590 return kw;
12592 else {
12593 return NULL;
12597 static ibf_offset_t
12598 ibf_dump_insns_info_body(struct ibf_dump *dump, const rb_iseq_t *iseq)
12600 ibf_offset_t offset = ibf_dump_pos(dump);
12601 const struct iseq_insn_info_entry *entries = ISEQ_BODY(iseq)->insns_info.body;
12603 unsigned int i;
12604 for (i = 0; i < ISEQ_BODY(iseq)->insns_info.size; i++) {
12605 ibf_dump_write_small_value(dump, entries[i].line_no);
12606 #ifdef USE_ISEQ_NODE_ID
12607 ibf_dump_write_small_value(dump, entries[i].node_id);
12608 #endif
12609 ibf_dump_write_small_value(dump, entries[i].events);
12612 return offset;
12615 static struct iseq_insn_info_entry *
12616 ibf_load_insns_info_body(const struct ibf_load *load, ibf_offset_t body_offset, unsigned int size)
12618 ibf_offset_t reading_pos = body_offset;
12619 struct iseq_insn_info_entry *entries = ALLOC_N(struct iseq_insn_info_entry, size);
12621 unsigned int i;
12622 for (i = 0; i < size; i++) {
12623 entries[i].line_no = (int)ibf_load_small_value(load, &reading_pos);
12624 #ifdef USE_ISEQ_NODE_ID
12625 entries[i].node_id = (int)ibf_load_small_value(load, &reading_pos);
12626 #endif
12627 entries[i].events = (rb_event_flag_t)ibf_load_small_value(load, &reading_pos);
12630 return entries;
12633 static ibf_offset_t
12634 ibf_dump_insns_info_positions(struct ibf_dump *dump, const unsigned int *positions, unsigned int size)
12636 ibf_offset_t offset = ibf_dump_pos(dump);
12638 unsigned int last = 0;
12639 unsigned int i;
12640 for (i = 0; i < size; i++) {
12641 ibf_dump_write_small_value(dump, positions[i] - last);
12642 last = positions[i];
12645 return offset;
12648 static unsigned int *
12649 ibf_load_insns_info_positions(const struct ibf_load *load, ibf_offset_t positions_offset, unsigned int size)
12651 ibf_offset_t reading_pos = positions_offset;
12652 unsigned int *positions = ALLOC_N(unsigned int, size);
12654 unsigned int last = 0;
12655 unsigned int i;
12656 for (i = 0; i < size; i++) {
12657 positions[i] = last + (unsigned int)ibf_load_small_value(load, &reading_pos);
12658 last = positions[i];
12661 return positions;
12664 static ibf_offset_t
12665 ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
12667 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
12668 const int size = body->local_table_size;
12669 ID *table = ALLOCA_N(ID, size);
12670 int i;
12672 for (i=0; i<size; i++) {
12673 VALUE v = ibf_dump_id(dump, body->local_table[i]);
12674 if (v == 0) {
12675 /* Dump hidden local variables as indexes, so load_from_binary will work with them */
12676 v = ibf_dump_object(dump, ULONG2NUM(body->local_table[i]));
12678 table[i] = v;
12681 IBF_W_ALIGN(ID);
12682 return ibf_dump_write(dump, table, sizeof(ID) * size);
12685 static ID *
12686 ibf_load_local_table(const struct ibf_load *load, ibf_offset_t local_table_offset, int size)
12688 if (size > 0) {
12689 ID *table = IBF_R(local_table_offset, ID, size);
12690 int i;
12692 for (i=0; i<size; i++) {
12693 table[i] = ibf_load_id(load, table[i]);
12695 return table;
12697 else {
12698 return NULL;
12702 static ibf_offset_t
12703 ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
12705 const struct iseq_catch_table *table = ISEQ_BODY(iseq)->catch_table;
12707 if (table) {
12708 int *iseq_indices = ALLOCA_N(int, table->size);
12709 unsigned int i;
12711 for (i=0; i<table->size; i++) {
12712 iseq_indices[i] = ibf_dump_iseq(dump, table->entries[i].iseq);
12715 const ibf_offset_t offset = ibf_dump_pos(dump);
12717 for (i=0; i<table->size; i++) {
12718 ibf_dump_write_small_value(dump, iseq_indices[i]);
12719 ibf_dump_write_small_value(dump, table->entries[i].type);
12720 ibf_dump_write_small_value(dump, table->entries[i].start);
12721 ibf_dump_write_small_value(dump, table->entries[i].end);
12722 ibf_dump_write_small_value(dump, table->entries[i].cont);
12723 ibf_dump_write_small_value(dump, table->entries[i].sp);
12725 return offset;
12727 else {
12728 return ibf_dump_pos(dump);
12732 static struct iseq_catch_table *
12733 ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size)
12735 if (size) {
12736 struct iseq_catch_table *table = ruby_xmalloc(iseq_catch_table_bytes(size));
12737 table->size = size;
12739 ibf_offset_t reading_pos = catch_table_offset;
12741 unsigned int i;
12742 for (i=0; i<table->size; i++) {
12743 int iseq_index = (int)ibf_load_small_value(load, &reading_pos);
12744 table->entries[i].type = (enum rb_catch_type)ibf_load_small_value(load, &reading_pos);
12745 table->entries[i].start = (unsigned int)ibf_load_small_value(load, &reading_pos);
12746 table->entries[i].end = (unsigned int)ibf_load_small_value(load, &reading_pos);
12747 table->entries[i].cont = (unsigned int)ibf_load_small_value(load, &reading_pos);
12748 table->entries[i].sp = (unsigned int)ibf_load_small_value(load, &reading_pos);
12750 table->entries[i].iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index);
12752 return table;
12754 else {
12755 return NULL;
12759 static ibf_offset_t
12760 ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
12762 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
12763 const unsigned int ci_size = body->ci_size;
12764 const struct rb_call_data *cds = body->call_data;
12766 ibf_offset_t offset = ibf_dump_pos(dump);
12768 unsigned int i;
12770 for (i = 0; i < ci_size; i++) {
12771 const struct rb_callinfo *ci = cds[i].ci;
12772 if (ci != NULL) {
12773 ibf_dump_write_small_value(dump, ibf_dump_id(dump, vm_ci_mid(ci)));
12774 ibf_dump_write_small_value(dump, vm_ci_flag(ci));
12775 ibf_dump_write_small_value(dump, vm_ci_argc(ci));
12777 const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
12778 if (kwarg) {
12779 int len = kwarg->keyword_len;
12780 ibf_dump_write_small_value(dump, len);
12781 for (int j=0; j<len; j++) {
12782 VALUE keyword = ibf_dump_object(dump, kwarg->keywords[j]);
12783 ibf_dump_write_small_value(dump, keyword);
12786 else {
12787 ibf_dump_write_small_value(dump, 0);
12790 else {
12791 // TODO: truncate NULL ci from call_data.
12792 ibf_dump_write_small_value(dump, (VALUE)-1);
12796 return offset;
12799 struct outer_variable_pair {
12800 ID id;
12801 VALUE name;
12802 VALUE val;
12805 struct outer_variable_list {
12806 size_t num;
12807 struct outer_variable_pair pairs[1];
12810 static enum rb_id_table_iterator_result
12811 store_outer_variable(ID id, VALUE val, void *dump)
12813 struct outer_variable_list *ovlist = dump;
12814 struct outer_variable_pair *pair = &ovlist->pairs[ovlist->num++];
12815 pair->id = id;
12816 pair->name = rb_id2str(id);
12817 pair->val = val;
12818 return ID_TABLE_CONTINUE;
12821 static int
12822 outer_variable_cmp(const void *a, const void *b, void *arg)
12824 const struct outer_variable_pair *ap = (const struct outer_variable_pair *)a;
12825 const struct outer_variable_pair *bp = (const struct outer_variable_pair *)b;
12826 return rb_str_cmp(ap->name, bp->name);
12829 static ibf_offset_t
12830 ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
12832 struct rb_id_table * ovs = ISEQ_BODY(iseq)->outer_variables;
12834 ibf_offset_t offset = ibf_dump_pos(dump);
12836 size_t size = ovs ? rb_id_table_size(ovs) : 0;
12837 ibf_dump_write_small_value(dump, (VALUE)size);
12838 if (size > 0) {
12839 VALUE buff;
12840 size_t buffsize =
12841 rb_size_mul_add_or_raise(sizeof(struct outer_variable_pair), size,
12842 offsetof(struct outer_variable_list, pairs),
12843 rb_eArgError);
12844 struct outer_variable_list *ovlist = RB_ALLOCV(buff, buffsize);
12845 ovlist->num = 0;
12846 rb_id_table_foreach(ovs, store_outer_variable, ovlist);
12847 ruby_qsort(ovlist->pairs, size, sizeof(struct outer_variable_pair), outer_variable_cmp, NULL);
12848 for (size_t i = 0; i < size; ++i) {
12849 ID id = ovlist->pairs[i].id;
12850 ID val = ovlist->pairs[i].val;
12851 ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
12852 ibf_dump_write_small_value(dump, val);
12856 return offset;
12859 /* note that we dump out rb_call_info but load back rb_call_data */
12860 static void
12861 ibf_load_ci_entries(const struct ibf_load *load,
12862 ibf_offset_t ci_entries_offset,
12863 unsigned int ci_size,
12864 struct rb_call_data **cd_ptr)
12866 ibf_offset_t reading_pos = ci_entries_offset;
12868 unsigned int i;
12870 struct rb_call_data *cds = ZALLOC_N(struct rb_call_data, ci_size);
12871 *cd_ptr = cds;
12873 for (i = 0; i < ci_size; i++) {
12874 VALUE mid_index = ibf_load_small_value(load, &reading_pos);
12875 if (mid_index != (VALUE)-1) {
12876 ID mid = ibf_load_id(load, mid_index);
12877 unsigned int flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
12878 unsigned int argc = (unsigned int)ibf_load_small_value(load, &reading_pos);
12880 struct rb_callinfo_kwarg *kwarg = NULL;
12881 int kwlen = (int)ibf_load_small_value(load, &reading_pos);
12882 if (kwlen > 0) {
12883 kwarg = rb_xmalloc_mul_add(kwlen, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
12884 kwarg->references = 0;
12885 kwarg->keyword_len = kwlen;
12886 for (int j=0; j<kwlen; j++) {
12887 VALUE keyword = ibf_load_small_value(load, &reading_pos);
12888 kwarg->keywords[j] = ibf_load_object(load, keyword);
12892 cds[i].ci = vm_ci_new(mid, flag, argc, kwarg);
12893 RB_OBJ_WRITTEN(load->iseq, Qundef, cds[i].ci);
12894 cds[i].cc = vm_cc_empty();
12896 else {
12897 // NULL ci
12898 cds[i].ci = NULL;
12899 cds[i].cc = NULL;
12904 static struct rb_id_table *
12905 ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variables_offset)
12907 ibf_offset_t reading_pos = outer_variables_offset;
12909 struct rb_id_table *tbl = NULL;
12911 size_t table_size = (size_t)ibf_load_small_value(load, &reading_pos);
12913 if (table_size > 0) {
12914 tbl = rb_id_table_create(table_size);
12917 for (size_t i = 0; i < table_size; i++) {
12918 ID key = ibf_load_id(load, (ID)ibf_load_small_value(load, &reading_pos));
12919 VALUE value = ibf_load_small_value(load, &reading_pos);
12920 if (!key) key = rb_make_temporary_id(i);
12921 rb_id_table_insert(tbl, key, value);
12924 return tbl;
12927 static ibf_offset_t
12928 ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
12930 RUBY_ASSERT(dump->current_buffer == &dump->global_buffer);
12932 unsigned int *positions;
12934 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
12936 const VALUE location_pathobj_index = ibf_dump_object(dump, body->location.pathobj); /* TODO: freeze */
12937 const VALUE location_base_label_index = ibf_dump_object(dump, body->location.base_label);
12938 const VALUE location_label_index = ibf_dump_object(dump, body->location.label);
12940 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
12941 ibf_offset_t iseq_start = ibf_dump_pos(dump);
12943 struct ibf_dump_buffer *saved_buffer = dump->current_buffer;
12944 struct ibf_dump_buffer buffer;
12945 buffer.str = rb_str_new(0, 0);
12946 buffer.obj_table = ibf_dump_object_table_new();
12947 dump->current_buffer = &buffer;
12948 #endif
12950 const ibf_offset_t bytecode_offset = ibf_dump_code(dump, iseq);
12951 const ibf_offset_t bytecode_size = ibf_dump_pos(dump) - bytecode_offset;
12952 const ibf_offset_t param_opt_table_offset = ibf_dump_param_opt_table(dump, iseq);
12953 const ibf_offset_t param_keyword_offset = ibf_dump_param_keyword(dump, iseq);
12954 const ibf_offset_t insns_info_body_offset = ibf_dump_insns_info_body(dump, iseq);
12956 positions = rb_iseq_insns_info_decode_positions(ISEQ_BODY(iseq));
12957 const ibf_offset_t insns_info_positions_offset = ibf_dump_insns_info_positions(dump, positions, body->insns_info.size);
12958 ruby_xfree(positions);
12960 const ibf_offset_t local_table_offset = ibf_dump_local_table(dump, iseq);
12961 const unsigned int catch_table_size = body->catch_table ? body->catch_table->size : 0;
12962 const ibf_offset_t catch_table_offset = ibf_dump_catch_table(dump, iseq);
12963 const int parent_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->parent_iseq);
12964 const int local_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->local_iseq);
12965 const int mandatory_only_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->mandatory_only_iseq);
12966 const ibf_offset_t ci_entries_offset = ibf_dump_ci_entries(dump, iseq);
12967 const ibf_offset_t outer_variables_offset = ibf_dump_outer_variables(dump, iseq);
12969 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
12970 ibf_offset_t local_obj_list_offset;
12971 unsigned int local_obj_list_size;
12973 ibf_dump_object_list(dump, &local_obj_list_offset, &local_obj_list_size);
12974 #endif
12976 ibf_offset_t body_offset = ibf_dump_pos(dump);
12978 /* dump the constant body */
12979 unsigned int param_flags =
12980 (body->param.flags.has_lead << 0) |
12981 (body->param.flags.has_opt << 1) |
12982 (body->param.flags.has_rest << 2) |
12983 (body->param.flags.has_post << 3) |
12984 (body->param.flags.has_kw << 4) |
12985 (body->param.flags.has_kwrest << 5) |
12986 (body->param.flags.has_block << 6) |
12987 (body->param.flags.ambiguous_param0 << 7) |
12988 (body->param.flags.accepts_no_kwarg << 8) |
12989 (body->param.flags.ruby2_keywords << 9) |
12990 (body->param.flags.anon_rest << 10) |
12991 (body->param.flags.anon_kwrest << 11) |
12992 (body->param.flags.use_block << 12);
12994 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
12995 # define IBF_BODY_OFFSET(x) (x)
12996 #else
12997 # define IBF_BODY_OFFSET(x) (body_offset - (x))
12998 #endif
13000 ibf_dump_write_small_value(dump, body->type);
13001 ibf_dump_write_small_value(dump, body->iseq_size);
13002 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(bytecode_offset));
13003 ibf_dump_write_small_value(dump, bytecode_size);
13004 ibf_dump_write_small_value(dump, param_flags);
13005 ibf_dump_write_small_value(dump, body->param.size);
13006 ibf_dump_write_small_value(dump, body->param.lead_num);
13007 ibf_dump_write_small_value(dump, body->param.opt_num);
13008 ibf_dump_write_small_value(dump, body->param.rest_start);
13009 ibf_dump_write_small_value(dump, body->param.post_start);
13010 ibf_dump_write_small_value(dump, body->param.post_num);
13011 ibf_dump_write_small_value(dump, body->param.block_start);
13012 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(param_opt_table_offset));
13013 ibf_dump_write_small_value(dump, param_keyword_offset);
13014 ibf_dump_write_small_value(dump, location_pathobj_index);
13015 ibf_dump_write_small_value(dump, location_base_label_index);
13016 ibf_dump_write_small_value(dump, location_label_index);
13017 ibf_dump_write_small_value(dump, body->location.first_lineno);
13018 ibf_dump_write_small_value(dump, body->location.node_id);
13019 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.lineno);
13020 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.column);
13021 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.lineno);
13022 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.column);
13023 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_body_offset));
13024 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_positions_offset));
13025 ibf_dump_write_small_value(dump, body->insns_info.size);
13026 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(local_table_offset));
13027 ibf_dump_write_small_value(dump, catch_table_size);
13028 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(catch_table_offset));
13029 ibf_dump_write_small_value(dump, parent_iseq_index);
13030 ibf_dump_write_small_value(dump, local_iseq_index);
13031 ibf_dump_write_small_value(dump, mandatory_only_iseq_index);
13032 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(ci_entries_offset));
13033 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(outer_variables_offset));
13034 ibf_dump_write_small_value(dump, body->variable.flip_count);
13035 ibf_dump_write_small_value(dump, body->local_table_size);
13036 ibf_dump_write_small_value(dump, body->ivc_size);
13037 ibf_dump_write_small_value(dump, body->icvarc_size);
13038 ibf_dump_write_small_value(dump, body->ise_size);
13039 ibf_dump_write_small_value(dump, body->ic_size);
13040 ibf_dump_write_small_value(dump, body->ci_size);
13041 ibf_dump_write_small_value(dump, body->stack_max);
13042 ibf_dump_write_small_value(dump, body->builtin_attrs);
13043 ibf_dump_write_small_value(dump, body->prism ? 1 : 0);
13045 #undef IBF_BODY_OFFSET
13047 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13048 ibf_offset_t iseq_length_bytes = ibf_dump_pos(dump);
13050 dump->current_buffer = saved_buffer;
13051 ibf_dump_write(dump, RSTRING_PTR(buffer.str), iseq_length_bytes);
13053 ibf_offset_t offset = ibf_dump_pos(dump);
13054 ibf_dump_write_small_value(dump, iseq_start);
13055 ibf_dump_write_small_value(dump, iseq_length_bytes);
13056 ibf_dump_write_small_value(dump, body_offset);
13058 ibf_dump_write_small_value(dump, local_obj_list_offset);
13059 ibf_dump_write_small_value(dump, local_obj_list_size);
13061 st_free_table(buffer.obj_table); // TODO: this leaks in case of exception
13063 return offset;
13064 #else
13065 return body_offset;
13066 #endif
13069 static VALUE
13070 ibf_load_location_str(const struct ibf_load *load, VALUE str_index)
13072 VALUE str = ibf_load_object(load, str_index);
13073 if (str != Qnil) {
13074 str = rb_fstring(str);
13076 return str;
13079 static void
13080 ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
13082 struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq) = rb_iseq_constant_body_alloc();
13084 ibf_offset_t reading_pos = offset;
13086 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13087 struct ibf_load_buffer *saved_buffer = load->current_buffer;
13088 load->current_buffer = &load->global_buffer;
13090 const ibf_offset_t iseq_start = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13091 const ibf_offset_t iseq_length_bytes = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13092 const ibf_offset_t body_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13094 struct ibf_load_buffer buffer;
13095 buffer.buff = load->global_buffer.buff + iseq_start;
13096 buffer.size = iseq_length_bytes;
13097 buffer.obj_list_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13098 buffer.obj_list_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13099 buffer.obj_list = pinned_list_new(buffer.obj_list_size);
13101 load->current_buffer = &buffer;
13102 reading_pos = body_offset;
13103 #endif
13105 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13106 # define IBF_BODY_OFFSET(x) (x)
13107 #else
13108 # define IBF_BODY_OFFSET(x) (offset - (x))
13109 #endif
13111 const unsigned int type = (unsigned int)ibf_load_small_value(load, &reading_pos);
13112 const unsigned int iseq_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13113 const ibf_offset_t bytecode_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13114 const ibf_offset_t bytecode_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13115 const unsigned int param_flags = (unsigned int)ibf_load_small_value(load, &reading_pos);
13116 const unsigned int param_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13117 const int param_lead_num = (int)ibf_load_small_value(load, &reading_pos);
13118 const int param_opt_num = (int)ibf_load_small_value(load, &reading_pos);
13119 const int param_rest_start = (int)ibf_load_small_value(load, &reading_pos);
13120 const int param_post_start = (int)ibf_load_small_value(load, &reading_pos);
13121 const int param_post_num = (int)ibf_load_small_value(load, &reading_pos);
13122 const int param_block_start = (int)ibf_load_small_value(load, &reading_pos);
13123 const ibf_offset_t param_opt_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13124 const ibf_offset_t param_keyword_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13125 const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos);
13126 const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos);
13127 const VALUE location_label_index = ibf_load_small_value(load, &reading_pos);
13128 const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos);
13129 const int location_node_id = (int)ibf_load_small_value(load, &reading_pos);
13130 const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
13131 const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos);
13132 const int location_code_location_end_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
13133 const int location_code_location_end_pos_column = (int)ibf_load_small_value(load, &reading_pos);
13134 const ibf_offset_t insns_info_body_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13135 const ibf_offset_t insns_info_positions_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13136 const unsigned int insns_info_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13137 const ibf_offset_t local_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13138 const unsigned int catch_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13139 const ibf_offset_t catch_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13140 const int parent_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13141 const int local_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13142 const int mandatory_only_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13143 const ibf_offset_t ci_entries_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13144 const ibf_offset_t outer_variables_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13145 const rb_snum_t variable_flip_count = (rb_snum_t)ibf_load_small_value(load, &reading_pos);
13146 const unsigned int local_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13148 const unsigned int ivc_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13149 const unsigned int icvarc_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13150 const unsigned int ise_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13151 const unsigned int ic_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13153 const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13154 const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos);
13155 const unsigned int builtin_attrs = (unsigned int)ibf_load_small_value(load, &reading_pos);
13156 const bool prism = (bool)ibf_load_small_value(load, &reading_pos);
13158 // setup fname and dummy frame
13159 VALUE path = ibf_load_object(load, location_pathobj_index);
13161 VALUE realpath = Qnil;
13163 if (RB_TYPE_P(path, T_STRING)) {
13164 realpath = path = rb_fstring(path);
13166 else if (RB_TYPE_P(path, T_ARRAY)) {
13167 VALUE pathobj = path;
13168 if (RARRAY_LEN(pathobj) != 2) {
13169 rb_raise(rb_eRuntimeError, "path object size mismatch");
13171 path = rb_fstring(RARRAY_AREF(pathobj, 0));
13172 realpath = RARRAY_AREF(pathobj, 1);
13173 if (!NIL_P(realpath)) {
13174 if (!RB_TYPE_P(realpath, T_STRING)) {
13175 rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
13176 "(%x), path=%+"PRIsVALUE,
13177 realpath, TYPE(realpath), path);
13179 realpath = rb_fstring(realpath);
13182 else {
13183 rb_raise(rb_eRuntimeError, "unexpected path object");
13185 rb_iseq_pathobj_set(iseq, path, realpath);
13188 // push dummy frame
13189 rb_execution_context_t *ec = GET_EC();
13190 VALUE dummy_frame = rb_vm_push_frame_fname(ec, path);
13192 #undef IBF_BODY_OFFSET
13194 load_body->type = type;
13195 load_body->stack_max = stack_max;
13196 load_body->param.flags.has_lead = (param_flags >> 0) & 1;
13197 load_body->param.flags.has_opt = (param_flags >> 1) & 1;
13198 load_body->param.flags.has_rest = (param_flags >> 2) & 1;
13199 load_body->param.flags.has_post = (param_flags >> 3) & 1;
13200 load_body->param.flags.has_kw = FALSE;
13201 load_body->param.flags.has_kwrest = (param_flags >> 5) & 1;
13202 load_body->param.flags.has_block = (param_flags >> 6) & 1;
13203 load_body->param.flags.ambiguous_param0 = (param_flags >> 7) & 1;
13204 load_body->param.flags.accepts_no_kwarg = (param_flags >> 8) & 1;
13205 load_body->param.flags.ruby2_keywords = (param_flags >> 9) & 1;
13206 load_body->param.flags.anon_rest = (param_flags >> 10) & 1;
13207 load_body->param.flags.anon_kwrest = (param_flags >> 11) & 1;
13208 load_body->param.flags.use_block = (param_flags >> 12) & 1;
13209 load_body->param.size = param_size;
13210 load_body->param.lead_num = param_lead_num;
13211 load_body->param.opt_num = param_opt_num;
13212 load_body->param.rest_start = param_rest_start;
13213 load_body->param.post_start = param_post_start;
13214 load_body->param.post_num = param_post_num;
13215 load_body->param.block_start = param_block_start;
13216 load_body->local_table_size = local_table_size;
13217 load_body->ci_size = ci_size;
13218 load_body->insns_info.size = insns_info_size;
13220 ISEQ_COVERAGE_SET(iseq, Qnil);
13221 ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
13222 load_body->variable.flip_count = variable_flip_count;
13223 load_body->variable.script_lines = Qnil;
13225 load_body->location.first_lineno = location_first_lineno;
13226 load_body->location.node_id = location_node_id;
13227 load_body->location.code_location.beg_pos.lineno = location_code_location_beg_pos_lineno;
13228 load_body->location.code_location.beg_pos.column = location_code_location_beg_pos_column;
13229 load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno;
13230 load_body->location.code_location.end_pos.column = location_code_location_end_pos_column;
13231 load_body->builtin_attrs = builtin_attrs;
13232 load_body->prism = prism;
13234 load_body->ivc_size = ivc_size;
13235 load_body->icvarc_size = icvarc_size;
13236 load_body->ise_size = ise_size;
13237 load_body->ic_size = ic_size;
13239 if (ISEQ_IS_SIZE(load_body)) {
13240 load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(load_body));
13242 else {
13243 load_body->is_entries = NULL;
13245 ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
13246 load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset);
13247 load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
13248 load_body->param.keyword = ibf_load_param_keyword(load, param_keyword_offset);
13249 load_body->param.flags.has_kw = (param_flags >> 4) & 1;
13250 load_body->insns_info.body = ibf_load_insns_info_body(load, insns_info_body_offset, insns_info_size);
13251 load_body->insns_info.positions = ibf_load_insns_info_positions(load, insns_info_positions_offset, insns_info_size);
13252 load_body->local_table = ibf_load_local_table(load, local_table_offset, local_table_size);
13253 load_body->catch_table = ibf_load_catch_table(load, catch_table_offset, catch_table_size);
13254 load_body->parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index);
13255 load_body->local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index);
13256 load_body->mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index);
13258 ibf_load_code(load, iseq, bytecode_offset, bytecode_size, iseq_size);
13259 #if VM_INSN_INFO_TABLE_IMPL == 2
13260 rb_iseq_insns_info_encode_positions(iseq);
13261 #endif
13263 rb_iseq_translate_threaded_code(iseq);
13265 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13266 load->current_buffer = &load->global_buffer;
13267 #endif
13269 RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, location_base_label_index));
13270 RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, location_label_index));
13272 #if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13273 load->current_buffer = saved_buffer;
13274 #endif
13275 verify_call_cache(iseq);
13277 RB_GC_GUARD(dummy_frame);
13278 rb_vm_pop_frame_no_int(ec);
13281 struct ibf_dump_iseq_list_arg
13283 struct ibf_dump *dump;
13284 VALUE offset_list;
13287 static int
13288 ibf_dump_iseq_list_i(st_data_t key, st_data_t val, st_data_t ptr)
13290 const rb_iseq_t *iseq = (const rb_iseq_t *)key;
13291 struct ibf_dump_iseq_list_arg *args = (struct ibf_dump_iseq_list_arg *)ptr;
13293 ibf_offset_t offset = ibf_dump_iseq_each(args->dump, iseq);
13294 rb_ary_push(args->offset_list, UINT2NUM(offset));
13296 return ST_CONTINUE;
13299 static void
13300 ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header)
13302 VALUE offset_list = rb_ary_hidden_new(dump->iseq_table->num_entries);
13304 struct ibf_dump_iseq_list_arg args;
13305 args.dump = dump;
13306 args.offset_list = offset_list;
13308 st_foreach(dump->iseq_table, ibf_dump_iseq_list_i, (st_data_t)&args);
13310 st_index_t i;
13311 st_index_t size = dump->iseq_table->num_entries;
13312 ibf_offset_t *offsets = ALLOCA_N(ibf_offset_t, size);
13314 for (i = 0; i < size; i++) {
13315 offsets[i] = NUM2UINT(RARRAY_AREF(offset_list, i));
13318 ibf_dump_align(dump, sizeof(ibf_offset_t));
13319 header->iseq_list_offset = ibf_dump_write(dump, offsets, sizeof(ibf_offset_t) * size);
13320 header->iseq_list_size = (unsigned int)size;
13323 #define IBF_OBJECT_INTERNAL FL_PROMOTED0
13326 * Binary format
13327 * - ibf_object_header
13328 * - ibf_object_xxx (xxx is type)
13331 struct ibf_object_header {
13332 unsigned int type: 5;
13333 unsigned int special_const: 1;
13334 unsigned int frozen: 1;
13335 unsigned int internal: 1;
13338 enum ibf_object_class_index {
13339 IBF_OBJECT_CLASS_OBJECT,
13340 IBF_OBJECT_CLASS_ARRAY,
13341 IBF_OBJECT_CLASS_STANDARD_ERROR,
13342 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR,
13343 IBF_OBJECT_CLASS_TYPE_ERROR,
13344 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR,
13347 struct ibf_object_regexp {
13348 long srcstr;
13349 char option;
13352 struct ibf_object_hash {
13353 long len;
13354 long keyval[FLEX_ARY_LEN];
13357 struct ibf_object_struct_range {
13358 long class_index;
13359 long len;
13360 long beg;
13361 long end;
13362 int excl;
13365 struct ibf_object_bignum {
13366 ssize_t slen;
13367 BDIGIT digits[FLEX_ARY_LEN];
13370 enum ibf_object_data_type {
13371 IBF_OBJECT_DATA_ENCODING,
13374 struct ibf_object_complex_rational {
13375 long a, b;
13378 struct ibf_object_symbol {
13379 long str;
13382 #define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \
13383 ((((offset) - 1) / (align) + 1) * (align))
13384 #define IBF_OBJBODY(type, offset) (const type *)\
13385 ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset))
13387 static const void *
13388 ibf_load_check_offset(const struct ibf_load *load, size_t offset)
13390 if (offset >= load->current_buffer->size) {
13391 rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, offset);
13393 return load->current_buffer->buff + offset;
13396 NORETURN(static void ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj));
13398 static void
13399 ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj)
13401 char buff[0x100];
13402 rb_raw_obj_info(buff, sizeof(buff), obj);
13403 rb_raise(rb_eNotImpError, "ibf_dump_object_unsupported: %s", buff);
13406 NORETURN(static VALUE ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset));
13408 static VALUE
13409 ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13411 rb_raise(rb_eArgError, "unsupported");
13412 UNREACHABLE_RETURN(Qnil);
13415 static void
13416 ibf_dump_object_class(struct ibf_dump *dump, VALUE obj)
13418 enum ibf_object_class_index cindex;
13419 if (obj == rb_cObject) {
13420 cindex = IBF_OBJECT_CLASS_OBJECT;
13422 else if (obj == rb_cArray) {
13423 cindex = IBF_OBJECT_CLASS_ARRAY;
13425 else if (obj == rb_eStandardError) {
13426 cindex = IBF_OBJECT_CLASS_STANDARD_ERROR;
13428 else if (obj == rb_eNoMatchingPatternError) {
13429 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR;
13431 else if (obj == rb_eTypeError) {
13432 cindex = IBF_OBJECT_CLASS_TYPE_ERROR;
13434 else if (obj == rb_eNoMatchingPatternKeyError) {
13435 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR;
13437 else {
13438 rb_obj_info_dump(obj);
13439 rb_p(obj);
13440 rb_bug("unsupported class");
13442 ibf_dump_write_small_value(dump, (VALUE)cindex);
13445 static VALUE
13446 ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13448 enum ibf_object_class_index cindex = (enum ibf_object_class_index)ibf_load_small_value(load, &offset);
13450 switch (cindex) {
13451 case IBF_OBJECT_CLASS_OBJECT:
13452 return rb_cObject;
13453 case IBF_OBJECT_CLASS_ARRAY:
13454 return rb_cArray;
13455 case IBF_OBJECT_CLASS_STANDARD_ERROR:
13456 return rb_eStandardError;
13457 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR:
13458 return rb_eNoMatchingPatternError;
13459 case IBF_OBJECT_CLASS_TYPE_ERROR:
13460 return rb_eTypeError;
13461 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR:
13462 return rb_eNoMatchingPatternKeyError;
13465 rb_raise(rb_eArgError, "ibf_load_object_class: unknown class (%d)", (int)cindex);
13469 static void
13470 ibf_dump_object_float(struct ibf_dump *dump, VALUE obj)
13472 double dbl = RFLOAT_VALUE(obj);
13473 (void)IBF_W(&dbl, double, 1);
13476 static VALUE
13477 ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13479 const double *dblp = IBF_OBJBODY(double, offset);
13480 return DBL2NUM(*dblp);
13483 static void
13484 ibf_dump_object_string(struct ibf_dump *dump, VALUE obj)
13486 long encindex = (long)rb_enc_get_index(obj);
13487 long len = RSTRING_LEN(obj);
13488 const char *ptr = RSTRING_PTR(obj);
13490 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
13491 rb_encoding *enc = rb_enc_from_index((int)encindex);
13492 const char *enc_name = rb_enc_name(enc);
13493 encindex = RUBY_ENCINDEX_BUILTIN_MAX + ibf_dump_object(dump, rb_str_new2(enc_name));
13496 ibf_dump_write_small_value(dump, encindex);
13497 ibf_dump_write_small_value(dump, len);
13498 IBF_WP(ptr, char, len);
13501 static VALUE
13502 ibf_load_object_string(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13504 ibf_offset_t reading_pos = offset;
13506 int encindex = (int)ibf_load_small_value(load, &reading_pos);
13507 const long len = (long)ibf_load_small_value(load, &reading_pos);
13508 const char *ptr = load->current_buffer->buff + reading_pos;
13510 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
13511 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
13512 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
13515 VALUE str;
13516 if (header->frozen && !header->internal) {
13517 str = rb_enc_interned_str(ptr, len, rb_enc_from_index(encindex));
13519 else {
13520 str = rb_enc_str_new(ptr, len, rb_enc_from_index(encindex));
13522 if (header->internal) rb_obj_hide(str);
13523 if (header->frozen) str = rb_fstring(str);
13525 return str;
13528 static void
13529 ibf_dump_object_regexp(struct ibf_dump *dump, VALUE obj)
13531 VALUE srcstr = RREGEXP_SRC(obj);
13532 struct ibf_object_regexp regexp;
13533 regexp.option = (char)rb_reg_options(obj);
13534 regexp.srcstr = (long)ibf_dump_object(dump, srcstr);
13536 ibf_dump_write_byte(dump, (unsigned char)regexp.option);
13537 ibf_dump_write_small_value(dump, regexp.srcstr);
13540 static VALUE
13541 ibf_load_object_regexp(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13543 struct ibf_object_regexp regexp;
13544 regexp.option = ibf_load_byte(load, &offset);
13545 regexp.srcstr = ibf_load_small_value(load, &offset);
13547 VALUE srcstr = ibf_load_object(load, regexp.srcstr);
13548 VALUE reg = rb_reg_compile(srcstr, (int)regexp.option, NULL, 0);
13550 if (header->internal) rb_obj_hide(reg);
13551 if (header->frozen) rb_obj_freeze(reg);
13553 return reg;
13556 static void
13557 ibf_dump_object_array(struct ibf_dump *dump, VALUE obj)
13559 long i, len = RARRAY_LEN(obj);
13560 ibf_dump_write_small_value(dump, len);
13561 for (i=0; i<len; i++) {
13562 long index = (long)ibf_dump_object(dump, RARRAY_AREF(obj, i));
13563 ibf_dump_write_small_value(dump, index);
13567 static VALUE
13568 ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13570 ibf_offset_t reading_pos = offset;
13572 const long len = (long)ibf_load_small_value(load, &reading_pos);
13574 VALUE ary = header->internal ? rb_ary_hidden_new(len) : rb_ary_new_capa(len);
13575 int i;
13577 for (i=0; i<len; i++) {
13578 const VALUE index = ibf_load_small_value(load, &reading_pos);
13579 rb_ary_push(ary, ibf_load_object(load, index));
13582 if (header->frozen) rb_obj_freeze(ary);
13584 return ary;
13587 static int
13588 ibf_dump_object_hash_i(st_data_t key, st_data_t val, st_data_t ptr)
13590 struct ibf_dump *dump = (struct ibf_dump *)ptr;
13592 VALUE key_index = ibf_dump_object(dump, (VALUE)key);
13593 VALUE val_index = ibf_dump_object(dump, (VALUE)val);
13595 ibf_dump_write_small_value(dump, key_index);
13596 ibf_dump_write_small_value(dump, val_index);
13597 return ST_CONTINUE;
13600 static void
13601 ibf_dump_object_hash(struct ibf_dump *dump, VALUE obj)
13603 long len = RHASH_SIZE(obj);
13604 ibf_dump_write_small_value(dump, (VALUE)len);
13606 if (len > 0) rb_hash_foreach(obj, ibf_dump_object_hash_i, (VALUE)dump);
13609 static VALUE
13610 ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13612 long len = (long)ibf_load_small_value(load, &offset);
13613 VALUE obj = rb_hash_new_with_size(len);
13614 int i;
13616 for (i = 0; i < len; i++) {
13617 VALUE key_index = ibf_load_small_value(load, &offset);
13618 VALUE val_index = ibf_load_small_value(load, &offset);
13620 VALUE key = ibf_load_object(load, key_index);
13621 VALUE val = ibf_load_object(load, val_index);
13622 rb_hash_aset(obj, key, val);
13624 rb_hash_rehash(obj);
13626 if (header->internal) rb_obj_hide(obj);
13627 if (header->frozen) rb_obj_freeze(obj);
13629 return obj;
13632 static void
13633 ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
13635 if (rb_obj_is_kind_of(obj, rb_cRange)) {
13636 struct ibf_object_struct_range range;
13637 VALUE beg, end;
13638 IBF_ZERO(range);
13639 range.len = 3;
13640 range.class_index = 0;
13642 rb_range_values(obj, &beg, &end, &range.excl);
13643 range.beg = (long)ibf_dump_object(dump, beg);
13644 range.end = (long)ibf_dump_object(dump, end);
13646 IBF_W_ALIGN(struct ibf_object_struct_range);
13647 IBF_WV(range);
13649 else {
13650 rb_raise(rb_eNotImpError, "ibf_dump_object_struct: unsupported class %"PRIsVALUE,
13651 rb_class_name(CLASS_OF(obj)));
13655 static VALUE
13656 ibf_load_object_struct(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13658 const struct ibf_object_struct_range *range = IBF_OBJBODY(struct ibf_object_struct_range, offset);
13659 VALUE beg = ibf_load_object(load, range->beg);
13660 VALUE end = ibf_load_object(load, range->end);
13661 VALUE obj = rb_range_new(beg, end, range->excl);
13662 if (header->internal) rb_obj_hide(obj);
13663 if (header->frozen) rb_obj_freeze(obj);
13664 return obj;
13667 static void
13668 ibf_dump_object_bignum(struct ibf_dump *dump, VALUE obj)
13670 ssize_t len = BIGNUM_LEN(obj);
13671 ssize_t slen = BIGNUM_SIGN(obj) > 0 ? len : len * -1;
13672 BDIGIT *d = BIGNUM_DIGITS(obj);
13674 (void)IBF_W(&slen, ssize_t, 1);
13675 IBF_WP(d, BDIGIT, len);
13678 static VALUE
13679 ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13681 const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset);
13682 int sign = bignum->slen > 0;
13683 ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
13684 const int big_unpack_flags = /* c.f. rb_big_unpack() */
13685 INTEGER_PACK_LSWORD_FIRST |
13686 INTEGER_PACK_NATIVE_BYTE_ORDER;
13687 VALUE obj = rb_integer_unpack(bignum->digits, len, sizeof(BDIGIT), 0,
13688 big_unpack_flags |
13689 (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
13690 if (header->internal) rb_obj_hide(obj);
13691 if (header->frozen) rb_obj_freeze(obj);
13692 return obj;
13695 static void
13696 ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
13698 if (rb_data_is_encoding(obj)) {
13699 rb_encoding *enc = rb_to_encoding(obj);
13700 const char *name = rb_enc_name(enc);
13701 long len = strlen(name) + 1;
13702 long data[2];
13703 data[0] = IBF_OBJECT_DATA_ENCODING;
13704 data[1] = len;
13705 (void)IBF_W(data, long, 2);
13706 IBF_WP(name, char, len);
13708 else {
13709 ibf_dump_object_unsupported(dump, obj);
13713 static VALUE
13714 ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13716 const long *body = IBF_OBJBODY(long, offset);
13717 const enum ibf_object_data_type type = (enum ibf_object_data_type)body[0];
13718 /* const long len = body[1]; */
13719 const char *data = (const char *)&body[2];
13721 switch (type) {
13722 case IBF_OBJECT_DATA_ENCODING:
13724 VALUE encobj = rb_enc_from_encoding(rb_enc_find(data));
13725 return encobj;
13729 return ibf_load_object_unsupported(load, header, offset);
13732 static void
13733 ibf_dump_object_complex_rational(struct ibf_dump *dump, VALUE obj)
13735 long data[2];
13736 data[0] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
13737 data[1] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
13739 (void)IBF_W(data, long, 2);
13742 static VALUE
13743 ibf_load_object_complex_rational(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13745 const struct ibf_object_complex_rational *nums = IBF_OBJBODY(struct ibf_object_complex_rational, offset);
13746 VALUE a = ibf_load_object(load, nums->a);
13747 VALUE b = ibf_load_object(load, nums->b);
13748 VALUE obj = header->type == T_COMPLEX ?
13749 rb_complex_new(a, b) : rb_rational_new(a, b);
13751 if (header->internal) rb_obj_hide(obj);
13752 if (header->frozen) rb_obj_freeze(obj);
13753 return obj;
13756 static void
13757 ibf_dump_object_symbol(struct ibf_dump *dump, VALUE obj)
13759 ibf_dump_object_string(dump, rb_sym2str(obj));
13762 static VALUE
13763 ibf_load_object_symbol(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
13765 ibf_offset_t reading_pos = offset;
13767 int encindex = (int)ibf_load_small_value(load, &reading_pos);
13768 const long len = (long)ibf_load_small_value(load, &reading_pos);
13769 const char *ptr = load->current_buffer->buff + reading_pos;
13771 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
13772 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
13773 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
13776 ID id = rb_intern3(ptr, len, rb_enc_from_index(encindex));
13777 return ID2SYM(id);
13780 typedef void (*ibf_dump_object_function)(struct ibf_dump *dump, VALUE obj);
13781 static const ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = {
13782 ibf_dump_object_unsupported, /* T_NONE */
13783 ibf_dump_object_unsupported, /* T_OBJECT */
13784 ibf_dump_object_class, /* T_CLASS */
13785 ibf_dump_object_unsupported, /* T_MODULE */
13786 ibf_dump_object_float, /* T_FLOAT */
13787 ibf_dump_object_string, /* T_STRING */
13788 ibf_dump_object_regexp, /* T_REGEXP */
13789 ibf_dump_object_array, /* T_ARRAY */
13790 ibf_dump_object_hash, /* T_HASH */
13791 ibf_dump_object_struct, /* T_STRUCT */
13792 ibf_dump_object_bignum, /* T_BIGNUM */
13793 ibf_dump_object_unsupported, /* T_FILE */
13794 ibf_dump_object_data, /* T_DATA */
13795 ibf_dump_object_unsupported, /* T_MATCH */
13796 ibf_dump_object_complex_rational, /* T_COMPLEX */
13797 ibf_dump_object_complex_rational, /* T_RATIONAL */
13798 ibf_dump_object_unsupported, /* 0x10 */
13799 ibf_dump_object_unsupported, /* 0x11 T_NIL */
13800 ibf_dump_object_unsupported, /* 0x12 T_TRUE */
13801 ibf_dump_object_unsupported, /* 0x13 T_FALSE */
13802 ibf_dump_object_symbol, /* 0x14 T_SYMBOL */
13803 ibf_dump_object_unsupported, /* T_FIXNUM */
13804 ibf_dump_object_unsupported, /* T_UNDEF */
13805 ibf_dump_object_unsupported, /* 0x17 */
13806 ibf_dump_object_unsupported, /* 0x18 */
13807 ibf_dump_object_unsupported, /* 0x19 */
13808 ibf_dump_object_unsupported, /* T_IMEMO 0x1a */
13809 ibf_dump_object_unsupported, /* T_NODE 0x1b */
13810 ibf_dump_object_unsupported, /* T_ICLASS 0x1c */
13811 ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */
13812 ibf_dump_object_unsupported, /* 0x1e */
13813 ibf_dump_object_unsupported, /* 0x1f */
13816 static void
13817 ibf_dump_object_object_header(struct ibf_dump *dump, const struct ibf_object_header header)
13819 unsigned char byte =
13820 (header.type << 0) |
13821 (header.special_const << 5) |
13822 (header.frozen << 6) |
13823 (header.internal << 7);
13825 IBF_WV(byte);
13828 static struct ibf_object_header
13829 ibf_load_object_object_header(const struct ibf_load *load, ibf_offset_t *offset)
13831 unsigned char byte = ibf_load_byte(load, offset);
13833 struct ibf_object_header header;
13834 header.type = (byte >> 0) & 0x1f;
13835 header.special_const = (byte >> 5) & 0x01;
13836 header.frozen = (byte >> 6) & 0x01;
13837 header.internal = (byte >> 7) & 0x01;
13839 return header;
13842 static ibf_offset_t
13843 ibf_dump_object_object(struct ibf_dump *dump, VALUE obj)
13845 struct ibf_object_header obj_header;
13846 ibf_offset_t current_offset;
13847 IBF_ZERO(obj_header);
13848 obj_header.type = TYPE(obj);
13850 IBF_W_ALIGN(ibf_offset_t);
13851 current_offset = ibf_dump_pos(dump);
13853 if (SPECIAL_CONST_P(obj) &&
13854 ! (SYMBOL_P(obj) ||
13855 RB_FLOAT_TYPE_P(obj))) {
13856 obj_header.special_const = TRUE;
13857 obj_header.frozen = TRUE;
13858 obj_header.internal = TRUE;
13859 ibf_dump_object_object_header(dump, obj_header);
13860 ibf_dump_write_small_value(dump, obj);
13862 else {
13863 obj_header.internal = SPECIAL_CONST_P(obj) ? FALSE : (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
13864 obj_header.special_const = FALSE;
13865 obj_header.frozen = FL_TEST(obj, FL_FREEZE) ? TRUE : FALSE;
13866 ibf_dump_object_object_header(dump, obj_header);
13867 (*dump_object_functions[obj_header.type])(dump, obj);
13870 return current_offset;
13873 typedef VALUE (*ibf_load_object_function)(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset);
13874 static const ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = {
13875 ibf_load_object_unsupported, /* T_NONE */
13876 ibf_load_object_unsupported, /* T_OBJECT */
13877 ibf_load_object_class, /* T_CLASS */
13878 ibf_load_object_unsupported, /* T_MODULE */
13879 ibf_load_object_float, /* T_FLOAT */
13880 ibf_load_object_string, /* T_STRING */
13881 ibf_load_object_regexp, /* T_REGEXP */
13882 ibf_load_object_array, /* T_ARRAY */
13883 ibf_load_object_hash, /* T_HASH */
13884 ibf_load_object_struct, /* T_STRUCT */
13885 ibf_load_object_bignum, /* T_BIGNUM */
13886 ibf_load_object_unsupported, /* T_FILE */
13887 ibf_load_object_data, /* T_DATA */
13888 ibf_load_object_unsupported, /* T_MATCH */
13889 ibf_load_object_complex_rational, /* T_COMPLEX */
13890 ibf_load_object_complex_rational, /* T_RATIONAL */
13891 ibf_load_object_unsupported, /* 0x10 */
13892 ibf_load_object_unsupported, /* T_NIL */
13893 ibf_load_object_unsupported, /* T_TRUE */
13894 ibf_load_object_unsupported, /* T_FALSE */
13895 ibf_load_object_symbol,
13896 ibf_load_object_unsupported, /* T_FIXNUM */
13897 ibf_load_object_unsupported, /* T_UNDEF */
13898 ibf_load_object_unsupported, /* 0x17 */
13899 ibf_load_object_unsupported, /* 0x18 */
13900 ibf_load_object_unsupported, /* 0x19 */
13901 ibf_load_object_unsupported, /* T_IMEMO 0x1a */
13902 ibf_load_object_unsupported, /* T_NODE 0x1b */
13903 ibf_load_object_unsupported, /* T_ICLASS 0x1c */
13904 ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */
13905 ibf_load_object_unsupported, /* 0x1e */
13906 ibf_load_object_unsupported, /* 0x1f */
13909 static VALUE
13910 ibf_load_object(const struct ibf_load *load, VALUE object_index)
13912 if (object_index == 0) {
13913 return Qnil;
13915 else {
13916 VALUE obj = pinned_list_fetch(load->current_buffer->obj_list, (long)object_index);
13917 if (!obj) {
13918 ibf_offset_t *offsets = (ibf_offset_t *)(load->current_buffer->obj_list_offset + load->current_buffer->buff);
13919 ibf_offset_t offset = offsets[object_index];
13920 const struct ibf_object_header header = ibf_load_object_object_header(load, &offset);
13922 #if IBF_ISEQ_DEBUG
13923 fprintf(stderr, "ibf_load_object: list=%#x offsets=%p offset=%#x\n",
13924 load->current_buffer->obj_list_offset, (void *)offsets, offset);
13925 fprintf(stderr, "ibf_load_object: type=%#x special=%d frozen=%d internal=%d\n",
13926 header.type, header.special_const, header.frozen, header.internal);
13927 #endif
13928 if (offset >= load->current_buffer->size) {
13929 rb_raise(rb_eIndexError, "object offset out of range: %u", offset);
13932 if (header.special_const) {
13933 ibf_offset_t reading_pos = offset;
13935 obj = ibf_load_small_value(load, &reading_pos);
13937 else {
13938 obj = (*load_object_functions[header.type])(load, &header, offset);
13941 pinned_list_store(load->current_buffer->obj_list, (long)object_index, obj);
13943 #if IBF_ISEQ_DEBUG
13944 fprintf(stderr, "ibf_load_object: index=%#"PRIxVALUE" obj=%#"PRIxVALUE"\n",
13945 object_index, obj);
13946 #endif
13947 return obj;
13951 struct ibf_dump_object_list_arg
13953 struct ibf_dump *dump;
13954 VALUE offset_list;
13957 static int
13958 ibf_dump_object_list_i(st_data_t key, st_data_t val, st_data_t ptr)
13960 VALUE obj = (VALUE)key;
13961 struct ibf_dump_object_list_arg *args = (struct ibf_dump_object_list_arg *)ptr;
13963 ibf_offset_t offset = ibf_dump_object_object(args->dump, obj);
13964 rb_ary_push(args->offset_list, UINT2NUM(offset));
13966 return ST_CONTINUE;
13969 static void
13970 ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size)
13972 st_table *obj_table = dump->current_buffer->obj_table;
13973 VALUE offset_list = rb_ary_hidden_new(obj_table->num_entries);
13975 struct ibf_dump_object_list_arg args;
13976 args.dump = dump;
13977 args.offset_list = offset_list;
13979 st_foreach(obj_table, ibf_dump_object_list_i, (st_data_t)&args);
13981 IBF_W_ALIGN(ibf_offset_t);
13982 *obj_list_offset = ibf_dump_pos(dump);
13984 st_index_t size = obj_table->num_entries;
13985 st_index_t i;
13987 for (i=0; i<size; i++) {
13988 ibf_offset_t offset = NUM2UINT(RARRAY_AREF(offset_list, i));
13989 IBF_WV(offset);
13992 *obj_list_size = (unsigned int)size;
13995 static void
13996 ibf_dump_mark(void *ptr)
13998 struct ibf_dump *dump = (struct ibf_dump *)ptr;
13999 rb_gc_mark(dump->global_buffer.str);
14001 rb_mark_set(dump->global_buffer.obj_table);
14002 rb_mark_set(dump->iseq_table);
14005 static void
14006 ibf_dump_free(void *ptr)
14008 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14009 if (dump->global_buffer.obj_table) {
14010 st_free_table(dump->global_buffer.obj_table);
14011 dump->global_buffer.obj_table = 0;
14013 if (dump->iseq_table) {
14014 st_free_table(dump->iseq_table);
14015 dump->iseq_table = 0;
14019 static size_t
14020 ibf_dump_memsize(const void *ptr)
14022 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14023 size_t size = 0;
14024 if (dump->iseq_table) size += st_memsize(dump->iseq_table);
14025 if (dump->global_buffer.obj_table) size += st_memsize(dump->global_buffer.obj_table);
14026 return size;
14029 static const rb_data_type_t ibf_dump_type = {
14030 "ibf_dump",
14031 {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
14032 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
14035 static void
14036 ibf_dump_setup(struct ibf_dump *dump, VALUE dumper_obj)
14038 dump->global_buffer.obj_table = NULL; // GC may run before a value is assigned
14039 dump->iseq_table = NULL;
14041 RB_OBJ_WRITE(dumper_obj, &dump->global_buffer.str, rb_str_new(0, 0));
14042 dump->global_buffer.obj_table = ibf_dump_object_table_new();
14043 dump->iseq_table = st_init_numtable(); /* need free */
14045 dump->current_buffer = &dump->global_buffer;
14048 VALUE
14049 rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
14051 struct ibf_dump *dump;
14052 struct ibf_header header = {{0}};
14053 VALUE dump_obj;
14054 VALUE str;
14056 if (ISEQ_BODY(iseq)->parent_iseq != NULL ||
14057 ISEQ_BODY(iseq)->local_iseq != iseq) {
14058 rb_raise(rb_eRuntimeError, "should be top of iseq");
14060 if (RTEST(ISEQ_COVERAGE(iseq))) {
14061 rb_raise(rb_eRuntimeError, "should not compile with coverage");
14064 dump_obj = TypedData_Make_Struct(0, struct ibf_dump, &ibf_dump_type, dump);
14065 ibf_dump_setup(dump, dump_obj);
14067 ibf_dump_write(dump, &header, sizeof(header));
14068 ibf_dump_iseq(dump, iseq);
14070 header.magic[0] = 'Y'; /* YARB */
14071 header.magic[1] = 'A';
14072 header.magic[2] = 'R';
14073 header.magic[3] = 'B';
14074 header.major_version = IBF_MAJOR_VERSION;
14075 header.minor_version = IBF_MINOR_VERSION;
14076 header.endian = IBF_ENDIAN_MARK;
14077 header.wordsize = (uint8_t)SIZEOF_VALUE;
14078 ibf_dump_iseq_list(dump, &header);
14079 ibf_dump_object_list(dump, &header.global_object_list_offset, &header.global_object_list_size);
14080 header.size = ibf_dump_pos(dump);
14082 if (RTEST(opt)) {
14083 VALUE opt_str = opt;
14084 const char *ptr = StringValuePtr(opt_str);
14085 header.extra_size = RSTRING_LENINT(opt_str);
14086 ibf_dump_write(dump, ptr, header.extra_size);
14088 else {
14089 header.extra_size = 0;
14092 ibf_dump_overwrite(dump, &header, sizeof(header), 0);
14094 str = dump->global_buffer.str;
14095 RB_GC_GUARD(dump_obj);
14096 return str;
14099 static const ibf_offset_t *
14100 ibf_iseq_list(const struct ibf_load *load)
14102 return (const ibf_offset_t *)(load->global_buffer.buff + load->header->iseq_list_offset);
14105 void
14106 rb_ibf_load_iseq_complete(rb_iseq_t *iseq)
14108 struct ibf_load *load = RTYPEDDATA_DATA(iseq->aux.loader.obj);
14109 rb_iseq_t *prev_src_iseq = load->iseq;
14110 ibf_offset_t offset = ibf_iseq_list(load)[iseq->aux.loader.index];
14111 load->iseq = iseq;
14112 #if IBF_ISEQ_DEBUG
14113 fprintf(stderr, "rb_ibf_load_iseq_complete: index=%#x offset=%#x size=%#x\n",
14114 iseq->aux.loader.index, offset,
14115 load->header->size);
14116 #endif
14117 ibf_load_iseq_each(load, iseq, offset);
14118 ISEQ_COMPILE_DATA_CLEAR(iseq);
14119 FL_UNSET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14120 rb_iseq_init_trace(iseq);
14121 load->iseq = prev_src_iseq;
14124 #if USE_LAZY_LOAD
14125 const rb_iseq_t *
14126 rb_iseq_complete(const rb_iseq_t *iseq)
14128 rb_ibf_load_iseq_complete((rb_iseq_t *)iseq);
14129 return iseq;
14131 #endif
14133 static rb_iseq_t *
14134 ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
14136 int iseq_index = (int)(VALUE)index_iseq;
14138 #if IBF_ISEQ_DEBUG
14139 fprintf(stderr, "ibf_load_iseq: index_iseq=%p iseq_list=%p\n",
14140 (void *)index_iseq, (void *)load->iseq_list);
14141 #endif
14142 if (iseq_index == -1) {
14143 return NULL;
14145 else {
14146 VALUE iseqv = pinned_list_fetch(load->iseq_list, iseq_index);
14148 #if IBF_ISEQ_DEBUG
14149 fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
14150 #endif
14151 if (iseqv) {
14152 return (rb_iseq_t *)iseqv;
14154 else {
14155 rb_iseq_t *iseq = iseq_imemo_alloc();
14156 #if IBF_ISEQ_DEBUG
14157 fprintf(stderr, "ibf_load_iseq: new iseq=%p\n", (void *)iseq);
14158 #endif
14159 FL_SET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14160 iseq->aux.loader.obj = load->loader_obj;
14161 iseq->aux.loader.index = iseq_index;
14162 #if IBF_ISEQ_DEBUG
14163 fprintf(stderr, "ibf_load_iseq: iseq=%p loader_obj=%p index=%d\n",
14164 (void *)iseq, (void *)load->loader_obj, iseq_index);
14165 #endif
14166 pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
14168 if (!USE_LAZY_LOAD || GET_VM()->builtin_function_table) {
14169 #if IBF_ISEQ_DEBUG
14170 fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
14171 #endif
14172 rb_ibf_load_iseq_complete(iseq);
14175 #if IBF_ISEQ_DEBUG
14176 fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
14177 (void *)iseq, (void *)load->iseq);
14178 #endif
14179 return iseq;
14184 static void
14185 ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, size_t size)
14187 struct ibf_header *header = (struct ibf_header *)bytes;
14188 load->loader_obj = loader_obj;
14189 load->global_buffer.buff = bytes;
14190 load->header = header;
14191 load->global_buffer.size = header->size;
14192 load->global_buffer.obj_list_offset = header->global_object_list_offset;
14193 load->global_buffer.obj_list_size = header->global_object_list_size;
14194 RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(header->iseq_list_size));
14195 RB_OBJ_WRITE(loader_obj, &load->global_buffer.obj_list, pinned_list_new(load->global_buffer.obj_list_size));
14196 load->iseq = NULL;
14198 load->current_buffer = &load->global_buffer;
14200 if (size < header->size) {
14201 rb_raise(rb_eRuntimeError, "broken binary format");
14203 if (strncmp(header->magic, "YARB", 4) != 0) {
14204 rb_raise(rb_eRuntimeError, "unknown binary format");
14206 if (header->major_version != IBF_MAJOR_VERSION ||
14207 header->minor_version != IBF_MINOR_VERSION) {
14208 rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
14209 header->major_version, header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
14211 if (header->endian != IBF_ENDIAN_MARK) {
14212 rb_raise(rb_eRuntimeError, "unmatched endian: %c", header->endian);
14214 if (header->wordsize != SIZEOF_VALUE) {
14215 rb_raise(rb_eRuntimeError, "unmatched word size: %d", header->wordsize);
14217 if (header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14218 rb_raise(rb_eArgError, "unaligned iseq list offset: %u",
14219 header->iseq_list_offset);
14221 if (load->global_buffer.obj_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14222 rb_raise(rb_eArgError, "unaligned object list offset: %u",
14223 load->global_buffer.obj_list_offset);
14227 static void
14228 ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
14230 StringValue(str);
14232 if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
14233 rb_raise(rb_eRuntimeError, "broken binary format");
14236 if (USE_LAZY_LOAD) {
14237 str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
14240 ibf_load_setup_bytes(load, loader_obj, StringValuePtr(str), RSTRING_LEN(str));
14241 RB_OBJ_WRITE(loader_obj, &load->str, str);
14244 static void
14245 ibf_loader_mark(void *ptr)
14247 struct ibf_load *load = (struct ibf_load *)ptr;
14248 rb_gc_mark(load->str);
14249 rb_gc_mark(load->iseq_list);
14250 rb_gc_mark(load->global_buffer.obj_list);
14253 static void
14254 ibf_loader_free(void *ptr)
14256 struct ibf_load *load = (struct ibf_load *)ptr;
14257 ruby_xfree(load);
14260 static size_t
14261 ibf_loader_memsize(const void *ptr)
14263 return sizeof(struct ibf_load);
14266 static const rb_data_type_t ibf_load_type = {
14267 "ibf_loader",
14268 {ibf_loader_mark, ibf_loader_free, ibf_loader_memsize,},
14269 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
14272 const rb_iseq_t *
14273 rb_iseq_ibf_load(VALUE str)
14275 struct ibf_load *load;
14276 rb_iseq_t *iseq;
14277 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
14279 ibf_load_setup(load, loader_obj, str);
14280 iseq = ibf_load_iseq(load, 0);
14282 RB_GC_GUARD(loader_obj);
14283 return iseq;
14286 const rb_iseq_t *
14287 rb_iseq_ibf_load_bytes(const char *bytes, size_t size)
14289 struct ibf_load *load;
14290 rb_iseq_t *iseq;
14291 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
14293 ibf_load_setup_bytes(load, loader_obj, bytes, size);
14294 iseq = ibf_load_iseq(load, 0);
14296 RB_GC_GUARD(loader_obj);
14297 return iseq;
14300 VALUE
14301 rb_iseq_ibf_load_extra_data(VALUE str)
14303 struct ibf_load *load;
14304 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
14305 VALUE extra_str;
14307 ibf_load_setup(load, loader_obj, str);
14308 extra_str = rb_str_new(load->global_buffer.buff + load->header->size, load->header->extra_size);
14309 RB_GC_GUARD(loader_obj);
14310 return extra_str;
14313 #include "prism_compile.c"