#!/bin/sh
# vim: ft=sh ts=4 sw=4 et
# This script finds miscompiled procedures.
#
# Given a stage2 directory that works (stage2.ok) and one that doesn't
# (stage2.bad), this script uses linear search to try to find in stage2.bad
# first the C source file and then the module within that C source file that
# when put together with everthing else from the stage2.ok directory, still
# causes the compiler to fail.
#
# If the bad C source file has different numbers of modules in the bad and ok
# versions, then the script stops after identifying only the file.
#
# The test for the composite stage2 compiler is either bootstrap checking
# (the default), or the successful execution of the all the test cases in
# one or more subdirectories of the tests directory.

usage="\
Usage: $0 [options]
Options:
    -b-, --no-bootcheck
        Do not perform a bootcheck; check only the tests directory.
    -c, --compile-only
        Compile only. Do not compare stage2.ok and stage3.
    -d <dirname>, --dir <dirname>
        Confine the search to one directory, library or compiler.
        (Usually useful only after a previous search.)
    -f <filename>, --file <filename>
        Confine the search to the named file(s).
        (Usually useful only after a previous search.)
    -h, --help
        Display this usage message.
    -j <num-jobs>, --jobs <num-jobs>
        Run using <num-jobs> different parallel processes.
    -m <mmake-args>, --mmake-args <mmake-args>
        Pass <mmake-args> as options to \`mmake'.
    -n, --negative-search
        Look for the module that suppresses the bug, not causes it.
    -o <filename>, --output-file <filename>
        Output results to <filename>.
    -r, --copy-runtime
        Copy the runtime directory instead of linking it.
    -t <testdir>, --test-dir <testdir>
        Execute runtests from the named subdirectory of tests.
"

# If you change this, you will also need to change the files indicated
# in scripts/c2init.in.
STD_LIB_NAME=mer_std

bootcheck=""
compile_only=""
jfactor=
mmake_opts=""
outfile=""
copy_runtime=false
testdirs=""
negative=false
alldirs="library compiler"
allmodules=""

while [ $# -gt 0 ]; do
    case "$1" in

    -b-|--no-bootcheck)
        bootcheck="-b-" ;;

    -c|--compile-only)
        compile_only="-c" ;;

    -d|--dir)
        alldirs="$2"; shift ;;
    -d*)
        alldirs="` expr $1 : '-d\(.*\)' `"; ;;

    -f|--file)
        allmodules="$2"; shift ;;
    -f*)
        allmodules="` expr $1 : '-f\(.*\)' `"; ;;

    -h|--help)
        echo "$usage"
        exit 0 ;;

    -j|--jobs)
        jfactor="-j$2"; shift ;;
    -j*)
        jfactor="-j` expr $1 : '-j\(.*\)' `" ;;
    --jobs*)
        jfactor="--jobs ` expr $1 : '--jobs\(.*\)' `" ;;

    -m|--mmake)
        mmake_opts="$mmake_opts $2"; shift ;;

    -n|--negative-search)
        negative=true ;;

    -o|--output-file)
        outfile="-o $2"; shift ;;
    -o*)
        outfile="-o ` expr $1 : '-o\(.*\)' `"; ;;

    -r|--copy-runtime)
        copy_runtime=true ;;

    -t|--test-dir)
        testdirs="$testdirs -t$2"; shift ;;
    -t*)
        testdirs="$testdirs ` expr $1 : '-t\(.*\)' `" ;;

    -*)
        echo "$0: unknown option \`$1'" 1>&2
        echo "$usage" 1>&2
        exit 1 ;;

    *)
        echo "$usage" 1>&2
        exit 1 ;;
    esac
    shift
done

if test "$negative" = true
then
    base=bad
    trial=ok
    expected=failure
else
    base=ok
    trial=bad
    expected=success
fi

if test -d stage2.ok -a -d stage2.bad
then
    echo "stage2.ok and stage2.bad both present"
else
    echo "at least one of stage2.ok and stage2.bad is missing"
    exit 1
fi

echo "starting at `date`"

root=`/bin/pwd`
PATH=$root/tools:$PATH
export PATH

[ -d stage2 ] || mkdir stage2
/bin/rm -fr stage2/*
cd stage2
mkdir compiler
cd $root/stage2/compiler
ln -s $root/compiler/*.m .
cp $root/compiler/Mmake* .
cd $root/stage2
mkdir library
cd library
ln -s $root/library/*.m .
# ln -s $root/library/*.nl .
ln -s $root/library/lib$STD_LIB_NAME.init .
cp $root/library/Mmake* .
cd $root/stage2
if test "$copy_runtime" = "true"
then
    mkdir runtime
    cd runtime
    ln -s $root/runtime/*.h .
    ln -s $root/runtime/*.c .
    ln -s $root/runtime/*.mod .
    ln -s $root/runtime/*.in .
    ln -s $root/runtime/machdeps .
    cp $root/runtime/Mmake* .
    cd $root/stage2
else
    ln -s $root/runtime .
fi
ln -s $root/boehm_gc .
ln -s $root/browser .
ln -s $root/ssdb .
ln -s $root/trace .
ln -s $root/doc .
ln -s $root/scripts .
ln -s $root/util .
ln -s $root/profiler .
ln -s $root/conf* .
rm -f config*.log
cp $root/stage2.ok/Mmake* .
cd $root

# We don't copy the .d files. This prevents mmake from trying to remake any
# of the .c and .o files, which we provide in the form they should be used.

# cp stage2.ok/library/*.d stage2/library
cp stage2.ok/library/*.dep stage2/library
cp stage2.ok/library/*.int stage2/library
cp stage2.ok/library/*.int2 stage2/library
cp stage2.ok/library/*.date stage2/library
# cp stage2.ok/compiler/*.d stage2/compiler
cp stage2.ok/compiler/*.dep stage2/compiler
cp stage2.ok/compiler/*.int stage2/compiler
cp stage2.ok/compiler/*.int2 stage2/compiler
cp stage2.ok/compiler/*.date stage2/compiler

cp stage2.$base/library/*.[co] stage2/library
cp stage2.$base/compiler/*.[co] stage2/compiler

if test "$copy_runtime" = "true"
then
    if (cd stage2 ; mmake $mmake_opts $jfactor runtime)
    then
        echo "building of stage 2 runtime successful"
    else
        echo "building of stage 2 runtime not successful"
        exit 1
    fi
fi

set -x

normal=
unusual=
unusualparts=

for testeddir in $alldirs
do
    # find the set of modules to search, if not given on command line
    if test "$allmodules" = ""
    then
        cd stage2/$testeddir
        allmodules=`sub X.c X *.c`
        cd $root
    fi

    for testedmodule in $allmodules
    do
        # at this point, all the files in stage2
        # should be from stage2.$base

        echo "testing module: $testedmodule"

        cp stage2.$trial/$testeddir/$testedmodule.[co] stage2/$testeddir

        if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs
        then
            echo "test succeeded"
            lasttest=success
        else
            echo "test failed"
            lasttest=failure
        fi

        if test "$lasttest" = "$expected"
        then
            normal="$normal $testeddir/$testedmodule"
        else
            unusual="$unusual $testeddir/$testedmodule"

            basecnt=`egrep '^MR_END_MODULE' stage2.$base/$testeddir/$testedmodule.c | wc -l`
            trialcnt=`egrep '^MR_END_MODULE' stage2.$trial/$testeddir/$testedmodule.c | wc -l`

            if test $basecnt -ne $trialcnt
            then
                basecnt=`echo $basecnt | tr -d ' '`
                trialcnt=`echo $trialcnt | tr -d ' '`

                echo "the two versions of the unusual module"
                echo "differ in the number of C modules they have"
                echo "$base version: $basecnt vs $trial version: $trialcnt"
                exit 1
            fi

            for dir in $base $trial
            do
                cd stage2.$dir/$testeddir
                divide $testedmodule.c $basecnt
                cd $root
            done

            allparts=
            i=0
            while test $i -le $basecnt
            do
                allparts="$allparts $i"
                i=`expr $i + 1`
            done

            for testedpart in $allparts
            do
                echo testing part $testedpart in `/bin/pwd`
                assemble $base $trial $testeddir $testedmodule $basecnt $testedpart

                cd stage2/$testeddir
                /bin/rm $testedmodule.o
                mmake $testedmodule.o
                cd $root

                if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs
                then
                    echo "test succeeded"
                    lasttest=success
                else
                    echo "test failed"
                    lasttest=failure
                fi

                if test "$lasttest" != "$expected"
                then
                    unusualparts="$unusualparts $testeddir/$testedmodule.part.$testedpart"
                fi
            done
        fi

        cp stage2.$base/$testeddir/$testedmodule.[co] stage2/$testeddir
    done
done

echo "modules whose stage.$trial versions behave as expected:"
echo $normal

echo "modules whose stage.$trial versions do not behave as expected:"
echo $unusual

echo "module parts whose stage.$trial versions do not behave as expected:"
echo $unusualparts

echo "finishing at `date`"
exit 0
