Skip to content

Commit 2b7fe08

Browse files
committed
[WebAssembly] Added Debug Fixup pass
This pass changes debug_value instructions referring to stackified registers into TI_OPERAND_STACK with correct stack depth.
1 parent 5144e48 commit 2b7fe08

File tree

6 files changed

+251
-15
lines changed

6 files changed

+251
-15
lines changed

llvm/lib/Target/WebAssembly/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_llvm_target(WebAssemblyCodeGen
1818
WebAssemblyAsmPrinter.cpp
1919
WebAssemblyCFGStackify.cpp
2020
WebAssemblyCFGSort.cpp
21+
WebAssemblyDebugFixup.cpp
2122
WebAssemblyDebugValueManager.cpp
2223
WebAssemblyLateEHPrepare.cpp
2324
WebAssemblyExceptionInfo.cpp

llvm/lib/Target/WebAssembly/WebAssembly.h

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ FunctionPass *createWebAssemblyCFGStackify();
5151
FunctionPass *createWebAssemblyExplicitLocals();
5252
FunctionPass *createWebAssemblyLowerBrUnless();
5353
FunctionPass *createWebAssemblyRegNumbering();
54+
FunctionPass *createWebAssemblyDebugFixup();
5455
FunctionPass *createWebAssemblyPeephole();
5556

5657
// PassRegistry initialization declarations.
@@ -75,6 +76,7 @@ void initializeWebAssemblyCFGStackifyPass(PassRegistry &);
7576
void initializeWebAssemblyExplicitLocalsPass(PassRegistry &);
7677
void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &);
7778
void initializeWebAssemblyRegNumberingPass(PassRegistry &);
79+
void initializeWebAssemblyDebugFixupPass(PassRegistry &);
7880
void initializeWebAssemblyPeepholePass(PassRegistry &);
7981

