#!/usr/bin/perl | |
# Wrapper around LLVM tools to generate a native .o from llvm-gxx 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-gxx. | |
$GCCOptions = ""; | |
for ($i = 0; $i <= $#ARGV; ++$i) { | |
if ($ARGV[$i] =~ /-mllvm-backend=([a-z0-9]*)/) { | |
$Backend = $1; | |
if ($ProgramName =~ /llvm-native-gxx/) { | |
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-gxx/) { | |
$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-gxx/) { | |
$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 -march=c -f -o $GeneratedCode $BCFile"; | |
} elsif ($Backend eq 'llc') { | |
if ($SaveTemps) { | |
$GeneratedCode = "${OutputFile}.s"; | |
} else { | |
$GeneratedCode = "/tmp/nativebuild-$$.s"; | |
} | |
run "llc -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 -march=c -f -o $GeneratedCode $BCFile"; | |
} elsif ($Backend eq 'llc') { | |
$GeneratedCode = "${OutputFile}.llc.s"; | |
run "llc -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-gxx/) { | |
# build the llvm-gxx command line. | |
$LLVMGCCCommand = join (" ", ("llvm-g++", @ARGV)); | |
CompileCToNative ($LLVMGCCCommand, $Backend, $OutputFile); | |
} | |
# we're done. | |
exit 0; | |
__END__ | |
=pod | |
=head1 NAME | |
llvm-native-gxx | |
=head1 SYNOPSIS | |
llvm-native-g++ [OPTIONS...] FILE | |
native-build [OPTIONS...] FILE | |
=head1 DESCRIPTION | |
llvm-native-g++ is a wrapper around the LLVM command-line tools which generates | |
a native object (.o) file by compiling FILE with llvm-g++, and then running | |
an LLVM back-end (CBE by default) over the resulting bitcode, and then | |
compiling the resulting code to a native object file. | |
If called as "native-build", it compiles bitcode to native code, and takes | |
different options. | |
=head1 OPTIONS | |
llvm-native-g++ takes the same options as llvm-gcc. All options | |
except -mllvm-backend=... are passed on to llvm-g++. | |
=over 4 | |
=item -mllvm-backend=BACKEND | |
Use BACKEND for native code generation. | |
=item -v | |
Print command lines that llvm-native-g++ runs. | |
=item -o FILE | |
llvm-native-g++ tries to guess the name of the llvm-g++ 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-g++ (and llvm-g++, and g++). | |
=back | |
=head1 BUGS | |
llvm-native-g++ only handles the case where llvm-g++ compiles a single | |
file per invocation. llvm-native-g++ has weak command-line argument | |
parsing and is a poor substitute for making g++/g++.c do this stuff. | |
This manual page does not adequately document native-build mode. | |
llvm-native-g++ 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 | |
g++(1) | |
=head1 AUTHOR | |
Brian R. Gaeke | |
=cut |