#include "WebAssembly.h"
#include "CommonArgs.h"
#include "Gnu.h"
#include "clang/Basic/Version.h"
#include "clang/Config/config.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
using namespace clang::driver;
using namespace clang::driver::tools;
using namespace clang::driver::toolchains;
using namespace clang;
using namespace llvm::opt;
std::string WebAssembly::getMultiarchTriple(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef SysRoot) const {
return (TargetTriple.getArchName() + "-" +
TargetTriple.getOSAndEnvironmentName()).str();
}
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
StringRef UseLinker = A->getValue();
if (!UseLinker.empty()) {
if (llvm::sys::path::is_absolute(UseLinker) &&
llvm::sys::fs::can_execute(UseLinker))
return std::string(UseLinker);
if (UseLinker != "lld" && UseLinker != "ld")
ToolChain.getDriver().Diag(diag::err_drv_invalid_linker_name)
<< A->getAsString(Args);
}
}
return ToolChain.GetProgramPath(ToolChain.getDefaultLinker());
}
void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
const ToolChain &ToolChain = getToolChain();
const char *Linker = Args.MakeArgString(getLinkerPath(Args));
ArgStringList CmdArgs;
CmdArgs.push_back("-m");
if (ToolChain.getTriple().isArch64Bit())
CmdArgs.push_back("wasm64");
else
CmdArgs.push_back("wasm32");
if (Args.hasArg(options::OPT_s))
CmdArgs.push_back("--strip-all");
Args.AddAllArgs(CmdArgs, options::OPT_L);
Args.AddAllArgs(CmdArgs, options::OPT_u);
ToolChain.AddFilePathLibArgs(Args, CmdArgs);
const char *Crt1 = "crt1.o";
const char *Entry = nullptr;
if (ToolChain.GetFilePath("crt1-command.o") != "crt1-command.o")
Crt1 = "crt1-command.o";
if (const Arg *A = Args.getLastArg(options::OPT_mexec_model_EQ)) {
StringRef CM = A->getValue();
if (CM == "command") {
} else if (CM == "reactor") {
Crt1 = "crt1-reactor.o";
Entry = "_initialize";
} else {
ToolChain.getDriver().Diag(diag::err_drv_invalid_argument_to_option)
<< CM << A->getOption().getName();
}
}
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles))
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(Crt1)));
if (Entry) {
CmdArgs.push_back(Args.MakeArgString("--entry"));
CmdArgs.push_back(Args.MakeArgString(Entry));
}
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
if (ToolChain.ShouldLinkCXXStdlib(Args))
ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
if (Args.hasArg(options::OPT_pthread)) {
CmdArgs.push_back("-lpthread");
CmdArgs.push_back("--shared-memory");
}
CmdArgs.push_back("-lc");
AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args);
}
CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileCurCP(),
Linker, CmdArgs, Inputs, Output));
if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
auto WasmOptPath = ToolChain.GetProgramPath("wasm-opt");
if (WasmOptPath != "wasm-opt") {
StringRef OOpt = "s";
if (A->getOption().matches(options::OPT_O4) ||
A->getOption().matches(options::OPT_Ofast))
OOpt = "4";
else if (A->getOption().matches(options::OPT_O0))
OOpt = "0";
else if (A->getOption().matches(options::OPT_O))
OOpt = A->getValue();
if (OOpt != "0") {
const char *WasmOpt = Args.MakeArgString(WasmOptPath);
ArgStringList CmdArgs;
CmdArgs.push_back(Output.getFilename());
CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-O") + OOpt));
CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
C.addCommand(std::make_unique<Command>(
JA, *this, ResponseFileSupport::AtFileCurCP(), WasmOpt, CmdArgs,
Inputs, Output));
}
}
}
}
static std::string AppendLTOLibDir(const std::string &Dir) {
return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
const llvm::opt::ArgList &Args)
: ToolChain(D, Triple, Args) {
assert(Triple.isArch32Bit() != Triple.isArch64Bit());
getProgramPaths().push_back(getDriver().getInstalledDir());
auto SysRoot = getDriver().SysRoot;
if (getTriple().getOS() == llvm::Triple::UnknownOS) {
getFilePaths().push_back(SysRoot + "/lib");
} else {
const std::string MultiarchTriple =
getMultiarchTriple(getDriver(), Triple, SysRoot);
if (D.isUsingLTO()) {
auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple);
getFilePaths().push_back(Dir);
}
getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple);
}
}
bool WebAssembly::IsMathErrnoDefault() const { return false; }
bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; }
bool WebAssembly::UseObjCMixedDispatch() const { return true; }
bool WebAssembly::isPICDefault() const { return false; }
bool WebAssembly::isPIEDefault(const llvm::opt::ArgList &Args) const {
return false;
}
bool WebAssembly::isPICDefaultForced() const { return false; }
bool WebAssembly::IsIntegratedAssemblerDefault() const { return true; }
bool WebAssembly::hasBlocksRuntime() const { return false; }
bool WebAssembly::SupportsProfiling() const { return false; }
bool WebAssembly::HasNativeLLVMSupport() const { return true; }
void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
ArgStringList &CC1Args,
Action::OffloadKind) const {
if (!DriverArgs.hasFlag(clang::driver::options::OPT_fuse_init_array,
options::OPT_fno_use_init_array, true))
CC1Args.push_back("-fno-use-init-array");
if (DriverArgs.hasFlag(options::OPT_pthread, options::OPT_no_pthread,
false)) {
if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-pthread"
<< "-mno-atomics";
if (DriverArgs.hasFlag(options::OPT_mno_bulk_memory,
options::OPT_mbulk_memory, false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-pthread"
<< "-mno-bulk-memory";
if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals,
options::OPT_mmutable_globals, false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-pthread"
<< "-mno-mutable-globals";
if (DriverArgs.hasFlag(options::OPT_mno_sign_ext, options::OPT_msign_ext,
false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-pthread"
<< "-mno-sign-ext";
CC1Args.push_back("-target-feature");
CC1Args.push_back("+atomics");
CC1Args.push_back("-target-feature");
CC1Args.push_back("+bulk-memory");
CC1Args.push_back("-target-feature");
CC1Args.push_back("+mutable-globals");
CC1Args.push_back("-target-feature");
CC1Args.push_back("+sign-ext");
}
if (!DriverArgs.hasFlag(options::OPT_mmutable_globals,
options::OPT_mno_mutable_globals, false)) {
llvm::Reloc::Model RelocationModel;
unsigned PICLevel;
bool IsPIE;
std::tie(RelocationModel, PICLevel, IsPIE) =
ParsePICArgs(*this, DriverArgs);
if (RelocationModel == llvm::Reloc::PIC_) {
if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals,
options::OPT_mmutable_globals, false)) {
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-fPIC"
<< "-mno-mutable-globals";
}
CC1Args.push_back("-target-feature");
CC1Args.push_back("+mutable-globals");
}
}
if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions)) {
if (DriverArgs.hasFlag(options::OPT_mno_exception_handing,
options::OPT_mexception_handing, false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-fwasm-exceptions"
<< "-mno-exception-handling";
for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions")
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-fwasm-exceptions"
<< "-mllvm -enable-emscripten-cxx-exceptions";
}
CC1Args.push_back("-target-feature");
CC1Args.push_back("+exception-handling");
CC1Args.push_back("-mllvm");
CC1Args.push_back("-wasm-enable-eh");
}
for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
StringRef Opt = A->getValue(0);
if (Opt.startswith("-emscripten-cxx-exceptions-allowed")) {
bool EmEHArgExists = false;
for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions") {
EmEHArgExists = true;
break;
}
}
if (!EmEHArgExists)
getDriver().Diag(diag::err_drv_argument_only_allowed_with)
<< "-mllvm -emscripten-cxx-exceptions-allowed"
<< "-mllvm -enable-emscripten-cxx-exceptions";
StringRef FuncNamesStr = Opt.split('=').second;
SmallVector<StringRef, 4> FuncNames;
FuncNamesStr.split(FuncNames, ',');
for (auto Name : FuncNames) {
CC1Args.push_back("-mllvm");
CC1Args.push_back(DriverArgs.MakeArgString("--force-attribute=" + Name +
":noinline"));
}
}
if (Opt.startswith("-wasm-enable-sjlj")) {
if (DriverArgs.hasFlag(options::OPT_mno_exception_handing,
options::OPT_mexception_handing, false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-mllvm -wasm-enable-sjlj"
<< "-mno-exception-handling";
for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions")
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-mllvm -wasm-enable-sjlj"
<< "-mllvm -enable-emscripten-cxx-exceptions";
}
for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
if (StringRef(A->getValue(0)) == "-enable-emscripten-sjlj")
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-mllvm -wasm-enable-sjlj"
<< "-mllvm -enable-emscripten-sjlj";
}
CC1Args.push_back("-target-feature");
CC1Args.push_back("+exception-handling");
CC1Args.push_back("-exception-model=wasm");
}
}
}
ToolChain::RuntimeLibType WebAssembly::GetDefaultRuntimeLibType() const {
return ToolChain::RLT_CompilerRT;
}
ToolChain::CXXStdlibType
WebAssembly::GetCXXStdlibType(const ArgList &Args) const {
if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) {
StringRef Value = A->getValue();
if (Value == "libc++")
return ToolChain::CST_Libcxx;
else if (Value == "libstdc++")
return ToolChain::CST_Libstdcxx;
else
getDriver().Diag(diag::err_drv_invalid_stdlib_name)
<< A->getAsString(Args);
}
return ToolChain::CST_Libcxx;
}
void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(clang::driver::options::OPT_nostdinc))
return;
const Driver &D = getDriver();
if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
SmallString<128> P(D.ResourceDir);
llvm::sys::path::append(P, "include");
addSystemInclude(DriverArgs, CC1Args, P);
}
if (DriverArgs.hasArg(options::OPT_nostdlibinc))
return;
StringRef CIncludeDirs(C_INCLUDE_DIRS);
if (CIncludeDirs != "") {
SmallVector<StringRef, 5> dirs;
CIncludeDirs.split(dirs, ":");
for (StringRef dir : dirs) {
StringRef Prefix =
llvm::sys::path::is_absolute(dir) ? "" : StringRef(D.SysRoot);
addExternCSystemInclude(DriverArgs, CC1Args, Prefix + dir);
}
return;
}
if (getTriple().getOS() != llvm::Triple::UnknownOS) {
const std::string MultiarchTriple =
getMultiarchTriple(D, getTriple(), D.SysRoot);
addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple);
}
addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include");
}
void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_nostdlibinc) ||
DriverArgs.hasArg(options::OPT_nostdincxx))
return;
switch (GetCXXStdlibType(DriverArgs)) {
case ToolChain::CST_Libcxx:
addLibCxxIncludePaths(DriverArgs, CC1Args);
break;
case ToolChain::CST_Libstdcxx:
addLibStdCXXIncludePaths(DriverArgs, CC1Args);
break;
}
}
void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const {
switch (GetCXXStdlibType(Args)) {
case ToolChain::CST_Libcxx:
CmdArgs.push_back("-lc++");
if (Args.hasArg(options::OPT_fexperimental_library))
CmdArgs.push_back("-lc++experimental");
CmdArgs.push_back("-lc++abi");
break;
case ToolChain::CST_Libstdcxx:
CmdArgs.push_back("-lstdc++");
break;
}
}
SanitizerMask WebAssembly::getSupportedSanitizers() const {
SanitizerMask Res = ToolChain::getSupportedSanitizers();
if (getTriple().isOSEmscripten()) {
Res |= SanitizerKind::Vptr | SanitizerKind::Leak | SanitizerKind::Address;
}
return Res;
}
Tool *WebAssembly::buildLinker() const {
return new tools::wasm::Linker(*this);
}
void WebAssembly::addLibCxxIncludePaths(
const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const {
const Driver &D = getDriver();
std::string SysRoot = computeSysRoot();
std::string LibPath = SysRoot + "/include";
const std::string MultiarchTriple =
getMultiarchTriple(D, getTriple(), SysRoot);
bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS);
std::string Version = detectLibcxxVersion(LibPath);
if (Version.empty())
return;
if (IsKnownOs) {
std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version;
addSystemInclude(DriverArgs, CC1Args, TargetDir);
}
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
}
void WebAssembly::addLibStdCXXIncludePaths(
const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const {
const Driver &D = getDriver();
std::string SysRoot = computeSysRoot();
std::string LibPath = SysRoot + "/include";
const std::string MultiarchTriple =
getMultiarchTriple(D, getTriple(), SysRoot);
bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS);
std::string Version;
{
std::error_code EC;
Generic_GCC::GCCVersion MaxVersion =
Generic_GCC::GCCVersion::Parse("0.0.0");
SmallString<128> Path(LibPath);
llvm::sys::path::append(Path, "c++");
for (llvm::vfs::directory_iterator LI = getVFS().dir_begin(Path, EC), LE;
!EC && LI != LE; LI = LI.increment(EC)) {
StringRef VersionText = llvm::sys::path::filename(LI->path());
if (VersionText[0] != 'v') {
auto Version = Generic_GCC::GCCVersion::Parse(VersionText);
if (Version > MaxVersion)
MaxVersion = Version;
}
}
if (MaxVersion.Major > 0)
Version = MaxVersion.Text;
}
if (Version.empty())
return;
if (IsKnownOs) {
std::string TargetDir = LibPath + "/c++/" + Version + "/" + MultiarchTriple;
addSystemInclude(DriverArgs, CC1Args, TargetDir);
}
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version + "/backward");
}