blob: 8e9f84ba7433b099a9b33000de4edf53dbb17ec2 [file] [log] [blame]
//===-- llvm-goparse.cpp - Debug test driver for go parser for llvm ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Invokes the gofrontend parser on specified input files.
//
//===----------------------------------------------------------------------===//
#include "go-llvm-linemap.h"
#include "go-llvm-diagnostics.h"
#include "go-llvm.h"
#include "go-c.h"
#include "mpfr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/CodeGen/CommandFlags.def"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Verifier.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/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 <sys/stat.h>
#include <unistd.h>
using namespace llvm;
static cl::opt<std::string>
TargetTriple("mtriple", cl::desc("Override target triple for module"));
static cl::list<std::string>
InputFilenames(cl::Positional,
cl::desc("<input go source files>"),
cl::OneOrMore);
static cl::opt<std::string>
IncludeDirs("I", cl::desc("<include dirs>"));
static cl::opt<std::string>
LibDirs("L", cl::desc("<system library dirs>"));
// Determine optimization level.
static cl::opt<char>
OptLevel("O",
cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
"(default = '-O2')"),
cl::Prefix,
cl::ZeroOrMore,
cl::init(' '));
static cl::opt<std::string>
OutputFileName("o",
cl::desc("Set name of output file."),
cl::init(""));
// These are provided for compatibility purposes -- they are currently ignored.
static cl::opt<bool>
M64Option("m64", cl::desc("Dummy -m64 arg."), cl::init(false), cl::ZeroOrMore);
static cl::opt<bool>
MinusGOption("g", cl::desc("Dummy -g arg."), cl::init(false), cl::ZeroOrMore);
static cl::opt<bool>
MinusCOption("c", cl::desc("Dummy -c arg."), cl::init(false), cl::ZeroOrMore);
static cl::opt<bool>
MinusVOption("v", cl::desc("Dummy -v arg."), cl::init(false), cl::ZeroOrMore);
static cl::opt<bool>
NoBackend("nobackend",
cl::desc("Stub out back end invocation."),
cl::init(false));
static cl::opt<bool>
NoVerify("noverify",
cl::desc("Stub out module verifier invocation."),
cl::init(false));
static cl::opt<bool>
CheckDivideZero("fgo-check-divide-zero",
cl::desc("Add explicit checks for divide-by-zero."),
cl::init(true));
static cl::opt<bool>
CheckDivideOverflow("fgo-check-divide-overflow",
cl::desc("Add explicit checks for division overflow in INT_MIN / -1."),
cl::init(true));
static cl::opt<bool>
CompilingRuntime("fgo-compiling-runtime",
cl::desc("Compiling the runtime package."),
cl::init(false));
static cl::opt<bool>
DumpAst("fgo-dump-ast",
cl::desc("Dump Go frontend internal AST structure."),
cl::init(false));
static cl::opt<bool>
DumpIR("dump-ir",
cl::desc("Dump LLVM IR for module at end of run."),
cl::init(false));
static cl::opt<bool>
NoInline("fno-inline",
cl::desc("Disable inlining."),
cl::init(false));
static cl::opt<bool>
OptimizeAllocs("fgo-optimize-allocs",
cl::desc("Enable escape analysis in the go frontend."),
cl::init(false));
static cl::opt<std::string>
PackagePath("fgo-pkgpath",
cl::desc("Set Go package path."),
cl::init(""));
static cl::opt<std::string>
PackagePrefix("fgo-prefix",
cl::desc("Set package-specific prefix for exported Go names."),
cl::init(""));
static cl::opt<std::string>
RelativeImportPath("fgo-relative-import-path",
cl::desc("Treat a relative import as relative to path."),
cl::init(""));
static cl::opt<std::string>
CHeader("fgo-c-header",
cl::desc("The C header file to write."),
cl::init(""));
static cl::opt<int>
EscapeDebugLevel("fgo-debug-escape",
cl::desc("Emit debugging information related to the "
"escape analysis pass when run with "
"-fgo-optimize-allocs."),
cl::init(0));
static cl::opt<unsigned>
TraceLevel("tracelevel",
cl::desc("Set debug trace level (def: 0, no trace output)."),
cl::init(0));
// Provide a way to turn off the full passmanager (to make it easier
// to triage failures). This should be a temporary flag (not for the
// long term).
static cl::opt<bool>
FullPasses("full-passes",
cl::desc("Enable all available optimization passes."),
cl::init(true));
static std::unique_ptr<ToolOutputFile>
GetOutputStream() {
// Decide if we need "binary" output.
bool Binary = false;
switch (FileType) {
case TargetMachine::CGFT_AssemblyFile:
break;
case TargetMachine::CGFT_ObjectFile:
case TargetMachine::CGFT_Null:
Binary = true;
break;
}
// Open the file.
std::error_code EC;
sys::fs::OpenFlags OpenFlags = sys::fs::F_None;
if (!Binary)
OpenFlags |= sys::fs::F_Text;
auto FDOut = llvm::make_unique<ToolOutputFile>(OutputFileName, EC,
OpenFlags);
if (EC) {
errs() << "error opening " << OutputFileName << ": "
<< EC.message() << '\n';
return nullptr;
}
return FDOut;
}
class BEDiagnosticHandler : public DiagnosticHandler {
bool *error_;
public:
BEDiagnosticHandler(bool *errorPtr)
: error_(errorPtr) {}
bool handleDiagnostics(const DiagnosticInfo &DI) override {
if (DI.getSeverity() == DS_Error)
*error_ = true;
if (auto *Remark = dyn_cast<DiagnosticInfoOptimizationBase>(&DI))
if (!Remark->isEnabled())
return true;
DiagnosticPrinterRawOStream DP(errs());
errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": ";
DI.print(DP);
errs() << "\n";
return true;
}
};
// Helper class for managing the overall Go compilation process.
class CompilationOrchestrator {
public:
CompilationOrchestrator(const char *argvZero);
~CompilationOrchestrator() { }
// Various stages in the setup/execution of the compilation. The
// convention here is to return false if there was a fatal error, true
// otherwise.
bool preamble();
bool initBridge();
bool invokeFrontEnd();
bool invokeBridge();
bool invokeBackEnd();
private:
Triple triple_;
llvm::LLVMContext context_;
const char *progname_;
CodeGenOpt::Level cgolvl_;
unsigned olvl_;
std::unique_ptr<Llvm_backend> bridge_;
std::unique_ptr<TargetMachine> target_;
std::unique_ptr<Llvm_linemap> linemap_;
std::unique_ptr<llvm::Module> module_;
std::unique_ptr<ToolOutputFile> out_;
std::unique_ptr<TargetLibraryInfoImpl> tlii_;
bool hasError_;
void createPasses(legacy::PassManager &MPM,
legacy::FunctionPassManager &FPM);
};
CompilationOrchestrator::CompilationOrchestrator(const char *argvZero)
: progname_(argvZero), cgolvl_(CodeGenOpt::Default),
olvl_(2), hasError_(false)
{
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
}
bool CompilationOrchestrator::preamble()
{
triple_ = Triple(Triple::normalize(TargetTriple));
if (triple_.getTriple().empty())
triple_.setTriple(sys::getDefaultTargetTriple());
// Get the target specific parser.
std::string Error;
const Target *TheTarget =
TargetRegistry::lookupTarget(MArch, triple_, Error);
if (!TheTarget) {
errs() << progname_ << ": " << Error;
return false;
}
// FIXME: cpu, features not yet supported
std::string CPUStr = getCPUStr(), FeaturesStr = getFeaturesStr();
// optimization level
switch (OptLevel) {
default:
errs() << progname_ << ": invalid optimization level.\n";
return false;
case '0':
olvl_ = 0;
cgolvl_ = CodeGenOpt::None;
break;
case '1':
olvl_ = 1;
cgolvl_ = CodeGenOpt::Less;
break;
case 's': // TODO: same as -O2 for now.
case ' ':
case '2':
break;
case '3':
olvl_ = 3;
cgolvl_ = CodeGenOpt::Aggressive;
break;
}
go_no_warn = NoWarn;
TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
// FIXME: turn off integrated assembler for now.
Options.DisableIntegratedAS = true;
// FIXME: this hard-wires on the equivalent of -ffunction-sections
// and -fdata-sections, since there doesn't seem to be a high-level
// hook for selecting a separate section for a specific variable or
// function (other than forcing it into a comdat, which is not
// always what we want).
Options.FunctionSections = true;
Options.DataSections = true;
// Create target machine
Optional<llvm::CodeModel::Model> CM = None;
target_.reset(
TheTarget->createTargetMachine(triple_.getTriple(), CPUStr, FeaturesStr,
Options, getRelocModel(), CM, cgolvl_));
assert(target_.get() && "Could not allocate target machine!");
return true;
}
// This helper performs the various initial steps needed to set up the
// compilation, including prepping the LLVM context, creating an LLVM
// module, creating the bridge itself (Llvm_backend object) and
// setting up the Go frontend via a call to go_create_gogo(). At the
// end of this routine things should be ready to kick off the front end.
bool CompilationOrchestrator::initBridge()
{
// Set up the LLVM context
context_.setDiagnosticHandler(
llvm::make_unique<BEDiagnosticHandler>(&this->hasError_));
// Construct linemap and module
linemap_.reset(new Llvm_linemap());
module_.reset(new llvm::Module("gomodule", context_));
// Add the target data from the target machine, if it exists
module_->setTargetTriple(triple_.getTriple());
module_->setDataLayout(target_->createDataLayout());
// Now construct Llvm_backend helper.
bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get()));
// Honor inline, tracelevel cmd line options
bridge_->setTraceLevel(TraceLevel);
bridge_->setNoInline(NoInline);
// Support -fgo-dump-ast
if (DumpAst)
go_enable_dump("ast");
// Populate 'args' struct with various bits of information needed by
// the front end, then pass it to the front end via go_create_gogo().
struct go_create_gogo_args args;
unsigned bpi = target_->getPointerSize() * 8;
args.int_type_size = bpi;
args.pointer_size = bpi;
args.pkgpath = PackagePath.empty() ? NULL : PackagePath.c_str();
args.prefix = PackagePrefix.empty() ? NULL : PackagePrefix.c_str();
args.relative_import_path =
RelativeImportPath.empty() ? NULL : RelativeImportPath.c_str();
args.c_header = CHeader.empty() ? NULL : CHeader.c_str();
args.check_divide_by_zero = CheckDivideZero;
args.check_divide_overflow = CheckDivideOverflow;
args.compiling_runtime = CompilingRuntime;
args.debug_escape_level = EscapeDebugLevel;
args.nil_check_size_threshold = -1;
args.linemap = linemap_.get();
args.backend = bridge_.get();
go_create_gogo (&args);
/* The default precision for floating point numbers. This is used
for floating point constants with abstract type. This may
eventually be controllable by a command line option. */
mpfr_set_default_prec (256);
// Escape analysis
if (OptimizeAllocs)
go_enable_optimize("allocs");
// Include dirs
if (! IncludeDirs.empty()) {
std::stringstream ss(IncludeDirs);
std::string dir;
while(std::getline(ss, dir, ':')) {
struct stat st;
if (stat (dir.c_str(), &st) == 0 && S_ISDIR (st.st_mode))
go_add_search_path(dir.c_str());
}
}
// Library dirs
// TODO: add version, architecture dirs
if (! LibDirs.empty()) {
std::stringstream ss(LibDirs);
std::string dir;
while(std::getline(ss, dir, ':')) {
struct stat st;
if (stat (dir.c_str(), &st) == 0 && S_ISDIR (st.st_mode))
go_add_search_path(dir.c_str());
}
}
// Figure out where we are going to send the output.
if (OutputFileName.empty()) {
errs() << "no output file specified (please use -o option)\n";
return false;
}
out_ = GetOutputStream();
if (!out_)
return false;
return true;
}
bool CompilationOrchestrator::invokeFrontEnd()
{
// Collect the input files and kick off the front end
// Kick off the front end
unsigned nfiles = InputFilenames.size();
std::unique_ptr<const char *> filenames(new const char *[nfiles]);
const char **fns = filenames.get();
unsigned idx = 0;
for (auto &fn : InputFilenames)
fns[idx++] = fn.c_str();
go_parse_input_files(fns, nfiles, false, true);
if (! NoBackend)
go_write_globals();
if (DumpIR)
bridge_->dumpModule();
if (! NoVerify && !go_be_saw_errors())
bridge_->verifyModule();
if (TraceLevel)
std::cerr << "linemap stats:" << linemap_->statistics() << "\n";
// Delete the bridge at this point. In the case that there were
// errors, this will help clean up any unreachable llvm Instructions
// (which would otherwise trigger asserts); in the non-error case it
// will help to free up bridge-related memory prior to kicking off
// the pass manager.
bridge_.reset(nullptr);
// Early exit at this point if we've seen errors
if (go_be_saw_errors())
return false;
return true;
}
void CompilationOrchestrator::createPasses(legacy::PassManager &MPM,
legacy::FunctionPassManager &FPM)
{
// FIXME: support LTO, ThinLTO, PGO
PassManagerBuilder pmb;
// Configure the inliner
if (NoInline || olvl_ == 0) {
// Nothing here at the moment. There is go:noinline, but no equivalent
// of go:alwaysinline.
} else {
bool disableInlineHotCallSite = false; // for autofdo, not yet supported
pmb.Inliner =
createFunctionInliningPass(olvl_, 2, disableInlineHotCallSite);
}
pmb.OptLevel = olvl_;
pmb.SizeLevel = 0; // TODO: decide on right value here
pmb.PrepareForThinLTO = false;
pmb.PrepareForLTO = false;
FPM.add(new TargetLibraryInfoWrapperPass(*tlii_));
if (! NoVerify)
FPM.add(createVerifierPass());
pmb.populateFunctionPassManager(FPM);
pmb.populateModulePassManager(MPM);
}
bool CompilationOrchestrator::invokeBackEnd()
{
tlii_.reset(new TargetLibraryInfoImpl(triple_));
// Set up module and function passes
legacy::PassManager modulePasses;
modulePasses.add(
createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
legacy::FunctionPassManager functionPasses(module_.get());
functionPasses.add(
createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
createPasses(modulePasses, functionPasses);
// Set up codegen passes
legacy::PassManager codeGenPasses;
codeGenPasses.add(
createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
// Codegen setup
raw_pwrite_stream *OS = &out_->os();
codeGenPasses.add(new TargetLibraryInfoWrapperPass(*tlii_));
if (target_->addPassesToEmitFile(codeGenPasses, *OS, FileType,
/*DisableVerify=*/ NoVerify)) {
errs() << "error: unable to interface with target\n";
return false;
}
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();
// Temporary -- to be removed in a follow-on CL
if (FullPasses) {
// Here we go... first function passes
functionPasses.doInitialization();
for (Function &F : *module_.get())
if (!F.isDeclaration())
functionPasses.run(F);
functionPasses.doFinalization();
// ... then module passes
modulePasses.run(*module_.get());
}
// ... and finally code generation
codeGenPasses.run(*module_.get());
if (hasError_)
return false;
// Declare success.
out_->keep();
return true;
}
int main(int argc, char **argv)
{
CompilationOrchestrator orchestrator(argv[0]);
cl::ParseCommandLineOptions(argc, argv, "gollvm driver\n");
// 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.
// Initialize target and sort out selected command line options.
if (!orchestrator.preamble())
return 1;
// Set up the bridge
if (!orchestrator.initBridge())
return 2;
// Invoke front end
if (! orchestrator.invokeFrontEnd())
return 3;
// Invoke back end
if (! orchestrator.invokeBackEnd())
return 4;
// We're done.
return 0;
}