45
45
46
46
* handles_sp: If it is true, VM deals with sp in the insn.
47
47
48
+ * leaf: indicates that the instruction is "leaf" i.e. it does
49
+ not introduce new stack frame on top of it. Default true.
50
+
48
51
- Attributes can access operands, but not stack (push/pop) variables.
49
52
50
53
- An instruction's body is a pure C block, copied verbatimly into
@@ -203,6 +206,8 @@ getinstancevariable
203
206
(ID id , IC ic )
204
207
()
205
208
(VALUE val )
209
+ /* "instance variable not initialized" warning can be hooked. */
210
+ // attr bool leaf = false; /* has rb_warning() */
206
211
{
207
212
val = vm_getinstancevariable (GET_SELF (), id , ic );
208
213
}
@@ -223,6 +228,8 @@ getclassvariable
223
228
(ID id )
224
229
()
225
230
(VALUE val )
231
+ /* "class variable access from toplevel" warning can be hooked. */
232
+ // attr bool leaf = false; /* has rb_warning() */
226
233
{
227
234
val = rb_cvar_get (vm_get_cvar_base (rb_vm_get_cref (GET_EP ()), GET_CFP ()), id );
228
235
}
@@ -233,6 +240,8 @@ setclassvariable
233
240
(ID id )
234
241
(VALUE val )
235
242
()
243
+ /* "class variable access from toplevel" warning can be hooked. */
244
+ // attr bool leaf = false; /* has rb_warning() */
236
245
{
237
246
vm_ensure_not_refinement_module (GET_SELF ());
238
247
rb_cvar_set (vm_get_cvar_base (rb_vm_get_cref (GET_EP ()), GET_CFP ()), id , val );
@@ -247,6 +256,8 @@ getconstant
247
256
(ID id )
248
257
(VALUE klass )
249
258
(VALUE val )
259
+ /* getconstant can kick autoload */
260
+ // attr bool leaf = false; /* has rb_autoload_load() */
250
261
{
251
262
val = vm_get_ev_const (ec , klass , id , 0 );
252
263
}
@@ -258,6 +269,11 @@ setconstant
258
269
(ID id )
259
270
(VALUE val , VALUE cbase )
260
271
()
272
+ /* Assigning an object to a constant is basically a leaf operation.
273
+ * The problem is, assigning a Module instance to a constant _names_
274
+ * that module. Naming involves string manipulations, which are
275
+ * method calls. */
276
+ // attr bool leaf = false; /* has StringValue() */
261
277
{
262
278
vm_check_if_namespace (cbase );
263
279
vm_ensure_not_refinement_module (GET_SELF ());
@@ -270,6 +286,7 @@ getglobal
270
286
(GENTRY entry )
271
287
()
272
288
(VALUE val )
289
+ // attr bool leaf = leafness_of_getglobal(entry);
273
290
{
274
291
val = GET_GLOBAL ((VALUE )entry );
275
292
}
@@ -280,6 +297,7 @@ setglobal
280
297
(GENTRY entry )
281
298
(VALUE val )
282
299
()
300
+ // attr bool leaf = leafness_of_setglobal(entry);
283
301
{
284
302
SET_GLOBAL ((VALUE )entry , val );
285
303
}
@@ -339,6 +357,7 @@ putiseq
339
357
(ISEQ iseq )
340
358
()
341
359
(VALUE ret )
360
+ // attr bool leaf = true; /* yes it is */
342
361
{
343
362
ret = (VALUE )iseq ;
344
363
}
@@ -392,6 +411,9 @@ toregexp
392
411
(rb_num_t opt , rb_num_t cnt )
393
412
(...)
394
413
(VALUE val )
414
+ /* This instruction has StringValue(), which is a method call. But it
415
+ * seems that path is never covered. */
416
+ // attr bool leaf = true; /* yes it is */
395
417
// attr rb_snum_t sp_inc = 1 - cnt;
396
418
{
397
419
const VALUE ary = rb_ary_tmp_new_from_values (0 , cnt , STACK_ADDR_FROM_TOP (cnt ));
@@ -444,6 +466,7 @@ expandarray
444
466
(rb_num_t num , rb_num_t flag )
445
467
(..., VALUE ary )
446
468
(...)
469
+ // attr bool leaf = false; /* has rb_check_array_type() */
447
470
// attr rb_snum_t sp_inc = num - 1 + (flag & 1 ? 1 : 0);
448
471
{
449
472
vm_expandarray (GET_SP (), ary , num , (int )flag );
@@ -455,6 +478,7 @@ concatarray
455
478
()
456
479
(VALUE ary1 , VALUE ary2 )
457
480
(VALUE ary )
481
+ // attr bool leaf = false; /* has rb_check_array_type() */
458
482
{
459
483
ary = vm_concat_array (ary1 , ary2 );
460
484
}
@@ -465,6 +489,7 @@ splatarray
465
489
(VALUE flag )
466
490
(VALUE ary )
467
491
(VALUE obj )
492
+ // attr bool leaf = false; /* has rb_check_array_type() */
468
493
{
469
494
obj = vm_splat_array (flag , ary );
470
495
}
@@ -475,6 +500,7 @@ newhash
475
500
(rb_num_t num )
476
501
(...)
477
502
(VALUE val )
503
+ // attr bool leaf = false; /* has rb_hash_key_str() */
478
504
// attr rb_snum_t sp_inc = 1 - num;
479
505
{
480
506
RUBY_DTRACE_CREATE_HOOK (HASH , num );
@@ -492,6 +518,8 @@ newrange
492
518
(rb_num_t flag )
493
519
(VALUE low , VALUE high )
494
520
(VALUE val )
521
+ /* rb_range_new() exercises "bad value for range" check. */
522
+ // attr bool leaf = false; /* see also: range.c:range_init() */
495
523
{
496
524
val = rb_range_new (low , high , (int )flag );
497
525
}
@@ -618,6 +646,7 @@ defined
618
646
(rb_num_t op_type , VALUE obj , VALUE needstr )
619
647
(VALUE v )
620
648
(VALUE val )
649
+ // attr bool leaf = leafness_of_defined(op_type);
621
650
{
622
651
val = vm_defined (ec , GET_CFP (), op_type , obj , needstr , v );
623
652
}
@@ -634,6 +663,7 @@ checkmatch
634
663
(rb_num_t flag )
635
664
(VALUE target , VALUE pattern )
636
665
(VALUE result )
666
+ // attr bool leaf = leafness_of_checkmatch(flag);
637
667
{
638
668
result = vm_check_match (ec , target , pattern , flag );
639
669
}
@@ -726,6 +756,7 @@ opt_str_freeze
726
756
(VALUE str )
727
757
()
728
758
(VALUE val )
759
+ // attr bool leaf = BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG);
729
760
{
730
761
val = vm_opt_str_freeze (str , BOP_FREEZE , idFreeze );
731
762
}
@@ -735,6 +766,7 @@ opt_str_uminus
735
766
(VALUE str )
736
767
()
737
768
(VALUE val )
769
+ // attr bool leaf = BASIC_OP_UNREDEFINED_P(BOP_UMINUS, STRING_REDEFINED_OP_FLAG);
738
770
{
739
771
val = vm_opt_str_freeze (str , BOP_UMINUS , idUMinus );
740
772
}
@@ -744,6 +776,11 @@ opt_newarray_max
744
776
(rb_num_t num )
745
777
(...)
746
778
(VALUE val )
779
+ /* This instruction typically has no funcalls. But it compares array
780
+ * contents each other by nature. That part could call methods when
781
+ * necessary. No way to detect such method calls beforehand. We
782
+ * cannot but mark it being not leaf. */
783
+ // attr bool leaf = false; /* has rb_funcall() */
747
784
// attr rb_snum_t sp_inc = 1 - num;
748
785
{
749
786
val = vm_opt_newarray_max (num , STACK_ADDR_FROM_TOP (num ));
@@ -754,6 +791,8 @@ opt_newarray_min
754
791
(rb_num_t num )
755
792
(...)
756
793
(VALUE val )
794
+ /* Same discussion as opt_newarray_max. */
795
+ // attr bool leaf = false; /* has rb_funcall() */
757
796
// attr rb_snum_t sp_inc = 1 - num;
758
797
{
759
798
val = vm_opt_newarray_min (num , STACK_ADDR_FROM_TOP (num ));
@@ -765,6 +804,7 @@ opt_send_without_block
765
804
(CALL_INFO ci , CALL_CACHE cc )
766
805
(...)
767
806
(VALUE val )
807
+ // attr bool leaf = false; /* Of course it isn't. */
768
808
// attr bool handles_sp = true;
769
809
// attr rb_snum_t sp_inc = -ci->orig_argc;
770
810
{
@@ -797,6 +837,7 @@ invokeblock
797
837
(CALL_INFO ci )
798
838
(...)
799
839
(VALUE val )
840
+ // attr bool leaf = false; /* Of course it isn't. */
800
841
// attr bool handles_sp = true;
801
842
// attr rb_snum_t sp_inc = 1 - ci->orig_argc;
802
843
{
@@ -824,6 +865,10 @@ leave
824
865
()
825
866
(VALUE val )
826
867
(VALUE val )
868
+ /* This is super surprising but when leaving from a frame, we check
869
+ * for interrupts. If any, that should be executed on top of the
870
+ * current execution context. This is a method call. */
871
+ // attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
827
872
// attr bool handles_sp = true;
828
873
{
829
874
if (OPT_CHECKED_RUN ) {
@@ -858,6 +903,8 @@ throw
858
903
(rb_num_t throw_state )
859
904
(VALUE throwobj )
860
905
(VALUE val )
906
+ /* Same discussion as leave. */
907
+ // attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
861
908
{
862
909
RUBY_VM_CHECK_INTS (ec );
863
910
val = vm_throw (ec , GET_CFP (), throw_state , throwobj );
875
922
(OFFSET dst )
876
923
()
877
924
()
925
+ /* Same discussion as leave. */
926
+ // attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
878
927
{
879
928
RUBY_VM_CHECK_INTS (ec );
880
929
JUMP (dst );
@@ -886,6 +935,8 @@ branchif
886
935
(OFFSET dst )
887
936
(VALUE val )
888
937
()
938
+ /* Same discussion as jump. */
939
+ // attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
889
940
{
890
941
if (RTEST (val )) {
891
942
RUBY_VM_CHECK_INTS (ec );
@@ -899,6 +950,8 @@ branchunless
899
950
(OFFSET dst )
900
951
(VALUE val )
901
952
()
953
+ /* Same discussion as jump. */
954
+ // attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
902
955
{
903
956
if (!RTEST (val )) {
904
957
RUBY_VM_CHECK_INTS (ec );
@@ -912,6 +965,8 @@ branchnil
912
965
(OFFSET dst )
913
966
(VALUE val )
914
967
()
968
+ /* Same discussion as jump. */
969
+ // attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
915
970
{
916
971
if (NIL_P (val )) {
917
972
RUBY_VM_CHECK_INTS (ec );
@@ -965,6 +1020,10 @@ opt_case_dispatch
965
1020
(CDHASH hash , OFFSET else_offset )
966
1021
(..., VALUE key )
967
1022
()
1023
+ /* Case dispatch involves hash lookup, which inevitably compares
1024
+ * several objects each other. The same discussion to
1025
+ * opt_newarray_max also goes here. */
1026
+ // attr bool leaf = false; /* has rb_any_cmp() */
968
1027
// attr rb_snum_t sp_inc = -1;
969
1028
{
970
1029
OFFSET dst = vm_case_dispatch (hash , else_offset , key );
@@ -982,6 +1041,9 @@ opt_plus
982
1041
(CALL_INFO ci , CALL_CACHE cc )
983
1042
(VALUE recv , VALUE obj )
984
1043
(VALUE val )
1044
+ /* Array + anything can be handled inside of opt_plus, and that
1045
+ * anything is converted into array using #to_ary. */
1046
+ // attr bool leaf = false; /* has rb_to_array_type() */
985
1047
{
986
1048
val = vm_opt_plus (recv , obj );
987
1049
@@ -1067,6 +1129,10 @@ opt_eq
1067
1129
(CALL_INFO ci , CALL_CACHE cc )
1068
1130
(VALUE recv , VALUE obj )
1069
1131
(VALUE val )
1132
+ /* This instruction can compare a string with non-string. This
1133
+ * (somewhat) coerces the non-string into a string, via a method
1134
+ * call. */
1135
+ // attr bool leaf = false; /* has rb_str_equal() */
1070
1136
{
1071
1137
val = opt_eq_func (recv , obj , ci , cc );
1072
1138
@@ -1084,6 +1150,8 @@ opt_neq
1084
1150
(CALL_INFO ci_eq , CALL_CACHE cc_eq , CALL_INFO ci , CALL_CACHE cc )
1085
1151
(VALUE recv , VALUE obj )
1086
1152
(VALUE val )
1153
+ /* Same discussion as opt_eq. */
1154
+ // attr bool leaf = false; /* has rb_str_equal() */
1087
1155
{
1088
1156
val = vm_opt_neq (ci , cc , ci_eq , cc_eq , recv , obj );
1089
1157
@@ -1186,6 +1254,11 @@ opt_aref
1186
1254
(CALL_INFO ci , CALL_CACHE cc )
1187
1255
(VALUE recv , VALUE obj )
1188
1256
(VALUE val )
1257
+ /* This is complicated. In case of hash, vm_opt_aref() resorts to
1258
+ * rb_hash_aref(). If `recv` has no `obj`, this function then yields
1259
+ * default_proc. This is a method call. So opt_aref is
1260
+ * (surprisingly) not leaf. */
1261
+ // attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */
1189
1262
{
1190
1263
val = vm_opt_aref (recv , obj );
1191
1264
@@ -1203,6 +1276,9 @@ opt_aset
1203
1276
(CALL_INFO ci , CALL_CACHE cc )
1204
1277
(VALUE recv , VALUE obj , VALUE set )
1205
1278
(VALUE val )
1279
+ /* This is another story than opt_aref. When vm_opt_aset() resorts
1280
+ * to rb_hash_aset(), which should call #hash for `obj`. */
1281
+ // attr bool leaf = false; /* has rb_funcall() */ /* calls #hash */
1206
1282
{
1207
1283
val = vm_opt_aset (recv , obj , set );
1208
1284
@@ -1220,6 +1296,8 @@ opt_aset_with
1220
1296
(VALUE key , CALL_INFO ci , CALL_CACHE cc )
1221
1297
(VALUE recv , VALUE val )
1222
1298
(VALUE val )
1299
+ /* Same discussion as opt_aset. */
1300
+ // attr bool leaf = false; /* has rb_funcall() */ /* calls #hash */
1223
1301
{
1224
1302
VALUE tmp = vm_opt_aset_with (recv , key , val );
1225
1303
@@ -1242,6 +1320,8 @@ opt_aref_with
1242
1320
(VALUE key , CALL_INFO ci , CALL_CACHE cc )
1243
1321
(VALUE recv )
1244
1322
(VALUE val )
1323
+ /* Same discussion as opt_aref. */
1324
+ // attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */
1245
1325
{
1246
1326
val = vm_opt_aref_with (recv , key );
1247
1327
@@ -1345,6 +1425,7 @@ opt_regexpmatch1
1345
1425
(VALUE recv )
1346
1426
(VALUE obj )
1347
1427
(VALUE val )
1428
+ // attr bool leaf = BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG);
1348
1429
{
1349
1430
val = vm_opt_regexpmatch1 (recv , obj );
1350
1431
}
@@ -1372,6 +1453,7 @@ opt_call_c_function
1372
1453
(rb_insn_func_t funcptr )
1373
1454
()
1374
1455
()
1456
+ // attr bool leaf = false; /* anything can happen inside */
1375
1457
// attr bool handles_sp = true;
1376
1458
{
1377
1459
reg_cfp = (funcptr )(ec , reg_cfp );
0 commit comments