Home | History | Annotate | Download | only in utils
      1 #!/bin/bash
      2 #
      3 #  findmisopt
      4 #
      5 #      This is a quick and dirty hack to potentially find a misoptimization
      6 #      problem. Mostly its to work around problems in bugpoint that prevent
      7 #      it from finding a problem unless the set of failing optimizations are
      8 #      known and given to it on the command line.
      9 #
     10 #      Given a bitcode file that produces correct output (or return code), 
     11 #      this script will run through all the optimizations passes that gccas
     12 #      uses (in the same order) and will narrow down which optimizations
     13 #      cause the program either generate different output or return a 
     14 #      different result code. When the passes have been narrowed down, 
     15 #      bugpoint is invoked to further refine the problem to its origin. If a
     16 #      release version of bugpoint is available it will be used, otherwise 
     17 #      debug.
     18 #
     19 #   Usage:
     20 #      findmisopt bcfile outdir progargs [match]
     21 #
     22 #   Where:
     23 #      bcfile 
     24 #          is the bitcode file input (the unoptimized working case)
     25 #      outdir
     26 #          is a directory into which intermediate results are placed
     27 #      progargs
     28 #          is a single argument containing all the arguments the program needs
     29 #      proginput
     30 #          is a file name from which stdin should be directed
     31 #      match
     32 #          if specified to any value causes the result code of the program to
     33 #          be used to determine success/fail. If not specified success/fail is
     34 #          determined by diffing the program's output with the non-optimized
     35 #          output.
     36 #       
     37 if [ "$#" -lt 3 ] ; then
     38   echo "usage: findmisopt bcfile outdir progargs [match]"
     39   exit 1
     40 fi
     41 
     42 dir="${0%%/utils/findmisopt}"
     43 if [ -x "$dir/Release/bin/bugpoint" ] ; then
     44   bugpoint="$dir/Release/bin/bugpoint"
     45 elif [ -x "$dir/Debug/bin/bugpoint" ] ; then
     46   bugpoint="$dir/Debug/bin/bugpoint"
     47 else
     48   echo "findmisopt: bugpoint not found"
     49   exit 1
     50 fi
     51 
     52 bcfile="$1"
     53 outdir="$2"
     54 args="$3"
     55 input="$4"
     56 if [ ! -f "$input" ] ; then
     57   input="/dev/null"
     58 fi
     59 match="$5"
     60 name=`basename $bcfile .bc`
     61 ll="$outdir/${name}.ll"
     62 s="$outdir/${name}.s"
     63 prog="$outdir/${name}"
     64 out="$outdir/${name}.out"
     65 optbc="$outdir/${name}.opt.bc"
     66 optll="$outdir/${name}.opt.ll"
     67 opts="$outdir/${name}.opt.s"
     68 optprog="$outdir/${name}.opt"
     69 optout="$outdir/${name}.opt.out"
     70 ldflags="-lstdc++ -lm -ldl -lc"
     71 
     72 echo "Test Name: $name"
     73 echo "Unoptimized program: $prog"
     74 echo "  Optimized program: $optprog"
     75 
     76 # Define the list of optimizations to run. This comprises the same set of 
     77 # optimizations that opt -std-compile-opts and gccld run, in the same order.
     78 opt_switches=`llvm-as < /dev/null -o - | opt -std-compile-opts -disable-output -debug-pass=Arguments 2>&1 | sed 's/Pass Arguments: //'`
     79 ld_switches=`llvm-as < /dev/null -o - | llvm-ld - -debug-pass=Arguments 2>&1 | sed 's/Pass Arguments: //'`
     80 all_switches="$opt_switches $ld_switches"
     81 echo "Passes : $all_switches"
     82 
     83 # Create output directory if it doesn't exist
     84 if [ -f "$outdir" ] ; then
     85   echo "$outdir is not a directory"
     86   exit 1
     87 fi
     88 
     89 if [ ! -d "$outdir" ] ; then
     90   mkdir "$outdir" || exit 1
     91 fi
     92 
     93 # Generate the disassembly
     94 llvm-dis "$bcfile" -o "$ll" -f || exit 1
     95 
     96 # Generate the non-optimized program and its output
     97 llc "$bcfile" -o "$s" -f || exit 1
     98 gcc "$s" -o "$prog" $ldflags || exit 1
     99 "$prog" $args > "$out" 2>&1 <$input
    100 ex1=$?
    101 
    102 # Current set of switches is empty
    103 function tryit {
    104   switches_to_use="$1"
    105   opt $switches_to_use "$bcfile" -o "$optbc" -f || exit
    106   llvm-dis "$optbc" -o "$optll" -f || exit
    107   llc "$optbc" -o "$opts" -f || exit
    108   gcc "$opts" -o "$optprog" $ldflags || exit
    109   "$optprog" $args > "$optout" 2>&1 <"$input"
    110   ex2=$?
    111 
    112   if [ -n "$match" ] ; then
    113     if [ "$ex1" -ne "$ex2" ] ; then
    114       echo "Return code not the same with these switches:"
    115       echo $switches
    116       echo "Unoptimized returned: $ex1"
    117       echo "Optimized   returned: $ex2"
    118       return 0
    119     fi
    120   else
    121     diff "$out" "$optout" > /dev/null
    122     if [ $? -ne 0 ] ; then
    123       echo "Diff fails with these switches:"
    124       echo $switches
    125       echo "Differences:"
    126       diff "$out" "$optout" | head
    127       return 0;
    128     fi
    129   fi
    130   return 1
    131 }
    132 
    133 echo "Trying to find optimization that breaks program:"
    134 for sw in $all_switches ; do
    135   echo -n " $sw"
    136   switches="$switches $sw"
    137   if tryit "$switches" ; then
    138     break;
    139   fi
    140 done
    141 
    142 # Terminate the previous output with a newline
    143 echo ""
    144 
    145 # Determine if we're done because none of the optimizations broke the program
    146 if [ "$switches" == " $all_switches" ] ; then
    147   echo "The program did not miscompile"
    148   exit 0
    149 fi
    150 
    151 final=""
    152 while [ ! -z "$switches" ] ; do
    153   trimmed=`echo "$switches" | sed -e 's/^ *\(-[^ ]*\).*/\1/'`
    154   switches=`echo "$switches" | sed -e 's/^ *-[^ ]* *//'`
    155   echo "Trimmed $trimmed from left"
    156   tryit "$final $switches"
    157   if [ "$?" -eq "0" ] ; then
    158     echo "Still Failing .. continuing ..."
    159     continue
    160   else
    161     echo "Found required early pass: $trimmed"
    162     final="$final $trimmed"
    163     continue
    164   fi
    165   echo "Next Loop"
    166 done
    167 
    168 if [ "$final" == " $all_switches" ] ; then
    169   echo "findmisopt: All optimizations pass. Perhaps this isn't a misopt?"
    170   exit 0
    171 fi
    172 echo "Smallest Optimization list=$final"
    173 
    174 bpcmd="$bugpoint -run-llc -disable-loop-extraction --output "$out" --input /dev/null $bcfile $final --args $args"
    175 
    176 echo "Running: $bpcmd"
    177 $bpcmd
    178 echo "findmisopt finished."
    179