1#
2# %CopyrightBegin%
3#
4# Copyright Ericsson AB 1997-2019. 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# A tail call will not refer to the current function on error unless it's a
79# BIF, so we can omit the line instruction for non-BIFs.
80#
81
82move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_not_bif D => \
83     move S X0 | call_ext_last Ar Func D
84move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_not_bif => \
85     move S X0 | call_ext_only Ar Func
86
87move S X0=x==0 | line Loc | call_last Ar Func D => \
88     move S X0 | call_last Ar Func D
89move S X0=x==0 | line Loc | call_only Ar Func => \
90     move S X0 | call_only Ar Func
91
92# To ensure that a "move Src x(0)" instruction can be combined with
93# the following call instruction, we need to make sure that there is
94# no line/1 instruction between the move and the call. (We don't
95# need to match the call instruction, because reordering the move
96# and line instructions would be harmless even if no call instruction
97# follows.)
98
99move S X0=x==0 | line Loc => line Loc | move S X0
100
101line Loc | func_info M F A => func_info M F A | line Loc
102
103line I
104
105allocate t t?
106allocate_heap t I t?
107
108# This instruction when a BIF is called tail-recursively when
109# ther is stack frame.
110deallocate Q
111
112init y
113allocate_zero t t?
114allocate_heap_zero t I t?
115
116move Src=y Dst=x | trim N Remaining => move_trim Src Dst N
117trim N Remaining => i_trim N
118
119move_trim y x t
120i_trim t
121
122test_heap I t?
123
124allocate_heap S u==0 R => allocate S R
125allocate_heap_zero S u==0 R => allocate_zero S R
126
127init Y1 | init Y2 | init Y3 | succ(Y1,Y2) | succ(Y2,Y3) => init_seq3 Y1
128init_seq3 Y1 | init Y4 | succ3(Y1,Y4) => init_seq4 Y1
129init_seq4 Y1 | init Y5 | succ4(Y1,Y5) => init_seq5 Y1
130
131init_seq3 y
132init_seq4 y
133init_seq5 y
134
135init Y1 | init Y2 | init Y3 => init3 Y1 Y2 Y3
136init Y1 | init Y2 => init2 Y1 Y2
137
138init2 y y
139init3 y y y
140
141
142# Selecting values
143
144select_val S=aiq Fail=f Size=u Rest=* => const_select_val(S, Fail, Size, Rest)
145
146select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \
147  gen_jump_tab(S, Fail, Size, Rest)
148
149is_integer Fail=f S | select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \
150  gen_jump_tab(S, Fail, Size, Rest)
151
152is_integer TypeFail=f S | select_val S=s Fail=f Size=u Rest=* | \
153	   mixed_types(Size, Rest) => \
154  gen_split_values(S, TypeFail, Fail, Size, Rest)
155
156select_val S=s Fail=f Size=u Rest=* | mixed_types(Size, Rest) => \
157  gen_split_values(S, Fail, Fail, Size, Rest)
158
159is_integer Fail=f S | select_val S=d Fail=f Size=u Rest=* | \
160  fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest)
161
162is_atom Fail=f S | select_val S=d Fail=f Size=u Rest=* | \
163  fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest)
164
165select_val S=s Fail=f Size=u Rest=* | floats_or_bignums(Size, Rest) => \
166  gen_select_literals(S, Fail, Size, Rest)
167
168select_val S=d Fail=f Size=u Rest=* | fixed_size_values(Size, Rest) => \
169  gen_select_val(S, Fail, Size, Rest)
170
171is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \
172  gen_select_tuple_arity(S, Fail, Size, Rest)
173
174select_tuple_arity S=d Fail=f Size=u Rest=* => \
175  gen_select_tuple_arity(S, Fail, Size, Rest)
176
177i_select_val_bins xy f? I
178
179i_select_val_lins xy f? I
180
181i_select_val2 xy f? c c
182
183i_select_tuple_arity xy f? I
184
185i_select_tuple_arity2 xy f? A A
186
187i_jump_on_val_zero xy f? I
188
189i_jump_on_val xy f? I W
190
191get_list xy xy xy
192
193# The following get_list instructions using x(0) are frequently used.
194get_list r x x
195get_list r r y
196get_list x r x
197get_list r x y
198get_list r y r
199get_list r x r
200
201get_hd xy xy
202get_tl xy xy
203
204# Old-style catch.
205catch y f
206catch_end y
207
208# Try/catch.
209try Y F => catch Y F
210
211try_case y
212try_end y
213
214%cold
215try_case_end s
216%hot
217
218# Destructive set tuple element
219
220set_tuple_element s S P
221
222# Get tuple element
223
224i_get_tuple_element xy P xy
225
226i_get_tuple_element2 x P x
227i_get_tuple_element2_dst x P x x
228i_get_tuple_element2_dst x P y y
229
230i_get_tuple_element3 x P x
231
232%cold
233is_number f? xy
234%hot
235
236is_number Fail=f i =>
237is_number Fail=f na => jump Fail
238is_number Fail Literal=q => move Literal x | is_number Fail x
239
240jump f
241
242#
243# Expection rasing instructions. Infrequently executed.
244#
245
246%cold
247case_end NotInX=cy => move NotInX x | case_end x
248badmatch NotInX=cy => move NotInX x | badmatch x
249
250case_end x
251
252badmatch x
253
254if_end
255
256# Operands for raise/2 are almost always in x(2) and x(1).
257# Optimize for that case.
258raise x==2 x==1 => i_raise
259raise Trace=y Value=y => move Trace x=2 | move Value x=1 | i_raise
260raise Trace Value => move Trace x | move Value x=1 | move x x=2 | i_raise
261
262i_raise
263
264# Internal now, but could be useful to make known to the compiler.
265badarg/1
266badarg p => badarg_body
267badarg Fail=f => jump Fail
268
269badarg_body
270
271system_limit/1
272system_limit p => system_limit_body
273system_limit Fail=f => jump Fail
274
275system_limit_body
276
277%hot
278
279#
280# Move instructions.
281#
282
283move Src=cxy Dst=xy | jump Lbl => move_jump Lbl Src Dst
284
285move_jump f cxy xy
286move_jump f c r
287
288
289# Movement to and from the stack is common.
290# Try to pack as much as we can into one instruction.
291
292# x -> y
293
294move X1=x Y1=y | move X2=x Y2=y | succ(Y1, Y2) => \
295    move_window2 X1 X2 Y1
296
297move_window2 X1 X2 Y1 | move X3=x Y3=y | offset(Y1, Y3, 2) => \
298    move_window3 X1 X2 X3 Y1
299
300move_window3 X1 X2 X3 Y1 | move X4=x Y4=y | offset(Y1, Y4, 3) => \
301    move_window4 X1 X2 X3 X4 Y1
302
303move_window4 X1 X2 X3 X4 Y1=y | move X5=x Y5=y | offset(Y1, Y5, 4) => \
304    move_window5 X1 X2 X3 X4 X5 Y1
305
306move_window2 x x y
307move_window3 x x x y
308move_window4 x x x x y
309move_window5 x x x x x y
310
311# y -> x
312
313move_src_window/4
314move_src_window/5
315
316move Y1=y X1=x | move Y2=y X2=x | succ(Y1, Y2) => \
317    move_src_window Y1 Y2 X1 X2
318
319move_src_window Y1 Y2 X1 X2 | move Y3=y X3=x | succ(Y2, Y3) => \
320    move_src_window Y1 Y3 X1 X2 X3
321move_src_window Y1 Y2 X1 X2 | move Y3=y X3=x | move Y4=y X4=x | succ(Y3, Y4) => \
322    move_src_window2 Y1 X1 X2 | move_src_window Y3 Y4 X3 X4
323move_src_window Y1 Y2 X1 X2 | move Y3=y X3=x => \
324    move3 Y1 X1 Y2 X2 Y3 X3
325
326move_src_window Y1 Y3 X1 X2 X3 | move Y4=y X4=x | succ(Y3, Y4) => \
327    move_src_window4 Y1 X1 X2 X3 X4
328
329move_src_window Y1 y X1 X2    => move_src_window2 Y1 X1 X2
330move_src_window Y1 y X1 X2 X3 => move_src_window3 Y1 X1 X2 X3
331
332move_src_window2 y x x
333move_src_window3 y x x x
334move_src_window4 y x x x x
335
336swap R1=x R2=y => swap R2 R1
337
338swap xy x
339swap y y
340
341swap R1=x R2=x | swap R3=x R1 => swap2 R1 R2 R3
342
343swap2 x x x
344
345# move_shift
346
347move SD=x    D=x | move Src=cxy SD=x  | distinct(D, Src) => move_shift Src SD D
348move SD=y    D=x | move Src=x  SD=y   | distinct(D, Src) => move_shift Src SD D
349move SD=y    D=x | init SD            |                  => move_shift n   SD D
350move SD=x    D=y | move Src=x  SD=x   | distinct(D, Src) => move_shift Src SD D
351move SD=x==0 D=y | move Src=y SD=x==0 | distinct(D, Src) => move_shift Src SD D
352
353move_shift cxy x x
354move_shift nx y x
355move_shift x x y
356move_shift y r y
357
358# move2_par x x x x
359
360move X1=x X2=x | move X3=x X4=x | independent_moves(X1, X2, X3, X4) => \
361     move2_par X1 X2 X3 X4
362move2_par x x x x
363
364# move2_par x x x y
365
366move X1=x X2=x | move X3=x Y1=y | independent_moves(X1, X2, X3, Y1) => \
367     move2_par X1 X2 X3 Y1
368move X3=x Y1=y | move X1=x X2=x | independent_moves(X3, Y1, X1, X2) => \
369     move2_par X1 X2 X3 Y1
370move2_par x x x y
371
372# move2_par y x y x
373
374move Y1=y X1=x | move Y2=y X2=x => move2_par Y1 X1 Y2 X2
375move2_par y x y x
376
377# move2_par y x x y
378
379move S1=y S2=x | move X1=x Y1=y | independent_moves(S1, S2, X1, Y1) => \
380     move2_par S1 S2 X1 Y1
381move X1=x Y1=y | move S1=y S2=x | independent_moves(S1, S2, X1, Y1) => \
382     move2_par S1 S2 X1 Y1
383move2_par y x x y
384
385# move2_par y x x x
386
387move Y1=y X1=x | move S1=x D1=x | independent_moves(Y1, X1, S1, D1) => \
388     move2_par Y1 X1 S1 D1
389move S1=x D1=x | move Y1=y X1=x | independent_moves(Y1, X1, S1, D1) => \
390     move2_par Y1 X1 S1 D1
391move2_par y x x x
392
393# move2_par y y y y
394
395move Y1=y Y2=y | move Y3=y Y4=y | independent_moves(Y1, Y2, Y3, Y4) => \
396     move2_par Y1 Y2 Y3 Y4
397move2_par y y y y
398
399# move3
400
401move2_par Y1=y X1=x Y2=y X2=x | move Y3=y X3=x => move3 Y1 X1 Y2 X2 Y3 X3
402move2_par X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6
403
404move3 y x y x y x
405move3 x x x x x x
406
407move xy xy
408move c xy
409move n x
410
411# The following move instructions using x(0) are frequently used.
412
413move x r
414move r x
415move y r
416move c r
417move r y
418
419# Receive operations.
420
421loop_rec Fail x==0 | smp_mark_target_label(Fail) => i_loop_rec Fail
422
423label L | wait_timeout Fail Src | smp_already_locked(L) => \
424    label L | wait_timeout_locked Src Fail
425wait_timeout Fail Src => wait_timeout_unlocked Src Fail
426
427wait_timeout_unlocked Src=aiq Fail => gen_literal_timeout(Fail, Src)
428wait_timeout_locked Src=aiq Fail => gen_literal_timeout_locked(Fail, Src)
429
430label L | wait Fail | smp_already_locked(L) => label L | wait_locked Fail
431wait Fail => wait_unlocked Fail
432
433label L | timeout | smp_already_locked(L) => label L | timeout_locked
434
435remove_message
436timeout
437timeout_locked
438i_loop_rec f
439loop_rec_end f
440wait_locked f
441wait_unlocked f
442
443# Note that a timeout value must fit in 32 bits.
444wait_timeout_unlocked_int I f
445wait_timeout_unlocked s f
446wait_timeout_locked_int I f
447wait_timeout_locked s f
448
449%cold
450i_wait_error
451i_wait_error_locked
452%hot
453
454send
455
456#
457# Optimized comparisons with one immediate/literal operand.
458#
459
460is_eq_exact Lbl S S =>
461is_eq_exact Lbl C1=c C2=c => move C1 x | is_eq_exact Lbl x C2
462is_eq_exact Lbl C=c R=xy => is_eq_exact Lbl R C
463
464is_eq_exact Lbl R=xy n => is_nil Lbl R
465is_eq_exact Lbl R=xy C=ia => i_is_eq_exact_immed Lbl R C
466is_eq_exact Lbl R=xy C=q => i_is_eq_exact_literal Lbl R C
467
468is_ne_exact Lbl S S => jump Lbl
469is_ne_exact Lbl C1=c C2=c => move C1 x | is_ne_exact Lbl x C2
470is_ne_exact Lbl C=c R=xy => is_ne_exact Lbl R C
471
472is_ne_exact Lbl R=xy C=ian => i_is_ne_exact_immed Lbl R C
473is_ne_exact Lbl R=xy C=q => i_is_ne_exact_literal Lbl R C
474
475i_is_eq_exact_immed f? rxy c
476
477i_is_eq_exact_literal f? xy c
478
479i_is_ne_exact_immed f? xy c
480
481i_is_ne_exact_literal f? xy c
482
483is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y
484is_eq_exact f? x xy
485is_eq_exact f? y y
486
487is_ne_exact f? S S
488
489# When either operand for is_lt or is_ge is a literal,
490# that literal is almost always an integer and almost never
491# an atom. Therefore we use a specialized instruction when
492# one of the operands is a literal.
493
494is_lt Fail Src=x Lit=c => is_lt_literal Fail Src Lit
495is_lt Fail Lit=c Src=x => is_lt_literal Fail Lit Src
496
497is_lt f? x x
498is_lt_literal f? x c
499is_lt_literal f? c x
500%cold
501is_lt f? s s
502%hot
503
504is_ge Fail Src=x Lit=c => is_ge_literal Fail Src Lit
505is_ge Fail Lit=c Src=x => is_ge_literal Fail Lit Src
506
507is_ge f? x x
508is_ge_literal f? x c
509is_ge_literal f? c x
510%cold
511is_ge f? s s
512%hot
513
514is_eq Fail=f Const=c Reg=xy => is_eq Fail Reg Const
515is_eq Fail=f C1=c C2=c => move C1 x | is_eq Fail x C2
516is_eq f? S s
517
518is_ne Fail=f Const=c Reg=xy => is_ne Fail Reg Const
519is_ne Fail=f C1=c C2=c => move C1 x | is_ne Fail x C2
520is_ne f? S s
521
522#
523# Putting tuples.
524#
525# Code compiled with OTP 22 and later uses put_tuple2 to
526# to construct a tuple.
527#
528# Code compiled before OTP 22 uses put_tuple + one put instruction
529# per element. Translate to put_tuple2.
530#
531
532i_put_tuple/2
533put_tuple Arity Dst => i_put_tuple Dst u
534
535i_put_tuple Dst Arity Puts=* | put S1 | put S2 | \
536  put S3 | put S4 | put S5 => \
537	    tuple_append_put5(Arity, Dst, Puts, S1, S2, S3, S4, S5)
538
539i_put_tuple Dst Arity Puts=* | put S => \
540	    tuple_append_put(Arity, Dst, Puts, S)
541
542i_put_tuple Dst Arity Puts=* => put_tuple2 Dst Arity Puts
543
544put_tuple2 xy I
545
546#
547# Putting lists.
548#
549# The instruction "put_list Const [] Dst" were generated in rare
550# circumstances up to and including OTP 18. Starting with OTP 19,
551# AFAIK, it should never be generated.
552#
553put_list Const=c n Dst => move Const x | put_list x n Dst
554
555put_list Src Dst=x Dst => update_list Src Dst
556
557update_list xyc x
558
559# put_list SrcReg1 SrcReg2 => Dst
560
561put_list xy xy x
562
563# put_list SrcReg [] => Dst
564
565put_list xy n xy
566
567# put_list SrcReg Constant => x
568
569put_list xy c x
570
571# put_list Constant SrcReg => Dst
572
573put_list c xy x
574
575# The following put_list instructions using x(0) are frequently used.
576
577put_list r n rx
578put_list r x rx
579put_list x x r
580
581%cold
582put_list s s d
583%hot
584
585
586#
587# Some more only used by the emulator
588#
589
590%cold
591normal_exit
592continue_exit
593call_bif W
594call_nif W W W
595call_nif_early
596call_error_handler
597error_action_code
598return_trace
599%hot
600
601#
602# Instruction transformations & folded instructions.
603#
604
605# Note: There is no 'move_return y r', since there never are any y registers
606# when we do move_return (if we have y registers, we must do move_deallocate_return).
607
608move S x==0 | return => move_return S
609
610move_return xcn
611
612move S x==0 | deallocate D | return => move_deallocate_return S D
613
614move_deallocate_return xycn Q
615
616deallocate u==0 | return => deallocate_return0
617deallocate u==1 | return => deallocate_return1
618deallocate u==2 | return => deallocate_return2
619deallocate u==3 | return => deallocate_return3
620deallocate u==4 | return => deallocate_return4
621
622deallocate D | return => deallocate_return D
623
624deallocate_return0
625deallocate_return1
626deallocate_return2
627deallocate_return3
628deallocate_return4
629
630deallocate_return Q
631
632test_heap Need u==1 | put_list Y=y x==0 x==0 => test_heap_1_put_list Need Y
633
634test_heap_1_put_list I y
635
636#
637# is_tagged_tuple Fail=f Src=rxy Arity Atom=a
638#
639
640is_tagged_tuple Fail Literal=q Arity Atom => \
641    move Literal x | is_tagged_tuple Fail x Arity Atom
642is_tagged_tuple Fail=f c Arity Atom  => jump Fail
643
644is_tagged_tuple f? rxy A a
645
646# Test tuple & arity (head)
647
648is_tuple Fail Literal=q => move Literal x | is_tuple Fail x
649is_tuple Fail=f c => jump Fail
650is_tuple Fail=f S=xy | test_arity Fail=f S=xy Arity => is_tuple_of_arity Fail S Arity
651
652is_tuple_of_arity f? rxy A
653
654is_tuple f? rxy
655
656test_arity Fail Literal=q Arity => move Literal x | test_arity Fail x Arity
657test_arity Fail=f c Arity => jump Fail
658test_arity Fail Tuple=x Arity | get_tuple_element Tuple Pos Dst=x => \
659   test_arity_get_tuple_element Fail Tuple Arity Pos Dst
660
661test_arity f? xy A
662
663test_arity_get_tuple_element f? x A P x
664
665is_tuple NotTupleFail Tuple=x | is_tagged_tuple WrongRecordFail Tuple Arity Atom => \
666   is_tagged_tuple_ff NotTupleFail WrongRecordFail Tuple Arity Atom
667
668is_tagged_tuple_ff f? f? rx A a
669
670get_tuple_element Reg=x P1 D1=x | \
671   get_tuple_element Reg=x P2 D2=x | \
672   get_tuple_element Reg=x P3 D3=x | \
673   succ(P1, P2) | succ(P2, P3) | succ(D1, D2) | succ(D2, D3) | \
674   distinct(D1, Reg) | distinct(D2, Reg) => \
675      i_get_tuple_element3 Reg P1 D1
676
677get_tuple_element Reg=x P1 D1=x | \
678   get_tuple_element Reg=x P2 D2=x | \
679   succ(P1, P2) | succ(D1, D2) | \
680   distinct(D1, Reg) => \
681      i_get_tuple_element2 Reg P1 D1
682
683get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \
684   succ(P1, P2) | distinct(D1, Reg) => i_get_tuple_element2_dst Reg P1 D1 D2
685
686get_tuple_element Reg=x P1 D1=y | get_tuple_element Reg=x P2 D2=y | \
687   succ(P1, P2) => i_get_tuple_element2_dst Reg P1 D1 D2
688
689get_tuple_element Reg P Dst => i_get_tuple_element Reg P Dst
690
691is_integer Fail=f i =>
692is_integer Fail=f an => jump Fail
693is_integer Fail Literal=q => move Literal x | is_integer Fail x
694
695is_integer Fail=f S=x | allocate Need Regs => is_integer_allocate Fail S Need Regs
696
697is_integer_allocate f? x t t
698
699is_integer f? xy
700
701is_list Fail=f n =>
702is_list Fail Literal=q => move Literal x | is_list Fail x
703is_list Fail=f c => jump Fail
704is_list f? x
705%cold
706is_list f? y
707%hot
708
709is_nonempty_list Fail=f S=x | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs
710
711is_nonempty_list Fail=f S=x | get_list S D1=x D2=x => \
712  is_nonempty_list_get_list Fail S D1 D2
713
714is_nonempty_list Fail=f S=x | get_hd S Dst=x => \
715  is_nonempty_list_get_hd Fail S Dst
716
717is_nonempty_list Fail=f S=x | get_tl S Dst=x => \
718  is_nonempty_list_get_tl Fail S Dst
719
720is_nonempty_list_allocate f? rx t t
721
722is_nonempty_list_get_list f? rx x x
723is_nonempty_list_get_hd f? x x
724is_nonempty_list_get_tl f? x x
725
726is_nonempty_list f? xy
727
728is_atom f? x
729%cold
730is_atom f? y
731%hot
732is_atom Fail=f a =>
733is_atom Fail=f niq => jump Fail
734
735is_float f? x
736%cold
737is_float f? y
738%hot
739is_float Fail=f nai => jump Fail
740is_float Fail Literal=q => move Literal x | is_float Fail x
741
742is_nil Fail=f n =>
743is_nil Fail=f qia => jump Fail
744
745is_nil f? xy
746
747is_binary Fail Literal=q => move Literal x | is_binary Fail x
748is_binary Fail=f c => jump Fail
749is_binary f? x
750%cold
751is_binary f? y
752%hot
753
754# XXX Deprecated.
755is_bitstr Fail Term => is_bitstring Fail Term
756
757is_bitstring Fail Literal=q => move Literal x | is_bitstring Fail x
758is_bitstring Fail=f c => jump Fail
759is_bitstring f? x
760%cold
761is_bitstring f? y
762%hot
763
764is_reference Fail=f cq => jump Fail
765is_reference f? x
766%cold
767is_reference f? y
768%hot
769
770is_pid Fail=f cq => jump Fail
771is_pid f? x
772%cold
773is_pid f? y
774%hot
775
776is_port Fail=f cq => jump Fail
777is_port f? x
778%cold
779is_port f? y
780%hot
781
782is_boolean Fail=f a==am_true =>
783is_boolean Fail=f a==am_false =>
784is_boolean Fail=f ac => jump Fail
785
786%cold
787is_boolean f? xy
788%hot
789
790is_function2 Fail=f Fun Arity => gen_is_function2(Fail, Fun, Arity)
791
792%cold
793cold_is_function2 f? x x
794%hot
795hot_is_function2 f? S t
796
797# Allocating & initializing.
798allocate Need Regs | init Y => allocate_init Need Regs Y
799init Y1 | init Y2 => init2 Y1 Y2
800
801allocate_init t t? y
802
803#################################################################
804# External function and bif calls.
805#################################################################
806
807# Expands into call_light_bif(_only)/2
808call_light_bif/1
809call_light_bif_only/1
810call_light_bif_last/2
811
812#
813# The load_nif/2 BIF is an instruction.
814#
815
816call_ext u==2 u$func:erlang:load_nif/2 => i_load_nif
817call_ext_last u==2 u$func:erlang:load_nif/2 D => i_load_nif | deallocate_return D
818call_ext_only u==2 u$func:erlang:load_nif/2 => i_load_nif | return
819
820%cold
821i_load_nif
822%hot
823
824#
825# apply/2 is an instruction, not a BIF.
826#
827
828call_ext u==2 u$func:erlang:apply/2 => i_apply_fun
829call_ext_last u==2 u$func:erlang:apply/2 D => i_apply_fun_last D
830call_ext_only u==2 u$func:erlang:apply/2 => i_apply_fun_only
831
832#
833# The apply/3 BIF is an instruction.
834#
835
836call_ext u==3 u$func:erlang:apply/3 => i_apply
837call_ext_last u==3 u$func:erlang:apply/3 D => i_apply_last D
838call_ext_only u==3 u$func:erlang:apply/3 => i_apply_only
839
840#
841# The yield/0 BIF is an instruction
842#
843
844call_ext u==0 u$func:erlang:yield/0 => i_yield
845call_ext_last u==0 u$func:erlang:yield/0 D => i_yield | deallocate_return D
846call_ext_only u==0 u$func:erlang:yield/0 => i_yield | return
847
848#
849# The hibernate/3 BIF is an instruction.
850#
851call_ext u==3 u$func:erlang:hibernate/3 => i_hibernate
852call_ext_last u==3 u$func:erlang:hibernate/3 D => i_hibernate
853call_ext_only u==3 u$func:erlang:hibernate/3 => i_hibernate
854
855#
856# If VM probes are not enabled, we want to short-circult calls to
857# the dt tag BIFs to make them as cheap as possible.
858#
859
860%unless USE_VM_PROBES
861
862call_ext Arity u$func:erlang:dt_get_tag/0 => \
863    move a=am_undefined x=0
864call_ext_last Arity u$func:erlang:dt_get_tag/0 D => \
865    move a=am_undefined x=0 | deallocate D | return
866call_ext_only Arity u$func:erlang:dt_get_tag/0 => \
867    move a=am_undefined x=0 | return
868
869move Any x==0 | call_ext Arity u$func:erlang:dt_put_tag/1 => \
870    move a=am_undefined x=0
871move Any x==0 | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
872    move a=am_undefined x=0 | deallocate D | return
873move Any x==0 | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
874    move a=am_undefined x=0 | return
875call_ext Arity u$func:erlang:dt_put_tag/1 => \
876    move a=am_undefined x=0
877call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
878    move a=am_undefined x=0 | deallocate D | return
879call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
880    move a=am_undefined x=0 | return
881
882call_ext Arity u$func:erlang:dt_get_tag_data/0 => \
883    move a=am_undefined x=0
884call_ext_last Arity u$func:erlang:dt_get_tag_data/0 D => \
885    move a=am_undefined x=0 | deallocate D | return
886call_ext_only Arity u$func:erlang:dt_get_tag_data/0 => \
887    move a=am_undefined x=0 | return
888
889move Any x==0 | call_ext Arity u$func:erlang:dt_spread_tag/1 => \
890    move a=am_true x=0
891move Any x==0 | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
892    move a=am_true x=0 | deallocate D | return
893move Any x==0 | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
894    move a=am_true x=0 | return
895call_ext Arity u$func:erlang:dt_spread_tag/1 => \
896    move a=am_true x=0
897call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
898    move a=am_true x=0 | deallocate D | return
899call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
900    move a=am_true x=0 | return
901
902move Any x==0 | call_ext Arity u$func:erlang:dt_restore_tag/1 => \
903    move a=am_true x=0
904move Any x==0 | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
905    move a=am_true x=0 | deallocate D | return
906move Any x==0 | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
907    move a=am_true x=0 | return
908call_ext Arity u$func:erlang:dt_restore_tag/1 => \
909    move a=am_true x=0
910call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
911    move a=am_true x=0 | deallocate D | return
912call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
913    move a=am_true x=0 | return
914
915move Any x==0 | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
916    move Any x=0
917move Any x==0 | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
918    move Any x=0 | deallocate D | return
919move Any x==0 | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
920    move Any x=0 | return
921call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 =>
922call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
923    deallocate D | return
924call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
925    return
926
927move Any x==0 | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \
928    move Any x=0
929move Any x==0 | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
930    move Any x=0 | deallocate D | return
931move Any x==0 | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
932    move Any x=0 | return
933call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 =>
934call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
935    deallocate D | return
936call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
937    return
938
939# After one of the transformations above there can be a redundant move
940# instruction that can be discarded.
941#
942# The distinct() guard protects against the second move instruction being
943# `{move,{x,0},{x,0}}`, which is never generated by the compiler but could
944# be generated by an alternative code generator or found in handwritten
945# BEAM code.
946
947move Discarded x==0 | move Something Dst=x==0 | distinct(Something, Dst) => \
948   move Something x=0
949
950%endif
951
952call_ext u==0 u$func:os:perf_counter/0 => \
953    i_perf_counter
954call_ext_last u==0 u$func:os:perf_counter/0 D => \
955    i_perf_counter | deallocate_return D
956call_ext_only u==0 u$func:os:perf_counter/0 => \
957    i_perf_counter | return
958
959#
960# BIFs like process_info/1,2 require up-to-date information about the current
961# emulator state, which the ordinary call_light_bif instruction doesn't save.
962#
963
964call_ext u Bif=u$is_bif | is_heavy_bif(Bif) => \
965    i_call_ext Bif
966call_ext_last u Bif=u$is_bif D | is_heavy_bif(Bif) => \
967    i_call_ext Bif | deallocate_return D
968call_ext_only Ar=u Bif=u$is_bif | is_heavy_bif(Bif) => \
969    allocate u Ar | i_call_ext Bif | deallocate_return u
970
971#
972# The general case for BIFs that have no special requirements.
973#
974
975call_ext u Bif=u$is_bif => call_light_bif Bif
976call_ext_last u Bif=u$is_bif D => call_light_bif_last Bif D
977call_ext_only Ar=u Bif=u$is_bif => call_light_bif_only Bif
978
979#
980# Any remaining calls are calls to Erlang functions, not BIFs.
981# We rename the instructions to internal names.  This is necessary,
982# to avoid an end-less loop, because we want to call a few BIFs
983# with call instructions.
984#
985
986move S=c x==0 | call_ext Ar=u Func=u$is_not_bif => i_move_call_ext S Func
987move S=c x==0 | call_ext_last Ar=u Func=u$is_not_bif D => i_move_call_ext_last Func D S
988move S=c x==0 | call_ext_only Ar=u Func=u$is_not_bif => i_move_call_ext_only Func S
989
990call_ext Ar Func        => i_call_ext Func
991call_ext_last Ar Func D => i_call_ext_last Func D
992call_ext_only Ar Func   => i_call_ext_only Func
993
994i_apply
995i_apply_last Q
996i_apply_only
997
998i_apply_fun
999i_apply_fun_last Q
1000i_apply_fun_only
1001
1002#
1003# When a BIF is traced, these instructions make a body call through the export
1004# entry instead of calling the BIF directly (setting up a temporary stack frame
1005# if needed). We therefore retain the stack frame in call_light_bif_last, and
1006# add a deallocate_return after call_light_bif_only to remove the temporary
1007# stack frame before returning.
1008#
1009
1010call_light_bif Bif=u$is_bif => \
1011    call_light_bif Bif Bif
1012
1013call_light_bif_last Bif=u$is_bif D => \
1014    call_light_bif Bif Bif | deallocate_return D
1015
1016call_light_bif_only Bif=u$is_bif => \
1017    call_light_bif_only Bif Bif | deallocate_return u
1018
1019call_light_bif b e
1020call_light_bif_only b e
1021
1022%cold
1023
1024i_hibernate
1025i_perf_counter
1026
1027%hot
1028
1029#
1030# Calls to non-building and guard BIFs.
1031#
1032
1033bif0 u$bif:erlang:self/0 Dst=d => self Dst
1034bif0 u$bif:erlang:node/0 Dst=d => node Dst
1035
1036bif1 Fail=f Bif=u$bif:erlang:hd/1 Src=x Dst=x => is_nonempty_list_get_hd Fail Src Dst
1037bif1 Fail=f Bif=u$bif:erlang:tl/1 Src=x Dst=x => is_nonempty_list_get_tl Fail Src Dst
1038
1039bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => gen_get(Src, Dst)
1040
1041bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => gen_element(Jump, S1, S2, Dst)
1042
1043bif1 p Bif S1 Dst         => i_bif1_body S1 Bif Dst
1044bif1 Fail=f Bif S1 Dst    => i_bif1 S1 Fail Bif Dst
1045
1046bif2 p Bif S1 S2 Dst      => i_bif2_body S2 S1 Bif Dst
1047bif2 Fail=f Bif S1 S2 Dst => i_bif2 S2 S1 Fail Bif Dst
1048
1049i_get_hash c I d
1050i_get s d
1051
1052self xy
1053
1054node x
1055%cold
1056node y
1057%hot
1058
1059# Note: 'I' is sufficient because this instruction will only be used
1060# if the arity fits in 24 bits.
1061i_fast_element xy j? I d
1062
1063i_element xy j? s d
1064
1065i_bif1 s f? b d
1066i_bif1_body s b d
1067i_bif2 s s f? b d
1068i_bif2_body s s b d
1069i_bif3 s s s f? b d
1070i_bif3_body s s s b d
1071
1072#
1073# Internal calls.
1074#
1075
1076move S=cxy x==0 | call Ar P=f => move_call S P
1077
1078move_call/2
1079move_call cxy f
1080
1081move S x==0 | call_last Ar P=f D => move_call_last S P D
1082
1083move_call_last/3
1084move_call_last cxy f Q
1085
1086move S=cx x==0 | call_only Ar P=f => move_call_only S P
1087
1088move_call_only/2
1089move_call_only cx f
1090
1091call Ar Func        => i_call Func
1092call_last Ar Func D => i_call_last Func D
1093call_only Ar Func   => i_call_only Func
1094
1095i_call f
1096i_call_last f Q
1097i_call_only f
1098
1099i_call_ext e
1100i_call_ext_last e Q
1101i_call_ext_only e
1102
1103i_move_call_ext c e
1104i_move_call_ext_last e Q c
1105i_move_call_ext_only e c
1106
1107# Fun calls.
1108
1109call_fun Arity | deallocate D | return => i_call_fun_last Arity D
1110call_fun Arity => i_call_fun Arity
1111
1112i_call_fun t
1113i_call_fun_last t Q
1114
1115
1116#
1117# A fun with an empty environment can be converted to a literal.
1118# As a further optimization, the we try to move the fun to its
1119# final destination directly.
1120
1121make_fun2 OldIndex=u => gen_make_fun2(OldIndex)
1122
1123move_fun/2
1124move_fun Fun X0 | move X0 Dst | move Src X0 => move Fun Dst | move Src X0
1125move_fun Fun X0 | move A B | move X0 Dst | move Src X0 | \
1126  independent_moves(Fun, X0, A, B) | distinct(Dst, A) => \
1127    move Fun Dst | move A B | move Src X0
1128move_fun Fun X0 | move X0 Dst | make_fun2 OldIndex | \
1129  is_killed_by_make_fun(X0, OldIndex)=> \
1130    move Fun Dst | make_fun2 OldIndex
1131
1132move_fun Fun Dst => move Fun Dst
1133
1134%cold
1135i_make_fun W t
1136%hot
1137
1138is_function f? xy
1139is_function Fail=f c => jump Fail
1140
1141func_info M F A => i_func_info u M F A
1142
1143# ================================================================
1144# Bit syntax matching obsoleted in OTP 22.
1145# ================================================================
1146
1147%cold
1148bs_start_match2 Fail=f ica X Y D => jump Fail
1149bs_start_match2 Fail Bin X Y D => i_bs_start_match2 Bin Fail X Y D
1150i_bs_start_match2 xy f t t d
1151
1152bs_save2 Y=y Index => move Y x | bs_save2 x Index
1153bs_save2 Reg Index => gen_bs_save(Reg, Index)
1154i_bs_save2 x t
1155
1156bs_restore2 Y=y Index => move Y x | bs_restore2 x Index
1157bs_restore2 Reg Index => gen_bs_restore(Reg, Index)
1158i_bs_restore2 x t
1159
1160bs_context_to_binary Y=y | line L | badmatch Y => \
1161    move Y x | bs_context_to_binary x | line L | badmatch x
1162bs_context_to_binary Y=y => move Y x | bs_context_to_binary x
1163bs_context_to_binary x
1164%warm
1165
1166# ================================================================
1167# New bit syntax matching (R11B).
1168# ================================================================
1169
1170%warm
1171
1172# Matching integers
1173bs_match_string Fail Ms Bits Val => i_bs_match_string Ms Fail Bits Val
1174
1175i_bs_match_string xy f W W
1176
1177# Fetching integers from binaries.
1178bs_get_integer2 Fail=f Ms=xy Live=u Sz=sq Unit=u Flags=u Dst=d => \
1179			gen_get_integer2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
1180
1181i_bs_get_integer_small_imm Ms Bits Fail Flags Y=y => \
1182   i_bs_get_integer_small_imm Ms Bits Fail Flags x | move x Y
1183
1184i_bs_get_integer_imm Ms Bits Live Fail Flags Y=y => \
1185   i_bs_get_integer_imm Ms Bits Live Fail Flags x | move x Y
1186
1187i_bs_get_integer_small_imm xy W f? t x
1188i_bs_get_integer_imm xy W t f? t x
1189i_bs_get_integer xy f? t t S d
1190i_bs_get_integer_8 xy f? d
1191i_bs_get_integer_16 xy f? d
1192
1193%if ARCH_64
1194i_bs_get_integer_32 xy f? d
1195%endif
1196
1197# Fetching binaries from binaries.
1198bs_get_binary2 Fail=f Ms=xy Live=u Sz=sq Unit=u Flags=u Dst=d => \
1199			gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
1200
1201i_bs_get_binary_imm2 xy f? t W t d
1202i_bs_get_binary2 xy f t? S t d
1203i_bs_get_binary_all2 xy f? t t d
1204
1205# Fetching float from binaries.
1206bs_get_float2 Fail=f Ms=xy Live=u Sz=s Unit=u Flags=u Dst=d => \
1207		gen_get_float2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
1208
1209bs_get_float2 Fail=f Ms=x Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail
1210
1211i_bs_get_float2 xy f? t s t d
1212
1213# Miscellanous
1214
1215bs_skip_bits2 Fail=f Ms=xy Sz=sq Unit=u Flags=u => \
1216			gen_skip_bits2(Fail, Ms, Sz, Unit, Flags)
1217
1218i_bs_skip_bits_imm2 f? xy W
1219i_bs_skip_bits2 xy xy f? t
1220
1221bs_test_tail2 Fail=f Ms=xy Bits=u==0 => bs_test_zero_tail2 Fail Ms
1222bs_test_tail2 Fail=f Ms=xy Bits=u => bs_test_tail_imm2 Fail Ms Bits
1223bs_test_zero_tail2 f? xy
1224bs_test_tail_imm2 f? xy W
1225
1226bs_test_unit F Ms Unit=u==8 => bs_test_unit8 F Ms
1227bs_test_unit f? xy t
1228bs_test_unit8 f? xy
1229
1230# Gets a bitstring from the tail of a context.
1231bs_get_tail xy d t
1232
1233# New bs_start_match variant for contexts with external position storage.
1234#
1235# bs_get/set_position is used to save positions into registers instead of
1236# "slots" in the context itself, which lets us continue matching even after
1237# we've passed it off to another function.
1238
1239bs_start_match4 a==am_no_fail Live=u Src=xy Ctx=d => \
1240    bs_start_match3 p Src Live Ctx
1241bs_start_match4 Fail=f Live=u Src=xy Ctx=d => \
1242    bs_start_match3 Fail Src Live Ctx
1243
1244%if ARCH_64
1245
1246# This instruction nops on 64-bit platforms
1247bs_start_match4 a==am_resume Live Same Same =>
1248bs_start_match4 a==am_resume Live Ctx Dst => move Ctx Dst
1249
1250bs_start_match3 Fail Bin Live Ctx | bs_get_position Ctx Pos=x Ignored => \
1251    i_bs_start_match3_gp Bin Live Fail Ctx Pos
1252i_bs_start_match3_gp xy t j d x
1253
1254%else
1255
1256bs_start_match4 a==am_resume Live Ctx Dst => \
1257    bs_start_match4 a=am_no_fail Live Ctx Dst
1258
1259%endif
1260
1261bs_start_match3 Fail=j ica Live Dst => jump Fail
1262bs_start_match3 Fail Bin Live Dst => i_bs_start_match3 Bin Live Fail Dst
1263
1264i_bs_start_match3 xy t j d
1265
1266# Match context position instructions. 64-bit assumes that all positions can
1267# fit into an unsigned small.
1268
1269%if ARCH_64
1270    bs_get_position Src Dst Live => i_bs_get_position Src Dst
1271    i_bs_get_position xy xy
1272    bs_set_position xy xy
1273%else
1274    bs_get_position xy d t?
1275    bs_set_position xy xy
1276%endif
1277
1278#
1279# Utf8/utf16/utf32 support. (R12B-5)
1280#
1281bs_get_utf8 Fail=f Ms=xy u u Dst=d => i_bs_get_utf8 Ms Fail Dst
1282i_bs_get_utf8 xy f? d
1283
1284bs_skip_utf8 Fail=f Ms=xy u u => i_bs_get_utf8 Ms Fail x
1285
1286bs_get_utf16 Fail=f Ms=xy u Flags=u Dst=d => i_bs_get_utf16 Ms Fail Flags Dst
1287bs_skip_utf16 Fail=f Ms=xy u Flags=u => i_bs_get_utf16 Ms Fail Flags x
1288
1289i_bs_get_utf16 xy f? t d
1290
1291bs_get_utf32 Fail=f Ms=xy Live=u Flags=u Dst=d => \
1292	bs_get_integer2 Fail Ms Live i=32 u=1 Flags Dst | \
1293	i_bs_validate_unicode_retract Fail Dst Ms
1294bs_skip_utf32 Fail=f Ms=xy Live=u Flags=u => \
1295	bs_get_integer2 Fail Ms Live i=32 u=1 Flags x | \
1296	i_bs_validate_unicode_retract Fail x Ms
1297
1298i_bs_validate_unicode_retract j s S
1299%hot
1300
1301#
1302# Constructing binaries
1303#
1304%warm
1305
1306bs_init2 Fail Sz Words Regs Flags Dst | binary_too_big(Sz) => system_limit Fail
1307
1308bs_init2 Fail Sz Words Regs Flags Dst=y => \
1309   bs_init2 Fail Sz Words Regs Flags x | move x Dst
1310
1311bs_init2 Fail Sz=u Words=u==0 Regs Flags Dst => i_bs_init Sz Regs Dst
1312
1313bs_init2 Fail Sz=u Words Regs Flags Dst => \
1314   i_bs_init_heap Sz Words Regs Dst
1315
1316bs_init2 Fail Sz Words=u==0 Regs Flags Dst => \
1317  i_bs_init_fail Sz Fail Regs Dst
1318bs_init2 Fail Sz Words Regs Flags Dst => \
1319  i_bs_init_fail_heap Sz Words Fail Regs Dst
1320
1321i_bs_init_fail xy j? t? x
1322
1323i_bs_init_fail_heap s I j? t? x
1324
1325i_bs_init W t? x
1326
1327i_bs_init_heap W I t? x
1328
1329
1330bs_init_bits Fail Sz=o Words Regs Flags Dst => system_limit Fail
1331bs_init_bits Fail Sz Words Regs Flags Dst=y => \
1332   bs_init_bits Fail Sz Words Regs Flags x | move x Dst
1333
1334bs_init_bits Fail Sz=u Words=u==0 Regs Flags Dst => i_bs_init_bits Sz Regs Dst
1335bs_init_bits Fail Sz=u Words Regs Flags Dst =>  i_bs_init_bits_heap Sz Words Regs Dst
1336
1337bs_init_bits Fail Sz Words=u==0 Regs Flags Dst => \
1338  i_bs_init_bits_fail Sz Fail Regs Dst
1339bs_init_bits Fail Sz Words Regs Flags Dst => \
1340  i_bs_init_bits_fail_heap Sz Words Fail Regs Dst
1341
1342i_bs_init_bits_fail xy j? t? x
1343
1344i_bs_init_bits_fail_heap s I j? t? x
1345
1346i_bs_init_bits W t? x
1347i_bs_init_bits_heap W I t? x
1348
1349bs_add Fail S1=i==0 S2 Unit=u==1 D => move S2 D
1350
1351bs_add j? s s t? x
1352
1353bs_append Fail Size Extra Live Unit Bin Flags Dst => \
1354  move Bin x | i_bs_append Fail Extra Live Unit Size Dst
1355
1356bs_private_append Fail Size Unit Bin Flags Dst => \
1357  i_bs_private_append Fail Unit Size Bin Dst
1358
1359i_bs_private_append Fail Unit Size Bin Dst=y => \
1360  i_bs_private_append Fail Unit Size Bin x | move x Dst
1361
1362bs_init_writable
1363
1364i_bs_append j? I t? t s xy
1365i_bs_private_append j? t s S x
1366
1367#
1368# Storing integers into binaries.
1369#
1370
1371bs_put_integer Fail=j Sz=sq Unit=u Flags=u Src=s => \
1372			gen_put_integer(Fail, Sz, Unit, Flags, Src)
1373
1374i_new_bs_put_integer j? S t s
1375i_new_bs_put_integer_imm xyc j? W t
1376
1377#
1378# Utf8/utf16/utf32 support. (R12B-5)
1379#
1380
1381bs_utf8_size j Src Dst=d => i_bs_utf8_size Src Dst
1382bs_utf16_size j Src Dst=d => i_bs_utf16_size Src Dst
1383
1384bs_put_utf8 Fail u Src => i_bs_put_utf8 Fail Src
1385
1386bs_put_utf32 Fail=j Flags=u Src=s => \
1387   i_bs_validate_unicode Fail Src | bs_put_integer Fail i=32 u=1 Flags Src
1388
1389i_bs_utf8_size S x
1390i_bs_utf16_size S x
1391
1392i_bs_put_utf8 j? S
1393bs_put_utf16 j? t S
1394
1395i_bs_validate_unicode j? S
1396
1397# Handle unoptimized code.
1398i_bs_utf8_size Src=c Dst => move Src x | i_bs_utf8_size x Dst
1399i_bs_utf16_size Src=c Dst => move Src x | i_bs_utf16_size x Dst
1400i_bs_put_utf8 Fail Src=c => move Src x | i_bs_put_utf8 Fail x
1401bs_put_utf16 Fail Flags Src=c => move Src x | bs_put_utf16 Fail Flags x
1402i_bs_validate_unicode Fail Src=c => move Src x | i_bs_validate_unicode Fail x
1403
1404#
1405# Storing floats into binaries.
1406#
1407bs_put_float Fail Sz=q Unit Flags Val => badarg Fail
1408
1409bs_put_float Fail=j Sz=s Unit=u Flags=u Src=s => \
1410			gen_put_float(Fail, Sz, Unit, Flags, Src)
1411
1412i_new_bs_put_float j? S t s
1413i_new_bs_put_float_imm j? W t s
1414
1415#
1416# Storing binaries into binaries.
1417#
1418
1419bs_put_binary Fail=j Sz=s Unit=u Flags=u Src=s => \
1420			gen_put_binary(Fail, Sz, Unit, Flags, Src)
1421
1422# In unoptimized code, the binary argument could be a literal. (In optimized code,
1423# there would be a bs_put_string instruction.)
1424i_new_bs_put_binary Fail Size Unit Lit=c => \
1425   move Lit x | i_new_bs_put_binary Fail Size Unit x
1426i_new_bs_put_binary_imm Fail Size Lit=c => \
1427   move Lit x | i_new_bs_put_binary_imm Fail Size x
1428i_new_bs_put_binary_all Lit=c Fail Unit => \
1429   move Lit x | i_new_bs_put_binary_all x Fail Unit
1430
1431i_new_bs_put_binary j? S t S
1432i_new_bs_put_binary_imm j? W S
1433i_new_bs_put_binary_all xy j? t
1434
1435#
1436# Warning: The i_bs_put_string and i_new_bs_put_string instructions
1437# are specially treated in the loader.
1438# Don't change the instruction format unless you change the loader too.
1439#
1440
1441bs_put_string W W
1442
1443#
1444# New floating point instructions (R8).
1445#
1446
1447fadd p FR1 FR2 FR3 => i_fadd FR1 FR2 FR3
1448fsub p FR1 FR2 FR3 => i_fsub FR1 FR2 FR3
1449fmul p FR1 FR2 FR3 => i_fmul FR1 FR2 FR3
1450fdiv p FR1 FR2 FR3 => i_fdiv FR1 FR2 FR3
1451fnegate p FR1 FR2 => i_fnegate FR1 FR2
1452
1453fconv Arg=iqan Dst=l => move Arg x | fconv x Dst
1454
1455fmove Arg=l Dst=d => fstore Arg Dst
1456fmove Arg=dq Dst=l => fload Arg Dst
1457
1458fstore l d
1459fload Sq l
1460
1461fconv S l
1462
1463i_fadd l l l
1464i_fsub l l l
1465i_fmul l l l
1466i_fdiv l l l
1467i_fnegate l l
1468
1469fclearerror | no_fpe_signals() =>
1470fcheckerror p | no_fpe_signals() =>
1471
1472%unless NO_FPE_SIGNALS
1473fcheckerror p => i_fcheckerror
1474
1475i_fcheckerror
1476fclearerror
1477%endif
1478
1479%hot
1480
1481#
1482# New apply instructions in R10B.
1483#
1484
1485apply t
1486apply_last t Q
1487
1488#
1489# Handle compatibility with OTP 17 here.
1490#
1491
1492i_put_map_assoc/4
1493
1494# We KNOW that in OTP 20 (actually OTP 18 and higher), a put_map_assoc instruction
1495# is always preceded by an is_map test. That means that put_map_assoc can never
1496# fail and does not need any failure label.
1497
1498put_map_assoc Fail Map Dst Live Size Rest=* | compiled_with_otp_20_or_higher() => \
1499	      i_put_map_assoc Map Dst Live Size Rest
1500
1501# Translate the put_map_assoc instruction if the module was compiled by a compiler
1502# before 20. This is only necessary if the OTP 17 compiler was used, but we
1503# have no safe and relatively easy way to know whether OTP 18/19 was used.
1504
1505put_map_assoc Fail=p Map Dst Live Size Rest=* => \
1506	      ensure_map Map | i_put_map_assoc Map Dst Live Size Rest
1507put_map_assoc Fail=f Map Dst Live Size Rest=* => \
1508	      is_map Fail Map | i_put_map_assoc Map Dst Live Size Rest
1509
1510ensure_map Lit=q | literal_is_map(Lit) =>
1511ensure_map Src=cqy => move Src x | ensure_map x
1512
1513%cold
1514ensure_map x
1515%hot
1516
1517#
1518# Map instructions. First introduced in R17.
1519#
1520
1521sorted_put_map_assoc/4
1522i_put_map_assoc Map Dst Live Size Rest=* | map_key_sort(Size, Rest) => \
1523  sorted_put_map_assoc Map Dst Live Size Rest
1524
1525sorted_put_map_exact/5
1526put_map_exact F Map Dst Live Size Rest=* | map_key_sort(Size, Rest) => \
1527  sorted_put_map_exact F Map Dst Live Size Rest
1528
1529sorted_put_map_assoc Map Dst Live Size Rest=* | is_empty_map(Map) => \
1530   new_map Dst Live Size Rest
1531sorted_put_map_assoc Src=xyc Dst Live Size Rest=* => \
1532   update_map_assoc Src Dst Live Size Rest
1533
1534sorted_put_map_exact Fail Src=xy Dst Live Size Rest=* => \
1535   update_map_exact Src Fail Dst Live Size Rest
1536# Literal map arguments for an exact update operation are extremely rare.
1537sorted_put_map_exact Fail Src Dst Live Size Rest=* => \
1538   move Src x | update_map_exact x Fail Dst Live Size Rest
1539
1540new_map Dst Live Size Rest=* | is_small_map_literal_keys(Size, Rest) => \
1541   gen_new_small_map_lit(Dst, Live, Size, Rest)
1542
1543new_map d t I
1544i_new_small_map_lit d t q
1545update_map_assoc xyc d t I
1546update_map_exact xy j? d t I
1547
1548is_map Fail Lit=q | literal_is_map(Lit) =>
1549is_map Fail cq => jump Fail
1550
1551is_map f? xy
1552
1553## Transform has_map_fields #{ K1 := _, K2 := _ } to has_map_elements
1554
1555has_map_fields Fail Src Size Rest=* => \
1556   gen_has_map_fields(Fail, Src, Size, Rest)
1557
1558## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 }
1559
1560get_map_elements Fail Src Size=u==2 Rest=* => \
1561    gen_get_map_element(Fail, Src, Size, Rest)
1562get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \
1563   gen_get_map_elements(Fail, Src, Size, Rest)
1564
1565i_get_map_elements f? s I
1566
1567i_get_map_element_hash Fail Src=c Key Hash Dst => \
1568    move Src x | i_get_map_element_hash Fail x Key Hash Dst
1569i_get_map_element_hash f? xy c I xy
1570
1571i_get_map_element Fail Src=c Key Dst => \
1572    move Src x | i_get_map_element Fail x Key Dst
1573i_get_map_element f? xy xy xy
1574
1575#
1576# Convert the plus operations to a generic plus instruction.
1577#
1578gen_plus/5
1579gen_minus/5
1580
1581gc_bif1 Fail Live u$bif:erlang:splus/1 Src Dst => \
1582   gen_plus Fail Live Src i Dst
1583gc_bif2 Fail Live u$bif:erlang:splus/2 S1 S2 Dst => \
1584   gen_plus Fail Live S1 S2 Dst
1585
1586gc_bif1 Fail Live u$bif:erlang:sminus/1 Src Dst => \
1587   gen_minus Fail Live i Src Dst
1588gc_bif2 Fail Live u$bif:erlang:sminus/2 S1 S2 Dst => \
1589   gen_minus Fail Live S1 S2 Dst
1590
1591#
1592# Optimize addition and subtraction of small literals using
1593# the i_increment/3 instruction (in bodies, not in guards).
1594#
1595
1596gen_plus p Live Int=i Reg=d Dst => \
1597	gen_increment(Reg, Int, Dst)
1598gen_plus p Live Reg=d Int=i Dst => \
1599	gen_increment(Reg, Int, Dst)
1600
1601gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \
1602	gen_increment_from_minus(Reg, Int, Dst)
1603
1604#
1605# Arithmetic instructions.
1606#
1607
1608# It is OK to swap arguments for '+' in a guard. It is also
1609# OK to turn minus into plus in a guard.
1610gen_plus Fail=f Live S1=c S2 Dst => i_plus S2 S1 Fail Dst
1611gen_minus Fail=f Live S1 S2=i Dst => gen_plus_from_minus(Fail, Live, S1, S2, Dst)
1612
1613gen_plus Fail Live S1 S2 Dst => i_plus S1 S2 Fail Dst
1614
1615gen_minus Fail Live S1 S2 Dst => i_minus S1 S2 Fail Dst
1616
1617gc_bif2 Fail Live u$bif:erlang:stimes/2 S1 S2 Dst => \
1618  i_times Fail S1 S2 Dst
1619
1620gc_bif2 Fail Live u$bif:erlang:div/2 S1 S2 Dst => \
1621  i_m_div Fail S1 S2 Dst
1622gc_bif2 Fail Live u$bif:erlang:intdiv/2 S1 S2 Dst => \
1623  i_int_div Fail S1 S2 Dst
1624
1625gc_bif2 Fail Live u$bif:erlang:rem/2 S1 S2 Dst => \
1626  i_rem S1 S2 Fail Dst
1627
1628gc_bif2 Fail Live u$bif:erlang:bsl/2 S1 S2 Dst => \
1629  i_bsl S1 S2 Fail Dst
1630gc_bif2 Fail Live u$bif:erlang:bsr/2 S1 S2 Dst => \
1631  i_bsr S1 S2 Fail Dst
1632
1633gc_bif2 Fail Live u$bif:erlang:band/2 S1 S2 Dst => \
1634  i_band S1 S2 Fail Dst
1635
1636gc_bif2 Fail Live u$bif:erlang:bor/2 S1 S2 Dst => \
1637  i_bor Fail S1 S2 Dst
1638
1639gc_bif2 Fail Live u$bif:erlang:bxor/2 S1 S2 Dst => \
1640  i_bxor Fail S1 S2 Dst
1641
1642gc_bif1 Fail Live u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src Dst
1643
1644i_increment rxy W d
1645
1646# Handle unoptimized code.
1647i_plus S1=c S2=c Fail Dst => move S1 x | i_plus x S2 Fail Dst
1648i_plus S1=c S2=xy Fail Dst => i_plus S2 S1 Fail Dst
1649
1650i_plus xy xyc j? d
1651
1652# A minus instruction with a constant right operand will be
1653# converted to an i_increment instruction, except in guards or
1654# when the negated value of the constant won't fit in a guard.
1655# Therefore, it very rare.
1656i_minus S1 S2=c Fail Dst => move S2 x | i_minus S1 x Fail Dst
1657
1658i_minus xy xy j? d
1659i_minus c xy j? d
1660
1661i_times j? s s d
1662
1663i_m_div j? s s d
1664i_int_div j? s s d
1665
1666i_rem x x j? d
1667i_rem s s j? d
1668
1669i_bsl s s j? d
1670i_bsr s s j? d
1671
1672i_band x c j? d
1673i_band s s j? d
1674
1675i_bor j? s s d
1676i_bxor j? s s d
1677
1678i_int_bnot Fail Src=c Dst => move Src x | i_int_bnot Fail x Dst
1679
1680i_int_bnot j? S d
1681
1682#
1683# Old guard BIFs that creates heap fragments are no longer allowed.
1684#
1685bif1 Fail u$bif:erlang:length/1 s d => too_old_compiler
1686bif1 Fail u$bif:erlang:size/1 s d => too_old_compiler
1687bif1 Fail u$bif:erlang:abs/1 s d => too_old_compiler
1688bif1 Fail u$bif:erlang:float/1 s d => too_old_compiler
1689bif1 Fail u$bif:erlang:round/1 s d => too_old_compiler
1690bif1 Fail u$bif:erlang:trunc/1 s d => too_old_compiler
1691
1692#
1693# Handle the length/1 guard BIF specially to make it trappable.
1694#
1695
1696gc_bif1 Fail=j Live u$bif:erlang:length/1 Src Dst => \
1697   i_length_setup Live Src | i_length Fail Live Dst
1698
1699i_length_setup Live Src=c => move Src x | i_length_setup Live x
1700
1701i_length_setup t xy
1702i_length j? t d
1703
1704#
1705# Guard BIFs.
1706#
1707gc_bif1 p Live Bif Src Dst           => i_bif1_body Src Bif Dst
1708gc_bif1 Fail=f Live Bif Src Dst      => i_bif1 Src Fail Bif Dst
1709
1710gc_bif2 p Live Bif S1 S2 Dst         => i_bif2_body S2 S1 Bif Dst
1711gc_bif2 Fail=f Live Bif S1 S2 Dst    => i_bif2 S2 S1 Fail Bif Dst
1712
1713gc_bif3 p Live Bif S1 S2 S3 Dst      => i_bif3_body S3 S2 S1 Bif Dst
1714gc_bif3 Fail=f Live Bif S1 S2 S3 Dst => i_bif3 S3 S2 S1 Fail Bif Dst
1715
1716#
1717# The following instruction is specially handled in beam_load.c
1718# to produce a user-friendly message if an unsupported guard BIF is
1719# encountered.
1720#
1721unsupported_guard_bif/3
1722unsupported_guard_bif A B C | never() =>
1723
1724#
1725# R13B03
1726#
1727on_load
1728
1729#
1730# R14A.
1731#
1732# Modified in OTP 21 because it turns out that we don't need the
1733# label after all.
1734#
1735
1736recv_mark f => i_recv_mark
1737i_recv_mark
1738
1739recv_set Fail | label Lbl | loop_rec Lf Reg => \
1740   i_recv_set | label Lbl | loop_rec Lf Reg
1741i_recv_set
1742
1743#
1744# OTP 21.
1745#
1746
1747build_stacktrace
1748raw_raise
1749
1750#
1751# Specialized move instructions. Since they don't require a second
1752# instruction, we have intentionally placed them after any other
1753# transformation rules that starts with a move instruction in order to
1754# produce better code for the transformation engine.
1755#
1756
1757# move_x1, move_x2
1758
1759move C=aiq X=x==1 => move_x1 C
1760move C=aiq X=x==2 => move_x2 C
1761
1762move n D=y => init D
1763
1764move_x1 c
1765move_x2 c
1766
1767