1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

[WebAssembly] Add NullifyDebugValueLists pass

`WebAssemblyDebugValueManager` does not currently handle
`DBG_VALUE_LIST`, which is a recent addition to LLVM. We tried to
nullify them within the constructor of `WebAssemblyDebugValueManager` in
D102589, but it made the class error-prone to use because it deletes
instructions within the constructor and thus invalidates existing
iterators within the BB, so the user of the class should take special
care not to use invalidated iterators. This actually caused a bug in
ExplicitLocals pass.

Instead of trying to fix ExplicitLocals pass to make the iterator usage
correct, which is possible but error-prone, this adds
NullifyDebugValueLists pass that nullifies all `DBG_VALUE_LIST`
instructions before we run WebAssembly specific passes in the backend.
We can remove this pass after we implement handlers for
`DBG_VALUE_LIST`s in `WebAssemblyDebugValueManager` and elsewhere.

Fixes https://github.com/emscripten-core/emscripten/issues/14255.

Reviewed By: dschuff

Differential Revision: https://reviews.llvm.org/D102999
This commit is contained in:
Heejin Ahn 2021-05-23 02:09:17 -07:00
parent 73bc91a5e6
commit 9c4b79c3d5
8 changed files with 123 additions and 76 deletions

View File

@ -38,6 +38,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyLowerGlobalDtors.cpp WebAssemblyLowerGlobalDtors.cpp
WebAssemblyMachineFunctionInfo.cpp WebAssemblyMachineFunctionInfo.cpp
WebAssemblyMCInstLower.cpp WebAssemblyMCInstLower.cpp
WebAssemblyNullifyDebugValueLists.cpp
WebAssemblyOptimizeLiveIntervals.cpp WebAssemblyOptimizeLiveIntervals.cpp
WebAssemblyOptimizeReturned.cpp WebAssemblyOptimizeReturned.cpp
WebAssemblyPeephole.cpp WebAssemblyPeephole.cpp

View File

@ -39,6 +39,7 @@ FunctionPass *createWebAssemblySetP2AlignOperands();
// Late passes. // Late passes.
FunctionPass *createWebAssemblyReplacePhysRegs(); FunctionPass *createWebAssemblyReplacePhysRegs();
FunctionPass *createWebAssemblyNullifyDebugValueLists();
FunctionPass *createWebAssemblyPrepareForLiveIntervals(); FunctionPass *createWebAssemblyPrepareForLiveIntervals();
FunctionPass *createWebAssemblyOptimizeLiveIntervals(); FunctionPass *createWebAssemblyOptimizeLiveIntervals();
FunctionPass *createWebAssemblyMemIntrinsicResults(); FunctionPass *createWebAssemblyMemIntrinsicResults();
@ -64,6 +65,7 @@ void initializeOptimizeReturnedPass(PassRegistry &);
void initializeWebAssemblyArgumentMovePass(PassRegistry &); void initializeWebAssemblyArgumentMovePass(PassRegistry &);
void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &); void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &);
void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &); void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &);
void initializeWebAssemblyNullifyDebugValueListsPass(PassRegistry &);
void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &); void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &);
void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &); void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &);
void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &); void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &);

View File

