diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -18,6 +18,7 @@ WebAssemblyAsmPrinter.cpp WebAssemblyCFGStackify.cpp WebAssemblyCFGSort.cpp + WebAssemblyDebugFixup.cpp WebAssemblyDebugValueManager.cpp WebAssemblyLateEHPrepare.cpp WebAssemblyExceptionInfo.cpp diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -51,6 +51,7 @@ FunctionPass *createWebAssemblyExplicitLocals(); FunctionPass *createWebAssemblyLowerBrUnless(); FunctionPass *createWebAssemblyRegNumbering(); +FunctionPass *createWebAssemblyDebugFixup(); FunctionPass *createWebAssemblyPeephole(); // PassRegistry initialization declarations. @@ -75,6 +76,7 @@ void initializeWebAssemblyExplicitLocalsPass(PassRegistry &); void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &); void initializeWebAssemblyRegNumberingPass(PassRegistry &); +void initializeWebAssemblyDebugFixupPass(PassRegistry &); void initializeWebAssemblyPeepholePass(PassRegistry &); namespace WebAssembly { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp @@ -0,0 +1,129 @@ +//===-- 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 "WebAssembly.h" +#include "WebAssemblyMachineFunctionInfo.h" +#include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.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(); + const auto *TII = MF.getSubtarget().getInstrInfo(); + + SmallVector 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. + if (MO.isReg() && MO.getReg() && MFI.isVRegStackified(MO.getReg())) { + // Found a DBG_VALUE with a stackified register we will + // change into a stack operand. + auto Depth = static_cast(Stack.size()); + assert(Depth > 0 && + "WebAssemblyDebugFixup: DBG_VALUE: No value on the stack!"); + assert(MO.getReg() == Stack.back() && + "WebAssemblyDebugFixup: DBG_VALUE: Register not matched!"); + Depth--; + LLVM_DEBUG(dbgs() << "Debug Value VReg " << MO.getReg() + << " -> Stack Relative " << Depth << "\n"); + MO.ChangeToTargetIndex(WebAssembly::TI_OPERAND_STACK, Depth); + // We still have a live range associated with the register, + // that could be until the end of the scope. We must limit it + // to when the value gets popped. + auto TMII = MII; // No + operator. + ++TMII; + ++TMII; + BuildMI(*MI.getParent(), TMII, MI.getDebugLoc(), + TII->get(WebAssembly::DBG_VALUE)) + .addReg(0, RegState::Debug) + .addReg(0, RegState::Debug) + .addMetadata(MI.getOperand(2).getMetadata()) + .addMetadata(MI.getOperand(3).getMetadata()); + } + } else { + // Track stack depth. + for (MachineOperand &MO : reverse(MI.explicit_uses())) { + if (MO.isReg() && MFI.isVRegStackified(MO.getReg())) { + auto PrevReg = Stack.pop_back_val(); + assert(PrevReg == MO.getReg() && + "WebAssemblyDebugFixup: Pop: Register not matched!"); + (void)PrevReg; + } + } + for (MachineOperand &MO : MI.defs()) { + // We also check for isDead here, since some tests that disable + // explicit locals may cause dead stackified regs. + // See also: maybeRewriteToDrop. + if (MO.isReg() && !MO.isDead() && MFI.isVRegStackified(MO.getReg())) { + Stack.push_back(MO.getReg()); + } + } + } + } + assert(Stack.empty() && + "WebAssemblyDebugFixup: Stack not empty at end of basic block!"); + } + + return true; +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -985,7 +985,7 @@ "Register stack pop should be paired with a push"); } for (MachineOperand &MO : MI.defs()) { - if (!MO.isReg()) + if (!MO.isReg() || MO.isDead()) continue; Register Reg = MO.getReg(); if (MFI.isVRegStackified(Reg)) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -75,6 +75,7 @@ initializeWebAssemblyExplicitLocalsPass(PR); initializeWebAssemblyLowerBrUnlessPass(PR); initializeWebAssemblyRegNumberingPass(PR); + initializeWebAssemblyDebugFixupPass(PR); initializeWebAssemblyPeepholePass(PR); } @@ -477,6 +478,9 @@ // Create a mapping from LLVM CodeGen virtual registers to wasm registers. addPass(createWebAssemblyRegNumbering()); + + // Fix debug_values whose defs have been stackified. + addPass(createWebAssemblyDebugFixup()); } yaml::MachineFunctionInfo * diff --git a/llvm/test/CodeGen/WebAssembly/stackified-debug.ll b/llvm/test/CodeGen/WebAssembly/stackified-debug.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/stackified-debug.ll @@ -0,0 +1,76 @@ +; RUN: llc < %s | FileCheck %s + +; This generates 330 lines of .S, so relevant parts that the +; WebAssemblyDebugFixup pass affects: + +; Reg %call (variable "i" in source program) below gets stackified: + +; CHECK: call input +; CHECK: .Ltmp0: +; CHECK: call output +; CHECK: .Ltmp1: + +; The variable is live just between tmp0 and tmp1, is of type +; TI_OPERAND_STACK (2) and is at stack depth 0: + +; CHECK: .section .debug_loc,"",@ +; CHECK: .Ldebug_loc0: +; CHECK: .int32 .Ltmp0-.Lfunc_begin0 +; CHECK: .int32 .Ltmp1-.Lfunc_begin0 +; CHECK: .int16 4 # Loc expr size +; CHECK: .int8 237 # DW_OP_WASM_location +; CHECK: .int8 2 # 2 +; CHECK: .int8 0 # 0 +; CHECK: .int8 159 # DW_OP_stack_value + + + +source_filename = "stackified.c" +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown-wasm" + +define hidden void @foo() local_unnamed_addr #0 !dbg !12 { +entry: + %call = tail call i32 bitcast (i32 (...)* @input to i32 ()*)() #4, !dbg !17 + call void @llvm.dbg.value(metadata i32 %call, metadata !16, metadata !DIExpression()), !dbg !18 + tail call void @output(i32 %call) #4, !dbg !19 + ret void, !dbg !20 +} + +declare i32 @input(...) local_unnamed_addr #1 + +declare !dbg !4 void @output(i32) local_unnamed_addr #2 + +declare void @llvm.dbg.value(metadata, metadata, metadata) #3 + +attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-prototype" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone speculatable willreturn } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 7bf4248521f158d9a536d6b56d93bc8da9759799)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) +!1 = !DIFile(filename: "stackified.c", directory: "C:\\stuff\\llvm-project") +!2 = !{} +!3 = !{!4} +!4 = !DISubprogram(name: "output", scope: !1, file: !1, line: 2, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{null, !7} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 7, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 7bf4248521f158d9a536d6b56d93bc8da9759799)"} +!12 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !13, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{null} +!15 = !{!16} +!16 = !DILocalVariable(name: "i", scope: !12, file: !1, line: 4, type: !7) +!17 = !DILocation(line: 4, column: 11, scope: !12) +!18 = !DILocation(line: 0, scope: !12) +!19 = !DILocation(line: 8, column: 3, scope: !12) +!20 = !DILocation(line: 9, column: 1, scope: !12)