#!/usr/bin/perl # Wrapper around LLVM tools to generate a native .o from llvm-gcc using an # LLVM back-end (CBE by default). # set up defaults. $Verbose = 0; $SaveTemps = 1; $PreprocessOnly = 0; $CompileDontLink = 0; $Backend = 'cbe'; chomp ($ProgramName = `basename $0`); sub boldprint { print "[1m", @_, "[0m"; } # process command-line options. # most of these are passed on to llvm-gcc. $GCCOptions = ""; for ($i = 0; $i <= $#ARGV; ++$i) { if ($ARGV[$i] =~ /-mllvm-backend=([a-z0-9]*)/) { $Backend = $1; if ($ProgramName =~ /llvm-native-gcc/) { splice (@ARGV, $i, 1); --$i; } } elsif ($ARGV[$i] eq "-E") { $PreprocessOnly = 1; } elsif ($ARGV[$i] eq "-c") { $GCCOptions .= " " . $ARGV[$i]; $CompileDontLink = 1; } elsif ($ARGV[$i] eq "-v") { $GCCOptions .= " " . $ARGV[$i]; $Verbose = 1; } elsif ($ARGV[$i] eq "-o") { $OutputFile = $ARGV[$i + 1]; } elsif ($ARGV[$i] eq "-save-temps") { $GCCOptions .= " " . $ARGV[$i]; $SaveTemps = 1; } elsif ($ARGV[$i] =~ /\.bc$/) { push (@BytecodeFiles, $ARGV[$i]); } elsif ($ARGV[$i] =~ /^-L/) { $GCCOptions .= " " . $ARGV[$i]; push (@LibDirs, $ARGV[$i]); } elsif ($ARGV[$i] =~ /^-l/) { $GCCOptions .= " " . $ARGV[$i]; push (@Libs, $ARGV[$i]); } elsif ($ARGV[$i] =~ /\.(c|cpp|cc|i|ii|C)$/) { $LastCFile = $ARGV[$i]; } } sub GetDefaultOutputFileName { my $DefaultOutputFileBase; if ($ProgramName =~ /llvm-native-gcc/) { $DefaultOutputFileBase = $LastCFile; } elsif ($ProgramName =~ /native-build/) { $DefaultOutputFileBase = $BytecodeFiles[0]; } my $def = $DefaultOutputFileBase; die "Can't figure out name of output file.\n" unless $DefaultOutputFileBase && (($ProgramName !~ /native-build/) || $#BytecodeFiles == 0); print "Warning: defaulting output file name ", "based on '$DefaultOutputFileBase'\n" if $Verbose; if ($ProgramName =~ /llvm-native-gcc/) { $def =~ s/\.(c|cpp|cc|i|ii|C)$/.o/; } elsif ($ProgramName =~ /native-build/) { $def =~ s/\.bc$/.$Backend/; if ($CompileDontLink) { $def .= ".o"; } } return $def; } # run a command, optionally echoing, and quitting if it fails: sub run { my $command = join(" ", @_); print "$command\n" if $Verbose; $command =~ s/\"/\\\"/g; system $command and die "$0: $command failed"; } sub LinkBytecodeFilesIntoTemporary { my $FinalOutputFileName = shift @_; my @BytecodeFiles = @_; my $BCFiles = join (" ", @BytecodeFiles); my $LinkedBCFile; if ($SaveTemps) { $LinkedBCFile = "${FinalOutputFileName}.llvm.bc"; } else { $LinkedBCFile = "/tmp/nativebuild-$$.llvm.bc"; } run "llvm-link -o $LinkedBCFile $BCFiles"; return $LinkedBCFile; } sub CompileBytecodeToNative { my ($BCFile, $Backend, $OutputFile) = @_; my $GeneratedCode; if ($Backend eq 'cbe') { if ($SaveTemps) { $GeneratedCode = "${OutputFile}.c"; } else { $GeneratedCode = "/tmp/nativebuild-$$.c"; } run "llc -enable-correct-eh-support -march=c -f -o $GeneratedCode $BCFile"; } elsif ($Backend eq 'llc') { if ($SaveTemps) { $GeneratedCode = "${OutputFile}.s"; } else { $GeneratedCode = "/tmp/nativebuild-$$.s"; } run "llc -enable-correct-eh-support -f -o $GeneratedCode $BCFile"; } my $LibDirs = join (" ", @LibDirs); my $Libs = join (" ", @Libs); run "gcc $GCCOptions $GeneratedCode -o $OutputFile $LibDirs $Libs"; run "rm $BCFile $GeneratedCode" unless $SaveTemps; } sub CompileCToNative { my ($LLVMGCCCommand, $Backend, $OutputFile) = @_; run $LLVMGCCCommand; if ($PreprocessOnly) { return; } my $BCFile = "${OutputFile}.llvm.bc"; if ($CompileDontLink) { run "mv ${OutputFile} $BCFile"; } else { # gccld messes with the output file name run "mv ${OutputFile}.bc $BCFile"; } my $GeneratedCode; if ($Backend eq 'cbe') { $GeneratedCode = "${OutputFile}.cbe.c"; run "llc -enable-correct-eh-support -march=c -f -o $GeneratedCode $BCFile"; } elsif ($Backend eq 'llc') { $GeneratedCode = "${OutputFile}.llc.s"; run "llc -enable-correct-eh-support -f -o $GeneratedCode $BCFile"; } my $NativeGCCOptions = ""; if ($CompileDontLink) { $NativeGCCOptions = "-c"; } run "gcc $NativeGCCOptions $GeneratedCode -o $OutputFile"; run "rm ${OutputFile}.llvm.bc $GeneratedCode" unless $SaveTemps; } # guess the name of the output file, if -o was not specified. $OutputFile = GetDefaultOutputFileName () unless $OutputFile; print "Output file is $OutputFile\n" if $Verbose; # do all the dirty work: if ($ProgramName eq /native-build/) { my $LinkedBCFile = LinkBytecodeFilesIntoTemporary (@BytecodeFiles); CompileBytecodeToNative ($LinkedBCFile, $Backend, $OutputFile); } elsif ($ProgramName =~ /llvm-native-gcc/) { # build the llvm-gcc command line. $LLVMGCCCommand = join (" ", ("llvm-gcc", @ARGV)); CompileCToNative ($LLVMGCCCommand, $Backend, $OutputFile); } # we're done. exit 0; __END__ =pod =head1 NAME llvm-native-gcc =head1 SYNOPSIS llvm-native-gcc [OPTIONS...] FILE native-build [OPTIONS...] FILE =head1 DESCRIPTION llvm-native-gcc is a wrapper around the LLVM command-line tools which generates a native object (.o) file by compiling FILE with llvm-gcc, and then running an LLVM back-end (CBE by default) over the resulting bytecode, and then compiling the resulting code to a native object file. If called as "native-build", it compiles bytecode to native code, and takes different options. =head1 OPTIONS llvm-native-gcc takes the same options as llvm-gcc. All options except -mllvm-backend=... are passed on to llvm-gcc. =over 4 =item -mllvm-backend=BACKEND Use BACKEND for native code generation. =item -v Print command lines that llvm-native-gcc runs. =item -o FILE llvm-native-gcc tries to guess the name of the llvm-gcc output file by looking for this option in the command line. If it can't find it, it finds the last C or C++ source file named on the command line, and turns its suffix into .o. See BUGS. =item -save-temps Save temporary files used by llvm-native-gcc (and llvm-gcc, and gcc). =back =head1 BUGS llvm-native-gcc only handles the case where llvm-gcc compiles a single file per invocation. llvm-native-gcc has weak command-line argument parsing and is a poor substitute for making gcc/gcc.c do this stuff. This manual page does not adequately document native-build mode. llvm-native-gcc is pretty gross because it represents the blind merging of two other scripts that predated it. It could use some code clean-up. =head1 SEE ALSO gcc(1) =head1 AUTHOR Brian R. Gaeke =cut