diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt index a5337496a3cb1..9704776acd33d 100644 --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -38,6 +38,7 @@ add_llvm_target(WebAssemblyCodeGen WebAssemblyLowerGlobalDtors.cpp WebAssemblyMachineFunctionInfo.cpp WebAssemblyMCInstLower.cpp + WebAssemblyNullifyDebugValueLists.cpp WebAssemblyOptimizeLiveIntervals.cpp WebAssemblyOptimizeReturned.cpp WebAssemblyPeephole.cpp diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index e812d00479617..bb928457c859e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -39,6 +39,7 @@ FunctionPass *createWebAssemblySetP2AlignOperands(); // Late passes. FunctionPass *createWebAssemblyReplacePhysRegs(); +FunctionPass *createWebAssemblyNullifyDebugValueLists(); FunctionPass *createWebAssemblyPrepareForLiveIntervals(); FunctionPass *createWebAssemblyOptimizeLiveIntervals(); FunctionPass *createWebAssemblyMemIntrinsicResults(); @@ -64,6 +65,7 @@ void initializeOptimizeReturnedPass(PassRegistry &); void initializeWebAssemblyArgumentMovePass(PassRegistry &); void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &); void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &); +void initializeWebAssemblyNullifyDebugValueListsPass(PassRegistry &); void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &); void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &); void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp index fc7a72edf5f2e..55be64ad7da01 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp @@ -20,38 +20,19 @@ using namespace llvm; WebAssemblyDebugValueManager::WebAssemblyDebugValueManager( MachineInstr *Instr) { - const auto *MF = Instr->getParent()->getParent(); - const auto *TII = MF->getSubtarget().getInstrInfo(); - // This code differs from MachineInstr::collectDebugValues in that it scans // the whole BB, not just contiguous DBG_VALUEs. if (!Instr->getOperand(0).isReg()) return; CurrentReg = Instr->getOperand(0).getReg(); - SmallVector DbgValueLists; MachineBasicBlock::iterator DI = *Instr; ++DI; for (MachineBasicBlock::iterator DE = Instr->getParent()->end(); DI != DE; ++DI) { if (DI->isDebugValue() && DI->hasDebugOperandForReg(Instr->getOperand(0).getReg())) - DI->getOpcode() == TargetOpcode::DBG_VALUE - ? 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(); + DbgValues.push_back(&*DI); } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp index 9bea20389809a..7ed224de1db5a 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp @@ -305,11 +305,12 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { if (!MFI.isVRegStackified(OldReg)) { const TargetRegisterClass *RC = MRI.getRegClass(OldReg); Register NewReg = MRI.createVirtualRegister(RC); + auto InsertPt = std::next(MI.getIterator()); if (UseEmpty[Register::virtReg2Index(OldReg)]) { unsigned Opc = getDropOpcode(RC); - MachineInstr *Drop = BuildMI(MBB, std::next(MI.getIterator()), - MI.getDebugLoc(), TII->get(Opc)) - .addReg(NewReg); + MachineInstr *Drop = + BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) + .addReg(NewReg); // After the drop instruction, this reg operand will not be used Drop->getOperand(0).setIsKill(); if (MFI.isFrameBaseVirtual() && OldReg == MFI.getFrameBaseVreg()) @@ -320,8 +321,7 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId); - BuildMI(MBB, std::next(MI.getIterator()), MI.getDebugLoc(), - TII->get(Opc)) + BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) .addImm(LocalId) .addReg(NewReg); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp new file mode 100644 index 0000000000000..62fa089a94d45 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp @@ -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().getInstrInfo(); + SmallVector 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(); +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 94f4ae705e963..c305da514ef39 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -77,6 +77,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() { initializeWebAssemblyMemIntrinsicResultsPass(PR); initializeWebAssemblyRegStackifyPass(PR); initializeWebAssemblyRegColoringPass(PR); + initializeWebAssemblyNullifyDebugValueListsPass(PR); initializeWebAssemblyFixIrreducibleControlFlowPass(PR); initializeWebAssemblyLateEHPreparePass(PR); initializeWebAssemblyExceptionInfoPass(PR); @@ -442,6 +443,9 @@ void WebAssemblyPassConfig::addPostRegAlloc() { void WebAssemblyPassConfig::addPreEmitPass() { TargetPassConfig::addPreEmitPass(); + // Nullify DBG_VALUE_LISTs that we cannot handle. + addPass(createWebAssemblyNullifyDebugValueLists()); + // Eliminate multiple-entry loops. addPass(createWebAssemblyFixIrreducibleControlFlow()); diff --git a/llvm/test/CodeGen/WebAssembly/reg-stackify-dbg.mir b/llvm/test/CodeGen/WebAssembly/reg-stackify-dbg.mir deleted file mode 100644 index 63be70b5ded43..0000000000000 --- a/llvm/test/CodeGen/WebAssembly/reg-stackify-dbg.mir +++ /dev/null @@ -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 -... diff --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-list.ll b/llvm/test/DebugInfo/WebAssembly/dbg-value-list.ll new file mode 100644 index 0000000000000..c33113673a2a5 --- /dev/null +++ b/llvm/test/DebugInfo/WebAssembly/dbg-value-list.ll @@ -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)