#!/bin/bash

#####################################################################
#                                                                   #
#               Compiles Faust DSP to machine code                  #
#               for several CPUs using dynamic-faust                #
#               (c) Grame, 2020                                     #
#                                                                   #
#####################################################################

. faustpath
. faustoptflags

FILES=""
# always include
CPU="generic"
OPT=""
SOURCES=false
MULTI=false
MULTIFUN=false
LLVM=false
TEST=false
LLVM_ADAPTER=$FAUSTINC/faust/dsp/llvm-dsp-adapter.h
CPP_ADAPTER=$FAUSTINC/faust/dsp/cpp-dsp-adapter.h
MULTI_ADAPTER=$FAUSTINC/faust/dsp/llvm-dsp-multi.h
MULTIFUN_ADAPTER=$FAUSTINC/faust/dsp/llvm-dsp-multifun.h
dspName=""

ALL_CPU_LIST="nocona core2 penryn bonnell atom silvermont slm goldmont goldmont-plus tremont nehalem corei7 westmere sandybridge corei7-avx ivybridge core-avx-i haswell core-avx2 broadwell skylake skylake-avx512 skx cascadelake cooperlake cannonlake icelake-client icelake-server tigerlake knl knm k8 athlon64 athlon-fx opteron k8-sse3 athlon64-sse3 opteron-sse3 amdfam10 barcelona btver1 btver2 bdver1 bdver2 bdver3 bdver4 znver1 znver2 x86-64 generic"

while [ $1 ]
do
    p=$1
 
    if [ $p = "-help" ] || [ $p = "-h" ]; then
        echo "faust2object [nocona] [core2] [penryn] [bonnell] [atom] [silvermont] [slm] [goldmont] [goldmont-plus] [tremont] [nehalem] [corei7] [westmere] [sandybridge] [corei7-avx] [ivybridge] [core-avx-i] [haswell] [core-avx2] [broadwell] [skylake] [skylake-avx512] [skx] [cascadelake] [cooperlake] [cannonlake] [icelake-client] [icelake-server] [tigerlake] [knl] [knm] [k8] [athlon64] [athlon-fx] [opteron] [k8-sse3] [athlon64-sse3] [opteron-sse3] [amdfam10] [barcelona] [btver1] [btver2] [bdver1] [bdver2] [bdver3] [bdver4] [znver1] [znver2] [x86-64] [generic] [-all] [-sources] [-multi] [-multifun] [-opt native|generic] [-llvm] [-test] [additional Faust options (-vec -vs 8...)] <file.dsp>"
        echo "Use 'xxx' to compile for 'xxx' CPU"
        echo "Use 'generic' to compile for generic CPU"
        echo "Use '-all' to compile for all CPUs"
        echo "Use '-sources' to only generate source files"
        echo "Use '-multi' to compile for several CPUs and aggregate them in a 'multi' class that choose the correct one at runtime"
        echo "Use '-multifun' to compile for several CPUs using GCC MultiFun feature and aggregate them in a 'multi' class that choose the correct one at runtime"
       	echo "Use '-opt native' to activate the best compilation options for the native CPU"
        echo "Use '-opt generic' to activate the best compilation options for a generic CPU"
        echo "Use '-llvm' to compile using the LLVM backend, otherwise the C++ backend is used"
        echo "Use '-test' to compile a test program which will bench the DSP and render it"
        exit
    fi

    if [ $p = "-opt" ]; then
        shift
        OPT=$1
    elif [ $p = "-cn" ]; then
        shift
        dspName=$1
    elif [ $p = "-sources" ]; then
        SOURCES=true
    elif [ $p = "-llvm" ]; then
        LLVM=true
    elif [ $p = "-multi" ]; then
        MULTI=true
    elif [ $p = "-multifun" ]; then
        MULTIFUN=true
    elif [ $p = "-test" ]; then
        TEST=true
    elif [ $p = "-all" ]; then
        CPU=$ALL_CPU_LIST
    elif [[ -f "$p" ]]; then
        FILES="$FILES $p"
    elif [ ${p:0:1} = "-" ]; then
        OPTIONS="$OPTIONS $p"
    elif [ $(echo $ALL_CPU_LIST | grep -w $p > /dev/null) ]; then
        # 'generic' is always included
        if [ $p != "generic" ]; then
            CPU="$CPU $p"
        fi
    else
        OPTIONS="$OPTIONS $p"
    fi