@ -20,38 +20,19 @@ using namespace llvm;
WebAssemblyDebugValueManager::WebAssemblyDebugValueManager( WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(
MachineInstr *Instr) { MachineInstr *Instr) {
const auto *MF = Instr->getParent()->getParent();
const auto *TII = MF->getSubtarget<WebAssemblySubtarget>().getInstrInfo();
// This code differs from MachineInstr::collectDebugValues in that it scans // This code differs from MachineInstr::collectDebugValues in that it scans
// the whole BB, not just contiguous DBG_VALUEs. // the whole BB, not just contiguous DBG_VALUEs.
if (!Instr->getOperand(0).isReg()) if (!Instr->getOperand(0).isReg())
return; return;
CurrentReg = Instr->getOperand(0).getReg(); CurrentReg = Instr->getOperand(0).getReg();
SmallVector<MachineInstr *, 2> DbgValueLists;
MachineBasicBlock::iterator DI = *Instr; MachineBasicBlock::iterator DI = *Instr;
++DI; ++DI;
for (MachineBasicBlock::iterator DE = Instr->getParent()->end(); DI != DE; for (MachineBasicBlock::iterator DE = Instr->getParent()->end(); DI != DE;
++DI) { ++DI) {
if (DI->isDebugValue() && if (DI->isDebugValue() &&
DI->hasDebugOperandForReg(Instr->getOperand(0).getReg())) DI->hasDebugOperandForReg(Instr->getOperand(0).getReg()))
DI->getOpcode() == TargetOpcode::DBG_VALUE DbgValues.push_back(&*DI);
? DbgValues.push_back(&*DI)
: DbgValueLists.push_back(&*DI);
}
// This class currently cannot handle DBG_VALUE_LISTs correctly. So this
// converts DBG_VALUE_LISTs to "DBG_VALUE $noreg", which will appear as
// "optimized out". This can invalidate existing iterators pointing to
// instructions within this BB from the caller.
// See https://bugs.llvm.org/show_bug.cgi?id=50361
// TODO Correctly handle DBG_VALUE_LISTs
for (auto *DVL : DbgValueLists) {
BuildMI(*DVL->getParent(), DVL, DVL->getDebugLoc(),
TII->get(TargetOpcode::DBG_VALUE), false, Register(),
DVL->getOperand(0).getMetadata(), DVL->getOperand(1).getMetadata());
DVL->eraseFromParent();
} }
} }

View File

@ -305,11 +305,12 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
if (!MFI.isVRegStackified(OldReg)) { if (!MFI.isVRegStackified(OldReg)) {
const TargetRegisterClass *RC = MRI.getRegClass(OldReg); const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
Register NewReg = MRI.createVirtualRegister(RC); Register NewReg = MRI.createVirtualRegister(RC);
auto InsertPt = std::next(MI.getIterator());
if (UseEmpty[Register::virtReg2Index(OldReg)]) { if (UseEmpty[Register::virtReg2Index(OldReg)]) {
unsigned Opc = getDropOpcode(RC); unsigned Opc = getDropOpcode(RC);
MachineInstr *Drop = BuildMI(MBB, std::next(MI.getIterator()), MachineInstr *Drop =
MI.getDebugLoc(), TII->get(Opc)) BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
.addReg(NewReg); .addReg(NewReg);
// After the drop instruction, this reg operand will not be used // After the drop instruction, this reg operand will not be used
Drop->getOperand(0).setIsKill(); Drop->getOperand(0).setIsKill();
if (MFI.isFrameBaseVirtual() && OldReg == MFI.getFrameBaseVreg()) if (MFI.isFrameBaseVirtual() && OldReg == MFI.getFrameBaseVreg())
@ -320,8 +321,7 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId); WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId);
BuildMI(MBB, std::next(MI.getIterator()), MI.getDebugLoc(), BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
TII->get(Opc))
.addImm(LocalId) .addImm(LocalId)
.addReg(NewReg); .addReg(NewReg);
} }

View File

