#!/usr/bin/perl -w

use strict;

# Remove these print commands because sometimes <configure> redirects outputs. 
sub run($)
{
    my ($cmd) = @_;
    print STDERR $cmd, "\n";
    open CMD, "$cmd |" || die $!;
    while(<CMD>) {
        print;
    }
    close CMD;
    return $?;
}

sub usage()
{
    print "$0 [gcc or g++ args ... ]\n";
    exit(0);
}

usage() if(scalar @ARGV == 0);

my @orig_args = @ARGV; # original args passed to gcc/g++

my $lang = "gcc";
if($0 =~ /g\+\+/) {
    $lang = "g++";
}

my $has_E = 0; # -E (or -M* except -MD or -MP) the preprocess only flag presents?
my $has_c = 0; # -c the compile only flag presents?
my $has_source = 0;
my $pic = 0;
my $output = "";

while(my $arg = shift @ARGV) {
    if($arg =~ /^-o$/) {
        $output = shift @ARGV;
    } else {
        if($arg =~ /^-E$/ || ($arg =~ /^-M/ && $arg !~ /^-MD/ && $arg !~ /^-MP/)) {
            # skip -MD or -MP because it does not imply -E; see
            # http://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html#Preprocessor-Options
            $has_E = 1;
        } elsif($arg =~ /^-c$/) {
            $has_c = 1;
        } elsif($arg =~ /\.c$/i || $arg =~/\.cc$/ || $arg =~/\.cpp$/) {
            $has_source = 1;
        } elsif($arg =~ /^-fPIC$/) {
            $pic = 1;
        }
    }
}

# after parsing argument, there are four possible modes with the following
# precedence
#   -- has_E == 1: preprocess only; no emit-llvm  (-E overrides other flags)
#   -- has_c == 1: compile source to bitcode (-c overrides -MD)
#   -- has_c == 0 && has_source == 0: link bitcode files
#   -- has_c == 0 && has_source == 1: compile source to bitcode,then to x86

my $mode = "";
if($has_c) {
    $mode = "compile";
} elsif($has_E) {
    $mode = "preprocess";
} elsif(!$has_source) {
    $mode = "link";
} else {
    $mode = "all";
}

# set up command line arguments
my $allargs = "";
my $linkargs = "";
foreach my $arg (@orig_args) {
    # Added by Jingyue
    my $quoted_arg = $arg;
    $quoted_arg =~ s/\\/\\\\/g;
    $quoted_arg =~ s/'/\\'/g;
    $quoted_arg =~ s/"/\\"/g;

    $allargs .= " $quoted_arg";
    # strip off gcc/g++ flags, which llvm-ld doesn't understand
    # Jingyue: llvm-ld doesn't even understand -lm. We usually
    # use it to link bc's together, rather than with an external
    # library. 
    # Jingyue: Forward -disable-opt to llvm-ld if any. 
    $linkargs .= " $arg" unless ($arg =~ /^-[^o]/ && $arg ne "-disable-opt");
}

# Added by Jingyue. 
my $ret = 0;
if($mode eq "preprocess") {
    run("llvm-$lang $allargs");
} elsif($mode eq "preprocess and compile") {
    run("llvm-$lang $allargs");
    run("llvm-$lang -emit-llvm $allargs");
} elsif($mode eq "compile") {
    run("llvm-$lang -emit-llvm $allargs");
} elsif($mode eq "link") {
    run("llvm-ld $linkargs");
    # output is dynamically linked library; rename .so.bc to .so
    if($output =~ /\.so[\d\.]*$/) {
        my $outputbc = $output.".bc";
        run("mv $outputbc $output");
    }
} else { # $mode eq "all"
    if($output eq "") {
        #($output) = $allargs =~ /\s(\S+)\.(c|cc|cpp)\b/i;
        $output = "a.out"
    }
    my $allargs_minus_output = $allargs;
    $allargs_minus_output =~ s/\s-o\s+\S+/ /;
    my $allargs_minus_output_and_input = $allargs_minus_output;
    $allargs_minus_output_and_input =~ s/\s\S+\.(c|cc|cpp)\b/ ${output}.s /i;
    my $llc_flag = $pic? "-relocation-model=pic" : "";

    # strip linker flags
    $allargs_minus_output =~ s/\s-Wl\S+/ /;

    $ret |= run("llvm-$lang -emit-llvm -o ${output}.bc -c $allargs_minus_output");
    $ret |= run("llc -f $llc_flag -o ${output}.s ${output}.bc");
    # use gcc or g++ to do the linking so we don't have to parse linker
    # flags (-Wl...) ourselves
    # Jingyue: Don't forward any -g arguments to assembly steps. 
    $allargs_minus_output_and_input .= " ";
    $allargs_minus_output_and_input =~ s/\s-g\s/ /g;
    $ret |= run("$lang -o $output $allargs_minus_output_and_input");
}

# FIXME: Jingyue: I cannot speak perl... 
if ($ret != 0) {
    $ret = 1;
}
exit($ret);
