FixIrreducibleControlFlow pass adds dispatch blocks with a `br_table` that has multiple predecessors and successors, because it serves as something like a traffic hub for BBs. As a result of this, there can be register uses that are not dominated by a def in every path from the entry block. For example, suppose register %a is defined in BB1 and used in BB2, and there is a single path from BB1 and BB2: ``` BB1 -> ... -> BB2 ``` After FixIrreducibleControlFlow runs, there can be a dispatch block between these two BBs: ``` BB1 -> ... -> Dispatch -> ... -> BB2 ``` And this dispatch block has multiple predecessors, now there is a path to BB2 that does not first visit BB1, and in that path %a is not dominated by a def anymore. To fix this problem, we have been adding `IMPLICIT_DEF`s to all registers in PrepareForLiveInternals pass, and then remove unnecessary ones in OptimizeLiveIntervals pass after computing `LiveIntervals`. But FixIrreducibleControlFlow pass itself ends up violating register use-def relationship, resulting in invalid code. This was OK so far because MIR verifier apparently didn't check this in validation. But @arsenm fixed this and it caught this bug in validation (https://github.com/llvm/llvm-project/issues/55249). This CL moves the `IMPLICIT_DEF` adding routine from PrepareForLiveInternals to FixIrreducibleControlFlow. We only run it when FixIrreducibleControlFlow changes the code. And then PrepareForLiveInternals doesn't do anything other than setting `TracksLiveness` property, which is a prerequisite for running `LiveIntervals` analysis, which is required by the next pass OptimizeLiveIntervals. But in our backend we don't seem to do anything that invalidates this up until OptimizeLiveIntervals, and I'm not sure why we are calling `invalidateLiveness` in ReplacePhysRegs pass, because what that pass does is to replace physical registers with virtual ones 1-to-1. I deleted the `invalidateLiveness` call there and we don't need to set that flag explicitly, which obviates all the need for PrepareForLiveInternals. (By the way, This 'Liveness' here is different from `LiveIntervals` analysis. Setting this only means BBs' live-in info is correct, all uses are dominated by defs, `kill` flag is conservatively correct, which means if there is a `kill` flag set it should be the last use. See https://github.com/llvm/llvm-project/blob/2a0837aab1489c88efb03784e34c4dc9f2e28302/llvm/include/llvm/CodeGen/MachineFunction.h#L125-L134 for details.) So this CL removes PrepareForLiveInternals pass altogether. Something similar to this was attempted by D56091 long ago but that came short of actually removing the pass, and I couldn't land it because FixIrreducibleControlFlow violated use-def relationship, which this CL fixes. This doesn't change output in any meaningful way. All test changes except `irreducible-cfg.mir` are register numbering. Also this will likely to reduce compilation time, because we have been adding `IMPLICIT_DEF` for all registers every time `-O2` is given, but now we do that only when there is irreducible control flow, which is rare. Fixes https://github.com/llvm/llvm-project/issues/55249. Reviewed By: dschuff, kripken Differential Revision: https://reviews.llvm.org/D125515
125 lines
4.7 KiB
C++
125 lines
4.7 KiB
C++
//===--- WebAssemblyOptimizeLiveIntervals.cpp - LiveInterval processing ---===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// Optimize LiveIntervals for use in a post-RA context.
|
|
//
|
|
/// LiveIntervals normally runs before register allocation when the code is
|
|
/// only recently lowered out of SSA form, so it's uncommon for registers to
|
|
/// have multiple defs, and when they do, the defs are usually closely related.
|
|
/// Later, after coalescing, tail duplication, and other optimizations, it's
|
|
/// more common to see registers with multiple unrelated defs. This pass
|
|
/// updates LiveIntervals to distribute the value numbers across separate
|
|
/// LiveIntervals.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "WebAssembly.h"
|
|
#include "WebAssemblyMachineFunctionInfo.h"
|
|
#include "WebAssemblySubtarget.h"
|
|
#include "llvm/CodeGen/LiveIntervals.h"
|
|
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/Passes.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "wasm-optimize-live-intervals"
|
|
|
|
namespace {
|
|
class WebAssemblyOptimizeLiveIntervals final : public MachineFunctionPass {
|
|
StringRef getPassName() const override {
|
|
return "WebAssembly Optimize Live Intervals";
|
|
}
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
AU.setPreservesCFG();
|
|
AU.addRequired<LiveIntervals>();
|
|
AU.addPreserved<MachineBlockFrequencyInfo>();
|
|
AU.addPreserved<SlotIndexes>();
|
|
AU.addPreserved<LiveIntervals>();
|
|
AU.addPreservedID(LiveVariablesID);
|
|
AU.addPreservedID(MachineDominatorsID);
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
MachineFunctionProperties getRequiredProperties() const override {
|
|
return MachineFunctionProperties().set(
|
|
MachineFunctionProperties::Property::TracksLiveness);
|
|
}
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
|
|
public:
|
|
static char ID; // Pass identification, replacement for typeid
|
|
WebAssemblyOptimizeLiveIntervals() : MachineFunctionPass(ID) {}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
char WebAssemblyOptimizeLiveIntervals::ID = 0;
|
|
INITIALIZE_PASS(WebAssemblyOptimizeLiveIntervals, DEBUG_TYPE,
|
|
"Optimize LiveIntervals for WebAssembly", false, false)
|
|
|
|
FunctionPass *llvm::createWebAssemblyOptimizeLiveIntervals() {
|
|
return new WebAssemblyOptimizeLiveIntervals();
|
|
}
|
|
|
|
bool WebAssemblyOptimizeLiveIntervals::runOnMachineFunction(
|
|
MachineFunction &MF) {
|
|
LLVM_DEBUG(dbgs() << "********** Optimize LiveIntervals **********\n"
|
|
"********** Function: "
|
|
<< MF.getName() << '\n');
|
|
|
|
MachineRegisterInfo &MRI = MF.getRegInfo();
|
|
auto &LIS = getAnalysis<LiveIntervals>();
|
|
|
|
// We don't preserve SSA form.
|
|
MRI.leaveSSA();
|
|
|
|
assert(MRI.tracksLiveness() && "OptimizeLiveIntervals expects liveness");
|
|
|
|
// Split multiple-VN LiveIntervals into multiple LiveIntervals.
|
|
SmallVector<LiveInterval *, 4> SplitLIs;
|
|
for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) {
|
|
Register Reg = Register::index2VirtReg(I);
|
|
auto &TRI = *MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
|
|
|
|
if (MRI.reg_nodbg_empty(Reg))
|
|
continue;
|
|
|
|
LIS.splitSeparateComponents(LIS.getInterval(Reg), SplitLIs);
|
|
if (Reg == TRI.getFrameRegister(MF) && SplitLIs.size() > 0) {
|
|
// The live interval for the frame register was split, resulting in a new
|
|
// VReg. For now we only support debug info output for a single frame base
|
|
// value for the function, so just use the last one. It will certainly be
|
|
// wrong for some part of the function, but until we are able to track
|
|
// values through live-range splitting and stackification, it will have to
|
|
// do.
|
|
MF.getInfo<WebAssemblyFunctionInfo>()->setFrameBaseVreg(
|
|
SplitLIs.back()->reg());
|
|
}
|
|
SplitLIs.clear();
|
|
}
|
|
|
|
// In FixIrreducibleControlFlow, we conservatively inserted IMPLICIT_DEF
|
|
// instructions to satisfy LiveIntervals' requirement that all uses be
|
|
// dominated by defs. Now that LiveIntervals has computed which of these
|
|
// defs are actually needed and which are dead, remove the dead ones.
|
|
for (MachineInstr &MI : llvm::make_early_inc_range(MF.front())) {
|
|
if (MI.isImplicitDef() && MI.getOperand(0).isDead()) {
|
|
LiveInterval &LI = LIS.getInterval(MI.getOperand(0).getReg());
|
|
LIS.removeVRegDefAt(LI, LIS.getInstructionIndex(MI).getRegSlot());
|
|
LIS.RemoveMachineInstrFromMaps(MI);
|
|
MI.eraseFromParent();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|