#!/bin/sh
# vim: ts=4 sw=4 et

# If you change max_spec_explicit_arg, you should consider changing
# the value of the max_specialized_do_call_closure option in
# compiler/options.m.
max_spec_explicit_arg=5
max_spec_hidden_arg=5

variants=""

spec_explicit_arg=-1
while test $spec_explicit_arg -le $max_spec_explicit_arg
do
    if test $spec_explicit_arg -lt 0
    then
        variant="compact"
    else
        variant="$spec_explicit_arg"
    fi

    echo "MR_define_entry(mercury__do_call_closure_$variant);"
    echo "{"
    echo "    MR_Closure    *closure;"
    echo "    int           num_explicit_args;"
    echo "    int           num_hidden_args;"
    echo "    int           i;"

    i=1;
    while test $i -le $spec_explicit_arg
    do
        echo "    MR_Word       arg$i;"
        i=`expr $i + 1`
    done

    echo
    echo "    closure = (MR_Closure *) MR_r1;"
    echo "    num_hidden_args = closure->MR_closure_num_hidden_args;"
    if test $spec_explicit_arg -lt 0
    then
        echo "    num_explicit_args = MR_r2;"
        num_explicit_args="num_explicit_args"
    else
        i=1;
        while test $i -le $spec_explicit_arg
        do
            j=`expr $i + 1`
            echo "    arg$i = MR_r$j;"
            i=`expr $i + 1`
        done
        num_explicit_args="$spec_explicit_arg"
    fi
    echo
    echo "    MR_maybe_record_closure_histogram($num_explicit_args, num_hidden_args);"
    echo

    if test $max_spec_hidden_arg -ge 0 -a $spec_explicit_arg -ge 0
    then
        echo "    switch (num_hidden_args) {"
        spec_hidden_arg=0
        while test $spec_hidden_arg -le $max_spec_hidden_arg
        do
            echo "        case $spec_hidden_arg:"
            i=1;
            while test $i -le $spec_hidden_arg
            do
                echo "            MR_r$i = closure->MR_closure_hidden_args($i);"
                i=`expr $i + 1`
            done
            j=1
            while test $j -le $spec_explicit_arg
            do
                echo "            MR_r$i = arg$j;"
                i=`expr $i + 1`
                j=`expr $j + 1`
            done

            echo "            MR_tailcall(closure->MR_closure_code, MR_prof_ho_caller_proc);"
            echo "            break;"
            echo
            spec_hidden_arg=`expr $spec_hidden_arg + 1`
        done
        echo "        default:"
        echo "            /* fall through to the general case below */"
        echo "            break;"
        echo "    }"
        echo
    fi

    echo "    MR_save_registers();"
    if test $spec_explicit_arg -lt 0
    then
        echo "    if (num_hidden_args < MR_HO_CALL_INPUTS_COMPACT) {"
        echo "        /* copy the explicit args to the left, from the left */"
        echo "        for (i = 1; i <= num_explicit_args; i++) {"
        echo "            MR_virtual_reg_assign(i + num_hidden_args,"
        echo "                MR_virtual_reg_value(i + MR_HO_CALL_INPUTS_COMPACT));"
        echo "        }"
        echo "    } else if (num_hidden_args > MR_HO_CALL_INPUTS_COMPACT) {"
        echo "        /* copy the explicit args to the right, from the right */"
        echo "        for (i = num_explicit_args; i > 0 ; i--) {"
        echo "            MR_virtual_reg_assign(i + num_hidden_args,"
        echo "                MR_virtual_reg_value(i + MR_HO_CALL_INPUTS_COMPACT));"
        echo "        }"
        echo "    } /* else the explicit args are in the right place */"
    else
        i=1
        while test $i -le $spec_explicit_arg
        do
            echo "    MR_virtual_reg_assign(num_hidden_args + $i, arg$i);"
            i=`expr $i + 1`
        done
    fi

    echo
    echo "    for (i = 1; i <= num_hidden_args; i++) {"
    echo "        MR_virtual_reg_assign(i, closure->MR_closure_hidden_args(i));"
    echo "    }"
    echo "    MR_restore_registers();"
    echo
    echo "    MR_tailcall(closure->MR_closure_code, MR_prof_ho_caller_proc);"
    echo "}"
    echo

    variants="$variants $variant"

    # On some systems, e.g. FreeBSD, expr will treat a leading argument
    # beginning with a minus sign as an option to the program rather than
    # as a negative number.  Since the value of $spec_explicit_arg may be
    # negative we arrange the arguments in the following so that first
    # argument position is occupied by a positive number.
    #
    spec_explicit_arg=`expr 1 + $spec_explicit_arg`
done

# Create these files atomically.

{
    for variant in $variants
    do
        echo "MR_define_extern_entry(mercury__do_call_closure_$variant);"
    done
} > mercury_ho_call_declares.i

{
    for variant in $variants
    do
        echo "    MR_init_entry_an(mercury__do_call_closure_$variant);"
    done
} > mercury_ho_call_inits.i