8082
namespace WebAssembly {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//===-- WebAssemblyDebugFixup.cpp - Debug Fixup ------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// Several prior passes may "stackify" registers, here we ensure any references
11+
/// in such registers in debug_value instructions become stack relative also.
12+
/// This is done in a separate pass such that not all previous passes need to
13+
/// track stack depth when values get stackified.
14+
///
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
18+
#include "WebAssembly.h"
19+
#include "WebAssemblyMachineFunctionInfo.h"
20+
#include "WebAssemblySubtarget.h"
21+
#include "WebAssemblyUtilities.h"
22+
#include "llvm/ADT/SCCIterator.h"
23+
#include "llvm/CodeGen/MachineFrameInfo.h"
24+
#include "llvm/CodeGen/MachineFunction.h"
25+
#include "llvm/CodeGen/MachineInstrBuilder.h"
26+
#include "llvm/CodeGen/MachineLoopInfo.h"
27+
#include "llvm/CodeGen/MachineRegisterInfo.h"
28+
#include "llvm/CodeGen/Passes.h"
29+
#include "llvm/Support/Debug.h"
30+
#include "llvm/Support/raw_ostream.h"
31+
using namespace llvm;
32+
33+
#define DEBUG_TYPE "wasm-debug-fixup"
34+
35+
namespace {
36+
class WebAssemblyDebugFixup final : public MachineFunctionPass {
37+
StringRef getPassName() const override { return "WebAssembly Debug Fixup"; }
38+
39+
void getAnalysisUsage(AnalysisUsage &AU) const override {
40+
AU.setPreservesCFG();
41+
MachineFunctionPass::getAnalysisUsage(AU);
42+
}
43+
44+
bool runOnMachineFunction(MachineFunction &MF) override;
45+
46+
public:
47+
static char ID; // Pass identification, replacement for typeid
48+
WebAssemblyDebugFixup() : MachineFunctionPass(ID) {}
49+
};
50+
} // end anonymous namespace
51+
52+
char WebAssemblyDebugFixup::ID = 0;
53+
INITIALIZE_PASS(
54+
WebAssemblyDebugFixup, DEBUG_TYPE,
55+
"Ensures debug_value's that have been stackified become stack relative",
56+
false, false)
57+
58+
FunctionPass *llvm::createWebAssemblyDebugFixup() {
59+
return new WebAssemblyDebugFixup();
60+
}
61+
62+
bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) {
63+
LLVM_DEBUG(dbgs() << "********** Debug Fixup **********\n"
64+
"********** Function: "
65+
<< MF.getName() << '\n');
66+
67+
WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
68+
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
69+
70+
struct StackElem {
71+
unsigned Reg;
72+
MachineInstr *DebugValue;
73+
};
74+
std::vector<StackElem> Stack;
75+
for (MachineBasicBlock &MBB : MF) {
76+
// We may insert into this list.
77+
for (auto MII = MBB.begin(); MII != MBB.end(); ++MII) {
78+
MachineInstr &MI = *MII;
79+
if (MI.isDebugValue()) {
80+
auto &MO = MI.getOperand(0);
81+
// Also check if not a $noreg: likely a DBG_VALUE we just inserted.
82+
if (MO.isReg() && MO.getReg().isValid() &&
83+
MFI.isVRegStackified(MO.getReg())) {
84+
// Found a DBG_VALUE with a stackified register we will
85+
// change into a stack operand.
86+
// Search for register rather than assume it is on top (which it
87+
// typically is if it appears right after the def), since
88+
// DBG_VALUE's may shift under some circumstances.
89+
size_t Depth = 0;
90+
for (auto &Elem : Stack) {
91+
if (MO.getReg() == Elem.Reg) {
92+
LLVM_DEBUG(dbgs() << "Debug Value VReg " << MO.getReg()
93+
<< " -> Stack Relative " << Depth << "\n");
94+
MO.ChangeToTargetIndex(WebAssembly::TI_OPERAND_STACK, Depth);
95+
// Save the DBG_VALUE instruction that defined this stackified
96+
// variable since later we need it to construct another one on
97+
// pop.
98+
Elem.DebugValue = &MI;
99+
break;
100+
}
101+
Depth++;
102+
}
103+
// If the Reg was not found, we have a DBG_VALUE outside of its
104+
// def-use range, and we leave it unmodified as reg, which means
105+
// it will be culled later.
106+
}
107+
} else {
108+
// Track stack depth.
109+
for (MachineOperand &MO : reverse(MI.explicit_uses())) {
110+
if (MO.isReg() && MFI.isVRegStackified(MO.getReg())) {
111+
auto Prev = Stack.back();
112+
Stack.pop_back();
113+
assert(Prev.Reg == MO.getReg() &&
114+
"WebAssemblyDebugFixup: Pop: Register not matched!");
115+
if (Prev.DebugValue) {
116+
// This stackified reg is a variable that started life at
117+
// Prev.DebugValue, so now that we're popping it we must insert
118+
// a $noreg DBG_VALUE for the variable to end it, right after
119+
// the current instruction.
120+
BuildMI(*Prev.DebugValue->getParent(), std::next(MII),
121+
MI.getDebugLoc(), TII->get(WebAssembly::DBG_VALUE), false,
122+
Register(), Prev.DebugValue->getOperand(2).getMetadata(),
123+
Prev.DebugValue->getOperand(3).getMetadata());
124+
}
125+
}
126+
}
127+
for (MachineOperand &MO : MI.defs()) {
128+
if (MO.isReg() && MFI.isVRegStackified(MO.getReg())) {
129+
Stack.push_back({MO.getReg(), nullptr});
130+
}
131+
}
132+
}
133+
}
134+
assert(Stack.empty() &&
135+
"WebAssemblyDebugFixup: Stack not empty at end of basic block!");
136+
}
137+
138+
return true;
139+
}

llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp

-14
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,6 @@ using namespace llvm;
3131

3232
#define DEBUG_TYPE "wasm-explicit-locals"
3333

