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