Files
mercury/tools/makebatch
Zoltan Somogyi 7ca1a07296 Allow the MLDS backend to generate indexing switches (switches implemented
Estimated hours taken: 16
Branches: main

Allow the MLDS backend to generate indexing switches (switches implemented
more efficiently than just a if-then-else chain) for strings even if the target
language does not support gotos.

Previously, we use always used gotos to break out of search loops
after we found a match:

    do {
        if (we have a match) {
            ... handle the match ...
            goto end
        } else {
            ... handle nonmatches ...
        }
    } while (loop should continue);
    maybe some code to handle the failure of the search
end:

Now, if the "maybe some code" is empty, we prefer to use break statements
if the target language supports this:

    do {
        if (we have a match) {
            ... handle the match ...
            break;
        } else {
            ... handle nonmatches ...
        }
    } while (loop should continue)

If we cannot use either gotos or break statements, we instead use
a boolean variable named "stop_loop":

    stop_loop = 0;
    do {
        if (we have a match) {
            ... handle the match ...
            stop_loop = 1;
        } else {
            ... handle nonmatches ...
        }
    } while (stop_loop == 0 && loop should continue)
    if (stop_loop == 0) {
    	maybe some code to handle the failure of the search
    }

We omit the final if statement if the then-part would be empty.

The break method generates the smallest code, followed by the goto code.
I don't have information on speed, since we don't have a benchmark that
runs long enough, and the compiler itself does not spend any significant
amount of time on string switches. Probably the break method is also the
fastest, simply because it leaves the code looking most like normal C code.
(Some optimizations are harder to apply to code containing gotos, and some
optimizer writers do not bother.)

For C, we now normally prefer to generate code using the second method
(breaks), if we can, though normally "maybe some code" is not empty,
in which case we use the first method (goto).

However, if the value of the --experiment option is set to "use_stop_loop",
we always use the third method, and if it is set to "use_end_label", we always
use the first, even when we could use the second. This allow us to test all
three approaches using the C back end.

With backends that support neither gotos nor break, we always use the third
method (stop_loop).

With backends that don't support gotos but do support breaks, we also always
use the third method. This is because trying to use the second method would
require us to commit to not creating the stop_loop variable BEFORE we know
that the "maybe some code to handle the failure of the search" is empty,
and if it isn't empty, then we don't have the goto method to fall back on.

compiler/ml_string_switch.m:
	Make the change described above. Where possible, make the required
	change not to the original code, but to a version in which common
	parts have been factored out. (Previously, the duplicated code was
	small; now, it would be big.)

compiler/ml_target_util.m:
	A new module containing existing functions that test various properties
	of the target language. Keeping some of those functions in their
	original modules would have introduced a circular dependency.

compiler/ml_switch_gen.m:
	Enable the new functionality by removing the tests that previously
	prevented the compiler from using indexing switches on strings
	if the target language did not support gotos.

	Remove the code moved to ml_target_util.m.

compiler/ml_optimize.m:
compiler/ml_unify_gen.m:
	Remove the code moved to ml_target_util.m.

compiler/ml_backend.m:
compiler/notes/compiler_design.m:
	Add the new module.

compiler/ml_proc_gen.m:
	Delete a predicate that hasn't been used for a long time.

tools/makebatch:
	Fix an old pair of typos.
2011-08-15 06:23:20 +00:00

335 lines
8.8 KiB
Bash
Executable File

#!/bin/sh
# vim: ts=4 sw=4 expandtab
#
# A program to prepare batches of versions of the compiler.
#
# The control and output files are all in the subdirectory batch.
# The control files are $batch.MCFLAGS and possibly $batch.CFLAGS,
# $batch.MGNUCFLAGS, $batch.MLFLAGS, $batch.GRADE, $batch.MMAKE,
# $batch.MMAKE.$n where $batch is the last argument of makebatch.
# They are all consulted if they exist.
#
# All the control files except $batch.MMAKE and $batch.MMAKE.$n
# must have the same number of lines. Each line corresponds to a version
# of the compiler that is built with the MCFLAGS, EXTRA_CFLAGS,
# EXTRA_MGNUCFLAGS, EXTRA_MLFLAGS and GRADE make variables being set
# from that line.
#
# The control file $batch.MMAKE.$n contains an Mmakefile fragment that is
# included in the parameters of the version numbered $n.
#
# The control file $batch.MMAKE contains an Mmakefile fragment that is
# included in the parameters of all the versions being built.
#
# The output goes in $batch.mercury_compile.$n.gz; the sizes of the versions
# are put in $batch.sizes. If a bootcheck fails, the bootcheck output goes
# in $batch.out.$n.
#
# The file $batch.checkpoint records which version is to be built next.
# Reinvoking makebatch while this file exists will cause it to start from
# that version. When makebatch exits normally, it removes the file to indicate
# completion.
usage="Usage: makebatch [-jN] [-cdeoqst] [--compile-times] [--save-stage2-on-error] [--save-stage2-on-no-compiler] [--test-params] batchname"
jfactor=-j1
runtests=""
objects=""
cfiles="false"
save_stage2="false"
save_stage2_on_error="false"
save_stage2_on_no_compiler="false"
errfiles="false"
compile_times=""
test_params=""
stop_after_stage_2=""
failed="continue"
if test -f $HOME/.makebatch_save_stage2
then
save_stage2="true"
fi
while test $# -gt 0
do
case $1 in
-c|--c-files)
cfiles="true" ;;
-d|--save-stage2-dirs)
save_stage2="true" ;;
-e|--err-files)
errfiles="true" ;;
-j|--jobs)
jfactor="-j$2" ; shift ;;
-j*)
jfactor="-j` expr $1 : '-j\(.*\)' `" ;;
--jobs*)
jfactor="--jobs` expr $1 : '--jobs\(.*\)' `" ;;
-o|--object-files)
objects="-k" ;;
--save-stage2-on-error)
save_stage2_on_error="true" ;;
--save-stage2-on-no-compiler)
save_stage2_on_no_compiler="true" ;;
-s|--stop-at-failure)
failed="stop" ;;
-q|--quick)
stop_after_stage_2="--stop-after-stage-2"
runtests="-t-"
;;
-t-|--no-test-suite)
runtests="-t-" ;;
--compile-times)
compile_times="--compile-times" ;;
--test-params)
test_params="--test-params" ;;
-*) echo "$0: unknown option \`$1'" 2>&1
echo $usage
exit 1 ;;
*) break ;;
esac
shift
done
if test $# != 1
then
echo $usage
exit 1
fi
batch=$1
if test ! -r batch/$batch.MCFLAGS
then
echo "batch/$batch.MCFLAGS does not exist or is not readable"
exit 1
fi
if test -r batch/$batch.MGNUCFLAGS
then
needmgnucflags=true
else
needmgnucflags=false
fi
if test -r batch/$batch.CFLAGS
then
needcflags=true
else
needcflags=false
fi
if test -r batch/$batch.MLFLAGS
then
needmlflags=true
else
needmlflags=false
fi
if test -r batch/$batch.GRADE
then
needgrade=true
else
needgrade=false
fi
if test -r batch/$batch.MMAKE
then
needmmake=true
else
needmmake=false
fi
if test -f Mmake.stage.params
then
echo moving Mmake.stage.params to Mmake.stage.params.$$
mv Mmake.stage.params Mmake.stage.params.$$
fi
if test -r batch/$batch.checkpoint
then
n=`cat batch/$batch.checkpoint`
else
n=1
cat /dev/null > batch/$batch.sizes
fi
maxn=`wc -l < batch/$batch.MCFLAGS`
while test $n -le $maxn
do
echo $n > batch/$batch.checkpoint
if $needmmake
then
cp batch/$batch.MMAKE Mmake.stage.params
else
cat < /dev/null > Mmake.stage.params
fi
mcflags=`awk "NR == $n" batch/$batch.MCFLAGS`
echo "EXTRA_MCFLAGS = $mcflags" >> Mmake.stage.params
if $needcflags
then
cflags=`awk "NR == $n" batch/$batch.CFLAGS`
echo "EXTRA_CFLAGS = $cflags" >> Mmake.stage.params
fi
if $needmgnucflags
then
mgnucflags=`awk "NR == $n" batch/$batch.MGNUCFLAGS`
echo "EXTRA_MGNUCFLAGS = $mgnucflags" >> Mmake.stage.params
fi
if $needmlflags
then
mlflags=`awk "NR == $n" batch/$batch.MLFLAGS`
echo "EXTRA_MLFLAGS = $mlflags" >> Mmake.stage.params
fi
if test -f batch/$batch.MMAKE.$n
then
cat batch/$batch.MMAKE.$n >> Mmake.stage.params
fi
if $needgrade
then
grade=`awk "NR == $n" batch/$batch.GRADE`
gradeopt="--grade $grade"
else
gradeopt=""
fi
if test $n -lt 10
then
visn="0$n"
else
visn="$n"
fi
cp Mmake.stage.params batch/$batch.params.$visn
if $needgrade
then
echo "GRADE = $grade" >> batch/$batch.params.$visn
fi
echo starting bootcheck of version $visn
succeeded=false
created_compiler=false
if tools/bootcheck $gradeopt --copy-runtime $jfactor $runtests \
$objects $compile_times $stop_after_stage_2 $test_params \
> batch/$batch.out.$visn 2>&1
then
succeeded=true
created_compiler=true
echo bootcheck of version $visn succeeded
cp stage2/compiler/mercury_compile batch/$batch.mercury_compile.$visn
size batch/$batch.mercury_compile.$visn | tail -1 | \
sed -e 's/mercury_compile.//' | sed -e 's/batch\///' \
>> batch/$batch.sizes
/bin/rm -f batch/$batch.mercury_compile.$visn.gz > /dev/null 2>&1
gzip batch/$batch.mercury_compile.$visn
elif test "$failed" = "stop"
then
echo bootcheck of version $visn failed
exit 1
else
if test -x stage2/compiler/mercury_compile
then
created_compiler=true
echo bootcheck of version $visn failed but produced compiler
cp stage2/compiler/mercury_compile \
batch/$batch.mercury_compile.$visn
size batch/$batch.mercury_compile.$visn | tail -1 | \
sed -e 's/mercury_compile.//' | sed -e 's/batch\///' \
>> batch/$batch.sizes
/bin/rm -f batch/$batch.mercury_compile.$visn.gz > /dev/null 2>&1
gzip batch/$batch.mercury_compile.$visn
else
echo bootcheck of version $visn failed and did not produce compiler
fi
fi
if test "$objects" = "-k" -a "$save_stage2" = "false"
then
echo saving object files
mkdir -p batch/objs/$batch.library.$visn
/bin/rm -fr batch/objs/$batch.library.$visn/*
cp stage2/library/*.o batch/objs/$batch.library.$visn
gzip batch/objs/$batch.library.$visn/*
mkdir -p batch/objs/$batch.compiler.$visn
/bin/rm -fr batch/objs/$batch.compiler.$visn/*
cp stage2/compiler/*.o batch/objs/$batch.compiler.$visn
gzip batch/objs/$batch.compiler.$visn/*
fi
if "$cfiles"
then
echo saving c files
mkdir -p batch/cfiles/$batch.library.$visn
/bin/rm -fr batch/cfiles/$batch.library.$visn/* > /dev/null
gzip stage2/library/*.c > /dev/null
cp stage2/library/*.c.gz batch/cfiles/$batch.library.$visn > /dev/null
mkdir -p batch/cfiles/$batch.compiler.$visn
/bin/rm -fr batch/cfiles/$batch.compiler.$visn/* > /dev/null
gzip stage2/compiler/*.c > /dev/null
cp stage2/compiler/*.c.gz batch/cfiles/$batch.compiler.$visn > /dev/null
fi
if "$errfiles"
then
echo saving err files
mkdir -p batch/errfiles/$batch.library.$visn
/bin/rm -fr batch/errfiles/$batch.library.$visn/*
cp stage2/library/*.err batch/errfiles/$batch.library.$visn
# gzip batch/errfiles/$batch.library.$visn/*
mkdir -p batch/errfiles/$batch.compiler.$visn
/bin/rm -fr batch/errfiles/$batch.compiler.$visn/*
cp stage2/compiler/*.err batch/errfiles/$batch.compiler.$visn
# gzip batch/errfiles/$batch.compiler.$visn/*
fi
if $save_stage2
then
# If a directory with this name already exists,
# it probably came from a previous invocation of this script.
# This invocation supersedes earlier ones.
/bin/rm -fr stage2.batch.$visn > /dev/null
mv stage2 stage2.batch.$visn
elif $save_stage2_on_error && test $succeeded = false
then
/bin/rm -fr stage2.batch.$visn > /dev/null
mv stage2 stage2.batch.$visn
elif $save_stage2_on_no_compiler && test $created_compiler = false
then
/bin/rm -fr stage2.batch.$visn > /dev/null
mv stage2 stage2.batch.$visn
fi
n=`expr $n + 1`
done
/bin/rm -f batch/$batch.checkpoint
if test -f Mmake.stage.params.$$
then
echo moving Mmake.stage.params.$$ to Mmake.stage.params
mv Mmake.stage.params.$$ Mmake.stage.params
fi
exit 0