34-
// A command-line option to disable this pass, and keep implicit locals
35-
// for the purpose of testing with lit/llc ONLY.
36-
// This produces output which is not valid WebAssembly, and is not supported
37-
// by assemblers/disassemblers and other MC based tools.
38-
static cl::opt<bool> WasmDisableExplicitLocals(
39-
"wasm-disable-explicit-locals", cl::Hidden,
40-
cl::desc("WebAssembly: output implicit locals in"
41-
" instruction output for test purposes only."),
42-
cl::init(false));
43-
4434
namespace {
4535
class WebAssemblyExplicitLocals final : public MachineFunctionPass {
4636
StringRef getPassName() const override {
@@ -211,10 +201,6 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
211201
"********** Function: "
212202
<< MF.getName() << '\n');
213203

214-
// Disable this pass if directed to do so.
215-
if (WasmDisableExplicitLocals)
216-
return false;
217-
218204
bool Changed = false;
219205
MachineRegisterInfo &MRI = MF.getRegInfo();
220206
WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ static cl::opt<bool> EnableEmSjLj(
4545
cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"),
4646
cl::init(false));
4747

48+
// A command-line option to keep implicit locals
49+
// for the purpose of testing with lit/llc ONLY.
50+
// This produces output which is not valid WebAssembly, and is not supported
51+
// by assemblers/disassemblers and other MC based tools.
52+
static cl::opt<bool> WasmDisableExplicitLocals(
53+
"wasm-disable-explicit-locals", cl::Hidden,
54+
cl::desc("WebAssembly: output implicit locals in"
55+
" instruction output for test purposes only."),
56+
cl::init(false));
57+
4858
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
4959
// Register the target.
5060
RegisterTargetMachine<WebAssemblyTargetMachine> X(
@@ -75,6 +85,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
7585
initializeWebAssemblyExplicitLocalsPass(PR);
7686
initializeWebAssemblyLowerBrUnlessPass(PR);
7787
initializeWebAssemblyRegNumberingPass(PR);
88+
initializeWebAssemblyDebugFixupPass(PR);
7889
initializeWebAssemblyPeepholePass(PR);
7990
}
8091

@@ -467,7 +478,8 @@ void WebAssemblyPassConfig::addPreEmitPass() {
467478
addPass(createWebAssemblyCFGStackify());
468479

469480
// Insert explicit local.get and local.set operators.
470-
addPass(createWebAssemblyExplicitLocals());
481+
if (!WasmDisableExplicitLocals)
482+
addPass(createWebAssemblyExplicitLocals());
471483

472484
// Lower br_unless into br_if.
473485
addPass(createWebAssemblyLowerBrUnless());
@@ -478,6 +490,10 @@ void WebAssemblyPassConfig::addPreEmitPass() {
478490

479491
// Create a mapping from LLVM CodeGen virtual registers to wasm registers.
480492
addPass(createWebAssemblyRegNumbering());
493+
494+
// Fix debug_values whose defs have been stackified.
495+
if (!WasmDisableExplicitLocals)
496+
addPass(createWebAssemblyDebugFixup());
481497
}
482498

483499
yaml::MachineFunctionInfo *
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
; RUN: llc < %s | FileCheck %s
2+
3+
; Input C code:
4+
5+
; int i = input(); // Nested case
6+
; int j = input(); // Trivial def-use.
7+
; output(i, j);
8+
9+
; The ll below generates 330 lines of .S, so relevant parts that the
10+
; WebAssemblyDebugFixup pass affects:
11+
12+
; CHECK: call input
13+
; CHECK: .Ltmp0:
14+
; CHECK: call input
15+
; CHECK: .Ltmp1:
16+
; CHECK: call output
17+
; CHECK: .Ltmp2:
18+
19+
; This defines variable "i" which is live on the stack between Ltmp0 and Ltmp2,
20+
; 2 = TI_OPERAND_STACK and 0 = stack offset.
21+
22+
; CHECK: .section .debug_loc,"",@
23+
; CHECK: .Ldebug_loc0:
24+
; CHECK: .int32 .Ltmp0-.Lfunc_begin0
25+
; CHECK: .int32 .Ltmp2-.Lfunc_begin0
26+
; CHECK: .int16 4 # Loc expr size
27+
; CHECK: .int8 237 # DW_OP_WASM_location
28+
; CHECK: .int8 2 # 2
29+
; CHECK: .int8 0 # 0
30+
; CHECK: .int8 159 # DW_OP_stack_value
31+
32+
; This defines variable "j" which is live on the stack between Ltmp1 and Ltmp2,
33+
; 2 = TI_OPERAND_STACK and 1 = stack offset.
34+
35+
; CHECK: .Ldebug_loc1:
36+
; CHECK: .int32 .Ltmp1-.Lfunc_begin0
37+
; CHECK: .int32 .Ltmp2-.Lfunc_begin0
38+
; CHECK: .int16 4 # Loc expr size
39+
; CHECK: .int8 237 # DW_OP_WASM_location
40+
; CHECK: .int8 2 # 2
41+
; CHECK: .int8 1 # 1
42+
; CHECK: .int8 159 # DW_OP_stack_value
43+
44+
45+
46+
47+
source_filename = "stackified.c"
48+
target triple = "wasm32-unknown-unknown"
49+
50+
define void @foo() !dbg !12 {
51+
entry:
52+
%call = call i32 @input(), !dbg !18
53+
call void @llvm.dbg.value(metadata i32 %call, metadata !16, metadata !DIExpression()), !dbg !19
54+
%call1 = call i32 @input(), !dbg !20
55+
call void @llvm.dbg.value(metadata i32 %call1, metadata !17, metadata !DIExpression()), !dbg !19
56+
call void @output(i32 %call, i32 %call1), !dbg !21
57+
ret void, !dbg !22
58+
}
59+
60+
declare i32 @input()
61+
62+
declare !dbg !4 void @output(i32, i32)
63+
64+
declare void @llvm.dbg.value(metadata, metadata, metadata)
65+
66+
!llvm.dbg.cu = !{!0}
67+
!llvm.module.flags = !{!8, !9, !10}
68+
!llvm.ident = !{!11}
69+
70+
!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, enums: !2, retainedTypes: !3, nameTableKind: None)
71+
!1 = !DIFile(filename: "stackified.c", directory: "C:\\stuff\\llvm-project")
72+
!2 = !{}
73+
!3 = !{!4}
74+
!4 = !DISubprogram(name: "output", scope: !1, file: !1, line: 2, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
75+
!5 = !DISubroutineType(types: !6)
76+
!6 = !{null, !7, !7}
77+
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
78+
!8 = !{i32 7, !"Dwarf Version", i32 4}
79+
!9 = !{i32 2, !"Debug Info Version", i32 3}
80+
!10 = !{i32 1, !"wchar_size", i32 4}
81+
!11 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git ed7aaf832444411ce93aa0443425ce401f5c7a8e)"}
82+
!12 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !13, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15)
83+
!13 = !DISubroutineType(types: !14)
84+
!14 = !{null}
85+
!15 = !{!16, !17}
86+
!16 = !DILocalVariable(name: "i", scope: !12, file: !1, line: 4, type: !7)
87+
!17 = !DILocalVariable(name: "j", scope: !12, file: !1, line: 5, type: !7)
88+
!18 = !DILocation(line: 4, column: 11, scope: !12)
89+
!19 = !DILocation(line: 0, scope: !12)
90+
!20 = !DILocation(line: 5, column: 11, scope: !12)
91+
!21 = !DILocation(line: 6, column: 3, scope: !12)
92+
!22 = !DILocation(line: 7, column: 1, scope: !12)

0 commit comments

Comments
 (0)