1#
2# %CopyrightBegin%
3#
4# Copyright Ericsson AB 1997-2018. All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18# %CopyrightEnd%
19#
20
21#
22# The instructions that follows are only known by the loader and the emulator.
23# They can be changed without recompiling old Beam files.
24#
25# Instructions starting with a "i_" prefix are instructions produced by
26# instruction transformations; thus, they never occur in BEAM files.
27#
28
29# The too_old_compiler/0 instruction is specially handled in beam_load.c
30# to produce a user-friendly message informing the user that the module
31# needs to be re-compiled with a modern compiler.
32
33too_old_compiler/0
34too_old_compiler | never() =>
35
36# In R9C and earlier, the loader used to insert special instructions inside
37# the module_info/0,1 functions. (In R10B and later, the compiler inserts
38# an explicit call to an undocumented BIF, so that no loader trickery is
39# necessary.) Since the instructions don't work correctly in R12B, simply
40# refuse to load the module.
41
42func_info M=a a==am_module_info A=u==0 | label L | move n x==0 => too_old_compiler
43func_info M=a a==am_module_info A=u==1 | label L | move n x==0 => too_old_compiler
44
45# The undocumented and unsupported guard BIF is_constant/1 was removed
46# in R13. The is_constant/2 operation is marked as obsolete in genop.tab,
47# so the loader will automatically generate a too_old_compiler message
48# it is used, but we need to handle the is_constant/1 BIF specially here.
49
50bif1 Fail u$func:erlang:is_constant/1 Src Dst => too_old_compiler
51
52# Since the constant pool was introduced in R12B, empty tuples ({})
53# are literals. Therefore we no longer need to allow put_tuple/2
54# with a tuple size of zero.
55
56put_tuple u==0 d => too_old_compiler
57
58#
59# All the other instructions.
60#
61
62%cold
63label L
64i_func_info I a a I
65int_code_end
66
67i_generic_breakpoint
68i_debug_breakpoint
69i_return_time_trace
70i_return_to_trace
71i_yield
72trace_jump W
73%hot
74
75return
76
77#
78# To ensure that a "move Src x(0)" instruction can be combined
79# with the following call instruction, we need to make sure that
80# there is no line/1 instruction between the move and the call.
81#
82# A tail-recursive call to an external function (non-BIF) will
83# never be saved on the stack, so there is no reason to keep
84# the line instruction. (The compiler did not remove the line
85# instruction because it cannot tell the difference between
86# BIFs and ordinary Erlang functions.)
87#
88
89move S X0=x==0 | line Loc | call_ext Ar Func => \
90     line Loc | move S X0 | call_ext Ar Func
91move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_not_bif D => \
92     move S X0 | call_ext_last Ar Func D
93move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_not_bif => \
94     move S X0 | call_ext_only Ar Func
95move S X0=x==0 | line Loc | call Ar Func => \
96     line Loc | move S X0 | call Ar Func
97
98line Loc | func_info M F A => func_info M F A | line Loc
99
100line I
101
102allocate t t?
103allocate_heap t I t?
104
105%cold
106deallocate Q
107%hot
108
109init y
110allocate_zero t t?
111allocate_heap_zero t I t?
112
113trim N Remaining => i_trim N
114i_trim t
115
116test_heap I t?
117
118allocate_heap S u==0 R => allocate S R
119allocate_heap_zero S u==0 R => allocate_zero S R
120
121init2 y y
122init3 y y y
123init Y1 | init Y2 | init Y3 => init3 Y1 Y2 Y3
124init Y1 | init Y2 => init2 Y1 Y2
125
126# Selecting values
127
128select_val S=aiq Fail=f Size=u Rest=* => const_select_val(S, Fail, Size, Rest)
129
130select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \
131  gen_jump_tab(S, Fail, Size, Rest)
132
133is_integer Fail=f S | select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \
134  gen_jump_tab(S, Fail, Size, Rest)
135
136is_integer TypeFail=f S | select_val S=s Fail=f Size=u Rest=* | \
137	   mixed_types(Size, Rest) => \
138  gen_split_values(S, TypeFail, Fail, Size, Rest)
139
140select_val S=s Fail=f Size=u Rest=* | mixed_types(Size, Rest) => \
141  gen_split_values(S, Fail, Fail, Size, Rest)
142
143is_integer Fail=f S | select_val S=d Fail=f Size=u Rest=* | \
144  fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest)
145
146is_atom Fail=f S | select_val S=d Fail=f Size=u Rest=* | \
147  fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest)
148
149select_val S=s Fail=f Size=u Rest=* | floats_or_bignums(Size, Rest) => \
150  gen_select_literals(S, Fail, Size, Rest)
151
152select_val S=d Fail=f Size=u Rest=* | fixed_size_values(Size, Rest) => \
153  gen_select_val(S, Fail, Size, Rest)
154
155is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \
156  gen_select_tuple_arity(S, Fail, Size, Rest)
157
158select_tuple_arity S=d Fail=f Size=u Rest=* => \
159  gen_select_tuple_arity(S, Fail, Size, Rest)
160
161i_select_val_bins xy f? I
162
163i_select_val_lins xy f? I
164
165i_select_val2 xy f? c c
166
167i_select_tuple_arity xy f? I
168
169i_select_tuple_arity2 xy f? A A
170
171i_jump_on_val_zero xy f? I
172
173i_jump_on_val xy f? I W
174
175get_list xy xy xy
176
177# The following get_list instructions using x(0) are frequently used.
178get_list r x x
179get_list r r y
180get_list x r x
181get_list r x y
182get_list r y r
183get_list r x r
184
185get_hd xy xy
186get_tl xy xy
187
188# Old-style catch.
189catch y f
190catch_end y
191
192# Try/catch.
193try Y F => catch Y F
194
195try_case y
196try_end y
197
198%cold
199try_case_end s
200%hot
201
202# Destructive set tuple element
203
204set_tuple_element s S P
205
206# Get tuple element
207
208i_get_tuple_element xy P x
209
210%cold
211i_get_tuple_element xy P y
212%hot
213
214i_get_tuple_element2 x P x
215i_get_tuple_element2y x P y y
216
217i_get_tuple_element3 x P x
218
219%cold
220is_number f? xy
221%hot
222
223is_number Fail=f i =>
224is_number Fail=f na => jump Fail
225is_number Fail Literal=q => move Literal x | is_number Fail x
226
227jump f
228
229#
230# Expection rasing instructions. Infrequently executed.
231#
232
233%cold
234case_end NotInX=cy => move NotInX x | case_end x
235badmatch NotInX=cy => move NotInX x | badmatch x
236
237case_end x
238
239badmatch x
240
241if_end
242
243# Operands for raise/2 are almost always in x(2) and x(1).
244# Optimize for that case.
245raise x==2 x==1 => i_raise
246raise Trace=y Value=y => move Trace x=2 | move Value x=1 | i_raise
247raise Trace Value => move Trace x | move Value x=1 | move x x=2 | i_raise
248
249i_raise
250
251# Internal now, but could be useful to make known to the compiler.
252badarg j
253system_limit j
254
255%hot
256
257#
258# Move instructions.
259#
260
261move C=cxy x==0 | jump Lbl => move_jump Lbl C
262
263move_jump f ncxy
264
265# Movement to and from the stack is common
266# Try to pack as much as we can into one instruction
267
268# Window move
269move_window/5
270move_window/6
271
272# x -> y
273
274move X1=x Y1=y | move X2=x Y2=y | move X3=x Y3=y | succ(Y1,Y2) | succ(Y2,Y3) => \
275    move_window X1 X2 X3 Y1 Y3
276
277move_window X1=x X2=x X3=x Y1=y Y3=y | move X4=x Y4=y | succ(Y3,Y4) => \
278    move_window X1 X2 X3 X4 Y1 Y4
279
280move_window X1=x X2=x X3=x X4=x Y1=y Y4=y | move X5=x Y5=y | succ(Y4,Y5) => \
281    move_window5 X1 X2 X3 X4 X5 Y1
282
283move_window X1=x X2=x X3=x Y1=y Y3=y => move_window3 X1 X2 X3 Y1
284move_window X1=x X2=x X3=x X4=x Y1=y Y4=y => move_window4 X1 X2 X3 X4 Y1
285
286move_window3 x x x y
287move_window4 x x x x y
288move_window5 x x x x x y
289
290# Swap registers.
291move R1=x Tmp=x | move R2=xy R1 | move Tmp R2 => swap_temp R1 R2 Tmp
292
293swap_temp R1 R2 Tmp | line Loc | apply Live | is_killed_apply(Tmp, Live) => \
294  swap R1 R2 | line Loc | apply Live
295
296swap_temp R1 R2 Tmp | line Loc | call Live Addr | is_killed(Tmp, Live) => \
297  swap R1 R2 | line Loc | call Live Addr
298swap_temp R1 R2 Tmp | call_only Live Addr | \
299  is_killed(Tmp, Live) => swap R1 R2 | call_only Live Addr
300swap_temp R1 R2 Tmp | call_last Live Addr D | \
301  is_killed(Tmp, Live) => swap R1 R2 | call_last Live Addr D
302
303swap_temp R1 R2 Tmp | line Loc | call_ext Live Addr | is_killed(Tmp, Live) => \
304  swap R1 R2 | line Loc | call_ext Live Addr
305swap_temp R1 R2 Tmp | line Loc | call_ext_only Live Addr | \
306  is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_only Live Addr
307swap_temp R1 R2 Tmp | line Loc | call_ext_last Live Addr D | \
308  is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_last Live Addr D
309
310swap_temp x xy x
311
312swap x xy
313
314move Src=x D1=x | move Src=x D2=x => move_dup Src D1 D2
315move Src=x SD=x | move SD=x D=x   => move_dup Src SD D
316move Src=x D1=x | move Src=x D2=y => move_dup Src D1 D2
317move Src=y SD=x | move SD=x D=y   => move_dup Src SD D
318move Src=x SD=x | move SD=x D=y   => move_dup Src SD D
319move Src=y SD=x | move SD=x D=x   => move_dup Src SD D
320
321move SD=x D=x | move Src=xy SD=x => move_shift Src SD D
322move SD=y D=x | move Src=x  SD=y => move_shift Src SD D
323move SD=x D=y | move Src=x  SD=x => move_shift Src SD D
324
325# The transformations above guarantee that the source for
326# the second move is not the same as the destination for
327# the first move. That means that we can do the moves in
328# parallel (fetch both values, then store them) which could
329# be faster.
330
331move X1=x Y1=y | move X2=x Y2=y => move2_par X1 Y1 X2 Y2
332move Y1=y X1=x | move Y2=y X2=x => move2_par Y1 X1 Y2 X2
333
334move X1=x X2=x | move X3=x X4=x => move2_par X1 X2 X3 X4
335
336move X1=x X2=x | move X3=x Y1=y => move2_par X1 X2 X3 Y1
337
338move S1=x S2=x | move X1=x Y1=y => move2_par S1 S2 X1 Y1
339
340move S1=y S2=x | move X1=x Y1=y => move2_par S1 S2 X1 Y1
341
342move Y1=y X1=x | move S1=x D1=x => move2_par Y1 X1 S1 D1
343move S1=x D1=x | move Y1=y X1=x => move2_par S1 D1 Y1 X1
344
345move2_par X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => move3 X1 Y1 X2 Y2 X3 Y3
346move2_par Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3
347move2_par X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6
348
349move C=aiq X=x==1 => move_x1 C
350move C=aiq X=x==2 => move_x2 C
351
352move_x1 c
353move_x2 c
354
355move_shift x x x
356move_shift y x x
357move_shift x y x
358move_shift x x y
359
360move_dup xy x xy
361
362move2_par x y x y
363move2_par y x y x
364move2_par x x x x
365
366move2_par x x x y
367
368move2_par y x x y
369
370move2_par x x y x
371move2_par y x x x
372
373move3 x y x y x y
374move3 y x y x y x
375move3 x x x x x x
376
377# The compiler almost never generates a "move Literal y(Y)" instruction,
378# so let's cheat if we encounter one.
379move S=n D=y => init D
380move S=c D=y => move S x | move x D
381
382move x x
383move x y
384move y x
385move c x
386move n x
387move y y
388
389# The following move instructions using x(0) are frequently used.
390
391move x r
392move r x
393move y r
394move c r
395move r y
396
397# Receive operations.
398
399loop_rec Fail x==0 | smp_mark_target_label(Fail) => i_loop_rec Fail
400
401label L | wait_timeout Fail Src | smp_already_locked(L) => \
402    label L | wait_timeout_locked Src Fail
403wait_timeout Fail Src => wait_timeout_unlocked Src Fail
404
405wait_timeout_unlocked Src=aiq Fail => gen_literal_timeout(Fail, Src)
406wait_timeout_locked Src=aiq Fail => gen_literal_timeout_locked(Fail, Src)
407
408label L | wait Fail | smp_already_locked(L) => label L | wait_locked Fail
409wait Fail => wait_unlocked Fail
410
411label L | timeout | smp_already_locked(L) => label L | timeout_locked
412
413remove_message
414timeout
415timeout_locked
416i_loop_rec f
417loop_rec_end f
418wait_locked f
419wait_unlocked f
420
421# Note that a timeout value must fit in 32 bits.
422wait_timeout_unlocked_int I f
423wait_timeout_unlocked s f
424wait_timeout_locked_int I f
425wait_timeout_locked s f
426
427%cold
428i_wait_error
429i_wait_error_locked
430%hot
431
432send
433
434#
435# Optimized comparisons with one immediate/literal operand.
436#
437
438is_eq_exact Lbl S S =>
439is_eq_exact Lbl C1=c C2=c => move C1 x | is_eq_exact Lbl x C2
440is_eq_exact Lbl C=c R=xy => is_eq_exact Lbl R C
441
442is_eq_exact Lbl R=xy n => is_nil Lbl R
443is_eq_exact Lbl R=xy C=ia => i_is_eq_exact_immed Lbl R C
444is_eq_exact Lbl R=xy C=q => i_is_eq_exact_literal Lbl R C
445
446is_ne_exact Lbl S S => jump Lbl
447is_ne_exact Lbl C1=c C2=c => move C1 x | is_ne_exact Lbl x C2
448is_ne_exact Lbl C=c R=xy => is_ne_exact Lbl R C
449
450is_ne_exact Lbl R=xy C=ian => i_is_ne_exact_immed Lbl R C
451is_ne_exact Lbl R=xy C=q => i_is_ne_exact_literal Lbl R C
452
453i_is_eq_exact_immed f? rxy c
454
455i_is_eq_exact_literal f? xy c
456
457i_is_ne_exact_immed f? xy c
458
459i_is_ne_exact_literal f? xy c
460
461is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y
462is_eq_exact f? x xy
463is_eq_exact f? y y
464
465is_ne_exact f? S S
466
467is_lt f? x x
468is_lt f? x c
469is_lt f? c x
470%cold
471is_lt f? s s
472%hot
473
474is_ge f? x x
475is_ge f? x c
476is_ge f? c x
477%cold
478is_ge f? s s
479%hot
480
481is_eq f? s s
482
483is_ne f? s s
484
485#
486# Putting things.
487#
488
489put_tuple Arity Dst => i_put_tuple Dst u
490
491i_put_tuple Dst Arity Puts=* | put S1 | put S2 | \
492  put S3 | put S4 | put S5 => \
493	    tuple_append_put5(Arity, Dst, Puts, S1, S2, S3, S4, S5)
494
495i_put_tuple Dst Arity Puts=* | put S => \
496	    tuple_append_put(Arity, Dst, Puts, S)
497
498i_put_tuple/2
499
500i_put_tuple xy I
501
502#
503# The instruction "put_list Const [] Dst" were generated in rare
504# circumstances up to and including OTP 18. Starting with OTP 19,
505# AFAIK, it should never be generated.
506#
507put_list Const=c n Dst => move Const x | put_list x n Dst
508
509put_list Src Dst=x Dst => update_list Src Dst
510
511update_list xyc x
512
513put_list x n x
514put_list y n x
515put_list x x x
516put_list y x x
517
518put_list y y x
519put_list x y x
520
521# put_list SrcReg Constant Dst
522
523put_list x c x
524put_list x c y
525
526put_list y c x
527
528# put_list Constant SrcReg Dst
529
530put_list c x x
531put_list c y x
532
533# The following put_list instructions using x(0) are frequently used.
534
535put_list r n r
536put_list r n x
537put_list r x x
538put_list r x r
539put_list x x r
540
541%cold
542put_list s s d
543%hot
544
545
546#
547# Some more only used by the emulator
548#
549
550%cold
551normal_exit
552continue_exit
553apply_bif
554call_nif
555call_error_handler
556error_action_code
557return_trace
558%hot
559
560#
561# Instruction transformations & folded instructions.
562#
563
564# Note: There is no 'move_return y r', since there never are any y registers
565# when we do move_return (if we have y registers, we must do move_deallocate_return).
566
567move S x==0 | return => move_return S
568
569move_return xcn
570
571move S x==0 | deallocate D | return => move_deallocate_return S D
572
573move_deallocate_return xycn Q
574
575deallocate D | return => deallocate_return D
576
577deallocate_return Q
578
579test_heap Need u==1 | put_list Y=y x==0 x==0 => test_heap_1_put_list Need Y
580
581test_heap_1_put_list I y
582
583#
584# is_tagged_tuple Fail=f Src=rxy Arity Atom=a
585#
586
587is_tagged_tuple Fail Literal=q Arity Atom => \
588    move Literal x | is_tagged_tuple Fail x Arity Atom
589is_tagged_tuple Fail=f c Arity Atom  => jump Fail
590
591is_tagged_tuple f? rxy A a
592
593# Test tuple & arity (head)
594
595is_tuple Fail Literal=q => move Literal x | is_tuple Fail x
596is_tuple Fail=f c => jump Fail
597is_tuple Fail=f S=xy | test_arity Fail=f S=xy Arity => is_tuple_of_arity Fail S Arity
598
599is_tuple_of_arity f? rxy A
600
601is_tuple f? rxy
602
603test_arity Fail Literal=q Arity => move Literal x | test_arity Fail x Arity
604test_arity Fail=f c Arity => jump Fail
605
606test_arity f? xy A
607
608get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \
609   get_tuple_element Reg=x P3 D3=x | \
610   succ(P1, P2) | succ(P2, P3) | \
611   succ(D1, D2) | succ(D2, D3) => i_get_tuple_element3 Reg P1 D1
612
613get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \
614   succ(P1, P2) | succ(D1, D2) => i_get_tuple_element2 Reg P1 D1
615
616get_tuple_element Reg=x P1 D1=y | get_tuple_element Reg=x P2 D2=y | \
617   succ(P1, P2) => i_get_tuple_element2y Reg P1 D1 D2
618
619get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst
620
621is_integer Fail=f i =>
622is_integer Fail=f an => jump Fail
623is_integer Fail Literal=q => move Literal x | is_integer Fail x
624
625is_integer Fail=f S=x | allocate Need Regs => is_integer_allocate Fail S Need Regs
626
627is_integer_allocate f? x t t
628
629is_integer f? xy
630
631is_list Fail=f n =>
632is_list Fail Literal=q => move Literal x | is_list Fail x
633is_list Fail=f c => jump Fail
634is_list f? x
635%cold
636is_list f? y
637%hot
638
639is_nonempty_list Fail=f S=x | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs
640
641is_nonempty_list F=f x==0 | test_heap I1 I2 => is_nonempty_list_test_heap F I1 I2
642
643is_nonempty_list Fail=f S=x | get_list S D1=x D2=x => \
644  is_nonempty_list_get_list Fail S D1 D2
645
646is_nonempty_list_allocate f? rx t t
647is_nonempty_list_test_heap f? I t
648is_nonempty_list_get_list f? rx x x
649is_nonempty_list f? xy
650
651is_atom f? x
652%cold
653is_atom f? y
654%hot
655is_atom Fail=f a =>
656is_atom Fail=f niq => jump Fail
657
658is_float f? x
659%cold
660is_float f? y
661%hot
662is_float Fail=f nai => jump Fail
663is_float Fail Literal=q => move Literal x | is_float Fail x
664
665is_nil Fail=f n =>
666is_nil Fail=f qia => jump Fail
667
668is_nil f? xy
669
670is_binary Fail Literal=q => move Literal x | is_binary Fail x
671is_binary Fail=f c => jump Fail
672is_binary f? x
673%cold
674is_binary f? y
675%hot
676
677# XXX Deprecated.
678is_bitstr Fail Term => is_bitstring Fail Term
679
680is_bitstring Fail Literal=q => move Literal x | is_bitstring Fail x
681is_bitstring Fail=f c => jump Fail
682is_bitstring f? x
683%cold
684is_bitstring f? y
685%hot
686
687is_reference Fail=f cq => jump Fail
688is_reference f? x
689%cold
690is_reference f? y
691%hot
692
693is_pid Fail=f cq => jump Fail
694is_pid f? x
695%cold
696is_pid f? y
697%hot
698
699is_port Fail=f cq => jump Fail
700is_port f? x
701%cold
702is_port f? y
703%hot
704
705is_boolean Fail=f a==am_true =>
706is_boolean Fail=f a==am_false =>
707is_boolean Fail=f ac => jump Fail
708
709%cold
710is_boolean f? xy
711%hot
712
713is_function2 Fail=f Literal=q Arity | literal_is_export(Literal) =>
714is_function2 Fail=f c Arity => jump Fail
715is_function2 Fail=f Fun a => jump Fail
716
717is_function2 f? S s
718
719# Allocating & initializing.
720allocate Need Regs | init Y => allocate_init Need Regs Y
721init Y1 | init Y2 => init2 Y1 Y2
722
723allocate_init t t? y
724
725#################################################################
726# External function and bif calls.
727#################################################################
728
729#
730# The BIFs erts_internal:check_process_code/1 must be called like a function,
731# to ensure that c_p->i (program counter) is set correctly (an ordinary
732# BIF call doesn't set it).
733#
734
735call_ext u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext Bif
736call_ext_last u==1 Bif=u$bif:erts_internal:check_process_code/1 D => i_call_ext_last Bif D
737call_ext_only u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext_only Bif
738
739#
740# The BIFs erts_internal:garbage_collect/1 must be called like a function,
741# to allow them to invoke the garbage collector. (The stack pointer must
742# be saved and p->arity must be zeroed, which is not done on ordinary BIF calls.)
743#
744call_ext u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext Bif
745call_ext_last u==1 Bif=u$bif:erts_internal:garbage_collect/1 D => i_call_ext_last Bif D
746call_ext_only u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext_only Bif
747
748#
749# put/2 and erase/1 must be able to do garbage collection, so we must call
750# them like functions.
751#
752
753call_ext u==2 Bif=u$bif:erlang:put/2 => i_call_ext Bif
754call_ext_last u==2 Bif=u$bif:erlang:put/2 D => i_call_ext_last Bif D
755call_ext_only u==2 Bif=u$bif:erlang:put/2 => i_call_ext_only Bif
756
757call_ext u==1 Bif=u$bif:erlang:erase/1 => i_call_ext Bif
758call_ext_last u==1 Bif=u$bif:erlang:erase/1 D => i_call_ext_last Bif D
759call_ext_only u==1 Bif=u$bif:erlang:erase/1 => i_call_ext_only Bif
760
761#
762# The process_info/1,2 BIF should be called like a function, to force
763# the emulator to set c_p->current before calling it (a BIF call doesn't
764# set it).
765#
766# In addition, we force the use of a non-tail-recursive call.  This will ensure
767# that c_p->cp points into the function making the call.
768#
769
770call_ext u==1 Bif=u$bif:erlang:process_info/1 => i_call_ext Bif
771call_ext_last u==1 Bif=u$bif:erlang:process_info/1 D => i_call_ext Bif | deallocate_return D
772call_ext_only Ar=u==1 Bif=u$bif:erlang:process_info/1 => allocate u Ar | i_call_ext Bif | deallocate_return u
773
774call_ext u==2 Bif=u$bif:erlang:process_info/2 => i_call_ext Bif
775call_ext_last u==2 Bif=u$bif:erlang:process_info/2 D => i_call_ext Bif | deallocate_return D
776call_ext_only Ar=u==2 Bif=u$bif:erlang:process_info/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
777
778#
779# load_nif/2 also needs to know calling function like process_info
780#
781call_ext u==2 Bif=u$bif:erlang:load_nif/2 => i_call_ext Bif
782call_ext_last u==2 Bif=u$bif:erlang:load_nif/2 D => i_call_ext Bif | deallocate_return D
783call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
784
785
786#
787# apply/2 is an instruction, not a BIF.
788#
789
790call_ext u==2 u$func:erlang:apply/2 => i_apply_fun
791call_ext_last u==2 u$func:erlang:apply/2 D => i_apply_fun_last D
792call_ext_only u==2 u$func:erlang:apply/2 => i_apply_fun_only
793
794#
795# The apply/3 BIF is an instruction.
796#
797
798call_ext u==3 u$bif:erlang:apply/3 => i_apply
799call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D
800call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only
801
802#
803# The exit/1 and throw/1 BIFs never execute the instruction following them;
804# thus there is no need to generate any return instruction.
805#
806
807call_ext_last u==1 Bif=u$bif:erlang:exit/1 D => call_bif Bif
808call_ext_last u==1 Bif=u$bif:erlang:throw/1 D => call_bif Bif
809
810call_ext_only u==1 Bif=u$bif:erlang:exit/1 => call_bif Bif
811call_ext_only u==1 Bif=u$bif:erlang:throw/1 => call_bif Bif
812
813#
814# The error/1 and error/2 BIFs never execute the instruction following them;
815# thus there is no need to generate any return instruction.
816# However, they generate stack backtraces, so if the call instruction
817# is call_ext_only/2 instruction, we explicitly do an allocate/2 to store
818# the continuation pointer on the stack.
819#
820
821call_ext_last u==1 Bif=u$bif:erlang:error/1 D => call_bif Bif
822call_ext_last u==2 Bif=u$bif:erlang:error/2 D => call_bif Bif
823
824call_ext_only Ar=u==1 Bif=u$bif:erlang:error/1 => \
825  allocate u Ar | call_bif Bif
826call_ext_only Ar=u==2 Bif=u$bif:erlang:error/2 => \
827  allocate u Ar | call_bif Bif
828
829#
830# The yield/0 BIF is an instruction
831#
832
833call_ext u==0 u$func:erlang:yield/0 => i_yield
834call_ext_last u==0 u$func:erlang:yield/0 D => i_yield | deallocate_return D
835call_ext_only u==0 u$func:erlang:yield/0 => i_yield | return
836
837#
838# The hibernate/3 BIF is an instruction.
839#
840call_ext u==3 u$func:erlang:hibernate/3 => i_hibernate
841call_ext_last u==3 u$func:erlang:hibernate/3 D => i_hibernate
842call_ext_only u==3 u$func:erlang:hibernate/3 => i_hibernate
843
844#
845# If VM probes are not enabled, we want to short-circult calls to
846# the dt tag BIFs to make them as cheap as possible.
847#
848
849%unless USE_VM_PROBES
850
851call_ext Arity u$func:erlang:dt_get_tag/0 => \
852    move a=am_undefined x=0
853call_ext_last Arity u$func:erlang:dt_get_tag/0 D => \
854    move a=am_undefined x=0 | deallocate D | return
855call_ext_only Arity u$func:erlang:dt_get_tag/0 => \
856    move a=am_undefined x=0 | return
857
858move Any x==0 | call_ext Arity u$func:erlang:dt_put_tag/1 => \
859    move a=am_undefined x=0
860move Any x==0 | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
861    move a=am_undefined x=0 | deallocate D | return
862move Any x==0 | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
863    move a=am_undefined x=0 | return
864call_ext Arity u$func:erlang:dt_put_tag/1 => \
865    move a=am_undefined x=0
866call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
867    move a=am_undefined x=0 | deallocate D | return
868call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
869    move a=am_undefined x=0 | return
870
871call_ext Arity u$func:erlang:dt_get_tag_data/0 => \
872    move a=am_undefined x=0
873call_ext_last Arity u$func:erlang:dt_get_tag_data/0 D => \
874    move a=am_undefined x=0 | deallocate D | return
875call_ext_only Arity u$func:erlang:dt_get_tag_data/0 => \
876    move a=am_undefined x=0 | return
877
878move Any x==0 | call_ext Arity u$func:erlang:dt_spread_tag/1 => \
879    move a=am_true x=0
880move Any x==0 | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
881    move a=am_true x=0 | deallocate D | return
882move Any x==0 | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
883    move a=am_true x=0 | return
884call_ext Arity u$func:erlang:dt_spread_tag/1 => \
885    move a=am_true x=0
886call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
887    move a=am_true x=0 | deallocate D | return
888call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
889    move a=am_true x=0 | return
890
891move Any x==0 | call_ext Arity u$func:erlang:dt_restore_tag/1 => \
892    move a=am_true x=0
893move Any x==0 | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
894    move a=am_true x=0 | deallocate D | return
895move Any x==0 | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
896    move a=am_true x=0 | return
897call_ext Arity u$func:erlang:dt_restore_tag/1 => \
898    move a=am_true x=0
899call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
900    move a=am_true x=0 | deallocate D | return
901call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
902    move a=am_true x=0 | return
903
904move Any x==0 | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
905    move Any x=0
906move Any x==0 | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
907    move Any x=0 | deallocate D | return
908move Any x==0 | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
909    move Any x=0 | return
910call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 =>
911call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
912    deallocate D | return
913call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
914    return
915
916move Any x==0 | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \
917    move Any x=0
918move Any x==0 | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
919    move Any x=0 | deallocate D | return
920move Any x==0 | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
921    move Any x=0 | return
922call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 =>
923call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
924    deallocate D | return
925call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
926    return
927
928# Can happen after one of the transformations above.
929move Discarded x==0 | move Something x==0 => move Something x=0
930
931%endif
932
933call_ext u==0 u$func:os:perf_counter/0 => \
934    i_perf_counter
935call_ext_last u==0 u$func:os:perf_counter/0 D => \
936    i_perf_counter | deallocate_return D
937call_ext_only u==0 u$func:os:perf_counter/0 => \
938    i_perf_counter | return
939
940#
941# The general case for BIFs that have no special instructions.
942# A BIF used in the tail must be followed by a return instruction.
943#
944# To make trapping and stack backtraces work correctly, we make sure that
945# the continuation pointer is always stored on the stack.
946
947call_ext u Bif=u$is_bif => call_bif Bif
948
949call_ext_last u Bif=u$is_bif D => call_bif Bif | deallocate_return D
950
951call_ext_only Ar=u Bif=u$is_bif => \
952  allocate u Ar | call_bif Bif | deallocate_return u
953
954#
955# Any remaining calls are calls to Erlang functions, not BIFs.
956# We rename the instructions to internal names.  This is necessary,
957# to avoid an end-less loop, because we want to call a few BIFs
958# with call instructions.
959#
960
961move S=c x==0 | call_ext Ar=u Func=u$is_not_bif => i_move_call_ext S Func
962move S=c x==0 | call_ext_last Ar=u Func=u$is_not_bif D => i_move_call_ext_last Func D S
963move S=c x==0 | call_ext_only Ar=u Func=u$is_not_bif => i_move_call_ext_only Func S
964
965call_ext Ar Func        => i_call_ext Func
966call_ext_last Ar Func D => i_call_ext_last Func D
967call_ext_only Ar Func   => i_call_ext_only Func
968
969i_apply
970i_apply_last Q
971i_apply_only
972
973i_apply_fun
974i_apply_fun_last Q
975i_apply_fun_only
976
977%cold
978i_hibernate
979
980i_perf_counter
981%hot
982
983call_bif e
984
985#
986# Calls to non-building and guard BIFs.
987#
988
989bif0 u$bif:erlang:self/0 Dst=d => self Dst
990bif0 u$bif:erlang:node/0 Dst=d => node Dst
991
992bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => gen_get(Src, Dst)
993
994bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => gen_element(Jump, S1, S2, Dst)
995
996bif1 p Bif S1 Dst => bif1_body Bif S1 Dst
997
998bif2 p Bif S1 S2 Dst => i_bif2_body Bif S1 S2 Dst
999bif2 Fail Bif S1 S2 Dst => i_bif2 Fail Bif S1 S2 Dst
1000
1001i_get_hash c I d
1002i_get s d
1003
1004self xy
1005
1006node x
1007%cold
1008node y
1009%hot
1010
1011# Note: 'I' is sufficient because this instruction will only be used
1012# if the arity fits in 24 bits.
1013i_fast_element xy j? I d
1014
1015i_element xy j? s d
1016
1017bif1 f? b s d
1018bif1_body b s d
1019i_bif2 f? b s s d
1020i_bif2_body b s s d
1021
1022#
1023# Internal calls.
1024#
1025
1026move S=cxy x==0 | call Ar P=f => move_call S P
1027
1028move_call/2
1029move_call cxy f
1030
1031move S x==0 | call_last Ar P=f D => move_call_last S P D
1032
1033move_call_last/3
1034move_call_last cxy f Q
1035
1036move S=cx x==0 | call_only Ar P=f => move_call_only S P
1037
1038move_call_only/2
1039move_call_only cx f
1040
1041call Ar Func        => i_call Func
1042call_last Ar Func D => i_call_last Func D
1043call_only Ar Func   => i_call_only Func
1044
1045i_call f
1046i_call_last f Q
1047i_call_only f
1048
1049i_call_ext e
1050i_call_ext_last e Q
1051i_call_ext_only e
1052
1053i_move_call_ext c e
1054i_move_call_ext_last e Q c
1055i_move_call_ext_only e c
1056
1057# Fun calls.
1058
1059call_fun Arity | deallocate D | return => i_call_fun_last Arity D
1060call_fun Arity => i_call_fun Arity
1061
1062i_call_fun t
1063i_call_fun_last t Q
1064
1065make_fun2 OldIndex=u => gen_make_fun2(OldIndex)
1066
1067%cold
1068i_make_fun W t
1069%hot
1070
1071is_function f? xy
1072is_function Fail=f c => jump Fail
1073
1074func_info M F A => i_func_info u M F A
1075
1076# ================================================================
1077# New bit syntax matching (R11B).
1078# ================================================================
1079
1080%warm
1081bs_start_match2 Fail=f ica X Y D => jump Fail
1082bs_start_match2 Fail Bin X Y D => i_bs_start_match2 Bin Fail X Y D
1083i_bs_start_match2 xy f t t x
1084
1085bs_save2 Reg Index => gen_bs_save(Reg, Index)
1086i_bs_save2 x t
1087
1088bs_restore2 Reg Index => gen_bs_restore(Reg, Index)
1089i_bs_restore2 x t
1090
1091# Matching integers
1092bs_match_string Fail Ms Bits Val => i_bs_match_string Ms Fail Bits Val
1093
1094i_bs_match_string x f W W
1095
1096# Fetching integers from binaries.
1097bs_get_integer2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
1098			gen_get_integer2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
1099
1100i_bs_get_integer_small_imm x W f? t x
1101i_bs_get_integer_imm x W t f? t x
1102i_bs_get_integer f? t t x s x
1103i_bs_get_integer_8 x f? x
1104i_bs_get_integer_16 x f? x
1105
1106%if ARCH_64
1107i_bs_get_integer_32 x f? x
1108%endif
1109
1110# Fetching binaries from binaries.
1111bs_get_binary2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
1112			gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
1113
1114i_bs_get_binary_imm2 f? x t W t x
1115i_bs_get_binary2 f x t? s t x
1116i_bs_get_binary_all2 f? x t t x
1117i_bs_get_binary_all_reuse x f? t
1118
1119# Fetching float from binaries.
1120bs_get_float2 Fail=f Ms=x Live=u Sz=s Unit=u Flags=u Dst=d => \
1121		gen_get_float2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
1122
1123bs_get_float2 Fail=f Ms=x Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail
1124
1125i_bs_get_float2 f? x t s t x
1126
1127# Miscellanous
1128
1129bs_skip_bits2 Fail=f Ms=x Sz=sq Unit=u Flags=u => \
1130			gen_skip_bits2(Fail, Ms, Sz, Unit, Flags)
1131
1132i_bs_skip_bits_imm2 f? x W
1133i_bs_skip_bits2 f? x xy t
1134i_bs_skip_bits_all2 f? x t
1135
1136bs_test_tail2 Fail=f Ms=x Bits=u==0 => bs_test_zero_tail2 Fail Ms
1137bs_test_tail2 Fail=f Ms=x Bits=u => bs_test_tail_imm2 Fail Ms Bits
1138bs_test_zero_tail2 f? x
1139bs_test_tail_imm2 f? x W
1140
1141bs_test_unit F Ms Unit=u==8 => bs_test_unit8 F Ms
1142bs_test_unit f? x t
1143bs_test_unit8 f? x
1144
1145# An y register operand for bs_context_to_binary is rare,
1146# but can happen because of inlining.
1147
1148bs_context_to_binary Y=y | line L | badmatch Y => \
1149    move Y x | bs_context_to_binary x | line L | badmatch x
1150
1151bs_context_to_binary Y=y => move Y x | bs_context_to_binary x
1152
1153bs_context_to_binary x
1154
1155#
1156# Utf8/utf16/utf32 support. (R12B-5)
1157#
1158bs_get_utf8 Fail=f Ms=x u u Dst=d => i_bs_get_utf8 Ms Fail Dst
1159i_bs_get_utf8 x f? x
1160
1161bs_skip_utf8 Fail=f Ms=x u u => i_bs_get_utf8 Ms Fail x
1162
1163bs_get_utf16 Fail=f Ms=x u Flags=u Dst=d => i_bs_get_utf16 Ms Fail Flags Dst
1164bs_skip_utf16 Fail=f Ms=x u Flags=u => i_bs_get_utf16 Ms Fail Flags x
1165
1166i_bs_get_utf16 x f? t x
1167
1168bs_get_utf32 Fail=f Ms=x Live=u Flags=u Dst=d => \
1169	bs_get_integer2 Fail Ms Live i=32 u=1 Flags Dst | \
1170	i_bs_validate_unicode_retract Fail Dst Ms
1171bs_skip_utf32 Fail=f Ms=x Live=u Flags=u => \
1172	bs_get_integer2 Fail Ms Live i=32 u=1 Flags x | \
1173	i_bs_validate_unicode_retract Fail x Ms
1174
1175i_bs_validate_unicode_retract j s S
1176%hot
1177
1178#
1179# Constructing binaries
1180#
1181%warm
1182
1183bs_init2 Fail Sz Words Regs Flags Dst | binary_too_big(Sz) => system_limit Fail
1184
1185bs_init2 Fail Sz=u Words=u==0 Regs Flags Dst => i_bs_init Sz Regs Dst
1186
1187bs_init2 Fail Sz=u Words Regs Flags Dst => \
1188   i_bs_init_heap Sz Words Regs Dst
1189
1190bs_init2 Fail Sz Words=u==0 Regs Flags Dst => \
1191  i_bs_init_fail Sz Fail Regs Dst
1192bs_init2 Fail Sz Words Regs Flags Dst => \
1193  i_bs_init_fail_heap Sz Words Fail Regs Dst
1194
1195i_bs_init_fail xy j? t? x
1196
1197i_bs_init_fail_heap s I j? t? x
1198
1199i_bs_init W t? x
1200
1201i_bs_init_heap W I t? x
1202
1203
1204bs_init_bits Fail Sz=o Words Regs Flags Dst => system_limit Fail
1205
1206bs_init_bits Fail Sz=u Words=u==0 Regs Flags Dst => i_bs_init_bits Sz Regs Dst
1207bs_init_bits Fail Sz=u Words Regs Flags Dst =>  i_bs_init_bits_heap Sz Words Regs Dst
1208
1209bs_init_bits Fail Sz Words=u==0 Regs Flags Dst => \
1210  i_bs_init_bits_fail Sz Fail Regs Dst
1211bs_init_bits Fail Sz Words Regs Flags Dst => \
1212  i_bs_init_bits_fail_heap Sz Words Fail Regs Dst
1213
1214i_bs_init_bits_fail xy j? t? x
1215
1216i_bs_init_bits_fail_heap s I j? t? x
1217
1218i_bs_init_bits W t? x
1219i_bs_init_bits_heap W I t? x
1220
1221bs_add Fail S1=i==0 S2 Unit=u==1 D => move S2 D
1222
1223bs_add j? s s t? x
1224
1225bs_append Fail Size Extra Live Unit Bin Flags Dst => \
1226  move Bin x | i_bs_append Fail Extra Live Unit Size Dst
1227
1228bs_private_append Fail Size Unit Bin Flags Dst => \
1229  i_bs_private_append Fail Unit Size Bin Dst
1230
1231bs_init_writable
1232
1233i_bs_append j? I t? t s x
1234i_bs_private_append j? t s S x
1235
1236#
1237# Storing integers into binaries.
1238#
1239
1240bs_put_integer Fail=j Sz=sq Unit=u Flags=u Src=s => \
1241			gen_put_integer(Fail, Sz, Unit, Flags, Src)
1242
1243i_new_bs_put_integer j? s t s
1244i_new_bs_put_integer_imm j? W t s
1245
1246#
1247# Utf8/utf16/utf32 support. (R12B-5)
1248#
1249
1250bs_utf8_size j Src=s Dst=d => i_bs_utf8_size Src Dst
1251
1252i_bs_utf8_size s x
1253
1254bs_utf16_size j Src=s Dst=d => i_bs_utf16_size Src Dst
1255
1256i_bs_utf16_size s x
1257
1258bs_put_utf8 Fail u Src=s => i_bs_put_utf8 Fail Src
1259
1260i_bs_put_utf8 j? s
1261
1262bs_put_utf16 j? t s
1263
1264bs_put_utf32 Fail=j Flags=u Src=s => \
1265   i_bs_validate_unicode Fail Src | bs_put_integer Fail i=32 u=1 Flags Src
1266
1267i_bs_validate_unicode j? s
1268
1269#
1270# Storing floats into binaries.
1271#
1272bs_put_float Fail Sz=q Unit Flags Val => badarg Fail
1273
1274bs_put_float Fail=j Sz=s Unit=u Flags=u Src=s => \
1275			gen_put_float(Fail, Sz, Unit, Flags, Src)
1276
1277i_new_bs_put_float j? s t s
1278i_new_bs_put_float_imm j? W t s
1279
1280#
1281# Storing binaries into binaries.
1282#
1283
1284bs_put_binary Fail=j Sz=s Unit=u Flags=u Src=s => \
1285			gen_put_binary(Fail, Sz, Unit, Flags, Src)
1286
1287i_new_bs_put_binary j? s t s
1288i_new_bs_put_binary_imm j? W s
1289i_new_bs_put_binary_all j? s t
1290
1291#
1292# Warning: The i_bs_put_string and i_new_bs_put_string instructions
1293# are specially treated in the loader.
1294# Don't change the instruction format unless you change the loader too.
1295#
1296
1297bs_put_string W W
1298
1299#
1300# New floating point instructions (R8).
1301#
1302
1303fadd p FR1 FR2 FR3 => i_fadd FR1 FR2 FR3
1304fsub p FR1 FR2 FR3 => i_fsub FR1 FR2 FR3
1305fmul p FR1 FR2 FR3 => i_fmul FR1 FR2 FR3
1306fdiv p FR1 FR2 FR3 => i_fdiv FR1 FR2 FR3
1307fnegate p FR1 FR2 => i_fnegate FR1 FR2
1308
1309fconv Arg=iqan Dst=l => move Arg x | fconv x Dst
1310
1311fmove Arg=l Dst=d => fstore Arg Dst
1312fmove Arg=dq Dst=l => fload Arg Dst
1313
1314fstore l d
1315fload Sq l
1316
1317fconv S l
1318
1319i_fadd l l l
1320i_fsub l l l
1321i_fmul l l l
1322i_fdiv l l l
1323i_fnegate l l
1324
1325fclearerror | no_fpe_signals() =>
1326fcheckerror p | no_fpe_signals() =>
1327
1328%unless NO_FPE_SIGNALS
1329fcheckerror p => i_fcheckerror
1330
1331i_fcheckerror
1332fclearerror
1333%endif
1334
1335%hot
1336
1337#
1338# New apply instructions in R10B.
1339#
1340
1341apply t
1342apply_last t Q
1343
1344#
1345# Handle compatibility with OTP 17 here.
1346#
1347
1348i_put_map_assoc/4
1349
1350# We KNOW that in OTP 20 (actually OTP 18 and higher), a put_map_assoc instruction
1351# is always preceded by an is_map test. That means that put_map_assoc can never
1352# fail and does not need any failure label.
1353
1354put_map_assoc Fail Map Dst Live Size Rest=* | compiled_with_otp_20_or_higher() => \
1355	      i_put_map_assoc Map Dst Live Size Rest
1356
1357# Translate the put_map_assoc instruction if the module was compiled by a compiler
1358# before 20. This is only necessary if the OTP 17 compiler was used, but we
1359# have no safe and relatively easy way to know whether OTP 18/19 was used.
1360
1361put_map_assoc Fail=p Map Dst Live Size Rest=* => \
1362	      ensure_map Map | i_put_map_assoc Map Dst Live Size Rest
1363put_map_assoc Fail=f Map Dst Live Size Rest=* => \
1364	      is_map Fail Map | i_put_map_assoc Map Dst Live Size Rest
1365
1366ensure_map Lit=q | literal_is_map(Lit) =>
1367ensure_map Src=cqy => move Src x | ensure_map x
1368
1369%cold
1370ensure_map x
1371%hot
1372
1373#
1374# Map instructions. First introduced in R17.
1375#
1376
1377sorted_put_map_assoc/4
1378i_put_map_assoc Map Dst Live Size Rest=* | map_key_sort(Size, Rest) => \
1379  sorted_put_map_assoc Map Dst Live Size Rest
1380
1381sorted_put_map_exact/5
1382put_map_exact F Map Dst Live Size Rest=* | map_key_sort(Size, Rest) => \
1383  sorted_put_map_exact F Map Dst Live Size Rest
1384
1385sorted_put_map_assoc Map Dst Live Size Rest=* | is_empty_map(Map) => \
1386   new_map Dst Live Size Rest
1387sorted_put_map_assoc Src=s Dst Live Size Rest=* => \
1388	       update_map_assoc Src Dst Live Size Rest
1389sorted_put_map_assoc Src Dst Live Size Rest=* => \
1390	       move Src x | update_map_assoc x Dst Live Size Rest
1391
1392sorted_put_map_exact F Src=s Dst Live Size Rest=* => \
1393	      update_map_exact F Src Dst Live Size Rest
1394sorted_put_map_exact F Src Dst Live Size Rest=* => \
1395	      move Src x | update_map_exact F x Dst Live Size Rest
1396
1397new_map Dst Live Size Rest=* | is_small_map_literal_keys(Size, Rest) => \
1398   gen_new_small_map_lit(Dst, Live, Size, Rest)
1399
1400new_map d t I
1401i_new_small_map_lit d t q
1402update_map_assoc s d t I
1403update_map_exact j? s d t I
1404
1405is_map Fail Lit=q | literal_is_map(Lit) =>
1406is_map Fail cq => jump Fail
1407
1408is_map f? xy
1409
1410## Transform has_map_fields #{ K1 := _, K2 := _ } to has_map_elements
1411
1412has_map_fields Fail Src Size Rest=* => \
1413   gen_has_map_fields(Fail, Src, Size, Rest)
1414
1415## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 }
1416
1417get_map_elements Fail Src Size=u==2 Rest=* => \
1418    gen_get_map_element(Fail, Src, Size, Rest)
1419get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \
1420   gen_get_map_elements(Fail, Src, Size, Rest)
1421
1422i_get_map_elements f? s I
1423
1424i_get_map_element_hash Fail Src=c Key Hash Dst => \
1425    move Src x | i_get_map_element_hash Fail x Key Hash Dst
1426i_get_map_element_hash f? xy c I xy
1427
1428i_get_map_element Fail Src=c Key Dst => \
1429    move Src x | i_get_map_element Fail x Key Dst
1430i_get_map_element f? xy xy xy
1431
1432#
1433# Convert the plus operations to a generic plus instruction.
1434#
1435gen_plus/5
1436gen_minus/5
1437
1438gc_bif1 Fail Live u$bif:erlang:splus/1 Src Dst => \
1439   gen_plus Fail Live Src i Dst
1440gc_bif2 Fail Live u$bif:erlang:splus/2 S1 S2 Dst => \
1441   gen_plus Fail Live S1 S2 Dst
1442
1443gc_bif1 Fail Live u$bif:erlang:sminus/1 Src Dst => \
1444   gen_minus Fail Live i Src Dst
1445gc_bif2 Fail Live u$bif:erlang:sminus/2 S1 S2 Dst => \
1446   gen_minus Fail Live S1 S2 Dst
1447
1448#
1449# Optimize addition and subtraction of small literals using
1450# the i_increment/4 instruction (in bodies, not in guards).
1451#
1452
1453gen_plus p Live Int=i Reg=d Dst => \
1454	gen_increment(Reg, Int, Live, Dst)
1455gen_plus p Live Reg=d Int=i Dst => \
1456	gen_increment(Reg, Int, Live, Dst)
1457
1458gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \
1459	gen_increment_from_minus(Reg, Int, Live, Dst)
1460
1461#
1462# GCing arithmetic instructions.
1463#
1464
1465gen_plus Fail Live S1 S2 Dst => i_plus S1 S2 Fail Live Dst
1466
1467gen_minus Fail Live S1 S2 Dst => i_minus S1 S2 Fail Live Dst
1468
1469gc_bif2 Fail Live u$bif:erlang:stimes/2 S1 S2 Dst => \
1470  i_times Fail Live S1 S2 Dst
1471
1472gc_bif2 Fail Live u$bif:erlang:div/2 S1 S2 Dst => \
1473  i_m_div Fail Live S1 S2 Dst
1474gc_bif2 Fail Live u$bif:erlang:intdiv/2 S1 S2 Dst => \
1475  i_int_div Fail Live S1 S2 Dst
1476
1477gc_bif2 Fail Live u$bif:erlang:rem/2 S1 S2 Dst => \
1478  i_rem S1 S2 Fail Live Dst
1479
1480gc_bif2 Fail Live u$bif:erlang:bsl/2 S1 S2 Dst => \
1481  i_bsl S1 S2 Fail Live Dst
1482gc_bif2 Fail Live u$bif:erlang:bsr/2 S1 S2 Dst => \
1483  i_bsr S1 S2 Fail Live Dst
1484
1485gc_bif2 Fail Live u$bif:erlang:band/2 S1 S2 Dst => \
1486  i_band S1 S2 Fail Live Dst
1487
1488gc_bif2 Fail Live u$bif:erlang:bor/2 S1 S2 Dst => \
1489  i_bor Fail Live S1 S2 Dst
1490
1491gc_bif2 Fail Live u$bif:erlang:bxor/2 S1 S2 Dst => \
1492  i_bxor Fail Live S1 S2 Dst
1493
1494gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst
1495
1496i_increment rxy W t d
1497
1498i_plus x xy j? t d
1499i_plus s s  j? t d
1500
1501i_minus x x j? t d
1502i_minus s s j? t d
1503
1504i_times j? t s s d
1505
1506i_m_div j? t s s d
1507i_int_div j? t s s d
1508
1509i_rem x x j? t d
1510i_rem s s j? t d
1511
1512i_bsl s s j? t d
1513i_bsr s s j? t d
1514
1515i_band x c j? t d
1516i_band s s j? t d
1517
1518i_bor j? I s s d
1519i_bxor j? I s s d
1520
1521i_int_bnot Fail Src=c Live Dst => move Src x | i_int_bnot Fail x Live Dst
1522
1523i_int_bnot j? S t d
1524
1525#
1526# Old guard BIFs that creates heap fragments are no longer allowed.
1527#
1528bif1 Fail u$bif:erlang:length/1 s d => too_old_compiler
1529bif1 Fail u$bif:erlang:size/1 s d => too_old_compiler
1530bif1 Fail u$bif:erlang:abs/1 s d => too_old_compiler
1531bif1 Fail u$bif:erlang:float/1 s d => too_old_compiler
1532bif1 Fail u$bif:erlang:round/1 s d => too_old_compiler
1533bif1 Fail u$bif:erlang:trunc/1 s d => too_old_compiler
1534
1535#
1536# Guard BIFs.
1537#
1538gc_bif1 Fail I Bif Src Dst => \
1539	gen_guard_bif1(Fail, I, Bif, Src, Dst)
1540
1541gc_bif2 Fail I Bif S1 S2 Dst => \
1542	gen_guard_bif2(Fail, I, Bif, S1, S2, Dst)
1543
1544gc_bif3 Fail I Bif S1 S2 S3 Dst => \
1545	gen_guard_bif3(Fail, I, Bif, S1, S2, S3, Dst)
1546
1547i_gc_bif1 j? W s t? d
1548
1549i_gc_bif2 j? W t? s s d
1550
1551ii_gc_bif3/7
1552
1553# A specific instruction can only have 6 operands, so we must
1554# pass one of the arguments in an x register.
1555ii_gc_bif3 Fail Bif Live S1 S2 S3 Dst => \
1556  move S1 x | i_gc_bif3 Fail Bif Live S2 S3 Dst
1557
1558i_gc_bif3 j? W t? s s d
1559
1560#
1561# The following instruction is specially handled in beam_load.c
1562# to produce a user-friendly message if an unsupported guard BIF is
1563# encountered.
1564#
1565unsupported_guard_bif/3
1566unsupported_guard_bif A B C | never() =>
1567
1568#
1569# R13B03
1570#
1571on_load
1572
1573#
1574# R14A.
1575#
1576# Modified in OTP 21 because it turns out that we don't need the
1577# label after all.
1578#
1579
1580recv_mark f => i_recv_mark
1581i_recv_mark
1582
1583recv_set Fail | label Lbl | loop_rec Lf Reg => \
1584   i_recv_set | label Lbl | loop_rec Lf Reg
1585i_recv_set
1586
1587#
1588# OTP 21.
1589#
1590
1591build_stacktrace
1592raw_raise
1593