// -*- c -*-
//
// %CopyrightBegin%
//
// Copyright Ericsson AB 2017. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// %CopyrightEnd%
//
// Macros only used to generate instructions.
FAIL(Fail) {
//| -no_prefetch
SET_I((BeamInstr *) $Fail);
Goto(*I);
}
JUMP(Fail) {
//| -no_next
SET_I((BeamInstr *) $Fail);
Goto(*I);
}
GC_TEST(Ns, Nh, Live) {
unsigned need = $Nh + $Ns;
if (E - HTOP < need) {
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live, FCALLS);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
SWAPIN;
}
HEAP_SPACE_VERIFIED($Nh);
}
// Make sure that there are NeedStack + NeedHeap + 1 words available
// on the combined heap/stack segment, then allocates NeedHeap + 1
// words on the stack and saves CP.
AH(NeedStack, NeedHeap, Live) {
unsigned needed = $NeedStack + 1;
$GC_TEST(needed, $NeedHeap, $Live);
E -= needed;
SAVE_CP(E);
}
// Start of instruction listings
// Call instructions
DO_CALL(CallDest, NextInstr) {
//| -no_next
SET_CP(c_p, $NextInstr);
SET_I((BeamInstr *) $CallDest);
DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
Dispatch();
}
i_call(CallDest) {
$DO_CALL($CallDest, $NEXT_INSTRUCTION);
}
move_call(Src, CallDest) {
x(0) = $Src;
$DO_CALL($CallDest, $NEXT_INSTRUCTION);
}
i_call_last(CallDest, Deallocate) {
//| -no_next
RESTORE_CP(E);
E = ADD_BYTE_OFFSET(E, ($Deallocate));
SET_I((BeamInstr *) $CallDest);
DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
Dispatch();
}
move_call_last(Src, CallDest, Deallocate) {
x(0) = $Src;
$i_call_last($CallDest, $Deallocate);
}
i_call_only(CallDest) {
//| -no_next
SET_I((BeamInstr *) $CallDest);
DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
Dispatch();
}
move_call_only(Src, CallDest) {
x(0) = $Src;
$i_call_only($CallDest);
}
// Other instructions
allocate(NeedStack, Live) {
$AH($NeedStack, 0, $Live);
}
allocate_heap(NeedStack, NeedHeap, Live) {
$AH($NeedStack, $NeedHeap, $Live);
}
allocate_init(NeedStack, Live, Y) {
$AH($NeedStack, 0, $Live);
make_blank($Y);
}
allocate_zero(NeedStack, Live) {
Eterm* ptr;
int i = $NeedStack;
$AH(i, 0, $Live);
for (ptr = E + i; ptr > E; ptr--) {
make_blank(*ptr);
}
}
allocate_heap_zero(NeedStack, NeedHeap, Live) {
Eterm* ptr;
int i = $NeedStack;
$AH(i, $NeedHeap, $Live);
for (ptr = E + i; ptr > E; ptr--) {
make_blank(*ptr);
}
}
// This instruction is probably never used (because it is combined with a
// a return). However, a future compiler might for some reason emit a
// deallocate not followed by a return, and that should work.
deallocate(Deallocate) {
//| -no_prefetch
RESTORE_CP(E);
E = ADD_BYTE_OFFSET(E, $Deallocate);
}
deallocate_return(Deallocate) {
//| -no_next
int words_to_pop = $Deallocate;
SET_I((BeamInstr *) cp_val(*E));
E = ADD_BYTE_OFFSET(E, words_to_pop);
CHECK_TERM(x(0));
DispatchReturn;
}
get_list(Src, Hd, Tl) {
Eterm* tmp_ptr = list_val($Src);
Eterm hd, tl;
hd = CAR(tmp_ptr);
tl = CDR(tmp_ptr);
$Hd = hd;
$Tl = tl;
}
i_get(Src, Dst) {
$Dst = erts_pd_hash_get(c_p, $Src);
}
i_get_hash(Src, Hash, Dst) {
$Dst = erts_pd_hash_get_with_hx(c_p, $Hash, $Src);
}
i_get_tuple_element(Src, Element, Dst) {
Eterm* src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
$Dst = *src;
}
i_get_tuple_element2(Src, Element, Dst) {
Eterm* src;
Eterm* dst;
Eterm E1, E2;
src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
dst = &($Dst);
E1 = src[0];
E2 = src[1];
dst[0] = E1;
dst[1] = E2;
}
i_get_tuple_element2y(Src, Element, D1, D2) {
Eterm* src;
Eterm E1, E2;
src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
E1 = src[0];
E2 = src[1];
$D1 = E1;
$D2 = E2;
}
i_get_tuple_element3(Src, Element, Dst) {
Eterm* src;
Eterm* dst;
Eterm E1, E2, E3;
src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
dst = &($Dst);
E1 = src[0];
E2 = src[1];
E3 = src[2];
dst[0] = E1;
dst[1] = E2;
dst[2] = E3;
}
init(Y) {
make_blank($Y);
}
init2(Y1, Y2) {
make_blank($Y1);
make_blank($Y2);
}
init3(Y1, Y2, Y3) {
make_blank($Y1);
make_blank($Y2);
make_blank($Y3);
}
i_make_fun(FunP, NumFree) {
HEAVY_SWAPOUT;
x(0) = new_fun(c_p, reg, (ErlFunEntry *) $FunP, $NumFree);
HEAVY_SWAPIN;
}
i_trim(Words) {
Uint cp = E[0];
E += $Words;
E[0] = cp;
}
move(Src, Dst) {
$Dst = $Src;
}
move3(S1, D1, S2, D2, S3, D3) {
$D1 = $S1;
$D2 = $S2;
$D3 = $S3;
}
move_deallocate_return(Src, Deallocate) {
//| -no_next
x(0) = $Src;
$deallocate_return($Deallocate);
}
move_dup(Src, D1, D2) {
$D1 = $D2 = $Src;
}
move2_par(S1, D1, S2, D2) {
Eterm V1, V2;
V1 = $S1;
V2 = $S2;
$D1 = V1;
$D2 = V2;
}
move_shift(Src, SD, D) {
Eterm V;
V = $Src;
$D = $SD;
$SD = V;
}
move_window3(S1, S2, S3, D) {
Eterm xt0, xt1, xt2;
Eterm* y = &$D;
xt0 = $S1;
xt1 = $S2;
xt2 = $S3;
y[0] = xt0;
y[1] = xt1;
y[2] = xt2;
}
move_window4(S1, S2, S3, S4, D) {
Eterm xt0, xt1, xt2, xt3;
Eterm* y = &$D;
xt0 = $S1;
xt1 = $S2;
xt2 = $S3;
xt3 = $S4;
y[0] = xt0;
y[1] = xt1;
y[2] = xt2;
y[3] = xt3;
}
move_window5(S1, S2, S3, S4, S5, D) {
Eterm xt0, xt1, xt2, xt3, xt4;
Eterm *y = &$D;
xt0 = $S1;
xt1 = $S2;
xt2 = $S3;
xt3 = $S4;
xt4 = $S5;
y[0] = xt0;
y[1] = xt1;
y[2] = xt2;
y[3] = xt3;
y[4] = xt4;
}
move_return(Src) {
//| -no_next
x(0) = $Src;
SET_I(c_p->cp);
c_p->cp = 0;
DispatchReturn;
}
move_x1(Src) {
x(1) = $Src;
}
move_x2(Src) {
x(2) = $Src;
}
node(Dst) {
$Dst = erts_this_node->sysname;
}
put_list(Hd, Tl, Dst) {
HTOP[0] = $Hd;
HTOP[1] = $Tl;
$Dst = make_list(HTOP);
HTOP += 2;
}
i_put_tuple(Dst, Arity) {
//| -no_next
$Dst = make_tuple(HTOP);
pt_arity = $Arity;
I = $NEXT_INSTRUCTION;
goto do_put_tuple;
}
self(Dst) {
$Dst = c_p->common.id;
}
set_tuple_element(Element, Tuple, Offset) {
Eterm* p;
ASSERT(is_tuple($Tuple));
p = (Eterm *) ((unsigned char *) tuple_val($Tuple) + $Offset);
*p = $Element;
}
swap(R1, R2) {
Eterm V = $R1;
$R1 = $R2;
$R2 = V;
}
swap_temp(R1, R2, Tmp) {
Eterm V = $R1;
$R1 = $R2;
$R2 = $Tmp = V;
}
test_heap(Nh, Live) {
$GC_TEST(0, $Nh, $Live);
}
test_heap_1_put_list(Nh, Reg) {
$test_heap($Nh, 1);
$put_list($Reg, x(0), x(0));
}
is_integer_allocate(Fail, Src, NeedStack, Live) {
//| -no_prefetch
$is_integer($Fail, $Src);
$AH($NeedStack, 0, $Live);
}
is_nonempty_list(Fail, Src) {
//| -no_prefetch
if (is_not_list($Src)) {
$FAIL($Fail);
}
}
is_nonempty_list_test_heap(Fail, Need, Live) {
//| -no_prefetch
$is_nonempty_list($Fail, x(0));
$test_heap($Need, $Live);
}
is_nonempty_list_allocate(Fail, Src, Need, Live) {
//| -no_prefetch
$is_nonempty_list($Fail, $Src);
$AH($Need, 0, $Live);
}
is_nonempty_list_get_list(Fail, Src, Hd, Tl) {
//| -no_prefetch
$is_nonempty_list($Fail, $Src);
$get_list($Src, $Hd, $Tl);
}
move_jump(Fail, Src) {
x(0) = $Src;
$JUMP($Fail);
}
//
// Test instructions.
//
is_atom(Fail, Src) {
if (is_not_atom($Src)) {
$FAIL($Fail);
}
}
is_boolean(Fail, Src) {
if (($Src) != am_true && ($Src) != am_false) {
$FAIL($Fail);
}
}
is_binary(Fail, Src) {
if (is_not_binary($Src) || binary_bitsize($Src) != 0) {
$FAIL($Fail);
}
}
is_bitstring(Fail, Src) {
if (is_not_binary($Src)) {
$FAIL($Fail);
}
}
is_float(Fail, Src) {
if (is_not_float($Src)) {
$FAIL($Fail);
}
}
is_function(Fail, Src) {
if ( !(is_any_fun($Src)) ) {
$FAIL($Fail);
}
}
is_function2(Fail, Fun, Arity) {
if (erl_is_function(c_p, $Fun, $Arity) != am_true ) {
$FAIL($Fail);
}
}
is_integer(Fail, Src) {
if (is_not_integer($Src)) {
$FAIL($Fail);
}
}
is_list(Fail, Src) {
if (is_not_list($Src) && is_not_nil($Src)) {
$FAIL($Fail);
}
}
is_map(Fail, Src) {
if (is_not_map($Src)) {
$FAIL($Fail);
}
}
is_nil(Fail, Src) {
if (is_not_nil($Src)) {
$FAIL($Fail);
}
}
is_number(Fail, Src) {
if (is_not_integer($Src) && is_not_float($Src)) {
$FAIL($Fail);
}
}
is_pid(Fail, Src) {
if (is_not_pid($Src)) {
$FAIL($Fail);
}
}
is_port(Fail, Src) {
if (is_not_port($Src)) {
$FAIL($Fail);
}
}
is_reference(Fail, Src) {
if (is_not_ref($Src)) {
$FAIL($Fail);
}
}
is_tagged_tuple(Fail, Src, Arityval, Tag) {
if (!(BEAM_IS_TUPLE($Src) &&
(tuple_val($Src))[0] == $Arityval &&
(tuple_val($Src))[1] == $Tag)) {
$FAIL($Fail);
}
}
is_tuple(Fail, Src) {
if (is_not_tuple($Src)) {
$FAIL($Fail);
}
}
is_tuple_of_arity(Fail, Src, Arityval) {
if (!(BEAM_IS_TUPLE($Src) && *tuple_val($Src) == $Arityval)) {
$FAIL($Fail);
}
}
test_arity(Fail, Pointer, Arity) {
if (*tuple_val($Pointer) != $Arity) {
$FAIL($Fail);
}
}
i_is_eq_exact_immed(Fail, X, Y) {
if ($X != $Y) {
$FAIL($Fail);
}
}
i_is_ne_exact_immed(Fail, X, Y) {
if ($X == $Y) {
$FAIL($Fail);
}
}
is_eq_exact(Fail, X, Y) {
if (!EQ($X, $Y)) {
$FAIL($Fail);
}
}
is_ne_exact(Fail, X, Y) {
if (EQ($X, $Y)) {
$FAIL($Fail);
}
}
is_eq(Fail, X, Y) {
CMP_EQ_ACTION($X, $Y, $FAIL($Fail));
}
is_ne(Fail, X, Y) {
CMP_NE_ACTION($X, $Y, $FAIL($Fail));
}
is_lt(Fail, X, Y) {
CMP_LT_ACTION($X, $Y, $FAIL($Fail));
}
is_ge(Fail, X, Y) {
CMP_GE_ACTION($X, $Y, $FAIL($Fail));
}
i_get_map_element(Fail, Src, Key, Dst) {
Eterm res = get_map_element($Src, $Key);
if (is_non_value(res)) {
$FAIL($Fail);
}
$Dst = res;
}
i_get_map_element_hash(Fail, Src, Key, Hx, Dst) {
Eterm res = get_map_element_hash($Src, $Key, $Hx);
if (is_non_value(res)) {
$FAIL($Fail);
}
$Dst = res;
}