/
optimize.c
2488 lines (2266 loc) · 106 KB
/
optimize.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "moar.h"
/* This is where the main optimization work on a spesh graph takes place,
* using facts discovered during analysis. */
/* Writes to stderr about each inline that we perform. */
#define MVM_LOG_INLINES 0
/* Obtains facts for an operand, just directly accessing them without
* inferring any kind of usage. */
static MVMSpeshFacts * get_facts_direct(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
return &g->facts[o.reg.orig][o.reg.i];
}
/* Obtains facts for an operand, indicating they are being used. */
MVMSpeshFacts * MVM_spesh_get_and_use_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
MVMSpeshFacts *facts = get_facts_direct(tc, g, o);
MVM_spesh_use_facts(tc, g, facts);
return facts;
}
/* Obtains facts for an operand, but doesn't (yet) indicate usefulness. */
MVMSpeshFacts * MVM_spesh_get_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
return get_facts_direct(tc, g, o);
}
/* Mark facts for an operand as being relied upon. */
void MVM_spesh_use_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshFacts *facts) {
if (facts->flags & MVM_SPESH_FACT_FROM_LOG_GUARD)
g->log_guards[facts->log_guard].used = 1;
if (facts->flags & MVM_SPESH_FACT_MERGED_WITH_LOG_GUARD) {
MVMSpeshIns *thePHI = facts->writer;
MVMuint32 op_i;
for (op_i = 1; op_i < thePHI->info->num_operands; op_i++) {
MVM_spesh_get_and_use_facts(tc, g, thePHI->operands[op_i]);
}
}
}
/* Obtains a string constant. */
MVMString * MVM_spesh_get_string(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
return MVM_cu_string(tc, g->sf->body.cu, o.lit_str_idx);
}
/* Copy facts between two register operands. */
static void copy_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand to,
MVMSpeshOperand from) {
MVMSpeshFacts *tfacts = get_facts_direct(tc, g, to);
MVMSpeshFacts *ffacts = get_facts_direct(tc, g, from);
tfacts->flags = ffacts->flags;
tfacts->type = ffacts->type;
tfacts->decont_type = ffacts->decont_type;
tfacts->value = ffacts->value;
tfacts->log_guard = ffacts->log_guard;
}
/* Adds a value into a spesh slot and returns its index.
* If a spesh slot already holds this value, return that instead. */
MVMint16 MVM_spesh_add_spesh_slot_try_reuse(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCollectable *c) {
MVMint16 prev_slot;
for (prev_slot = 0; prev_slot < g->num_spesh_slots; prev_slot++) {
if (g->spesh_slots[prev_slot] == c)
return prev_slot;
}
return MVM_spesh_add_spesh_slot(tc, g, c);
}
/* Adds a value into a spesh slot and returns its index. */
MVMint16 MVM_spesh_add_spesh_slot(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCollectable *c) {
if (g->num_spesh_slots >= g->alloc_spesh_slots) {
g->alloc_spesh_slots += 8;
if (g->spesh_slots)
g->spesh_slots = MVM_realloc(g->spesh_slots,
g->alloc_spesh_slots * sizeof(MVMCollectable *));
else
g->spesh_slots = MVM_malloc(g->alloc_spesh_slots * sizeof(MVMCollectable *));
}
g->spesh_slots[g->num_spesh_slots] = c;
return g->num_spesh_slots++;
}
static void optimize_repr_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
MVMSpeshIns *ins, MVMint32 type_operand);
static void optimize_findmeth_s_perhaps_constant(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMSpeshFacts *name_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]);
if (name_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
if (name_facts->writer && name_facts->writer->info->opcode == MVM_OP_const_s) {
name_facts->usages--;
ins->info = MVM_op_get_op(MVM_OP_findmeth);
ins->operands[2].lit_i64 = 0;
ins->operands[2].lit_str_idx = name_facts->writer->operands[1].lit_str_idx;
MVM_spesh_use_facts(tc, g, name_facts);
}
}
}
/* Performs optimization on a method lookup. If we know the type that we'll
* be dispatching on, resolve it right off. If not, add a cache. */
static void optimize_method_lookup(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
/* See if we can resolve the method right off due to knowing the type. */
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
MVMint32 resolved = 0;
if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
/* Try to resolve. */
MVMString *name = MVM_spesh_get_string(tc, g, ins->operands[2]);
MVMObject *meth = MVM_spesh_try_find_method(tc, obj_facts->type, name);
if (!MVM_is_null(tc, meth)) {
/* Could compile-time resolve the method. Add it in a spesh slot. */
MVMint16 ss = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)meth);
/* Tweak facts for the target, given we know the method. */
MVMSpeshFacts *meth_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]);
meth_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
meth_facts->value.o = meth;
/* Update the instruction to grab the spesh slot. */
ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
ins->operands[1].lit_i16 = ss;
resolved = 1;
MVM_spesh_use_facts(tc, g, obj_facts);
obj_facts->usages--;
}
}
/* If not, add space to cache a single type/method pair, to save hash
* lookups in the (common) monomorphic case, and rewrite to caching
* version of the instruction. */
if (!resolved) {
MVMSpeshOperand *orig_o = ins->operands;
ins->info = MVM_op_get_op(MVM_OP_sp_findmeth);
ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));
memcpy(ins->operands, orig_o, 3 * sizeof(MVMSpeshOperand));
ins->operands[3].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, NULL);
MVM_spesh_add_spesh_slot(tc, g, NULL);
}
}
/* Sees if we can resolve an istype at compile time. */
static void optimize_istype(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
MVMSpeshFacts *type_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]);
MVMSpeshFacts *result_facts;
if (type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE &&
obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
MVMint32 result;
if (!MVM_6model_try_cache_type_check(tc, obj_facts->type, type_facts->type, &result))
return;
ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
ins->operands[1].lit_i16 = result;
result_facts->value.i = result;
obj_facts->usages--;
type_facts->usages--;
MVM_spesh_facts_depend(tc, g, result_facts, obj_facts);
MVM_spesh_use_facts(tc, g, obj_facts);
MVM_spesh_facts_depend(tc, g, result_facts, type_facts);
MVM_spesh_use_facts(tc, g, type_facts);
}
}
static void optimize_is_reprid(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
MVMuint32 wanted_repr_id;
MVMuint64 result_value;
if (!(obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)) {
return;
}
switch (ins->info->opcode) {
case MVM_OP_islist: wanted_repr_id = MVM_REPR_ID_VMArray; break;
case MVM_OP_ishash: wanted_repr_id = MVM_REPR_ID_MVMHash; break;
case MVM_OP_isint: wanted_repr_id = MVM_REPR_ID_P6int; break;
case MVM_OP_isnum: wanted_repr_id = MVM_REPR_ID_P6num; break;
case MVM_OP_isstr: wanted_repr_id = MVM_REPR_ID_P6str; break;
default: return;
}
MVM_spesh_use_facts(tc, g, obj_facts);
result_value = REPR(obj_facts->type)->ID == wanted_repr_id;
if (result_value == 0) {
MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
ins->operands[1].lit_i16 = 0;
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
result_facts->value.i = 0;
MVM_spesh_facts_depend(tc, g, result_facts, obj_facts);
} else {
ins->info = MVM_op_get_op(MVM_OP_isnonnull);
}
}
static void optimize_gethow(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
MVMObject *how_obj = NULL;
if (obj_facts->flags & (MVM_SPESH_FACT_KNOWN_TYPE))
how_obj = MVM_spesh_try_get_how(tc, obj_facts->type);
/* There may be other valid ways to get the facts (known value?) */
if (how_obj) {
MVMSpeshFacts *how_facts;
/* Transform gethow lookup to spesh slot lookup */
MVMint16 spesh_slot = MVM_spesh_add_spesh_slot_try_reuse(tc, g, (MVMCollectable*)how_obj);
MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--;
ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
ins->operands[1].lit_i16 = spesh_slot;
/* Store facts about the value in the write operand */
how_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
how_facts->flags |= (MVM_SPESH_FACT_KNOWN_VALUE | MVM_SPESH_FACT_KNOWN_TYPE);
how_facts->value.o = how_obj;
how_facts->type = STABLE(how_obj)->WHAT;
}
}
/* Sees if we can resolve an isconcrete at compile time. */
static void optimize_isconcrete(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
if (obj_facts->flags & (MVM_SPESH_FACT_CONCRETE | MVM_SPESH_FACT_TYPEOBJ)) {
MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
result_facts->value.i = obj_facts->flags & MVM_SPESH_FACT_CONCRETE ? 1 : 0;
ins->operands[1].lit_i16 = result_facts->value.i;
MVM_spesh_use_facts(tc, g, obj_facts);
MVM_spesh_facts_depend(tc, g, result_facts, obj_facts);
obj_facts->usages--;
}
}
static void optimize_exception_ops(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
MVMuint16 op = ins->info->opcode;
if (op == MVM_OP_newexception) {
MVMSpeshOperand target = ins->operands[0];
MVMObject *type = tc->instance->boot_types.BOOTException;
MVMSTable *st = STABLE(type);
ins->info = MVM_op_get_op(MVM_OP_sp_fastcreate);
ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
ins->operands[0] = target;
ins->operands[1].lit_i16 = st->size;
ins->operands[2].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)st);
} else {
/*
MVMSpeshFacts *target_facts;
*/
/* XXX This currently still causes problems. */
return;
/*
switch (op) {
case MVM_OP_bindexmessage:
case MVM_OP_bindexpayload: {
MVMSpeshOperand target = ins->operands[0];
MVMSpeshOperand value = ins->operands[1];
target_facts = MVM_spesh_get_facts(tc, g, target);
if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
|| !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
break;
ins->info = MVM_op_get_op(op == MVM_OP_bindexmessage ? MVM_OP_sp_bind_s : MVM_OP_sp_bind_o);
ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
ins->operands[0] = target;
ins->operands[1].lit_i16 = op == MVM_OP_bindexmessage ? offsetof(MVMException, body.message)
: offsetof(MVMException, body.payload);
ins->operands[2] = value;
break;
}
case MVM_OP_bindexcategory: {
MVMSpeshOperand target = ins->operands[0];
MVMSpeshOperand category = ins->operands[1];
target_facts = MVM_spesh_get_facts(tc, g, target);
if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
|| !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
break;
ins->info = MVM_op_get_op(MVM_OP_sp_bind_i32);
ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
ins->operands[0] = target;
ins->operands[1].lit_i16 = offsetof(MVMException, body.category);
ins->operands[2] = category;
break;
}
case MVM_OP_getexmessage:
case MVM_OP_getexpayload: {
MVMSpeshOperand destination = ins->operands[0];
MVMSpeshOperand target = ins->operands[1];
target_facts = MVM_spesh_get_facts(tc, g, target);
if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
|| !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
break;
ins->info = MVM_op_get_op(op == MVM_OP_getexmessage ? MVM_OP_sp_get_s : MVM_OP_sp_get_o);
ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
ins->operands[0] = destination;
ins->operands[1] = target;
ins->operands[2].lit_i16 = op == MVM_OP_getexmessage ? offsetof(MVMException, body.message)
: offsetof(MVMException, body.payload);
break;
}
case MVM_OP_getexcategory: {
MVMSpeshOperand destination = ins->operands[0];
MVMSpeshOperand target = ins->operands[1];
target_facts = MVM_spesh_get_facts(tc, g, target);
if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
|| !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
break;
ins->info = MVM_op_get_op(MVM_OP_sp_get_i32);
ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
ins->operands[0] = destination;
ins->operands[1] = target;
ins->operands[2].lit_i16 = offsetof(MVMException, body.category);
break;
}
}
*/
}
}
/* iffy ops that operate on a known value register can turn into goto
* or be dropped. */
static void optimize_iffy(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins, MVMSpeshBB *bb) {
MVMSpeshFacts *flag_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
MVMuint8 negated_op;
MVMuint8 truthvalue;
switch (ins->info->opcode) {
case MVM_OP_if_i:
case MVM_OP_if_s:
case MVM_OP_if_n:
case MVM_OP_if_o:
case MVM_OP_ifnonnull:
negated_op = 0;
break;
case MVM_OP_unless_i:
case MVM_OP_unless_s:
case MVM_OP_unless_n:
case MVM_OP_unless_o:
negated_op = 1;
break;
default:
return;
}
if (flag_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
switch (ins->info->opcode) {
case MVM_OP_if_i:
case MVM_OP_unless_i:
truthvalue = flag_facts->value.i;
break;
case MVM_OP_if_o:
case MVM_OP_unless_o: {
MVMObject *objval = flag_facts->value.o;
MVMBoolificationSpec *bs = objval->st->boolification_spec;
MVMRegister resultreg;
switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) {
case MVM_BOOL_MODE_UNBOX_INT:
case MVM_BOOL_MODE_UNBOX_NUM:
case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY:
case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY_OR_ZERO:
case MVM_BOOL_MODE_BIGINT:
case MVM_BOOL_MODE_ITER:
case MVM_BOOL_MODE_HAS_ELEMS:
case MVM_BOOL_MODE_NOT_TYPE_OBJECT:
MVM_coerce_istrue(tc, objval, &resultreg, NULL, NULL, 0);
truthvalue = resultreg.i64;
break;
case MVM_BOOL_MODE_CALL_METHOD:
default:
return;
}
break;
}
case MVM_OP_if_n:
case MVM_OP_unless_n:
truthvalue = flag_facts->value.n != 0.0;
break;
default:
return;
}
MVM_spesh_use_facts(tc, g, flag_facts);
flag_facts->usages--;
truthvalue = truthvalue ? 1 : 0;
if (truthvalue != negated_op) {
/* This conditional can be turned into an unconditional jump. */
ins->info = MVM_op_get_op(MVM_OP_goto);
ins->operands[0] = ins->operands[1];
/* Since we have an unconditional jump now, we can remove the successor
* that's in the linear_next. */
MVM_spesh_manipulate_remove_successor(tc, bb, bb->linear_next);
} else {
/* This conditional can be dropped completely. */
MVM_spesh_manipulate_remove_successor(tc, bb, ins->operands[1].ins_bb);
MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
}
/* Since the CFG has changed, we may have some dead basic blocks; do
* an elimination pass. */
MVM_spesh_eliminate_dead_bbs(tc, g, 1);
return;
}
/* Sometimes our code-gen ends up boxing an integer and immediately
* calling if_o or unless_o on it. If we if_i/unless_i/... instead,
* we can get rid of the unboxing and perhaps the boxing as well. */
if ((ins->info->opcode == MVM_OP_if_o || ins->info->opcode == MVM_OP_unless_o)
&& flag_facts->flags & MVM_SPESH_FACT_KNOWN_BOX_SRC && flag_facts->writer) {
/* We may have to go through several layers of set instructions to find
* the proper writer. */
MVMSpeshIns *cur = flag_facts->writer;
while (cur && cur->info->opcode == MVM_OP_set) {
cur = MVM_spesh_get_facts(tc, g, cur->operands[1])->writer;
}
if (cur) {
MVMSpeshIns *safety_cur;
MVMuint8 orig_operand_type = cur->info->operands[1] & MVM_operand_type_mask;
MVMuint8 succ = 0;
/* Now we have to be extra careful. Any operation that writes to
* our "unboxed flag" register (in any register version) will be
* trouble. Also, we'd have to take more care with PHI nodes,
* which we'll just consider immediate failure for now. */
safety_cur = ins;
while (safety_cur) {
if (safety_cur == cur) {
/* If we've made it to here without finding anything
* dangerous, we can consider this optimization
* a winner. */
break;
}
if (safety_cur->info->opcode == MVM_SSA_PHI) {
/* Oh dear god in heaven! A PHI! */
safety_cur = NULL;
break;
}
if (((safety_cur->info->operands[0] & MVM_operand_rw_mask) == MVM_operand_write_reg)
&& (safety_cur->operands[0].reg.orig == cur->operands[1].reg.orig)) {
/* Someone's clobbering our register between the boxing and
* our attempt to unbox it. We shall give up.
* Maybe in the future we can be clever/sneaky and use
* some other register for bridging the gap? */
safety_cur = NULL;
break;
}
safety_cur = safety_cur->prev;
}
if (safety_cur) {
switch (orig_operand_type) {
case MVM_operand_int64:
ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_i : MVM_OP_if_i);
succ = 1;
break;
case MVM_operand_num64:
ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_n : MVM_OP_if_n);
succ = 1;
break;
case MVM_operand_str:
ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_s : MVM_OP_if_s);
succ = 1;
break;
}
if (succ) {
ins->operands[0] = cur->operands[1];
flag_facts->usages--;
MVM_spesh_get_and_use_facts(tc, g, cur->operands[1])->usages++;
optimize_iffy(tc, g, ins, bb);
return;
}
}
}
}
/* If we know the type, we can devirtualize if_o and unless_o. */
if ((ins->info->opcode == MVM_OP_if_o || ins->info->opcode == MVM_OP_unless_o) &&
flag_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && flag_facts->type) {
/* Go by boolification mode to pick a new instruction, if any. */
MVMObject *type = flag_facts->type;
MVMBoolificationSpec *bs = type->st->boolification_spec;
MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64);
MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
MVMuint8 guaranteed_concrete = flag_facts->flags & MVM_SPESH_FACT_CONCRETE;
MVMuint8 mode = bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode;
switch (mode) {
case MVM_BOOL_MODE_ITER:
if (!guaranteed_concrete)
return;
if (flag_facts->flags & MVM_SPESH_FACT_ARRAY_ITER) {
new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter_arr);
} else if (flag_facts->flags & MVM_SPESH_FACT_HASH_ITER) {
new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter_hash);
} else {
new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter);
}
break;
case MVM_BOOL_MODE_UNBOX_INT:
if (!guaranteed_concrete)
return;
new_ins->info = MVM_op_get_op(MVM_OP_unbox_i);
break;
/* We need to change the register type for our temporary register for this.
case MVM_BOOL_MODE_UNBOX_NUM:
new_ins->info = MVM_op_get_op(MVM_OP_unbox_i);
break;
*/
case MVM_BOOL_MODE_BIGINT:
if (!guaranteed_concrete)
return;
new_ins->info = MVM_op_get_op(MVM_OP_bool_I);
break;
case MVM_BOOL_MODE_HAS_ELEMS:
if (!guaranteed_concrete)
return;
new_ins->info = MVM_op_get_op(MVM_OP_elems);
break;
case MVM_BOOL_MODE_NOT_TYPE_OBJECT:
new_ins->info = MVM_op_get_op(MVM_OP_isconcrete);
break;
default:
return;
}
/* If we get here, we're inserting a new instruction that will do a
* cheaper test and put the result into the temporary register. */
new_ins->operands = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand) * 2);
new_ins->operands[0] = temp;
new_ins->operands[1] = ins->operands[0];
MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, new_ins);
/* Tweak the new instruction into an if_i/unless_i on the temp. */
ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_i : MVM_OP_if_i);
ins->operands[0] = temp;
MVM_spesh_get_facts(tc, g, temp)->usages++;
MVM_spesh_use_facts(tc, g, flag_facts);
MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
/* If the boolification mode was "not type object" then we might know
* that from the facts, and may even be able to elimiante this
* conditional altogether. */
if (mode == MVM_BOOL_MODE_NOT_TYPE_OBJECT) {
optimize_isconcrete(tc, g, new_ins);
optimize_iffy(tc, g, ins, bb);
}
}
}
/* A not_i on a known value can be turned into a constant. */
static void optimize_not_i(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins, MVMSpeshBB *bb) {
MVMSpeshFacts *src_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
if (src_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
/* Do the not_i. */
MVMint64 value = src_facts->value.i;
MVMint16 result = value ? 0 : 1;
/* Turn the op into a constant and set result facts. */
MVMSpeshFacts *dest_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
dest_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
dest_facts->value.i = result;
ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
ins->operands[1].lit_i16 = result;
/* This op no longer uses the source value. */
src_facts->usages--;
/* Need to depend on the source facts. */
MVM_spesh_use_facts(tc, g, src_facts);
MVM_spesh_facts_depend(tc, g, dest_facts, src_facts);
}
}
/* objprimspec can be done at spesh-time if we know the type of something.
* Another thing is, that if we rely on the type being known, we'll be assured
* we'll have a guard that promises the object in question to be non-null. */
static void optimize_objprimspec(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
result_facts->value.i = REPR(obj_facts->type)->get_storage_spec(tc, STABLE(obj_facts->type))->boxed_primitive;
ins->operands[1].lit_i16 = result_facts->value.i;
MVM_spesh_use_facts(tc, g, obj_facts);
obj_facts->usages--;
}
}
/* Optimizes a hllize instruction away if the type is known and already in the
* right HLL, by turning it into a set. */
static void optimize_hllize(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
if (STABLE(obj_facts->type)->hll_owner == g->sf->body.cu->body.hll_config) {
ins->info = MVM_op_get_op(MVM_OP_set);
MVM_spesh_use_facts(tc, g, obj_facts);
copy_facts(tc, g, ins->operands[0], ins->operands[1]);
}
}
}
/* Turns a decont into a set, if we know it's not needed. Also make sure we
* propagate any needed information. */
static void optimize_decont(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
if (obj_facts->flags & (MVM_SPESH_FACT_DECONTED | MVM_SPESH_FACT_TYPEOBJ)) {
/* Know that we don't need to decont. */
ins->info = MVM_op_get_op(MVM_OP_set);
MVM_spesh_use_facts(tc, g, obj_facts);
copy_facts(tc, g, ins->operands[0], ins->operands[1]);
}
else {
/* Propagate facts if we know what this deconts to. */
MVMSpeshFacts *res_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
int set_facts = 0;
if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) {
res_facts->type = obj_facts->decont_type;
res_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
set_facts = 1;
}
if (obj_facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE) {
res_facts->flags |= MVM_SPESH_FACT_CONCRETE;
set_facts = 1;
}
else if (obj_facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) {
res_facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
set_facts = 1;
}
/* If it's a known type... */
if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
/* Can try to specialize the fetch. */
MVMSTable *stable = STABLE(obj_facts->type);
MVMContainerSpec const *contspec = stable->container_spec;
if (contspec && contspec->fetch_never_invokes && contspec->spesh) {
contspec->spesh(tc, stable, g, bb, ins);
MVM_spesh_use_facts(tc, g, obj_facts);
}
/* If we didn't yet set facts, and the incoming type is a native
* reference, then we can set facts based on knowing what it will
* decont/box to. */
if (!set_facts && stable->REPR->ID == MVM_REPR_ID_NativeRef) {
MVMNativeRefREPRData *repr_data = (MVMNativeRefREPRData *)stable->REPR_data;
MVMHLLConfig *hll = stable->hll_owner;
MVMObject *out_type = NULL;
if (!hll)
hll = g->sf->body.cu->body.hll_config;
switch (repr_data->primitive_type) {
case MVM_STORAGE_SPEC_BP_INT:
out_type = hll->int_box_type;
break;
case MVM_STORAGE_SPEC_BP_NUM:
out_type = hll->num_box_type;
break;
case MVM_STORAGE_SPEC_BP_STR:
out_type = hll->str_box_type;
break;
}
if (out_type) {
res_facts->type = out_type;
res_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE;
set_facts = 1;
}
}
}
/* Depend on incoming facts if we used them. */
if (set_facts)
MVM_spesh_facts_depend(tc, g, res_facts, obj_facts);
/* If the op is still a decont, then turn it into sp_decont, which
* will at least not write log entries. */
if (ins->info->opcode == MVM_OP_decont)
ins->info = MVM_op_get_op(MVM_OP_sp_decont);
}
}
/* Checks like iscont, iscont_[ins] and isrwcont can be done at spesh time. */
static void optimize_container_check(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshBB *bb, MVMSpeshIns *ins) {
MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
MVMint32 known_result = -1;
if (ins->info->opcode == MVM_OP_isrwcont) {
if (facts->flags & MVM_SPESH_FACT_RW_CONT)
known_result = 1;
}
else {
if (facts->flags & MVM_SPESH_FACT_TYPEOBJ) {
/* Type object can never be a container. */
known_result = 0;
}
else if ((facts->flags & MVM_SPESH_FACT_CONCRETE) &&
(facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)) {
/* Know the type and know it's concrete. */
MVMContainerSpec const *cs = facts->type->st->container_spec;
if (!cs) {
/* No container spec, so can be sure it's not a container. */
known_result = 0;
}
else if (ins->info->opcode == MVM_OP_iscont) {
/* General is container check, so answer is yes. */
known_result = 1;
}
else {
if (REPR(facts->type)->ID == MVM_REPR_ID_NativeRef) {
/* Which native ref primitive? */
switch (((MVMNativeRefREPRData *)STABLE(facts->type)->REPR_data)->primitive_type) {
case MVM_STORAGE_SPEC_BP_INT:
known_result = ins->info->opcode == MVM_OP_iscont_i;
break;
case MVM_STORAGE_SPEC_BP_NUM:
known_result = ins->info->opcode == MVM_OP_iscont_n;
break;
case MVM_STORAGE_SPEC_BP_STR:
known_result = ins->info->opcode == MVM_OP_iscont_s;
break;
}
}
else {
/* Need a native ref but don't have one, so certain no. */
known_result = 0;
}
}
}
}
if (known_result != -1) {
ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
result_facts->value.i = known_result;
ins->operands[1].lit_i16 = known_result;
MVM_spesh_use_facts(tc, g, facts);
facts->usages--;
}
}
/* Optimize away assertparamcheck if we know it will pass. */
static void optimize_assertparamcheck(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
if (facts->flags & MVM_SPESH_FACT_KNOWN_VALUE && facts->value.i) {
MVM_spesh_use_facts(tc, g, facts);
MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
}
}
static void optimize_can_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
/* This used to cause problems, Spesh: failed to fix up handlers (-1, 110, 110) */
MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
MVMString *method_name;
MVMint64 can_result;
if (ins->info->opcode == MVM_OP_can_s) {
MVMSpeshFacts *name_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]);
if (!(name_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE)) {
return;
}
method_name = name_facts->value.s;
name_facts->usages--;
ins->info = MVM_op_get_op(MVM_OP_can);
ins->operands[2].lit_str_idx = name_facts->writer->operands[1].lit_str_idx;
} else {
method_name = MVM_spesh_get_string(tc, g, ins->operands[2]);
}
if (!(obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) || !obj_facts->type) {
return;
}
if (MVM_is_null(tc, obj_facts->type))
can_result = 0; /* VMNull can't have any methods. */
else
can_result = MVM_spesh_try_can_method(tc, obj_facts->type, method_name);
if (can_result == -1) {
return;
} else {
MVMSpeshFacts *result_facts;
if (ins->info->opcode == MVM_OP_can_s)
MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
ins->operands[1].lit_i16 = can_result;
result_facts->value.i = can_result;
obj_facts->usages--;
MVM_spesh_use_facts(tc, g, obj_facts);
}
}
/* If we have a const_i and a coerce_in, we can emit a const_n instead. */
static void optimize_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
if (facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
MVMnum64 result = facts->value.i;
MVM_spesh_use_facts(tc, g, facts);
facts->usages--;
ins->info = MVM_op_get_op(MVM_OP_const_n64);
ins->operands[1].lit_n64 = result;
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
result_facts->value.n = result;
}
}
/* If we know the type of a significant operand, we might try to specialize by
* representation. */
static void optimize_repr_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
MVMSpeshIns *ins, MVMint32 type_operand) {
/* Immediately mark guards as used, as the JIT would like to devirtualize
* repr ops later and we don't want guards to be thrown out before that */
MVMSpeshFacts *facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[type_operand]);
if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && facts->type)
if (REPR(facts->type)->spesh) {
REPR(facts->type)->spesh(tc, STABLE(facts->type), g, bb, ins);
MVM_spesh_use_facts(tc, g, facts);
}
}
/* smrt_strify and smrt_numify can turn into unboxes, but at least
* for smrt_numify it's "complicated". Also, later when we know how
* to put new invocations into spesh'd code, we could make direct
* invoke calls to the .Str and .Num methods. */
static void optimize_smart_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
MVMuint16 is_strify = ins->info->opcode == MVM_OP_smrt_strify;
if (facts->flags & (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE) && facts->type) {
const MVMStorageSpec *ss;
MVMint64 can_result;
ss = REPR(facts->type)->get_storage_spec(tc, STABLE(facts->type));
if (is_strify && ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) {
MVM_spesh_use_facts(tc, g, facts);
ins->info = MVM_op_get_op(MVM_OP_unbox_s);
/* And now that we have a repr op, we can try to optimize
* it even further. */
optimize_repr_op(tc, g, bb, ins, 1);
return;
}
can_result = MVM_spesh_try_can_method(tc, facts->type,
is_strify ? tc->instance->str_consts.Str : tc->instance->str_consts.Num);
if (can_result == -1) {
/* Couldn't safely figure out if the type has a Str method or not. */
return;
} else if (can_result == 0) {
MVM_spesh_use_facts(tc, g, facts);
/* We can't .Str this object, so we'll duplicate the "guessing"
* logic from smrt_strify here to remove indirection. */
if (is_strify && REPR(facts->type)->ID == MVM_REPR_ID_MVMException) {
MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 3);
MVMSpeshOperand *old_opers = ins->operands;
ins->info = MVM_op_get_op(MVM_OP_sp_get_s);
ins->operands = operands;
operands[0] = old_opers[0];
operands[1] = old_opers[1];
operands[2].lit_i16 = offsetof( MVMException, body.message );
} else if(ss->can_box & (MVM_STORAGE_SPEC_CAN_BOX_NUM | MVM_STORAGE_SPEC_CAN_BOX_INT)) {
MVMuint16 register_type =
ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT ? MVM_reg_int64 : MVM_reg_num64;
MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2);
MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, register_type);
MVMSpeshOperand orig_dst = ins->operands[0];
ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_unbox_n : MVM_OP_unbox_i);
ins->operands[0] = temp;
if (is_strify)
new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_coerce_ns : MVM_OP_coerce_is);
else
new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_set : MVM_OP_coerce_in);
new_ins->operands = operands;
operands[0] = orig_dst;
operands[1] = temp;
/* We can directly "eliminate" a set instruction here. */
if (new_ins->info->opcode != MVM_OP_set) {
MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins);
MVM_spesh_get_facts(tc, g, temp)->usages++;
} else {
ins->operands[0] = orig_dst;
}
/* Finally, let's try to optimize the unboxing REPROp. */
optimize_repr_op(tc, g, bb, ins, 1);
/* And as a last clean-up step, we release the temporary register. */
MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
return;
} else if (!is_strify && (REPR(facts->type)->ID == MVM_REPR_ID_VMArray ||
(REPR(facts->type)->ID == MVM_REPR_ID_MVMHash))) {
/* A smrt_numify on an array or hash can be replaced by an
* elems operation, that can then be optimized by our
* versatile and dilligent friend optimize_repr_op. */
MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2);
MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64);
MVMSpeshOperand orig_dst = ins->operands[0];
ins->info = MVM_op_get_op(MVM_OP_elems);
ins->operands[0] = temp;
new_ins->info = MVM_op_get_op(MVM_OP_coerce_in);
new_ins->operands = operands;
operands[0] = orig_dst;
operands[1] = temp;
MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins);
optimize_repr_op(tc, g, bb, ins, 1);
MVM_spesh_get_facts(tc, g, temp)->usages++;
MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
return;
}
} else if (can_result == 1) {
/* When we know how to generate additional callsites, we could
* make an invocation to .Str or .Num here and perhaps have it
* in-lined. */
}
}
}
/* boolification has a major indirection, which we can spesh away.
* Afterwards, we may be able to spesh even further, so we defer
* to other optimization methods. */
static void optimize_istrue_isfalse(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
MVMuint8 negated_op;
MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
if (ins->info->opcode == MVM_OP_istrue) {
negated_op = 0;
} else if (ins->info->opcode == MVM_OP_isfalse) {
negated_op = 1;
} else {
return;
}
/* Let's try to figure out the boolification spec. */
if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
MVMBoolificationSpec *bs = STABLE(facts->type)->boolification_spec;
MVMSpeshOperand orig = ins->operands[0];
MVMSpeshOperand temp;
if (negated_op)
temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64);
switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) {
case MVM_BOOL_MODE_UNBOX_INT:
/* This optimization can only handle values known to be concrete. */
if (!(facts->flags & MVM_SPESH_FACT_CONCRETE)) {
return;
}
/* We can just unbox the int and pretend it's a bool. */