blob: 6167db3bdadb85637db972d273af647bd496253b [file] [log] [blame]
//===-- llvm-goc.cpp - compiler driver for gollvm ------------------------===//
//
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
//===----------------------------------------------------------------------===//
//
// Compiler driver for gollvm. Invokes frontend / backend to compile
// Go code into assembly and/or object files, and orchestrates process
// of assembling and linking if needed.
//
//===----------------------------------------------------------------------===//
#include "go-llvm-linemap.h"
#include "go-llvm-diagnostics.h"
#include "go-llvm.h"
#include "go-c.h"
#include "mpfr.h"
#include "GollvmOptions.h"
#include "GollvmConfig.h"
#include "Compilation.h"
#include "Driver.h"
#include "ToolChain.h"
#include "Tool.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include <algorithm>
#include <cstring>
#include <string>
#include <system_error>
#include <sys/types.h>
#include <unistd.h>
#ifdef USING_SPLIT_STACK
constexpr bool using_splitstack = true;
#else
constexpr bool using_splitstack = false;
#endif
using namespace llvm;
using namespace gollvm::driver;
class CommandLineParser {
public:
CommandLineParser(opt::OptTable *opts)
: opts_(opts)
{ }
bool parseCommandLine(int argc, char **argv);
opt::InputArgList &args() { return args_; }
private:
opt::OptTable *opts_;
opt::InputArgList args_;
};
bool CommandLineParser::parseCommandLine(int argc, char **argv)
{
const char *progname = argv[0];
unsigned missingArgIndex, missingArgCount;
ArrayRef<const char *> argvv = makeArrayRef(argv, argc);
args_ = opts_->ParseArgs(argvv.slice(1), missingArgIndex, missingArgCount);
// Honor --help first
if (args_.hasArg(gollvm::options::OPT_help)) {
opts_->PrintHelp(errs(), progname, "Gollvm (LLVM-based Go compiler)",
0, 0, false);
exit(0);
}
// Honor -dumpversion
if (args_.hasArg(gollvm::options::OPT_dumpversion)) {
llvm::outs() << GOLLVM_COMPILERVERSION << "\n";
exit(0);
}
// Honor -version
if (args_.hasArg(gollvm::options::OPT_version)) {
Driver::emitVersion();
exit(0);
}
// Honor --print-multi-lib. FIXME: add real multilib support.
if (args_.hasArg(gollvm::options::OPT_print_multi_lib)) {
llvm::outs() << ".;@m64\n";
exit(0);
}
// Complain about missing arguments.
if (missingArgIndex != 0) {
errs() << progname << ": error: argument to '"
<< args_.getArgString(missingArgIndex)
<< "' option missing, espected "
<< missingArgCount << " value(s)\n";
return false;
}
// Check for unsupported options.
for (const opt::Arg *arg : args_) {
if (arg->getOption().hasFlag(gollvm::options::Unsupported)) {
errs() << progname << ": error: unsupported command line option '"
<< arg->getAsString(args_) << "'\n";
return false;
}
}
// Check for unknown options.
bool foundUnknown = false;
for (const opt::Arg *arg : args_.filtered(gollvm::options::OPT_UNKNOWN)) {
errs() << progname << ": error: unrecognized command line option '"
<< arg->getAsString(args_) << "'\n";
foundUnknown = true;
}
if (foundUnknown)
return false;
// Honor -mllvm
auto llvmargs = args_.getAllArgValues(gollvm::options::OPT_mllvm);
if (! llvmargs.empty()) {
unsigned nargs = llvmargs.size();
auto args = std::make_unique<const char*[]>(nargs + 2);
args[0] = "gollvm (LLVM option parsing)";
for (unsigned i = 0; i != nargs; ++i)
args[i + 1] = llvmargs[i].c_str();
args[nargs + 1] = nullptr;
llvm::cl::ParseCommandLineOptions(nargs + 1, args.get());
}
// HACK: the go tool likes to invoke the C and Go compiler drivers
// at various points to detect whether a given command line flag is
// supported (ex: "gcc -x c - -someflag < /dev/null"), and tends to
// pass "-x c" even when the driver is gccgo. Gccgo is (mirabile
// dictu) a functional C compiler, but gollvm is not. For the time
// being we will "fake it" by allowing "-x ... -" but then requiring
// that standard input be empty (so as to support the Go tool, but
// not act as a general-purposes C compiler).
opt::Arg *xarg = args_.getLastArg(gollvm::options::OPT_x);
if (xarg != nullptr &&
! llvm::StringRef(xarg->getValue()).equals("c") &&
! llvm::StringRef(xarg->getValue()).equals("go")) {
errs() << progname << ": invalid argument '"
<< xarg->getValue() << "' to '"
<< xarg->getAsString(args_) << "' option\n";
return false;
}
// Check if '-fsplit-stack' is supported by this compiler configuration.
#ifndef USING_SPLIT_STACK
if (args_.hasArg(gollvm::options::OPT_fsplit_stack)) {
errs() << progname
<< ": '-fsplit-stack' is not supported by this compiler "
"configuration\n";
return false;
}
#endif
return true;
}
int main(int argc, char **argv)
{
// Print a stack trace if we signal out.
sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
// Parse command line.
std::unique_ptr<opt::OptTable> opts =
gollvm::options::createGollvmDriverOptTable();
CommandLineParser clp(opts.get());
if (!clp.parseCommandLine(argc, argv))
return 1;
// Create driver.
Driver driver(clp.args(), opts.get(), argv[0], using_splitstack);
// Set up driver, select target and toolchain.
ToolChain *toolchain = driver.setup();
if (toolchain == nullptr)
return 1;
// Build compilation; construct actions for this compile.
std::unique_ptr<Compilation> compilation =
driver.buildCompilation(*toolchain);
if (!driver.buildActions(*compilation))
return 2;
// Process the action list. This will carry out actions that don't
// require use of an external tool, and will generate a list of
// commands for invoking external tools.
if (!driver.processActions(*compilation))
return 3;
// Execute the external-tool command list created above.
if (! compilation->executeCommands())
return 4;
// We're done.
return 0;
}