shift
done

# If no CPU is gven, compile by default for 'generic'
if [ -z "$CPU" ]; then
    echo "ERROR : no CPU was given"
    exit
fi

for p in $FILES; do

    f=$(basename "$p")

    if [ -z $dspName ]; then
        dspName="${f%.dsp}"
    fi

    # discover best compilation options
    if [ "$OPT" = "generic" ]; then
        echo "Look for best compilation options in 'generic' mode..."
        OPTIONS="$OPTIONS $(faustbench-llvm -notrace -generic $OPTIONS $f)"
        echo $OPTIONS
    elif [ "$OPT" = "native" ]; then
        echo "Look for best compilation options in 'native' mode..."
        OPTIONS="$OPTIONS $(faustbench-llvm -notrace $OPTIONS $f)"
        echo $OPTIONS
    fi

    # compile for each CPU
    for arch_cpu in $CPU; do

        # replace '-' with '_'
        cpu=$(echo $arch_cpu | sed -e 's/-/_/g')
        echo $cpu

        # renaming
        if [ $arch_cpu = "generic" ]; then
            arch_cpu=""
        fi

        if [ $LLVM = true ]; then
        	# generating the LLVM code
            echo "Compiled using LLVM"
            sed -e "s/mydsp/$dspName$cpu/g" $LLVM_ADAPTER > $dspName$cpu".h"
            dynamic-faust -target x86_64-apple-darwin15.6.0:$arch_cpu -cn $dspName$cpu $OPTIONS $f -o $dspName$cpu".o"
        else
        	# generating the C++ code
            echo "Compiled using C++"
            sed -e "s/mydsp/$dspName$cpu/g" $CPP_ADAPTER > $dspName$cpu".h"
            if [ $MULTIFUN = true ]; then
            	faust -lang c -a $FAUSTARCH/minimal-effect.c -cn $dspName$cpu $OPTIONS $f -o $dspName$cpu".cpp"
            else
            	faust -a $FAUSTARCH/minimal-effect.cpp -cn $dspName$cpu $OPTIONS $f -o $dspName$cpu".cpp"
            fi

            # compiling
            if [ $SOURCES = false ]; then
            	$CXX -Ofast -march=$arch_cpu -I `faust -includedir` -c $dspName$cpu".cpp"
            fi
        fi
    done

    # possibly aggregate in a unique multi file
    if [ $MULTI = true ]; then
        # create file
        echo "" > $dspName"multi.h"
        # add conditional #define 'cpu'
        for arch_cpu in $CPU; do
            # replace '-' with '_'
            cpu=$(echo $arch_cpu | sed -e 's/-/_/g')
            echo "#define "$cpu >> $dspName"multi.h"
        done
        # class naming
        sed -e "s/mydsp/$dspName/g" $MULTI_ADAPTER >> $dspName"multi.h"
    fi
    
    # possibly aggregate in a unique multi file
    if [ $MULTIFUN = true ]; then
        # class naming
        sed -e "s/mydsp/$dspName/g" $MULTIFUN_ADAPTER >> $dspName"multi.h"
    fi
    
    # create a test program
    if [ $TEST = true ]; then
    
    	if [ $MULTI = false ] && [ $MULTIFUN = false ]; then
    		echo "ERROR : -test option can only be used with -multi or -multifun options"
    		exit
    	fi
    	
    	# create and compile the test
    	faust -inj $dspName"multi.h" -a $FAUSTARCH/minimal-bench.cpp -cn $dspName"multi" $OPTIONS $p -o $dspName"multi.cpp"
    	if [ $MULTI = true ]; then
    		$CXX -Ofast -std=c++14 $dspName"multi.cpp" -dead_strip -I `llvm-config --includedir` `llvm-config --ldflags --libs all --system-libs` $dspName*".o" -o $dspName"multi"
    	elif [ $MULTIFUN = true ]; then
    		$CXX -Ofast -std=c++14 $dspName"multi.cpp" $dspName"generic.cpp" -o $dspName"multi"
    	fi
   		
    	echo "Run ./"$dspName"multi"" to test the program"
    fi

done
