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

# 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_rf_args;"
    echo "    int           num_hidden_r_args;"
    echo "#ifdef MR_BOXED_FLOAT"
    echo "    int           num_explicit_f_args = 0;"
    echo "    int           num_hidden_f_args;"
    echo "#endif"
    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_rf_args = closure->MR_closure_num_hidden_args_rf;"
    echo "    num_hidden_r_args = MR_closure_num_hidden_r_args(closure);"
    echo "#ifdef MR_BOXED_FLOAT"
    echo "    num_hidden_f_args = MR_closure_num_hidden_f_args(closure);"
    echo "#endif"

    if test $spec_explicit_arg -lt 0
    then
        echo "#ifdef MR_BOXED_FLOAT"
        echo "    num_explicit_args = (MR_r2 & 0xffff);"
        echo "    num_explicit_f_args = (MR_r2 >> 16);"
        echo "#else"
        echo "    num_explicit_args = MR_r2;"
        echo "#endif"
        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_r_args);"
    echo

    if test $max_spec_hidden_arg -ge 0 -a $spec_explicit_arg -ge 0
    then
        echo "    switch (num_hidden_rf_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_r_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_r_args,"
        echo "                MR_virtual_reg_value(i + MR_HO_CALL_INPUTS_COMPACT));"
        echo "        }"
        echo "    } else if (num_hidden_r_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_r_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_r_args + $i, arg$i);"
            i=`expr $i + 1`
        done
    fi

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

    echo
    echo "#ifdef MR_BOXED_FLOAT"
    echo "    if (num_hidden_f_args > 0) {"
    echo "        // Copy the explicit args to the right, from the right."
    echo "        for (i = num_explicit_f_args; i > 0 ; i--) {"
    echo "            MR_f(i + num_hidden_f_args) = MR_f(i);"
    echo "        }"
    echo "        for (i = 1; i <= num_hidden_f_args; i++) {"
    echo "            MR_f(i) = MR_word_to_float("
    echo "                closure->MR_closure_hidden_args("
    echo "                    num_hidden_r_args + i));"
    echo "        }"
    echo "    }"
    echo "#endif"

    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
