#include "WebAssemblyTargetMachine.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "TargetInfo/WebAssemblyTargetInfo.h"
#include "Utils/WebAssemblyUtilities.h"
#include "WebAssembly.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblyTargetObjectFile.h"
#include "WebAssemblyTargetTransformInfo.h"
#include "llvm/CodeGen/MIRParser/MIParser.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/RegAllocRegistry.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/Function.h"
#include "llvm/InitializePasses.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/LowerAtomicPass.h"
#include "llvm/Transforms/Utils.h"
using namespace llvm;
#define DEBUG_TYPE "wasm"
static cl::opt<bool> WasmDisableExplicitLocals(
"wasm-disable-explicit-locals", cl::Hidden,
cl::desc("WebAssembly: output implicit locals in"
" instruction output for test purposes only."),
cl::init(false));
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
RegisterTargetMachine<WebAssemblyTargetMachine> X(
getTheWebAssemblyTarget32());
RegisterTargetMachine<WebAssemblyTargetMachine> Y(
getTheWebAssemblyTarget64());
auto &PR = *PassRegistry::getPassRegistry();
initializeWebAssemblyAddMissingPrototypesPass(PR);
initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR);
initializeLowerGlobalDtorsLegacyPassPass(PR);
initializeFixFunctionBitcastsPass(PR);
initializeOptimizeReturnedPass(PR);
initializeWebAssemblyArgumentMovePass(PR);
initializeWebAssemblySetP2AlignOperandsPass(PR);
initializeWebAssemblyReplacePhysRegsPass(PR);
initializeWebAssemblyOptimizeLiveIntervalsPass(PR);
initializeWebAssemblyMemIntrinsicResultsPass(PR);
initializeWebAssemblyRegStackifyPass(PR);
initializeWebAssemblyRegColoringPass(PR);
initializeWebAssemblyNullifyDebugValueListsPass(PR);
initializeWebAssemblyFixIrreducibleControlFlowPass(PR);
initializeWebAssemblyLateEHPreparePass(PR);
initializeWebAssemblyExceptionInfoPass(PR);
initializeWebAssemblyCFGSortPass(PR);
initializeWebAssemblyCFGStackifyPass(PR);
initializeWebAssemblyExplicitLocalsPass(PR);
initializeWebAssemblyLowerBrUnlessPass(PR);
initializeWebAssemblyRegNumberingPass(PR);
initializeWebAssemblyDebugFixupPass(PR);
initializeWebAssemblyPeepholePass(PR);
initializeWebAssemblyMCLowerPrePassPass(PR);
}
static Reloc::Model getEffectiveRelocModel(Optional<Reloc::Model> RM,
const Triple &TT) {
if (!RM) {
return Reloc::Static;
}
if (!TT.isOSEmscripten()) {
return Reloc::Static;
}
return *RM;
}
WebAssemblyTargetMachine::WebAssemblyTargetMachine(
const Target &T, const Triple &TT, StringRef CPU, StringRef FS,
const TargetOptions &Options, Optional<Reloc::Model> RM,
Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT)
: LLVMTargetMachine(
T,
TT.isArch64Bit()
? (TT.isOSEmscripten() ? "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-"
"f128:64-n32:64-S128-ni:1:10:20"
: "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-"
"n32:64-S128-ni:1:10:20")
: (TT.isOSEmscripten() ? "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-"
"f128:64-n32:64-S128-ni:1:10:20"
: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-"
"n32:64-S128-ni:1:10:20"),
TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT),
getEffectiveCodeModel(CM, CodeModel::Large), OL),
TLOF(new WebAssemblyTargetObjectFile()) {
this->Options.TrapUnreachable = true;
this->Options.FunctionSections = true;
this->Options.DataSections = true;
this->Options.UniqueSectionNames = true;
initAsmInfo();
}
WebAssemblyTargetMachine::~WebAssemblyTargetMachine() = default;
const WebAssemblySubtarget *WebAssemblyTargetMachine::getSubtargetImpl() const {
return getSubtargetImpl(std::string(getTargetCPU()),
std::string(getTargetFeatureString()));
}
const WebAssemblySubtarget *
WebAssemblyTargetMachine::getSubtargetImpl(std::string CPU,
std::string FS) const {
auto &I = SubtargetMap[CPU + FS];
if (!I) {
I = std::make_unique<WebAssemblySubtarget>(TargetTriple, CPU, FS, *this);
}
return I.get();
}
const WebAssemblySubtarget *
WebAssemblyTargetMachine::getSubtargetImpl(const Function &F) const {
Attribute CPUAttr = F.getFnAttribute("target-cpu");
Attribute FSAttr = F.getFnAttribute("target-features");
std::string CPU =
CPUAttr.isValid() ? CPUAttr.getValueAsString().str() : TargetCPU;
std::string FS =
FSAttr.isValid() ? FSAttr.getValueAsString().str() : TargetFS;
resetTargetOptions(F);
return getSubtargetImpl(CPU, FS);
}
namespace {
class CoalesceFeaturesAndStripAtomics final : public ModulePass {
static char ID;
WebAssemblyTargetMachine *WasmTM;
public:
CoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine *WasmTM)
: ModulePass(ID), WasmTM(WasmTM) {}
bool runOnModule(Module &M) override {
FeatureBitset Features = coalesceFeatures(M);
std::string FeatureStr = getFeatureString(Features);
WasmTM->setTargetFeatureString(FeatureStr);
for (auto &F : M)
replaceFeatures(F, FeatureStr);
bool StrippedAtomics = false;
bool StrippedTLS = false;
if (!Features[WebAssembly::FeatureAtomics]) {
StrippedAtomics = stripAtomics(M);
StrippedTLS = stripThreadLocals(M);
} else if (!Features[WebAssembly::FeatureBulkMemory]) {
StrippedTLS |= stripThreadLocals(M);
}
if (StrippedAtomics && !StrippedTLS)
stripThreadLocals(M);
else if (StrippedTLS && !StrippedAtomics)
stripAtomics(M);
recordFeatures(M, Features, StrippedAtomics || StrippedTLS);
return true;
}
private:
FeatureBitset coalesceFeatures(const Module &M) {
FeatureBitset Features =
WasmTM
->getSubtargetImpl(std::string(WasmTM->getTargetCPU()),
std::string(WasmTM->getTargetFeatureString()))
->getFeatureBits();
for (auto &F : M)
Features |= WasmTM->getSubtargetImpl(F)->getFeatureBits();
return Features;
}
std::string getFeatureString(const FeatureBitset &Features) {
std::string Ret;
for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
if (Features[KV.Value])
Ret += (StringRef("+") + KV.Key + ",").str();
}
return Ret;
}
void replaceFeatures(Function &F, const std::string &Features) {
F.removeFnAttr("target-features");
F.removeFnAttr("target-cpu");
F.addFnAttr("target-features", Features);
}
bool stripAtomics(Module &M) {
bool Stripped = false;
for (auto &F : M) {
for (auto &B : F) {
for (auto &I : B) {
if (I.isAtomic()) {
Stripped = true;
goto done;
}
}
}
}
done:
if (!Stripped)
return false;
LowerAtomicPass Lowerer;
FunctionAnalysisManager FAM;
for (auto &F : M)
Lowerer.run(F, FAM);
return true;
}
bool stripThreadLocals(Module &M) {
bool Stripped = false;
for (auto &GV : M.globals()) {
if (GV.isThreadLocal()) {
Stripped = true;
GV.setThreadLocal(false);
}
}
return Stripped;
}
void recordFeatures(Module &M, const FeatureBitset &Features, bool Stripped) {
for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
if (Features[KV.Value]) {
std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
wasm::WASM_FEATURE_PREFIX_USED);
}
}
if (Stripped) {
M.addModuleFlag(Module::ModFlagBehavior::Error, "wasm-feature-shared-mem",
wasm::WASM_FEATURE_PREFIX_DISALLOWED);
}
}
};
char CoalesceFeaturesAndStripAtomics::ID = 0;
class WebAssemblyPassConfig final : public TargetPassConfig {
public:
WebAssemblyPassConfig(WebAssemblyTargetMachine &TM, PassManagerBase &PM)
: TargetPassConfig(TM, PM) {}
WebAssemblyTargetMachine &getWebAssemblyTargetMachine() const {
return getTM<WebAssemblyTargetMachine>();
}
FunctionPass *createTargetRegisterAllocator(bool) override;
void addIRPasses() override;
void addISelPrepare() override;
bool addInstSelector() override;
void addPostRegAlloc() override;
bool addGCPasses() override { return false; }
void addPreEmitPass() override;
bool addPreISel() override;
bool addRegAssignAndRewriteFast() override { return false; }
bool addRegAssignAndRewriteOptimized() override { return false; }
};
}
TargetTransformInfo
WebAssemblyTargetMachine::getTargetTransformInfo(const Function &F) const {
return TargetTransformInfo(WebAssemblyTTIImpl(this, F));
}
TargetPassConfig *
WebAssemblyTargetMachine::createPassConfig(PassManagerBase &PM) {
return new WebAssemblyPassConfig(*this, PM);
}
FunctionPass *WebAssemblyPassConfig::createTargetRegisterAllocator(bool) {
return nullptr; }
using WebAssembly::WasmEnableEH;
using WebAssembly::WasmEnableEmEH;
using WebAssembly::WasmEnableEmSjLj;
using WebAssembly::WasmEnableSjLj;
static void basicCheckForEHAndSjLj(TargetMachine *TM) {
TM->Options.ExceptionModel = TM->getMCAsmInfo()->getExceptionHandlingType();
if (TM->Options.ExceptionModel != ExceptionHandling::None &&
TM->Options.ExceptionModel != ExceptionHandling::Wasm)
report_fatal_error("-exception-model should be either 'none' or 'wasm'");
if (WasmEnableEmEH && TM->Options.ExceptionModel == ExceptionHandling::Wasm)
report_fatal_error("-exception-model=wasm not allowed with "
"-enable-emscripten-cxx-exceptions");
if (WasmEnableEH && TM->Options.ExceptionModel != ExceptionHandling::Wasm)
report_fatal_error(
"-wasm-enable-eh only allowed with -exception-model=wasm");
if (WasmEnableSjLj && TM->Options.ExceptionModel != ExceptionHandling::Wasm)
report_fatal_error(
"-wasm-enable-sjlj only allowed with -exception-model=wasm");
if ((!WasmEnableEH && !WasmEnableSjLj) &&
TM->Options.ExceptionModel == ExceptionHandling::Wasm)
report_fatal_error(
"-exception-model=wasm only allowed with at least one of "
"-wasm-enable-eh or -wasm-enable-sjj");
if (WasmEnableEmEH && WasmEnableEH)
report_fatal_error(
"-enable-emscripten-cxx-exceptions not allowed with -wasm-enable-eh");
if (WasmEnableEmSjLj && WasmEnableSjLj)
report_fatal_error(
"-enable-emscripten-sjlj not allowed with -wasm-enable-sjlj");
if (WasmEnableEmEH && WasmEnableSjLj)
report_fatal_error(
"-enable-emscripten-cxx-exceptions not allowed with -wasm-enable-sjlj");
}
void WebAssemblyPassConfig::addIRPasses() {
addPass(createWebAssemblyAddMissingPrototypes());
addPass(createLowerGlobalDtorsLegacyPass());
addPass(createWebAssemblyFixFunctionBitcasts());
if (getOptLevel() != CodeGenOpt::None)
addPass(createWebAssemblyOptimizeReturned());
basicCheckForEHAndSjLj(TM);
if (!WasmEnableEmEH && !WasmEnableEH) {
addPass(createLowerInvokePass());
addPass(createUnreachableBlockEliminationPass());
}
if (WasmEnableEmEH || WasmEnableEmSjLj || WasmEnableSjLj)
addPass(createWebAssemblyLowerEmscriptenEHSjLj());
addPass(createIndirectBrExpandPass());
TargetPassConfig::addIRPasses();
}
void WebAssemblyPassConfig::addISelPrepare() {
addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine()));
addPass(createAtomicExpandPass());
TargetPassConfig::addISelPrepare();
}
bool WebAssemblyPassConfig::addInstSelector() {
(void)TargetPassConfig::addInstSelector();
addPass(
createWebAssemblyISelDag(getWebAssemblyTargetMachine(), getOptLevel()));
addPass(createWebAssemblyArgumentMove());
addPass(createWebAssemblySetP2AlignOperands());
addPass(createWebAssemblyFixBrTableDefaults());
return false;
}
void WebAssemblyPassConfig::addPostRegAlloc() {
disablePass(&MachineCopyPropagationID);
disablePass(&PostRAMachineSinkingID);
disablePass(&PostRASchedulerID);
disablePass(&FuncletLayoutID);
disablePass(&StackMapLivenessID);
disablePass(&LiveDebugValuesID);
disablePass(&PatchableFunctionID);
disablePass(&ShrinkWrapID);
disablePass(&MachineBlockPlacementID);
TargetPassConfig::addPostRegAlloc();
}
void WebAssemblyPassConfig::addPreEmitPass() {
TargetPassConfig::addPreEmitPass();
addPass(createWebAssemblyNullifyDebugValueLists());
addPass(createWebAssemblyFixIrreducibleControlFlow());
if (TM->Options.ExceptionModel == ExceptionHandling::Wasm)
addPass(createWebAssemblyLateEHPrepare());
addPass(createWebAssemblyReplacePhysRegs());
if (getOptLevel() != CodeGenOpt::None) {
addPass(createWebAssemblyOptimizeLiveIntervals());
addPass(createWebAssemblyMemIntrinsicResults());
addPass(createWebAssemblyRegStackify());
addPass(createWebAssemblyRegColoring());
}
addPass(createWebAssemblyCFGSort());
addPass(createWebAssemblyCFGStackify());
if (!WasmDisableExplicitLocals)
addPass(createWebAssemblyExplicitLocals());
addPass(createWebAssemblyLowerBrUnless());
if (getOptLevel() != CodeGenOpt::None)
addPass(createWebAssemblyPeephole());
addPass(createWebAssemblyRegNumbering());
if (!WasmDisableExplicitLocals)
addPass(createWebAssemblyDebugFixup());
addPass(createWebAssemblyMCLowerPrePass());
}
bool WebAssemblyPassConfig::addPreISel() {
TargetPassConfig::addPreISel();
addPass(createWebAssemblyLowerRefTypesIntPtrConv());
return false;
}
yaml::MachineFunctionInfo *
WebAssemblyTargetMachine::createDefaultFuncInfoYAML() const {
return new yaml::WebAssemblyFunctionInfo();
}
yaml::MachineFunctionInfo *WebAssemblyTargetMachine::convertFuncInfoToYAML(
const MachineFunction &MF) const {
const auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
return new yaml::WebAssemblyFunctionInfo(*MFI);
}
bool WebAssemblyTargetMachine::parseMachineFunctionInfo(
const yaml::MachineFunctionInfo &MFI, PerFunctionMIParsingState &PFS,
SMDiagnostic &Error, SMRange &SourceRange) const {
const auto &YamlMFI = static_cast<const yaml::WebAssemblyFunctionInfo &>(MFI);
MachineFunction &MF = PFS.MF;
MF.getInfo<WebAssemblyFunctionInfo>()->initializeBaseYamlFields(YamlMFI);
return false;
}