aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/beam/select_instrs.tab
blob: e85ed2c3042cc1b519083ccceedd52112e64c800 (plain) (tree)



































































































































































































                                                                                      
// -*- 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%
//

i_select_val_bins := select_val_bins.fetch.select;

select_val_bins.head() {
    Eterm select_val;
}

select_val_bins.fetch(Src) {
    select_val = $Src;
}

select_val_bins.select(Fail, NumElements) {
    struct Pairs {
        BeamInstr val;
        BeamInstr* addr;
    };
    struct Pairs* low;
    struct Pairs* high;
    struct Pairs* mid;
    int bdiff; /* int not long because the arrays aren't that large */

    low = (struct Pairs *) (&$NumElements + 1);
    high = low + $NumElements;

    /* The pointer subtraction (high-low) below must produce
     * a signed result, because high could be < low. That
     * requires the compiler to insert quite a bit of code.
     *
     * However, high will be > low so the result will be
     * positive. We can use that knowledge to optimise the
     * entire sequence, from the initial comparison to the
     * computation of mid.
     *
     * -- Mikael Pettersson, Acumem AB
     *
     * Original loop control code:
     *
     * while (low < high) {
     *    mid = low + (high-low) / 2;
     *
     */
    while ((bdiff = (int)((char*)high - (char*)low)) > 0) {
        unsigned int boffset = ((unsigned int)bdiff >> 1) & ~(sizeof(struct Pairs)-1);

        mid = (struct Pairs*)((char*)low + boffset);
        if (select_val < mid->val) {
            high = mid;
        } else if (select_val > mid->val) {
            low = mid + 1;
        } else {
            $NEXT(mid->addr);
        }
    }
    $NEXT($Fail);
}

i_select_tuple_arity2 := select_val2.src.ta_fail.execute;
i_select_val2 := select_val2.src.fail.execute;

select_val2.head() {
    Eterm select_val2;
    BeamInstr* select_fail;
}

select_val2.src(Src) {
    select_val2 = $Src;
}

select_val2.ta_fail(Fail) {
    select_fail = &$Fail;
    if (is_not_tuple(select_val2)) {
        $FAIL(*select_fail);
    }
    select_val2 = *tuple_val(select_val2);
}

select_val2.fail(Fail) {
    select_fail = &$Fail;
}

select_val2.execute(T1, T2, D1, D2) {
    if (select_val2 == $T1) {
        $JUMP($D1);
    } else if (select_val2 == $T2) {
        $JUMP($D2);
    } else {
        $FAIL(*select_fail);
    }
}

i_select_tuple_arity := select_val_lin.fetch.ta_fail.execute;
i_select_val_lins := select_val_lin.fetch.fail.execute;

select_val_lin.head() {
    Eterm select_val;
    BeamInstr* select_fail;
}

select_val_lin.fetch(Src) {
    select_val = $Src;
}

select_val_lin.ta_fail(Fail) {
    select_fail = &$Fail;
    if (is_tuple(select_val)) {
        select_val = *tuple_val(select_val);
    } else {
        $JUMP(*select_fail);
    }
}

select_val_lin.fail(Fail) {
    select_fail = &$Fail;
}

select_val_lin.execute(N) {
    BeamInstr* vs = $NEXT_INSTRUCTION;
    int ix = 0;

    for (;;) {
        if (vs[ix+0] >= select_val) {
            ix += 0;
            break;
        }
        if (vs[ix+1] >= select_val) {
            ix += 1;
            break;
        }
        ix += 2;
    }

    if (vs[ix] == select_val) {
        I = $NEXT_INSTRUCTION + $N + ix;
        $JUMP(*I);
    } else {
        $JUMP(*select_fail);
    }
}

JUMP_ON_VAL(Fail, Index, N, Base) {
    if (is_small($Index)) {
        $Index = (Uint) (signed_val($Index) - $Base);
        if ($Index < $N) {
            $JUMP((($NEXT_INSTRUCTION)[$Index]));
        }
    }
    $FAIL($Fail);
}

i_jump_on_val_zero := jump_on_val_zero.fetch.execute;

jump_on_val_zero.head() {
    Eterm index;
}

jump_on_val_zero.fetch(Src) {
    index = $Src;
}

jump_on_val_zero.execute(Fail, N) {
    $JUMP_ON_VAL($Fail, index, $N, 0);
}

i_jump_on_val := jump_on_val.fetch.execute;

jump_on_val.head() {
    Eterm index;
}

jump_on_val.fetch(Src) {
    index = $Src;
}

jump_on_val.execute(Fail, N, Base) {
    $JUMP_ON_VAL($Fail, index, $N, $Base);
}