@ -0,0 +1,68 @@
//=== WebAssemblyNullifyDebugValueLists.cpp - Nullify DBG_VALUE_LISTs ---===//
//
// 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
/// Nullify DBG_VALUE_LISTs instructions as a temporary measure before we
/// implement DBG_VALUE_LIST handling in WebAssemblyDebugValueManager.
/// See https://bugs.llvm.org/show_bug.cgi?id=50361.
/// TODO Correctly handle DBG_VALUE_LISTs
///
//===----------------------------------------------------------------------===//
#include "WebAssembly.h"
#include "WebAssemblySubtarget.h"
using namespace llvm;
#define DEBUG_TYPE "wasm-nullify-dbg-value-lists"
namespace {
class WebAssemblyNullifyDebugValueLists final : public MachineFunctionPass {
StringRef getPassName() const override {
return "WebAssembly Nullify DBG_VALUE_LISTs";
}
bool runOnMachineFunction(MachineFunction &MF) override;
public:
static char ID; // Pass identification, replacement for typeid
WebAssemblyNullifyDebugValueLists() : MachineFunctionPass(ID) {}
};
} // end anonymous namespace
char WebAssemblyNullifyDebugValueLists::ID = 0;
INITIALIZE_PASS(WebAssemblyNullifyDebugValueLists, DEBUG_TYPE,
"WebAssembly Nullify DBG_VALUE_LISTs", false, false)
FunctionPass *llvm::createWebAssemblyNullifyDebugValueLists() {
return new WebAssemblyNullifyDebugValueLists();
}
bool WebAssemblyNullifyDebugValueLists::runOnMachineFunction(
MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "********** Nullify DBG_VALUE_LISTs **********\n"
"********** Function: "
<< MF.getName() << '\n');
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
SmallVector<MachineInstr *, 2> DbgValueLists;
for (auto &MBB : MF)
for (auto &MI : MBB)
if (MI.getOpcode() == TargetOpcode::DBG_VALUE_LIST)
DbgValueLists.push_back(&MI);
// Our backend, including WebAssemblyDebugValueManager, currently cannot
// handle DBG_VALUE_LISTs correctly. So this converts DBG_VALUE_LISTs to
// "DBG_VALUE $noreg", which will appear as "optimized out".
for (auto *DVL : DbgValueLists) {
BuildMI(*DVL->getParent(), DVL, DVL->getDebugLoc(),
TII.get(TargetOpcode::DBG_VALUE), false, Register(),
DVL->getOperand(0).getMetadata(), DVL->getOperand(1).getMetadata());
DVL->eraseFromParent();
}
return !DbgValueLists.empty();
}

View File

