mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 12:43:36 +01:00
5ec2c45efb
When a stackified variable has an associated `DBG_VALUE` instruction, DebugFixup pass adds a `DBG_VALUE` instruction after the stackified value's last use to clear the variable's debug range info. But when the last use instruction is a terminator, it can cause a verification failure (when run with `-verify-machineinstrs`) because there are no instructions allowed after a terminator. For example: ``` %myvar = ... DBG_VALUE target-index(wasm-operand-stack), $noreg, !"myvar", ... BR_IF 0, %myvar, ... DBG_VALUE $noreg, $noreg, !"myvar", ... ``` In this test, `%myvar` is stackified, so the first `DBG_VALUE` instruction's first operand has changed to `wasm-operand-stack` to denote it. And an additional `DBG_VALUE` instruction is added after its last use, `BR_IF`, to signal variable `myvar` is not in the operand stack anymore. But because the `DBG_VALUE` instruction is added after the `BR_IF`, a terminator, it fails MachineVerifier. `DBG_VALUE` instructions are used in `DbgEntityHistoryCalculator` to compute value ranges to emit DWARF info, and it turns out the `DbgEntityHistoryCalculator` terminates ranges at the end of a BB, so we don't need to emit `DBG_VALUE` after a terminator. Fixes https://bugs.llvm.org/show_bug.cgi?id=50175. Reviewed By: dschuff Differential Revision: https://reviews.llvm.org/D102309
142 lines
5.6 KiB
C++
142 lines
5.6 KiB
C++
//===-- WebAssemblyDebugFixup.cpp - Debug Fixup ------------------===//
|
|
//
|
|
// 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
|
|
/// Several prior passes may "stackify" registers, here we ensure any references
|
|
/// in such registers in debug_value instructions become stack relative also.
|
|
/// This is done in a separate pass such that not all previous passes need to
|
|
/// track stack depth when values get stackified.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
|
|
#include "Utils/WebAssemblyUtilities.h"
|
|
#include "WebAssembly.h"
|
|
#include "WebAssemblyMachineFunctionInfo.h"
|
|
#include "WebAssemblySubtarget.h"
|
|
#include "llvm/ADT/SCCIterator.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineLoopInfo.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-debug-fixup"
|
|
|
|
namespace {
|
|
class WebAssemblyDebugFixup final : public MachineFunctionPass {
|
|
StringRef getPassName() const override { return "WebAssembly Debug Fixup"; }
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
AU.setPreservesCFG();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
|
|
public:
|
|
static char ID; // Pass identification, replacement for typeid
|
|
WebAssemblyDebugFixup() : MachineFunctionPass(ID) {}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
char WebAssemblyDebugFixup::ID = 0;
|
|
INITIALIZE_PASS(
|
|
WebAssemblyDebugFixup, DEBUG_TYPE,
|
|
"Ensures debug_value's that have been stackified become stack relative",
|
|
false, false)
|
|
|
|
FunctionPass *llvm::createWebAssemblyDebugFixup() {
|
|
return new WebAssemblyDebugFixup();
|
|
}
|
|
|
|
bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) {
|
|
LLVM_DEBUG(dbgs() << "********** Debug Fixup **********\n"
|
|
"********** Function: "
|
|
<< MF.getName() << '\n');
|
|
|
|
WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
|
|
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
|
|
|
|
struct StackElem {
|
|
unsigned Reg;
|
|
MachineInstr *DebugValue;
|
|
};
|
|
std::vector<StackElem> Stack;
|
|
for (MachineBasicBlock &MBB : MF) {
|
|
// We may insert into this list.
|
|
for (auto MII = MBB.begin(); MII != MBB.end(); ++MII) {
|
|
MachineInstr &MI = *MII;
|
|
if (MI.isDebugValue()) {
|
|
auto &MO = MI.getOperand(0);
|
|
// Also check if not a $noreg: likely a DBG_VALUE we just inserted.
|
|
if (MO.isReg() && MO.getReg().isValid() &&
|
|
MFI.isVRegStackified(MO.getReg())) {
|
|
// Found a DBG_VALUE with a stackified register we will
|
|
// change into a stack operand.
|
|
// Search for register rather than assume it is on top (which it
|
|
// typically is if it appears right after the def), since
|
|
// DBG_VALUE's may shift under some circumstances.
|
|
for (auto &Elem : reverse(Stack)) {
|
|
if (MO.getReg() == Elem.Reg) {
|
|
auto Depth = static_cast<unsigned>(&Elem - &Stack[0]);
|
|
LLVM_DEBUG(dbgs() << "Debug Value VReg " << MO.getReg()
|
|
<< " -> Stack Relative " << Depth << "\n");
|
|
MO.ChangeToTargetIndex(WebAssembly::TI_OPERAND_STACK, Depth);
|
|
// Save the DBG_VALUE instruction that defined this stackified
|
|
// variable since later we need it to construct another one on
|
|
// pop.
|
|
Elem.DebugValue = &MI;
|
|
break;
|
|
}
|
|
}
|
|
// If the Reg was not found, we have a DBG_VALUE outside of its
|
|
// def-use range, and we leave it unmodified as reg, which means
|
|
// it will be culled later.
|
|
}
|
|
} else {
|
|
// Track stack depth.
|
|
for (MachineOperand &MO : reverse(MI.explicit_uses())) {
|
|
if (MO.isReg() && MFI.isVRegStackified(MO.getReg())) {
|
|
auto Prev = Stack.back();
|
|
Stack.pop_back();
|
|
assert(Prev.Reg == MO.getReg() &&
|
|
"WebAssemblyDebugFixup: Pop: Register not matched!");
|
|
// We should not put a DBG_VALUE after a terminator; debug ranges
|
|
// are terminated at the end of a BB anyway.
|
|
if (Prev.DebugValue && !MI.isTerminator()) {
|
|
// This stackified reg is a variable that started life at
|
|
// Prev.DebugValue, so now that we're popping it we must insert
|
|
// a $noreg DBG_VALUE for the variable to end it, right after
|
|
// the current instruction.
|
|
BuildMI(*Prev.DebugValue->getParent(), std::next(MII),
|
|
Prev.DebugValue->getDebugLoc(),
|
|
TII->get(WebAssembly::DBG_VALUE), false, Register(),
|
|
Prev.DebugValue->getOperand(2).getMetadata(),
|
|
Prev.DebugValue->getOperand(3).getMetadata());
|
|
}
|
|
}
|
|
}
|
|
for (MachineOperand &MO : MI.defs()) {
|
|
if (MO.isReg() && MFI.isVRegStackified(MO.getReg())) {
|
|
Stack.push_back({MO.getReg(), nullptr});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert(Stack.empty() &&
|
|
"WebAssemblyDebugFixup: Stack not empty at end of basic block!");
|
|
}
|
|
|
|
return true;
|
|
}
|