Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ endif()

set(LIB_SOURCES
src/compilerlib/compiler.cpp
src/compilerlib/emit/llvm_output.cpp
src/compilerlib/toolchain.cpp
src/compilerlib/instrumentation/alloc.cpp
src/compilerlib/instrumentation/bounds.cpp
Expand Down
116 changes: 27 additions & 89 deletions src/compilerlib/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "compilerlib/instrumentation/config.hpp"
#include "compilerlib/instrumentation/trace.hpp"
#include "compilerlib/instrumentation/vtable.hpp"
#include "emit/llvm_output.hpp"

#include <clang/Frontend/FrontendActions.h>
#include <clang/Driver/Compilation.h>
Expand All @@ -22,7 +23,6 @@
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/Module.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/Support/CodeGen.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Program.h>
#include <llvm/Support/TargetSelect.h>
Expand Down Expand Up @@ -103,23 +103,6 @@ namespace compilerlib
}
};

llvm::CodeGenOptLevel toCodeGenOptLevel(unsigned level)
{
switch (level)
{
case 0:
return llvm::CodeGenOptLevel::None;
case 1:
return llvm::CodeGenOptLevel::Less;
case 2:
return llvm::CodeGenOptLevel::Default;
case 3:
return llvm::CodeGenOptLevel::Aggressive;
default:
return llvm::CodeGenOptLevel::Default;
}
}

const char* findArgValue(const llvm::opt::ArgStringList& args, llvm::StringRef opt)
{
for (size_t i = 0; i + 1 < args.size(); ++i)
Expand Down Expand Up @@ -421,70 +404,6 @@ namespace compilerlib
return plan;
}

CT_NODISCARD bool emitObjectFile(llvm::Module& module, const clang::CompilerInstance& ci,
llvm::StringRef outputPath, std::string& error)
{
std::string targetTriple = module.getTargetTriple();

if (targetTriple.empty())
targetTriple = llvm::sys::getDefaultTargetTriple();
module.setTargetTriple(targetTriple);

std::string targetError;
const llvm::Target* target =
llvm::TargetRegistry::lookupTarget(targetTriple, targetError);
if (!target)
{
error = targetError;
return false;
}

const auto& targetOpts = ci.getTargetOpts();
std::string features;
for (const auto& feature : targetOpts.FeaturesAsWritten)
{
if (!features.empty())
features += ",";
features += feature;
}

llvm::TargetOptions options;
auto codegenLevel = toCodeGenOptLevel(ci.getCodeGenOpts().OptimizationLevel);
// For position-independent code (needed for instrumented code and PIE executables),
// explicitly set the relocation model to PIC
llvm::Reloc::Model relocModel = llvm::Reloc::PIC_;
std::unique_ptr<llvm::TargetMachine> targetMachine(
target->createTargetMachine(targetTriple, targetOpts.CPU, features, options,
relocModel, std::nullopt, codegenLevel));
if (!targetMachine)
{
error = "failed to create target machine";
return false;
}

module.setDataLayout(targetMachine->createDataLayout());

std::error_code ec;
llvm::raw_fd_ostream dest(outputPath, ec, llvm::sys::fs::OF_None);
if (ec)
{
error = ec.message();
return false;
}

llvm::legacy::PassManager pass;
if (targetMachine->addPassesToEmitFile(pass, dest, nullptr,
llvm::CodeGenFileType::ObjectFile))
{
error = "target does not support object emission";
return false;
}

pass.run(module);
dest.flush();
return true;
}

class Cc1Runner
{
public:
Expand All @@ -504,9 +423,15 @@ namespace compilerlib
return false;
}

if (ci->getFrontendOpts().ProgramAction != clang::frontend::EmitObj)
auto actionKind = ci->getFrontendOpts().ProgramAction;
switch (actionKind)
{
error = "instrumentation only supports object/binary output";
case clang::frontend::EmitObj:
case clang::frontend::EmitLLVM:
case clang::frontend::EmitBC:
break;
default:
error = "instrumentation only supports object or LLVM IR/bitcode output";
return false;
}

Expand Down Expand Up @@ -544,15 +469,28 @@ namespace compilerlib
}
emitRuntimeConfigGlobals(*module, ctx_.runtimeConfig);

const char* outputObj = findArgValue(ccArgs, "-o");
if (!outputObj)
const char* outputPath = findArgValue(ccArgs, "-o");
if (!outputPath)
{
error = "unable to determine output object file";
error = "unable to determine output file";
return false;
}

if (!emitObjectFile(*module, *ci, outputObj, error))
switch (actionKind)
{
case clang::frontend::EmitObj:
if (!emit::emitObjectFile(*module, *ci, outputPath, error))
return false;
break;
case clang::frontend::EmitLLVM:
if (!emit::emitLLVMIRFile(*module, outputPath, error))
return false;
break;
case clang::frontend::EmitBC:
if (!emit::emitBitcodeFile(*module, outputPath, error))
return false;
break;
default:
error = "instrumentation only supports object or LLVM IR/bitcode output";
return false;
}