@ -77,6 +77,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
initializeWebAssemblyMemIntrinsicResultsPass(PR); initializeWebAssemblyMemIntrinsicResultsPass(PR);
initializeWebAssemblyRegStackifyPass(PR); initializeWebAssemblyRegStackifyPass(PR);
initializeWebAssemblyRegColoringPass(PR); initializeWebAssemblyRegColoringPass(PR);
initializeWebAssemblyNullifyDebugValueListsPass(PR);
initializeWebAssemblyFixIrreducibleControlFlowPass(PR); initializeWebAssemblyFixIrreducibleControlFlowPass(PR);
initializeWebAssemblyLateEHPreparePass(PR); initializeWebAssemblyLateEHPreparePass(PR);
initializeWebAssemblyExceptionInfoPass(PR); initializeWebAssemblyExceptionInfoPass(PR);
@ -442,6 +443,9 @@ void WebAssemblyPassConfig::addPostRegAlloc() {
void WebAssemblyPassConfig::addPreEmitPass() { void WebAssemblyPassConfig::addPreEmitPass() {
TargetPassConfig::addPreEmitPass(); TargetPassConfig::addPreEmitPass();
// Nullify DBG_VALUE_LISTs that we cannot handle.
addPass(createWebAssemblyNullifyDebugValueLists());
// Eliminate multiple-entry loops. // Eliminate multiple-entry loops.
addPass(createWebAssemblyFixIrreducibleControlFlow()); addPass(createWebAssemblyFixIrreducibleControlFlow());

View File

@ -1,51 +0,0 @@
# RUN: llc -mtriple=wasm32-unknown-unknown -run-pass wasm-reg-stackify %s -o - | FileCheck %s
--- |
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
define void @dbg_value_list_test() {
ret void
}
!llvm.module.flags = !{!0}
!llvm.dbg.cu = !{!1}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang version 3.9.0 (trunk 266005) (llvm/trunk 266105)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3)
!2 = !DIFile(filename: "test.c", directory: "/")
!3 = !{}
!4 = distinct !DISubprogram(name: "dbg_value_list_test", scope: !2, file: !2, line: 10, type: !5, isLocal: false, isDefinition: true, scopeLine: 11, flags: DIFlagPrototyped, isOptimized: true, unit: !1, retainedNodes: !3)
!5 = !DISubroutineType(types: !3)
!6 = !DILocalVariable(name: "var", scope: !4, file: !2, line: 15, type: !7)
!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
!8 = !DILocation(line: 15, column: 6, scope: !4)
...
# WebAssemblyDebugValueManager currently does not handle DBG_VALUE_LIST
# instructions correctly and instead effectively nullifying them by turning them
# into "DBG_VALUE $noreg". See https://bugs.llvm.org/show_bug.cgi?id=50361.
# (Otherwise DBG_VALUE_LIST instructions can be exponentially and possibly
# incorrectly copied.)
# This tests if DBG_VALUE_LIST is nullified as intended.
# CHECK-LABEL: name: dbg_value_list_test
name: dbg_value_list_test
liveins:
- { reg: '$arguments' }
body: |
bb.0:
; CHECK: DBG_VALUE $noreg, $noreg
%0:i32 = ARGUMENT_i32 0, implicit $arguments
%1:i32 = ARGUMENT_i32 1, implicit $arguments
%2:i32 = ARGUMENT_i32 2, implicit $arguments
%3:i32 = LOAD_I32_A32 2, 0, %0:i32, implicit-def dead $arguments
%4:i32 = LT_U_I32 %3:i32, %1:i32, implicit-def dead $arguments
%5:i32 = GE_U_I32 %4:i32, %2:i32, implicit-def dead $arguments
%6:i32 = OR_I32 %5:i32, %4:i32, implicit-def dead $arguments
; This should become "DBG_VALUE $noreg" and should not be copied when %4 is
; tee'd
; CHECK-NOT: DBG_VALUE_LIST
DBG_VALUE_LIST !6, !DIExpression(), %4:i32, debug-location !8
RETURN %6:i32, implicit-def dead $arguments
...

View File

@ -0,0 +1,42 @@
; RUN: llc %s -stop-before wasm-nullify-dbg-value-lists -o - | FileCheck %s --check-prefix=BEFORE
; RUN: llc %s -stop-after wasm-nullify-dbg-value-lists -o - | FileCheck %s --check-prefix=AFTER
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
; WebAssembly backend does not currently handle DBG_VALUE_LIST instructions
; correctly. In the meantime, they are converted to 'DBG_VALUE $noreg's in
; WebAssemblyNullifyDebugValueLists pass.
; BEFORE: DBG_VALUE_LIST
; AFTER-NOT: DBG_VALUE_LIST
; AFTER: DBG_VALUE $noreg, $noreg
define i32 @dbg_value_list_test() !dbg !6 {
entry:
%0 = call i32 @foo(), !dbg !9
%1 = call i32 @foo(), !dbg !10
%2 = add i32 %0, %1, !dbg !11
; This DIArgList operand generates a DBG_VALUE_LIST instruction
call void @llvm.dbg.value(metadata !DIArgList(i32 %0, i32 %1), metadata !8, metadata !DIExpression()), !dbg !11
ret i32 %2, !dbg !12
}
declare i32 @foo()
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git ed7aaf832444411ce93aa0443425ce401f5c7a8e)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!1 = !DIFile(filename: "test.c", directory: "/home/llvm-project")
!2 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!3 = !{i32 7, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = distinct !DISubprogram(name: "", scope: !1, file: !1, line: 3, type: !7, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
!7 = !DISubroutineType(types: !{null})
!8 = !DILocalVariable(name: "i", scope: !6, file: !1, line: 4, type: !2)
!9 = !DILocation(line: 4, column: 11, scope: !6)
!10 = !DILocation(line: 5, column: 11, scope: !6)
!11 = !DILocation(line: 6, column: 3, scope: !6)
!12 = !DILocation(line: 7, column: 1, scope: !6)