blob: c53072764bc82bd51239ccf3423a40135b394d6c [file] [edit]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "WebAssembly.h"
#include "WebAssemblyAsmPrinter.h"
#include "WebAssemblyExceptionInfo.h"
#include "WebAssemblyTargetMachine.h"
#include "llvm/CodeGen/AtomicExpand.h"
#include "llvm/CodeGen/FuncletLayout.h"
#include "llvm/CodeGen/GlobalISel/IRTranslator.h"
#include "llvm/CodeGen/GlobalISel/InstructionSelect.h"
#include "llvm/CodeGen/GlobalISel/Legalizer.h"
#include "llvm/CodeGen/GlobalISel/RegBankSelect.h"
#include "llvm/CodeGen/IndirectBrExpand.h"
#include "llvm/CodeGen/MachineBlockPlacement.h"
#include "llvm/CodeGen/MachineCopyPropagation.h"
#include "llvm/CodeGen/MachineLateInstrsCleanup.h"
#include "llvm/CodeGen/PatchableFunction.h"
#include "llvm/CodeGen/PostRAMachineSink.h"
#include "llvm/CodeGen/PostRASchedulerList.h"
#include "llvm/CodeGen/RegisterCoalescerPass.h"
#include "llvm/CodeGen/RemoveLoadsIntoFakeUses.h"
#include "llvm/CodeGen/ShrinkWrap.h"
#include "llvm/CodeGen/UnreachableBlockElim.h"
#include "llvm/IR/PassInstrumentation.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Passes/CodeGenPassBuilder.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Error.h"
#include "llvm/Target/CGPassBuilderOption.h"
#include "llvm/Transforms/Utils/LowerGlobalDtors.h"
#include "llvm/Transforms/Utils/LowerInvoke.h"
using namespace llvm;
namespace WebAssembly {
extern cl::opt<bool> WasmDisableExplicitLocals;
extern cl::opt<bool> WasmEnableEH;
extern cl::opt<bool> WasmEnableEmSjLj;
extern cl::opt<bool> WasmEnableSjLj;
} // namespace WebAssembly
using llvm::WebAssembly::WasmDisableExplicitLocals;
using llvm::WebAssembly::WasmEnableEH;
using llvm::WebAssembly::WasmEnableEmSjLj;
using llvm::WebAssembly::WasmEnableSjLj;
namespace {
class WebAssemblyCodeGenPassBuilder : public CodeGenPassBuilder {
using Base = CodeGenPassBuilder;
WebAssemblyTargetMachine &getTM() const {
return static_cast<WebAssemblyTargetMachine &>(TM);
}
public:
explicit WebAssemblyCodeGenPassBuilder(WebAssemblyTargetMachine &TM,
const CGPassBuilderOption &Opts,
PassInstrumentationCallbacks *PIC)
: CodeGenPassBuilder(TM, Opts, PIC) {
disablePass<MachineLateInstrsCleanupPass, MachineCopyPropagationPass,
PostRAMachineSinkingPass, PostRASchedulerPass,
FuncletLayoutPass, StackMapLivenessPass, PatchableFunctionPass,
ShrinkWrapPass, RemoveLoadsIntoFakeUsesPass,
MachineBlockPlacementPass>();
// Currently RegisterCoalesce degrades wasm debug info quality by a
// significant margin. As a quick fix, disable this for -O1, which is often
// used for debugging large applications. Disabling this increases code size
// of Emscripten core benchmarks by ~5%, which is acceptable for -O1, which
// is usually not used for production builds.
// TODO Investigate why RegisterCoalesce degrades debug info quality and fix
// it properly
if (getOptLevel() == CodeGenOptLevel::Less)
disablePass<RegisterCoalescerPass>();
}
void addIRPasses(PassManagerWrapper &PMW) override;
void addISelPrepare(PassManagerWrapper &PMW) override;
Error addInstSelector(PassManagerWrapper &PMW) override;
Error addIRTranslator(PassManagerWrapper &PMW) override;
void addPreLegalizeMachineIR(PassManagerWrapper &PMW) override;
Error addLegalizeMachineIR(PassManagerWrapper &PMW) override;
void addPreRegBankSelect(PassManagerWrapper &PMW) override;
Error addRegBankSelect(PassManagerWrapper &PMW) override;
Error addGlobalInstructionSelect(PassManagerWrapper &PMW) override;
Error addRegAssignAndRewriteFast(PassManagerWrapper &PMW) override;
Expected<bool>
addRegAssignAndRewriteOptimized(PassManagerWrapper &PMW) override;
void addPreEmitPass(PassManagerWrapper &PMW) override;
void addAsmPrinterBegin(PassManagerWrapper &PMW) override;
void addAsmPrinter(PassManagerWrapper &PMW) override;
void addAsmPrinterEnd(PassManagerWrapper &PMW) override;
};
void WebAssemblyCodeGenPassBuilder::addIRPasses(PassManagerWrapper &PMW) {
// Add signatures to prototype-less function declarations
flushFPMsToMPM(PMW);
addModulePass(WebAssemblyAddMissingPrototypesPass(), PMW);
// Lower .llvm.global_dtors into .llvm.global_ctors with __cxa_atexit calls.
addModulePass(LowerGlobalDtorsPass(), PMW);
// Fix function bitcasts, as WebAssembly requires caller and callee signatures
// to match.
addModulePass(WebAssemblyFixFunctionBitcastsPass(), PMW);
// Optimize "returned" function attributes.
if (getOptLevel() != CodeGenOptLevel::None)
addFunctionPass(WebAssemblyOptimizeReturnedPass(), PMW);
// If exception handling is not enabled and setjmp/longjmp handling is
// enabled, we lower invokes into calls and delete unreachable landingpad
// blocks. Lowering invokes when there is no EH support is done in
// TargetPassConfig::addPassesToHandleExceptions, but that runs after these IR
// passes and Emscripten SjLj handling expects all invokes to be lowered
// before.
bool EnableEmEH = TM.Options.ExceptionModel == ExceptionHandling::Emscripten;
if (!EnableEmEH && !WasmEnableEH) {
addFunctionPass(LowerInvokePass(), PMW);
// The lower invoke pass may create unreachable code. Remove it in order not
// to process dead blocks in setjmp/longjmp handling.
addFunctionPass(UnreachableBlockElimPass(), PMW);
}
// Handle exceptions and setjmp/longjmp if enabled. Unlike Wasm EH preparation
// done in WasmEHPrepare pass, Wasm SjLj preparation shares libraries and
// transformation algorithms with Emscripten SjLj, so we run
// LowerEmscriptenEHSjLj pass also when Wasm SjLj is enabled.
if (EnableEmEH || WasmEnableEmSjLj || WasmEnableSjLj) {
flushFPMsToMPM(PMW);
addModulePass(WebAssemblyLowerEmscriptenEHSjLjPass(EnableEmEH), PMW);
}
// Expand indirectbr instructions to switches.
addFunctionPass(IndirectBrExpandPass(TM), PMW);
// Try to expand `vecreduce_{and, or}` into `{any, all}_true`.
addFunctionPass(WebAssemblyReduceToAnyAllTruePass(getTM()), PMW);
Base::addIRPasses(PMW);
}
void WebAssemblyCodeGenPassBuilder::addISelPrepare(PassManagerWrapper &PMW) {
// We need to move reference type allocas to WASM_ADDRESS_SPACE_VAR so that
// loads and stores are promoted to local.gets/local.sets.
addFunctionPass(WebAssemblyRefTypeMem2LocalPass(), PMW);
// Lower atomics and TLS if necessary
flushFPMsToMPM(PMW);
addModulePass(WebAssemblyCoalesceFeaturesAndStripAtomicsPass(getTM()), PMW);
// This is a no-op if atomics are not used in the module
addFunctionPass(AtomicExpandPass(TM), PMW);
Base::addISelPrepare(PMW);
}
Error WebAssemblyCodeGenPassBuilder::addInstSelector(PassManagerWrapper &PMW) {
addMachineFunctionPass(WebAssemblyISelDAGToDAGPass(getTM(), getOptLevel()),
PMW);
// Run the argument-move pass immediately after the ScheduleDAG scheduler
// so that we can fix up the ARGUMENT instructions before anything else
// sees them in the wrong place.
addMachineFunctionPass(WebAssemblyArgumentMovePass(), PMW);
// Set the p2align operands. This information is present during ISel, however
// it's inconvenient to collect. Collect it now, and update the immediate
// operands.
addMachineFunctionPass(WebAssemblySetP2AlignOperandsPass(), PMW);
// Eliminate range checks and add default targets to br_table instructions.
addMachineFunctionPass(WebAssemblyFixBrTableDefaultsPass(), PMW);
// unreachable is terminator, non-terminator instruction after it is not
// allowed.
addMachineFunctionPass(WebAssemblyCleanCodeAfterTrapPass(), PMW);
return Error::success();
}
Error WebAssemblyCodeGenPassBuilder::addIRTranslator(PassManagerWrapper &PMW) {
addMachineFunctionPass(IRTranslatorPass(getOptLevel()), PMW);
return Error::success();
}
void WebAssemblyCodeGenPassBuilder::addPreLegalizeMachineIR(
PassManagerWrapper &PMW) {
if (getOptLevel() != CodeGenOptLevel::None)
addMachineFunctionPass(WebAssemblyPreLegalizerCombinerPass(), PMW);
}
Error WebAssemblyCodeGenPassBuilder::addLegalizeMachineIR(
PassManagerWrapper &PMW) {
addMachineFunctionPass(LegalizerPass(), PMW);
return Error::success();
}
void WebAssemblyCodeGenPassBuilder::addPreRegBankSelect(
PassManagerWrapper &PMW) {
if (getOptLevel() != CodeGenOptLevel::None)
addMachineFunctionPass(WebAssemblyPostLegalizerCombinerPass(), PMW);
}
Error WebAssemblyCodeGenPassBuilder::addRegBankSelect(PassManagerWrapper &PMW) {
addMachineFunctionPass(RegBankSelectPass(), PMW);
return Error::success();
}
Error WebAssemblyCodeGenPassBuilder::addGlobalInstructionSelect(
PassManagerWrapper &PMW) {
addMachineFunctionPass(InstructionSelectPass(getOptLevel()), PMW);
if (isGlobalISelAbortEnabled()) {
addMachineFunctionPass(WebAssemblyArgumentMovePass(), PMW);
addMachineFunctionPass(WebAssemblySetP2AlignOperandsPass(), PMW);
addMachineFunctionPass(WebAssemblyFixBrTableDefaultsPass(), PMW);
addMachineFunctionPass(WebAssemblyCleanCodeAfterTrapPass(), PMW);
}
return Error::success();
}
Error WebAssemblyCodeGenPassBuilder::addRegAssignAndRewriteFast(
PassManagerWrapper &PMW) {
return Error::success();
}
Expected<bool> WebAssemblyCodeGenPassBuilder::addRegAssignAndRewriteOptimized(
PassManagerWrapper &PMW) {
return false;
}
void WebAssemblyCodeGenPassBuilder::addPreEmitPass(PassManagerWrapper &PMW) {
Base::addPreEmitPass(PMW);
// Nullify DBG_VALUE_LISTs that we cannot handle.
addMachineFunctionPass(WebAssemblyNullifyDebugValueListsPass(), PMW);
// Remove any unreachable blocks that may be left floating around.
// Rare, but possible. Needed for WebAssemblyFixIrreducibleControlFlow.
addMachineFunctionPass(UnreachableMachineBlockElimPass(), PMW);
// Eliminate multiple-entry loops.
addMachineFunctionPass(WebAssemblyFixIrreducibleControlFlowPass(), PMW);
// Do various transformations for exception handling.
// Every CFG-changing optimizations should come before this.
if (TM.Options.ExceptionModel == ExceptionHandling::Wasm)
addMachineFunctionPass(WebAssemblyLateEHPreparePass(), PMW);
// Now that we have a prologue and epilogue and all frame indices are
// rewritten, eliminate SP and FP. This allows them to be stackified,
// colored, and numbered with the rest of the registers.
addMachineFunctionPass(WebAssemblyReplacePhysRegsPass(), PMW);
// Preparations and optimizations related to register stackification.
if (getOptLevel() != CodeGenOptLevel::None) {
// Depend on LiveIntervals and perform some optimizations on it.
addMachineFunctionPass(WebAssemblyOptimizeLiveIntervalsPass(), PMW);
// Prepare memory intrinsic calls for register stackifying.
addMachineFunctionPass(WebAssemblyMemIntrinsicResultsPass(), PMW);
}
// Mark registers as representing wasm's value stack. This is a key
// code-compression technique in WebAssembly. We run this pass (and
// MemIntrinsicResults above) very late, so that it sees as much code as
// possible, including code emitted by PEI and expanded by late tail
// duplication.
addMachineFunctionPass(WebAssemblyRegStackifyPass(getOptLevel()), PMW);
if (getOptLevel() != CodeGenOptLevel::None) {
// Run the register coloring pass to reduce the total number of registers.
// This runs after stackification so that it doesn't consider registers
// that become stackified.
addMachineFunctionPass(WebAssemblyRegColoringPass(), PMW);
}
// Sort the blocks of the CFG into topological order, a prerequisite for
// BLOCK and LOOP markers.
addMachineFunctionPass(WebAssemblyCFGSortPass(), PMW);
// Insert BLOCK and LOOP markers.
addMachineFunctionPass(WebAssemblyCFGStackifyPass(), PMW);
// Insert explicit local.get and local.set operators.
if (!WasmDisableExplicitLocals)
addMachineFunctionPass(WebAssemblyExplicitLocalsPass(), PMW);
// Lower br_unless into br_if.
addMachineFunctionPass(WebAssemblyLowerBrUnlessPass(), PMW);
// Perform the very last peephole optimizations on the code.
if (getOptLevel() != CodeGenOptLevel::None)
addMachineFunctionPass(WebAssemblyPeepholePass(), PMW);
// Create a mapping from LLVM CodeGen virtual registers to wasm registers.
addMachineFunctionPass(WebAssemblyRegNumberingPass(), PMW);
// Fix debug_values whose defs have been stackified.
if (!WasmDisableExplicitLocals)
addMachineFunctionPass(WebAssemblyDebugFixupPass(), PMW);
// Collect information to prepare for MC lowering / asm printing.
flushFPMsToMPM(PMW);
addModulePass(WebAssemblyMCLowerPrePass(), PMW);
}
void WebAssemblyCodeGenPassBuilder::addAsmPrinterBegin(
PassManagerWrapper &PMW) {
addModulePass(WebAssemblyAsmPrinterBeginPass(), PMW, /*Force=*/true);
}
void WebAssemblyCodeGenPassBuilder::addAsmPrinter(PassManagerWrapper &PMW) {
addMachineFunctionPass(WebAssemblyAsmPrinterPass(), PMW);
}
void WebAssemblyCodeGenPassBuilder::addAsmPrinterEnd(PassManagerWrapper &PMW) {
addModulePass(WebAssemblyAsmPrinterEndPass(), PMW);
}
} // namespace
void WebAssemblyTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB){
#define GET_PASS_REGISTRY "WebAssemblyPassRegistry.def"
#include "llvm/Passes/TargetPassRegistry.inc"
}
Error WebAssemblyTargetMachine::buildCodeGenPipeline(
ModulePassManager &MPM, ModuleAnalysisManager &MAM, raw_pwrite_stream &Out,
raw_pwrite_stream *DwoOut, CodeGenFileType FileType,
const CGPassBuilderOption &Opt, MCContext &Ctx,
PassInstrumentationCallbacks *PIC) {
auto CGPB = WebAssemblyCodeGenPassBuilder(*this, Opt, PIC);
return CGPB.buildPipeline(MPM, MAM, Out, DwoOut, FileType, Ctx);
}