mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-08-25 00:29:20 +00:00
- Renamed FileReadable -> FileIsReadable - Remove support for GROKS_DASH_O; deferr it to %opts% substitution - Require a -o option except in case of linking where it defaults to a.out - Fix problem processing *.o and *.bc files. - Ignore optimization phase if -emit-raw-code - Correct the assembly generation phase to support -emit-native-code - Implement dependent library lookup git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@16020 91177308-0d34-0410-b5e6-96231b3b80d8
575 lines
19 KiB
C++
575 lines
19 KiB
C++
//===- CompilerDriver.cpp - The LLVM Compiler Driver ------------*- C++ -*-===//
|
|
//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file was developed by Reid Spencer and is distributed under the
|
|
// University of Illinois Open Source License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the bulk of the LLVM Compiler Driver (llvmc).
|
|
//
|
|
//===------------------------------------------------------------------------===
|
|
|
|
#include "CompilerDriver.h"
|
|
#include "ConfigLexer.h"
|
|
#include "llvm/Bytecode/Reader.h"
|
|
#include "llvm/Module.h"
|
|
#include "Support/FileUtilities.h"
|
|
#include "Support/SystemUtils.h"
|
|
#include "Support/StringExtras.h"
|
|
#include <iostream>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
inline std::string RemoveSuffix(const std::string& fullName) {
|
|
size_t dotpos = fullName.rfind('.',fullName.size());
|
|
if ( dotpos == std::string::npos ) return fullName;
|
|
return fullName.substr(0, dotpos);
|
|
}
|
|
|
|
const char OutputSuffix[] = ".o";
|
|
|
|
void WriteAction(CompilerDriver::Action* action ) {
|
|
std::cerr << action->program;
|
|
std::vector<std::string>::iterator I = action->args.begin();
|
|
while (I != action->args.end()) {
|
|
std::cerr << " " + *I;
|
|
++I;
|
|
}
|
|
std::cerr << "\n";
|
|
}
|
|
|
|
void DumpAction(CompilerDriver::Action* action) {
|
|
std::cerr << "command = " << action->program;
|
|
std::vector<std::string>::iterator I = action->args.begin();
|
|
while (I != action->args.end()) {
|
|
std::cerr << " " + *I;
|
|
++I;
|
|
}
|
|
std::cerr << "\n";
|
|
std::cerr << "flags = " << action->flags << "\n";
|
|
}
|
|
|
|
void DumpConfigData(CompilerDriver::ConfigData* cd, const std::string& type ){
|
|
std::cerr << "Configuration Data For '" << cd->langName << "' (" << type
|
|
<< ")\n";
|
|
std::cerr << "PreProcessor: ";
|
|
DumpAction(&cd->PreProcessor);
|
|
std::cerr << "Translator: ";
|
|
DumpAction(&cd->Translator);
|
|
std::cerr << "Optimizer: ";
|
|
DumpAction(&cd->Optimizer);
|
|
std::cerr << "Assembler: ";
|
|
DumpAction(&cd->Assembler);
|
|
std::cerr << "Linker: ";
|
|
DumpAction(&cd->Linker);
|
|
}
|
|
|
|
/// This specifies the passes to run for OPT_FAST_COMPILE (-O1)
|
|
/// which should reduce the volume of code and make compilation
|
|
/// faster. This is also safe on any llvm module.
|
|
static const char* DefaultFastCompileOptimizations[] = {
|
|
"-simplifycfg", "-mem2reg", "-instcombine"
|
|
};
|
|
}
|
|
|
|
// Stuff in this namespace properly belongs in lib/System and needs
|
|
// to be portable but we're avoiding that for now.
|
|
namespace sys {
|
|
|
|
bool FileIsReadable(const std::string& fname) {
|
|
return 0 == access(fname.c_str(), F_OK | R_OK);
|
|
}
|
|
|
|
void CleanupTempFile(const std::string& fname) {
|
|
if (FileIsReadable(fname))
|
|
unlink(fname.c_str());
|
|
}
|
|
|
|
std::string MakeTemporaryDirectory() {
|
|
char temp_name[64];
|
|
strcpy(temp_name,"/tmp/llvm_XXXXXX");
|
|
if (0 == mkdtemp(temp_name))
|
|
throw std::string("Can't create temporary directory");
|
|
return temp_name;
|
|
}
|
|
|
|
std::string FindExecutableInPath(const std::string& program) {
|
|
// First, just see if the program is already executable
|
|
if (isExecutableFile(program)) return program;
|
|
|
|
// Get the path. If its empty, we can't do anything
|
|
const char *PathStr = getenv("PATH");
|
|
if (PathStr == 0) return "";
|
|
|
|
// Now we have a colon separated list of directories to search; try them.
|
|
unsigned PathLen = strlen(PathStr);
|
|
while (PathLen) {
|
|
// Find the first colon...
|
|
const char *Colon = std::find(PathStr, PathStr+PathLen, ':');
|
|
|
|
// Check to see if this first directory contains the executable...
|
|
std::string FilePath = std::string(PathStr, Colon) + '/' + program;
|
|
if (isExecutableFile(FilePath))
|
|
return FilePath; // Found the executable!
|
|
|
|
// Nope it wasn't in this directory, check the next range!
|
|
PathLen -= Colon-PathStr;
|
|
PathStr = Colon;
|
|
|
|
// Advance past duplicate coons
|
|
while (*PathStr == ':') {
|
|
PathStr++;
|
|
PathLen--;
|
|
}
|
|
}
|
|
|
|
// If we fell out, we ran out of directories in PATH to search, return failure
|
|
return "";
|
|
}
|
|
}
|
|
|
|
CompilerDriver::CompilerDriver(ConfigDataProvider& confDatProv )
|
|
: cdp(&confDatProv)
|
|
, finalPhase(LINKING)
|
|
, optLevel(OPT_FAST_COMPILE)
|
|
, isDryRun(false)
|
|
, isVerbose(false)
|
|
, isDebug(false)
|
|
, timeActions(false)
|
|
, emitRawCode(false)
|
|
, emitNativeCode(false)
|
|
, keepTemps(false)
|
|
, machine()
|
|
, LibraryPaths()
|
|
, AdditionalArgs()
|
|
, TempDir()
|
|
{
|
|
// FIXME: These libraries are platform specific
|
|
LibraryPaths.push_back("/lib");
|
|
LibraryPaths.push_back("/usr/lib");
|
|
AdditionalArgs.reserve(NUM_PHASES);
|
|
StringVector emptyVec;
|
|
for (unsigned i = 0; i < NUM_PHASES; ++i)
|
|
AdditionalArgs.push_back(emptyVec);
|
|
}
|
|
|
|
CompilerDriver::~CompilerDriver() {
|
|
cdp = 0;
|
|
LibraryPaths.clear();
|
|
AdditionalArgs.clear();
|
|
}
|
|
|
|
CompilerDriver::ConfigData::ConfigData()
|
|
: langName()
|
|
, PreProcessor()
|
|
, Translator()
|
|
, Optimizer()
|
|
, Assembler()
|
|
, Linker()
|
|
{
|
|
StringVector emptyVec;
|
|
for (unsigned i = 0; i < NUM_PHASES; ++i)
|
|
opts.push_back(emptyVec);
|
|
}
|
|
|
|
void CompilerDriver::error( const std::string& errmsg ) {
|
|
std::cerr << "llvmc: Error: " << errmsg << ".\n";
|
|
exit(1);
|
|
}
|
|
|
|
CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd,
|
|
const std::string& input,
|
|
const std::string& output,
|
|
Phases phase)
|
|
{
|
|
Action* pat = 0; ///< The pattern/template for the action
|
|
Action* action = new Action; ///< The actual action to execute
|
|
|
|
// Get the action pattern
|
|
switch (phase) {
|
|
case PREPROCESSING: pat = &cd->PreProcessor; break;
|
|
case TRANSLATION: pat = &cd->Translator; break;
|
|
case OPTIMIZATION: pat = &cd->Optimizer; break;
|
|
case ASSEMBLY: pat = &cd->Assembler; break;
|
|
case LINKING: pat = &cd->Linker; break;
|
|
default:
|
|
assert(!"Invalid driver phase!");
|
|
break;
|
|
}
|
|
assert(pat != 0 && "Invalid command pattern");
|
|
|
|
// Copy over some pattern things that don't need to change
|
|
action->program = pat->program;
|
|
action->flags = pat->flags;
|
|
|
|
// Do the substitutions from the pattern to the actual
|
|
StringVector::iterator PI = pat->args.begin();
|
|
StringVector::iterator PE = pat->args.end();
|
|
while (PI != PE) {
|
|
if ((*PI)[0] == '%') {
|
|
if (*PI == "%in%") {
|
|
action->args.push_back(input);
|
|
} else if (*PI == "%out%") {
|
|
action->args.push_back(output);
|
|
} else if (*PI == "%time%") {
|
|
if (timePasses)
|
|
action->args.push_back("-time-passes");
|
|
} else if (*PI == "%stats%") {
|
|
if (showStats)
|
|
action->args.push_back("-stats");
|
|
} else if (*PI == "%target%") {
|
|
// FIXME: Ignore for now
|
|
} else if (*PI == "%opt%") {
|
|
if (!emitRawCode) {
|
|
if (cd->opts.size() > static_cast<unsigned>(optLevel) &&
|
|
!cd->opts[optLevel].empty())
|
|
action->args.insert(action->args.end(), cd->opts[optLevel].begin(),
|
|
cd->opts[optLevel].end());
|
|
else
|
|
error("Optimization options for level " + utostr(unsigned(optLevel)) +
|
|
" were not specified");
|
|
}
|
|
} else if (*PI == "%args%") {
|
|
if (AdditionalArgs.size() > unsigned(phase))
|
|
if (!AdditionalArgs[phase].empty()) {
|
|
// Get specific options for each kind of action type
|
|
StringVector& addargs = AdditionalArgs[phase];
|
|
// Add specific options for each kind of action type
|
|
action->args.insert(action->args.end(), addargs.begin(), addargs.end());
|
|
}
|
|
} else {
|
|
error("Invalid substitution name" + *PI);
|
|
}
|
|
} else {
|
|
// Its not a substitution, just put it in the action
|
|
action->args.push_back(*PI);
|
|
}
|
|
PI++;
|
|
}
|
|
|
|
|
|
// Finally, we're done
|
|
return action;
|
|
}
|
|
|
|
bool CompilerDriver::DoAction(Action*action) {
|
|
assert(action != 0 && "Invalid Action!");
|
|
if (isVerbose)
|
|
WriteAction(action);
|
|
if (!isDryRun) {
|
|
std::string prog(sys::FindExecutableInPath(action->program));
|
|
if (prog.empty())
|
|
error("Can't find program '" + action->program + "'");
|
|
|
|
// Get the program's arguments
|
|
const char* argv[action->args.size() + 1];
|
|
argv[0] = prog.c_str();
|
|
unsigned i = 1;
|
|
for (; i <= action->args.size(); ++i)
|
|
argv[i] = action->args[i-1].c_str();
|
|
argv[i] = 0;
|
|
|
|
// Invoke the program
|
|
return !ExecWait(argv, environ);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int CompilerDriver::execute(const InputList& InpList,
|
|
const std::string& Output ) {
|
|
// Echo the configuration of options if we're running verbose
|
|
if (isDebug)
|
|
{
|
|
std::cerr << "Compiler Driver Options:\n";
|
|
std::cerr << "DryRun = " << isDryRun << "\n";
|
|
std::cerr << "Verbose = " << isVerbose << " \n";
|
|
std::cerr << "TimeActions = " << timeActions << "\n";
|
|
std::cerr << "EmitRawCode = " << emitRawCode << "\n";
|
|
std::cerr << "OutputMachine = " << machine << "\n";
|
|
std::cerr << "EmitNativeCode = " << emitNativeCode << "\n";
|
|
InputList::const_iterator I = InpList.begin();
|
|
while ( I != InpList.end() ) {
|
|
std::cerr << "Input: " << I->first << "(" << I->second << ")\n";
|
|
++I;
|
|
}
|
|
std::cerr << "Output: " << Output << "\n";
|
|
}
|
|
|
|
// If there's no input, we're done.
|
|
if (InpList.empty())
|
|
error("Nothing to compile.");
|
|
|
|
// If they are asking for linking and didn't provide an output
|
|
// file then its an error (no way for us to "make up" a meaningful
|
|
// file name based on the various linker input files).
|
|
if (finalPhase == LINKING && Output.empty())
|
|
error("An output file name must be specified for linker output");
|
|
|
|
// This vector holds all the resulting actions of the following loop.
|
|
std::vector<Action*> actions;
|
|
|
|
// Create a temporary directory for our temporary files
|
|
std::string TempDir(sys::MakeTemporaryDirectory());
|
|
std::string TempPreprocessorOut(TempDir + "/preproc.o");
|
|
std::string TempTranslatorOut(TempDir + "/trans.o");
|
|
std::string TempOptimizerOut(TempDir + "/opt.o");
|
|
std::string TempAssemblerOut(TempDir + "/asm.o");
|
|
|
|
/// PRE-PROCESSING / TRANSLATION / OPTIMIZATION / ASSEMBLY phases
|
|
// for each input item
|
|
std::vector<std::string> LinkageItems;
|
|
std::string OutFile(Output);
|
|
InputList::const_iterator I = InpList.begin();
|
|
while ( I != InpList.end() ) {
|
|
// Get the suffix of the file name
|
|
const std::string& ftype = I->second;
|
|
|
|
// If its a library, bytecode file, or object file, save
|
|
// it for linking below and short circuit the
|
|
// pre-processing/translation/assembly phases
|
|
if (ftype.empty() || ftype == "o" || ftype == "bc") {
|
|
// We shouldn't get any of these types of files unless we're
|
|
// later going to link. Enforce this limit now.
|
|
if (finalPhase != LINKING) {
|
|
error("Pre-compiled objects found but linking not requested");
|
|
}
|
|
LinkageItems.push_back(I->first);
|
|
++I; continue; // short circuit remainder of loop
|
|
}
|
|
|
|
// At this point, we know its something we need to translate
|
|
// and/or optimize. See if we can get the configuration data
|
|
// for this kind of file.
|
|
ConfigData* cd = cdp->ProvideConfigData(I->second);
|
|
if (cd == 0)
|
|
error(std::string("Files of type '") + I->second +
|
|
"' are not recognized." );
|
|
if (isDebug)
|
|
DumpConfigData(cd,I->second);
|
|
|
|
// Initialize the input file
|
|
std::string InFile(I->first);
|
|
|
|
// PRE-PROCESSING PHASE
|
|
Action& action = cd->PreProcessor;
|
|
|
|
// Get the preprocessing action, if needed, or error if appropriate
|
|
if (!action.program.empty()) {
|
|
if (action.isSet(REQUIRED_FLAG) || finalPhase == PREPROCESSING) {
|
|
if (finalPhase == PREPROCESSING)
|
|
actions.push_back(GetAction(cd,InFile,OutFile,PREPROCESSING));
|
|
else {
|
|
actions.push_back(GetAction(cd,InFile,TempPreprocessorOut,
|
|
PREPROCESSING));
|
|
InFile = TempPreprocessorOut;
|
|
}
|
|
}
|
|
} else if (finalPhase == PREPROCESSING) {
|
|
error(cd->langName + " does not support pre-processing");
|
|
} else if (action.isSet(REQUIRED_FLAG)) {
|
|
error(std::string("Don't know how to pre-process ") +
|
|
cd->langName + " files");
|
|
}
|
|
|
|
// Short-circuit remaining actions if all they want is pre-processing
|
|
if (finalPhase == PREPROCESSING) { ++I; continue; };
|
|
|
|
/// TRANSLATION PHASE
|
|
action = cd->Translator;
|
|
|
|
// Get the translation action, if needed, or error if appropriate
|
|
if (!action.program.empty()) {
|
|
if (action.isSet(REQUIRED_FLAG) || finalPhase == TRANSLATION) {
|
|
if (finalPhase == TRANSLATION)
|
|
actions.push_back(GetAction(cd,InFile,OutFile,TRANSLATION));
|
|
else {
|
|
actions.push_back(GetAction(cd,InFile,TempTranslatorOut,TRANSLATION));
|
|
InFile = TempTranslatorOut;
|
|
}
|
|
|
|
// ll -> bc Helper
|
|
if (action.isSet(OUTPUT_IS_ASM_FLAG)) {
|
|
/// The output of the translator is an LLVM Assembly program
|
|
/// We need to translate it to bytecode
|
|
Action* action = new Action();
|
|
action->program = "llvm-as";
|
|
action->args.push_back(InFile);
|
|
action->args.push_back("-o");
|
|
InFile += ".bc";
|
|
action->args.push_back(InFile);
|
|
actions.push_back(action);
|
|
}
|
|
}
|
|
} else if (finalPhase == TRANSLATION) {
|
|
error(cd->langName + " does not support translation");
|
|
} else if (action.isSet(REQUIRED_FLAG)) {
|
|
error(std::string("Don't know how to translate ") +
|
|
cd->langName + " files");
|
|
}
|
|
|
|
// Short-circuit remaining actions if all they want is translation
|
|
if (finalPhase == TRANSLATION) { ++I; continue; }
|
|
|
|
/// OPTIMIZATION PHASE
|
|
action = cd->Optimizer;
|
|
|
|
// Get the optimization action, if needed, or error if appropriate
|
|
if (!emitRawCode) {
|
|
if (!action.program.empty()) {
|
|
if (action.isSet(REQUIRED_FLAG) || finalPhase == OPTIMIZATION) {
|
|
if (finalPhase == OPTIMIZATION)
|
|
actions.push_back(GetAction(cd,InFile,OutFile,OPTIMIZATION));
|
|
else {
|
|
actions.push_back(GetAction(cd,InFile,TempOptimizerOut,OPTIMIZATION));
|
|
InFile = TempOptimizerOut;
|
|
}
|
|
// ll -> bc Helper
|
|
if (action.isSet(OUTPUT_IS_ASM_FLAG)) {
|
|
/// The output of the translator is an LLVM Assembly program
|
|
/// We need to translate it to bytecode
|
|
Action* action = new Action();
|
|
action->program = "llvm-as";
|
|
action->args.push_back(InFile);
|
|
action->args.push_back("-f");
|
|
action->args.push_back("-o");
|
|
InFile += ".bc";
|
|
action->args.push_back(InFile);
|
|
actions.push_back(action);
|
|
}
|
|
}
|
|
} else if (finalPhase == OPTIMIZATION) {
|
|
error(cd->langName + " does not support optimization");
|
|
} else if (action.isSet(REQUIRED_FLAG)) {
|
|
error(std::string("Don't know how to optimize ") +
|
|
cd->langName + " files");
|
|
}
|
|
}
|
|
|
|
// Short-circuit remaining actions if all they want is optimization
|
|
if (finalPhase == OPTIMIZATION) { ++I; continue; }
|
|
|
|
/// ASSEMBLY PHASE
|
|
action = cd->Assembler;
|
|
|
|
if (finalPhase == ASSEMBLY || emitNativeCode) {
|
|
if (emitNativeCode) {
|
|
if (action.program.empty()) {
|
|
error(std::string("Native Assembler not specified for ") +
|
|
cd->langName + " files");
|
|
} else if (finalPhase == ASSEMBLY) {
|
|
actions.push_back(GetAction(cd,InFile,OutFile,ASSEMBLY));
|
|
} else {
|
|
actions.push_back(GetAction(cd,InFile,TempAssemblerOut,ASSEMBLY));
|
|
InFile = TempAssemblerOut;
|
|
}
|
|
} else {
|
|
// Just convert back to llvm assembly with llvm-dis
|
|
Action* action = new Action();
|
|
action->program = "llvm-dis";
|
|
action->args.push_back(InFile);
|
|
action->args.push_back("-f");
|
|
action->args.push_back("-o");
|
|
action->args.push_back(OutFile);
|
|
actions.push_back(action);
|
|
}
|
|
}
|
|
|
|
// Short-circuit remaining actions if all they want is assembly output
|
|
if (finalPhase == ASSEMBLY) { ++I; continue; }
|
|
|
|
// Register the OutFile as a link candidate
|
|
LinkageItems.push_back(InFile);
|
|
|
|
// Go to next file to be processed
|
|
++I;
|
|
}
|
|
|
|
/// LINKING PHASE
|
|
if (finalPhase == LINKING) {
|
|
if (emitNativeCode) {
|
|
error("llvmc doesn't know how to link native code yet");
|
|
} else {
|
|
// First, we need to examine the files to ensure that they all contain
|
|
// bytecode files. Since the final output is bytecode, we can only
|
|
// link bytecode.
|
|
StringVector::const_iterator I = LinkageItems.begin();
|
|
StringVector::const_iterator E = LinkageItems.end();
|
|
StringVector lib_list;
|
|
while (I != E ) {
|
|
if (sys::FileIsReadable(*I)) {
|
|
if (CheckMagic(*I, "llvm")) {
|
|
// Examine the bytecode file for additional bytecode libraries to
|
|
// link in.
|
|
ModuleProvider* mp = getBytecodeModuleProvider(*I);
|
|
assert(mp!=0 && "Couldn't get module provider from bytecode file");
|
|
Module* M = mp->releaseModule();
|
|
assert(M!=0 && "Couldn't get module from module provider");
|
|
Module::lib_iterator LI = M->lib_begin();
|
|
Module::lib_iterator LE = M->lib_end();
|
|
while ( LI != LE ) {
|
|
if (sys::FileIsReadable(*LI)) {
|
|
if (CheckMagic(*I,"llvm")) {
|
|
lib_list.push_back(*LI);
|
|
} else {
|
|
error(std::string("Library '") + *LI + "' required by file '"
|
|
+ *I + "' is not an LLVM bytecode file");
|
|
}
|
|
} else {
|
|
error(std::string("Library '") + *LI + "' required by file '"
|
|
+ *I + "' is not readable");
|
|
}
|
|
++I;
|
|
}
|
|
} else {
|
|
error(std::string("File '") + *I + " is not an LLVM byte code file");
|
|
}
|
|
} else {
|
|
error(std::string("File '") + *I + " is not readable");
|
|
}
|
|
++I;
|
|
}
|
|
|
|
// We're emitting bytecode so let's build an llvm-link Action
|
|
Action* link = new Action();
|
|
link->program = "llvm-link";
|
|
link->args = LinkageItems;
|
|
link->args.insert(link->args.end(), lib_list.begin(), lib_list.end());
|
|
link->args.push_back("-f");
|
|
link->args.push_back("-o");
|
|
link->args.push_back(OutFile);
|
|
if (timePasses)
|
|
link->args.push_back("-time-passes");
|
|
if (showStats)
|
|
link->args.push_back("-stats");
|
|
actions.push_back(link);
|
|
}
|
|
}
|
|
|
|
/// RUN THE ACTIONS
|
|
std::vector<Action*>::iterator aIter = actions.begin();
|
|
while (aIter != actions.end()) {
|
|
if (!DoAction(*aIter))
|
|
error("Action failed");
|
|
aIter++;
|
|
}
|
|
|
|
if (!keepTemps) {
|
|
// Cleanup files
|
|
::sys::CleanupTempFile(TempPreprocessorOut);
|
|
::sys::CleanupTempFile(TempTranslatorOut);
|
|
::sys::CleanupTempFile(TempOptimizerOut);
|
|
|
|
// Cleanup temporary directory we created
|
|
if (::sys::FileIsReadable(TempDir))
|
|
rmdir(TempDir.c_str());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab
|