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