#!/bin/sh
# vim: ts=4 sw=4 expandtab
#---------------------------------------------------------------------------#
# Copyright (C) 2002, 2004 The University of Melbourne.
# This file may only be copied under the terms of the GNU General
# Public License - see the file COPYING in the Mercury distribution.
#---------------------------------------------------------------------------#
#
# This script generates the files mercury_profiling_builtin.[ch] in the
# runtime directory, files which contain the C declarations and code of the
# primitives needed for the deep profiling of the unify and operations
# on builtin types. (The Mercury declarations are in profiling_builtin.m
# in the library.)
#
# It should be executed in the runtime directory.

prefix="mercury__profiling_builtin__"
tmp_code="/tmp/make_port_code_$$"
tmp_prolog="/tmp/make_port_code_prolog$$"
tmp_declare_entry="/tmp/make_port_code_decl_$$"
tmp_define_entry="/tmp/make_port_code_defn_$$"
tmp_init_entry="/tmp/make_port_code_init_$$"
tmp_hlc="/tmp/make_port_code_hlc_$$"
trap "/bin/rm ${tmp_code} ${tmp_prolog} ${tmp_declare_entry} ${tmp_define_entry} ${tmp_init_entry} ${tmp_hlc}" 0 1 2 3 15
> ${tmp_code}
> ${tmp_declare_entry}
> ${tmp_define_entry}
> ${tmp_init_entry}
> ${tmp_hlc}

cat > ${tmp_prolog} << END
// Copyright (C) 2002 The University of Melbourne.
// Copyright (C) 2016, 2018, 2025 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.

// The contents of this file were generated by the make_port_code script
// in the tools directory. Do not edit.
//
// The function of the code in this module is described in the paper on deep
// profiling by Thomas Conway and Zoltan Somogyi.

END

source="mercury_profiling_builtin.c"
header="mercury_profiling_builtin.h"
module="runtime_profiling_builtin_module"
protect="MR_MERCURY_PROFILING_BUILTIN_H"

for impl in ac sr
do
    case $impl in
    ac) IMPL=AC
        ;;
    sr) IMPL=SR
        ;;
    esac

    for detism in det semi non
    do
        case $detism in
        det)    ports="call exit"
                ;;
        semi)   ports="call exit fail"
                ;;
        non)    ports="call exit redo fail"
                ;;
        esac

        for port in $ports
        do
            case $port in
            call)   
                file="mercury_deep_call_port_body.h"
                portdef="MR_CALL_PORT"
                return="MR_proceed();"
                if test $detism = non
                then
                    outermost_define="#define"
                else
                    outermost_define="#undef "
                fi
                decls=
                inputs="ProcLayout"
                outputs="TopCSD MiddleCSD"
                if test $impl = sr
                then
                    outputs="$outputs OldOutermostActivationPtr"
                fi
                if test $detism = non
                then
                    outputs="$outputs NewOutermostActivationPtr"
                fi
                ;;

            exit|fail)
                file="mercury_deep_leave_port_body.h"
                if test $port = exit
                then
                    portdef="MR_EXIT_PORT"
                    return="MR_proceed();"
                else
                    portdef="MR_FAIL_PORT"
                    return="MR_r1 = MR_FALSE; MR_proceed();"
                fi
                outermost_define="#undef "
                inputs="TopCSD MiddleCSD"
                outputs=""
                if test $impl = sr
                then
                    inputs="$inputs OldOutermostActivationPtr"
                fi
                ;;

            redo)
                file="mercury_deep_redo_port_body.h"
                portdef="MR_REDO_PORT"
                return="MR_r1 = MR_FALSE; MR_proceed();"
                outermost_define="#undef "
                inputs="MiddleCSD NewOutermostActivationPtr"
                outputs=""
                ;;
            esac

            arity=0
            for arg in ${inputs} ${outputs}
            do
                arity=`expr ${arity} + 1`
            done

            name="${detism}_${port}_port_code_${impl}_${arity}_0"
            hlcname="${detism}_${port}_port_code_${impl}_${arity}_p_0"
            msgname="${detism}_${port}_port_code_${impl}"
            (
                echo ""
                echo "MR_define_entry(${prefix}${name});"
                echo "{"
                for arg in ${inputs} ${outputs}
                do
                    echo "MR_Word ${arg};"
                done
                echo ""
                n=1
                for arg in ${inputs}
                do
                    echo "${arg} = MR_r${n};"
                    n=`expr $n + 1`
                done
                echo ""
                echo "#define MR_PROCNAME \"${msgname}\""
                echo "#define MR_VERSION_${IMPL}"
                echo "#define ${portdef}"
                echo "${outermost_define} MR_NEED_NEW_OUTERMOST"
                # We could do "echo "#include \"${file}\"" here to get a
                # smaller source file. However, if we did that, using gdb to
                # debug deep profiling would be harder, since gdb doesn't
                # handle breakpoints on multiply-included lines sensibly.
                cat ${file}
                echo "#undef  MR_PROCNAME"
                echo "#undef  MR_VERSION_${IMPL}"
                echo "#undef  ${portdef}"
                echo "#undef  MR_NEED_NEW_OUTERMOST"
                echo ""
                n=1
                for arg in ${outputs}
                do
                    echo "MR_r${n} = ${arg};"
                    n=`expr $n + 1`
                done
                echo "}"
                echo ${return}
            ) >> ${tmp_code}

            decl="MR_declare_entry(${prefix}${name});"
            echo ${decl} >> ${tmp_declare_entry}

            defn="MR_define_extern_entry(${prefix}${name});"
            echo ${defn} >> ${tmp_define_entry}

            init="MR_init_entry_an(${prefix}${name});"
            echo ${init} >> ${tmp_init_entry}

            # We generate prototypes for these functions before the definition
            # to shut up the warning you would otherwise get.
            #
            # Note that the types in the prototype and the definition are
            # lies. This is OK, because the only reason why we have these
            # functions is that in the non-deep profiling grades, we need
            # their addresses, because the library/profiling_builtin.m declares
            # the corresponding predicates. These functions should never be
            # called, since the combination of hlc and deep profiling is not
            # (yet) supported.
            (
                echo
                echo "extern void MR_CALL"
                echo "${prefix}${hlcname}("
                line="    "
                n=1
                for arg in ${inputs} ${outputs}
                do
                    if test $n -gt 1
                    then
                        line="${line}, void *arg${n}"
                    else
                        line="${line}void *arg${n}"
                    fi
                    n=`expr $n + 1`
                done
                echo "${line});"
                echo "void MR_CALL"
                echo "${prefix}${hlcname}("
                line="    "
                n=1
                for arg in ${inputs} ${outputs}
                do
                    if test $n -gt 1
                    then
                        line="${line}, void *arg${n}"
                    else
                        line="${line}void *arg${n}"
                    fi
                    n=`expr $n + 1`
                done
                echo "${line})"
                echo "{ MR_fatal_error(\"call to ${prefix}${hlcname}\"); }"
            ) >> ${tmp_hlc}
        done
    done