Expand Down
159 changes: 159 additions & 0 deletions src/compilerlib/emit/llvm_output.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include "llvm_output.hpp"

#include <clang/Frontend/CompilerInstance.h>

#include <llvm/Bitcode/BitcodeWriter.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/Support/CodeGen.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/TargetParser/Host.h>

#include <memory>
#include <string>

namespace compilerlib::emit
{
namespace
{
CT_NODISCARD llvm::CodeGenOptLevel toCodeGenOptLevel(unsigned level)
{
switch (level)
{
case 0:
return llvm::CodeGenOptLevel::None;
case 1:
return llvm::CodeGenOptLevel::Less;
case 2:
return llvm::CodeGenOptLevel::Default;
case 3:
return llvm::CodeGenOptLevel::Aggressive;
default:
return llvm::CodeGenOptLevel::Default;
}
}

template <typename Writer>
CT_NODISCARD bool writeOutputFile(llvm::StringRef outputPath, std::string& error,
Writer&& writer)
{
std::error_code ec;
llvm::raw_fd_ostream dest(outputPath, ec, llvm::sys::fs::OF_None);
if (ec)
{
error = ec.message();
return false;
}

if (!writer(dest))
{
if (error.empty())
error = "failed to write file";
return false;
}
dest.flush();
if (dest.has_error())
{
error = "failed to write file";
return false;
}
return true;
}

std::string buildTargetFeatures(const clang::CompilerInstance& ci)
{
const auto& targetOpts = ci.getTargetOpts();
std::string features;
for (const auto& feature : targetOpts.FeaturesAsWritten)
{
if (!features.empty())
features += ",";
features += feature;
}
return features;
}

std::unique_ptr<llvm::TargetMachine> createTargetMachine(llvm::Module& module,
const clang::CompilerInstance& ci,
std::string& error)
{
std::string targetTriple = module.getTargetTriple();

if (targetTriple.empty())
targetTriple = llvm::sys::getDefaultTargetTriple();
module.setTargetTriple(targetTriple);

std::string targetError;
const llvm::Target* target =
llvm::TargetRegistry::lookupTarget(targetTriple, targetError);
if (!target)
{
error = targetError;
return nullptr;
}

llvm::TargetOptions options;
auto codegenLevel = toCodeGenOptLevel(ci.getCodeGenOpts().OptimizationLevel);
// For position-independent code (needed for instrumented code and PIE executables),
// explicitly set the relocation model to PIC.
llvm::Reloc::Model relocModel = llvm::Reloc::PIC_;
std::unique_ptr<llvm::TargetMachine> targetMachine(target->createTargetMachine(
targetTriple, ci.getTargetOpts().CPU, buildTargetFeatures(ci), options, relocModel,
std::nullopt, codegenLevel));
if (!targetMachine)
{
error = "failed to create target machine";
return nullptr;
}

module.setDataLayout(targetMachine->createDataLayout());
return targetMachine;
}
} // namespace

bool emitObjectFile(llvm::Module& module, const clang::CompilerInstance& ci,
llvm::StringRef outputPath, std::string& error)
{
std::unique_ptr<llvm::TargetMachine> targetMachine = createTargetMachine(module, ci, error);
if (!targetMachine)
return false;

return writeOutputFile(outputPath, error,
[&](llvm::raw_fd_ostream& dest) -> bool
{
llvm::legacy::PassManager pass;
if (targetMachine->addPassesToEmitFile(
pass, dest, nullptr, llvm::CodeGenFileType::ObjectFile))
{
error = "target does not support object emission";
return false;
}

pass.run(module);
return true;
});
}

bool emitLLVMIRFile(llvm::Module& module, llvm::StringRef outputPath, std::string& error)
{
return writeOutputFile(outputPath, error,
[&](llvm::raw_fd_ostream& dest) -> bool
{
module.print(dest, nullptr);
return !dest.has_error();
});
}

bool emitBitcodeFile(llvm::Module& module, llvm::StringRef outputPath, std::string& error)
{
return writeOutputFile(outputPath, error,
[&](llvm::raw_fd_ostream& dest) -> bool
{
llvm::WriteBitcodeToFile(module, dest);
return !dest.has_error();
});
}
} // namespace compilerlib::emit
27 changes: 27 additions & 0 deletions src/compilerlib/emit/llvm_output.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include "compilerlib/attributes.hpp"

#include <llvm/ADT/StringRef.h>

#include <string>

namespace llvm
{
class Module;
}

namespace clang
{
class CompilerInstance;
}

namespace compilerlib::emit
{
CT_NODISCARD bool emitObjectFile(llvm::Module& module, const clang::CompilerInstance& ci,
llvm::StringRef outputPath, std::string& error);
CT_NODISCARD bool emitLLVMIRFile(llvm::Module& module, llvm::StringRef outputPath,
std::string& error);
CT_NODISCARD bool emitBitcodeFile(llvm::Module& module, llvm::StringRef outputPath,
std::string& error);
} // namespace compilerlib::emit
Loading
Loading