done

#---------------------------------------------------------------------------#
# assemble the header file

cat ${tmp_prolog} > ${header}
cat >> ${header} << END
#ifndef ${protect}
#define ${protect}

#include "mercury_goto.h"

#ifndef  MR_HIGHLEVEL_CODE
END
cat ${tmp_declare_entry} >> ${header}
cat >> ${header} << END
#endif  // MR_HIGHLEVEL_CODE
#endif  // ${protect}
END

#---------------------------------------------------------------------------#
# assemble the source file

cat ${tmp_prolog} > ${source}
cat >> ${source} << END
#include "mercury_imp.h"

#ifndef MR_HIGHLEVEL_CODE
#include "mercury_deep_profiling_hand.h"
#include "${header}"

END
cat ${tmp_define_entry} >> ${source}
echo >> ${source}
echo "MR_BEGIN_MODULE(${module})" >> ${source}
cat ${tmp_init_entry} >> ${source}
echo >> ${source}
echo "MR_BEGIN_CODE" >> ${source}
cat ${tmp_code} >> ${source}
cat >> ${source} << END
MR_END_MODULE

#else   // MR_HIGHLEVEL_CODE
END
cat ${tmp_hlc} >> ${source}
cat >> ${source} << END

#endif  // ! MR_HIGHLEVEL_CODE
 
// Ensure that the initialization code for the above module gets to run.
/*
INIT mercury_sys_init_${module}
*/

// Forward declarations to suppress gcc -Wmissing-decl warnings.
void mercury_sys_init_${module}_init(void);
void mercury_sys_init_${module}_init_type_tables(void);
#ifdef  MR_DEEP_PROFILING
void mercury_sys_init_${module}_write_out_proc_statics(FILE *fp);
#endif

void mercury_sys_init_${module}_init(void)
{
#ifndef  MR_HIGHLEVEL_CODE
    ${module}();
#endif
}

void mercury_sys_init_${module}_init_type_tables(void)
{
    // No types to register.
}

#ifdef  MR_DEEP_PROFILING
void mercury_sys_init_${module}_write_out_proc_statics(FILE *fp)
{
    // No proc_statics to write out.
}
#endif
END

exit 0
