Skip to content

[lldb] Support DW_OP_WASM_location in DWARFExpression #151010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 30, 2025

Conversation

JDevlieghere
Copy link
Member

@JDevlieghere JDevlieghere commented Jul 28, 2025

Add support for DW_OP_WASM_location in DWARFExpression. This PR rebases #78977 and cleans up the unit test. The DWARF extensions are documented here: https://yurydelendik.github.io/webassembly-dwarf/ and supported by LLVM.

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-lldb

Author: Jonas Devlieghere (JDevlieghere)

Changes

Add support for DW_OP_WASM_location in DWARFExpression. This PR rebases #78977 and cleans up the unit test.


Patch is 41.66 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151010.diff

14 Files Affected:

  • (modified) lldb/include/lldb/Expression/DWARFExpression.h (+8-4)
  • (modified) lldb/source/Expression/DWARFExpression.cpp (+36-4)
  • (modified) lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp (+5-1)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt (+1)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp (+3-1)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h (+5-3)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (+5)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h (+3)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp (+5-3)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h (+2-1)
  • (added) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp (+78)
  • (added) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h (+34)
  • (modified) lldb/unittests/Expression/CMakeLists.txt (+2)
  • (modified) lldb/unittests/Expression/DWARFExpressionTest.cpp (+409-147)
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index 37853c0b5a8fc..8fcc5d37b91c9 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -52,10 +52,10 @@ class DWARFExpression {
     GetVendorDWARFOpcodeSize(const DataExtractor &data,
                              const lldb::offset_t data_offset,
                              const uint8_t op) const = 0;
-    virtual bool ParseVendorDWARFOpcode(uint8_t op,
-                                        const DataExtractor &opcodes,
-                                        lldb::offset_t &offset,
-                                        Stack &stack) const = 0;
+    virtual bool
+    ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
+                           lldb::offset_t &offset, RegisterContext *reg_ctx,
+                           lldb::RegisterKind reg_kind, Stack &stack) const = 0;
 
     Delegate(const Delegate &) = delete;
     Delegate &operator=(const Delegate &) = delete;
@@ -163,6 +163,10 @@ class DWARFExpression {
 
   bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const;
 
+  static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
+                                               lldb::RegisterKind reg_kind,
+                                               uint32_t reg_num, Value &value);
+
 private:
   /// A data extractor capable of reading opcode bytes
   DataExtractor m_data;
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 79bc6c87fa9c5..68093e9710a28 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -91,9 +91,10 @@ void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
   m_reg_kind = reg_kind;
 }
 
-static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
-                                             lldb::RegisterKind reg_kind,
-                                             uint32_t reg_num, Value &value) {
+llvm::Error
+DWARFExpression::ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
+                                           lldb::RegisterKind reg_kind,
+                                           uint32_t reg_num, Value &value) {
   if (reg_ctx == nullptr)
     return llvm::createStringError("no register context in frame");
 
@@ -2300,9 +2301,40 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
       break;
     }
 
+    case DW_OP_WASM_location: {
+      uint8_t wasm_op = opcodes.GetU8(&offset);
+      uint32_t index;
+
+      /* LLDB doesn't have an address space to represents WebAssembly locals,
+       * globals and operand stacks.
+       * We encode these elements into virtual registers:
+       *   | tag: 2 bits | index: 30 bits |
+       *   where tag is:
+       *    0: Not a WebAssembly location
+       *    1: Local
+       *    2: Global
+       *    3: Operand stack value
+       */
+      if (wasm_op == 3) {
+        index = opcodes.GetU32(&offset);
+        wasm_op = 2; // Global
+      } else {
+        index = opcodes.GetULEB128(&offset);
+      }
+
+      reg_num = (((wasm_op + 1) & 0x03) << 30) | (index & 0x3fffffff);
+
+      if (llvm::Error error =
+              ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp))
+        return std::move(error);
+      stack.push_back(tmp);
+      break;
+    }
+
     default:
       if (dwarf_cu) {
-        if (dwarf_cu->ParseVendorDWARFOpcode(op, opcodes, offset, stack)) {
+        if (dwarf_cu->ParseVendorDWARFOpcode(op, opcodes, offset, reg_ctx,
+                                             reg_kind, stack)) {
           break;
         }
       }
diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
index 67963a790a4fe..b1efd25949379 100644
--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
+++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
@@ -376,9 +376,13 @@ DataExtractor ObjectFileWasm::ReadImageData(offset_t offset, uint32_t size) {
         DataBufferSP buffer_sp(data_up.release());
         data.SetData(buffer_sp, 0, buffer_sp->GetByteSize());
       }
+    } else if (offset < m_data.GetByteSize()) {
+      size =
+          std::min(static_cast<uint64_t>(size), m_data.GetByteSize() - offset);
+      return DataExtractor(m_data.GetDataStart() + offset, size, GetByteOrder(),
+                           GetAddressByteSize());
     }
   }
-
   data.SetByteOrder(GetByteOrder());
   return data;
 }
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt
index 212cc3610acfb..c3f1bb55e03be 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt
+++ b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt
@@ -35,6 +35,7 @@ add_lldb_library(lldbPluginSymbolFileDWARF PLUGIN
   SymbolFileDWARF.cpp
   SymbolFileDWARFDwo.cpp
   SymbolFileDWARFDebugMap.cpp
+  SymbolFileWasm.cpp
   UniqueDWARFASTType.cpp
 
   LINK_COMPONENTS
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
index a66af5b126eb1..94fc2e83e899d 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -736,9 +736,11 @@ DWARFUnit::GetVendorDWARFOpcodeSize(const DataExtractor &data,
 
 bool DWARFUnit::ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
                                        lldb::offset_t &offset,
+                                       RegisterContext *reg_ctx,
+                                       lldb::RegisterKind reg_kind,
                                        std::vector<Value> &stack) const {
   return GetSymbolFileDWARF().ParseVendorDWARFOpcode(op, opcodes, offset,
-                                                     stack);
+                                                     reg_ctx, reg_kind, stack);
 }
 
 bool DWARFUnit::ParseDWARFLocationList(
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
index f55400eeaa448..91a693860c55a 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -164,9 +164,11 @@ class DWARFUnit : public DWARFExpression::Delegate, public UserID {
                                           const lldb::offset_t data_offset,
                                           const uint8_t op) const override;
 
-  bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
-                              lldb::offset_t &offset,
-                              std::vector<Value> &stack) const override;
+  virtual bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
+                                      lldb::offset_t &offset,
+                                      RegisterContext *reg_ctx,
+                                      lldb::RegisterKind reg_kind,
+                                      std::vector<Value> &stack) const override;
 
   bool ParseDWARFLocationList(const DataExtractor &data,
                               DWARFExpressionList &loc_list) const;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 4b4a58297ded4..fe90dc1099c8b 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -32,6 +32,7 @@
 
 #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
 #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
 
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
@@ -41,6 +42,7 @@
 
 #include "Plugins/ExpressionParser/Clang/ClangUtil.h"
 #include "Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileWasm.h"
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
 #include "lldb/Symbol/Block.h"
 #include "lldb/Symbol/CompileUnit.h"
@@ -327,6 +329,9 @@ llvm::StringRef SymbolFileDWARF::GetPluginDescriptionStatic() {
 }
 
 SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFileSP objfile_sp) {
+  if (llvm::isa<lldb_private::wasm::ObjectFileWasm>(*objfile_sp))
+    return new SymbolFileWasm(std::move(objfile_sp),
+                              /*dwo_section_list*/ nullptr);
   return new SymbolFileDWARF(std::move(objfile_sp),
                              /*dwo_section_list*/ nullptr);
 }
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 2dc862cccca14..56d8ccbd97e46 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -329,6 +329,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
 
   virtual bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
                                       lldb::offset_t &offset,
+                                      RegisterContext *reg_ctx,
+                                      lldb::RegisterKind reg_kind,
                                       std::vector<Value> &stack) const {
     return false;
   }
@@ -556,6 +558,7 @@ class SymbolFileDWARF : public SymbolFileCommon {
   /// an index that identifies the .DWO or .o file.
   std::optional<uint64_t> m_file_index;
 };
+
 } // namespace dwarf
 } // namespace lldb_private::plugin
 
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
index c1829abe72933..52de3abca4b77 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
@@ -97,9 +97,11 @@ uint64_t SymbolFileDWARFDwo::GetDebugInfoSize(bool load_all_debug_info) {
 }
 
 bool SymbolFileDWARFDwo::ParseVendorDWARFOpcode(
-    uint8_t op, const lldb_private::DataExtractor &opcodes,
-    lldb::offset_t &offset, std::vector<lldb_private::Value> &stack) const {
-  return GetBaseSymbolFile().ParseVendorDWARFOpcode(op, opcodes, offset, stack);
+    uint8_t op, const DataExtractor &opcodes, lldb::offset_t &offset,
+    RegisterContext *reg_ctx, lldb::RegisterKind reg_kind,
+    std::vector<Value> &stack) const {
+  return GetBaseSymbolFile().ParseVendorDWARFOpcode(op, opcodes, offset,
+                                                    reg_ctx, reg_kind, stack);
 }
 
 llvm::DenseMap<const DWARFDebugInfoEntry *, Type *> &
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h
index 75f5986f14014..1ab6494f8ef7f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h
@@ -50,7 +50,8 @@ class SymbolFileDWARFDwo : public SymbolFileDWARF {
   uint64_t GetDebugInfoSize(bool load_all_debug_info = false) override;
 
   bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
-                              lldb::offset_t &offset,
+                              lldb::offset_t &offset, RegisterContext *reg_ctx,
+                              lldb::RegisterKind reg_kind,
                               std::vector<Value> &stack) const override;
 
   void FindGlobalVariables(ConstString name,
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp
new file mode 100644
index 0000000000000..94250d30b787c
--- /dev/null
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileWasm.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::plugin::dwarf;
+SymbolFileWasm::SymbolFileWasm(ObjectFileSP objfile_sp,
+                               SectionList *dwo_section_list)
+    : SymbolFileDWARF(objfile_sp, dwo_section_list) {}
+
+SymbolFileWasm::~SymbolFileWasm() = default;
+
+lldb::offset_t
+SymbolFileWasm::GetVendorDWARFOpcodeSize(const DataExtractor &data,
+                                         const lldb::offset_t data_offset,
+                                         const uint8_t op) const {
+  if (op != llvm::dwarf::DW_OP_WASM_location) {
+    return LLDB_INVALID_OFFSET;
+  }
+
+  lldb::offset_t offset = data_offset;
+  uint8_t wasm_op = data.GetU8(&offset);
+  if (wasm_op == 3) {
+    data.GetU32(&offset);
+  } else {
+    data.GetULEB128(&offset);
+  }
+  return offset - data_offset;
+}
+
+bool SymbolFileWasm::ParseVendorDWARFOpcode(uint8_t op,
+                                            const DataExtractor &opcodes,
+                                            lldb::offset_t &offset,
+                                            RegisterContext *reg_ctx,
+                                            lldb::RegisterKind reg_kind,
+                                            std::vector<Value> &stack) const {
+  if (op != llvm::dwarf::DW_OP_WASM_location) {
+    return false;
+  }
+
+  uint8_t wasm_op = opcodes.GetU8(&offset);
+
+  /* LLDB doesn't have an address space to represents WebAssembly locals,
+   * globals and operand stacks.
+   * We encode these elements into virtual registers:
+   *   | tag: 2 bits | index: 30 bits |
+   *   where tag is:
+   *    0: Not a WebAssembly location
+   *    1: Local
+   *    2: Global
+   *    3: Operand stack value
+   */
+  uint32_t index;
+  if (wasm_op == 3) {
+    index = opcodes.GetU32(&offset);
+    wasm_op = 2; // Global
+  } else {
+    index = opcodes.GetULEB128(&offset);
+  }
+
+  uint32_t reg_num = (((wasm_op + 1) & 0x03) << 30) | (index & 0x3fffffff);
+
+  Value tmp;
+  llvm::Error error = DWARFExpression::ReadRegisterValueAsScalar(
+      reg_ctx, reg_kind, reg_num, tmp);
+  if (error)
+    return false;
+
+  stack.push_back(tmp);
+  return true;
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h
new file mode 100644
index 0000000000000..0e0b742f53f18
--- /dev/null
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEWASM_H
+#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEWASM_H
+
+#include "SymbolFileDWARF.h"
+
+namespace lldb_private::plugin {
+namespace dwarf {
+class SymbolFileWasm : public SymbolFileDWARF {
+public:
+  SymbolFileWasm(lldb::ObjectFileSP objfile_sp, SectionList *dwo_section_list);
+
+  ~SymbolFileWasm() override;
+
+  lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data,
+                                          const lldb::offset_t data_offset,
+                                          const uint8_t op) const override;
+
+  bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
+                              lldb::offset_t &offset, RegisterContext *reg_ctx,
+                              lldb::RegisterKind reg_kind,
+                              std::vector<Value> &stack) const override;
+};
+} // namespace dwarf
+} // namespace lldb_private::plugin
+
+#endif
diff --git a/lldb/unittests/Expression/CMakeLists.txt b/lldb/unittests/Expression/CMakeLists.txt
index 185b19f84cae7..533cdc673e6d1 100644
--- a/lldb/unittests/Expression/CMakeLists.txt
+++ b/lldb/unittests/Expression/CMakeLists.txt
@@ -8,6 +8,8 @@ add_lldb_unittest(ExpressionTests
   LINK_LIBS
     lldbCore
     lldbPluginObjectFileELF
+    lldbPluginObjectFileWasm
+    lldbPluginSymbolVendorWasm
     lldbPluginPlatformLinux
     lldbPluginExpressionParserClang
     lldbPluginTypeSystemClang
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 819c9739dde7d..a5d59ecac7bfc 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -7,9 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Expression/DWARFExpression.h"
+#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
 #include "Plugins/Platform/Linux/PlatformLinux.h"
 #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileWasm.h"
+#include "Plugins/SymbolVendor/wasm/SymbolVendorWasm.h"
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
 #include "TestingSupport/Symbol/YAMLModuleTester.h"
 #include "lldb/Core/Debugger.h"
@@ -18,27 +21,114 @@
 #include "lldb/Core/dwarf.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/RegisterValue.h"
 #include "lldb/Utility/StreamString.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 
-using namespace lldb_private;
 using namespace lldb_private::plugin::dwarf;
+using namespace lldb_private::wasm;
+using namespace lldb_private;
 using namespace llvm::dwarf;
 
+namespace {
+struct MockProcess : Process {
+  MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
+      : Process(target_sp, listener_sp) {}
+
+  llvm::StringRef GetPluginName() override { return "mock process"; }
+  bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
+    return false;
+  };
+  Status DoDestroy() override { return {}; }
+  void RefreshStateAfterStop() override {}
+  bool DoUpdateThreadList(ThreadList &old_thread_list,
+                          ThreadList &new_thread_list) override {
+    return false;
+  };
+  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+                      Status &error) override {
+    for (size_t i = 0; i < size; ++i)
+      ((char *)buf)[i] = (vm_addr + i) & 0xff;
+    error.Clear();
+    return size;
+  }
+};
+
+class MockThread : public Thread {
+public:
+  MockThread(Process &process) : Thread(process, 1 /* tid */), m_reg_ctx_sp() {}
+  ~MockThread() override { DestroyThread(); }
+
+  void RefreshStateAfterStop() override {}
+  lldb::RegisterContextSP GetRegisterContext() override { return m_reg_ctx_sp; }
+  lldb::RegisterContextSP
+  CreateRegisterContextForFrame(StackFrame *frame) override {
+    return m_reg_ctx_sp;
+  }
+  bool CalculateStopInfo() override { return false; }
+
+  void SetRegisterContext(lldb::RegisterContextSP reg_ctx_sp) {
+    m_reg_ctx_sp = reg_ctx_sp;
+  }
+
+private:
+  lldb::RegisterContextSP m_reg_ctx_sp;
+};
+
+class MockRegisterContext : public RegisterContext {
+public:
+  MockRegisterContext(Thread &thread, const RegisterValue &reg_value)
+      : RegisterContext(thread, 0 /*concrete_frame_idx*/),
+        m_reg_value(reg_value) {}
+
+  void InvalidateAllRegisters() override {}
+  size_t GetRegisterCount() override { return 0; }
+  const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override {
+    if (reg == 0x4000002a) {
+      return &m_reg_info;
+    }
+    return &m_reg_info; /* nullptr; */
+  }
+  size_t GetRegisterSetCount() override { return 0; }
+  const RegisterSet *GetRegisterSet(size_t reg_set) override { return nullptr; }
+  lldb::ByteOrder GetByteOrder() override {
+    return lldb::ByteOrder::eByteOrderLittle;
+  }
+  bool ReadRegister(const RegisterInfo *reg_info,
+                    RegisterValue &reg_value) override {
+    reg_value = m_reg_value;
+    return true;
+  }
+  bool WriteRegister(const RegisterInfo *reg_info,
+                     const RegisterValue &reg_value) override {
+    return false;
+  }
+  uint32_t ConvertRegisterKindToRegisterNumb...
[truncated]

@JDevlieghere JDevlieghere force-pushed the DW_OP_WASM_location branch from a2a7cf5 to 8207dbf Compare July 28, 2025 20:16
JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Jul 28, 2025
This PR implements a register context for Wasm, which uses virtual
registers to resolve Wasm local, globals and stack values. The registers
are used to implement supprot for `DW_OP_WASM_location` in the DWARF
expression evaluator (llvm#151010). This also adds a more comprehensive
test, showing that we can use this to show local variables.
JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Jul 28, 2025
This PR implements a register context for Wasm, which uses virtual
registers to resolve Wasm local, globals and stack values. The registers
are used to implement supprot for `DW_OP_WASM_location` in the DWARF
expression evaluator (llvm#151010). This also adds a more comprehensive
test, showing that we can use this to show local variables.

(cherry picked from commit 6eda77e)
@DavidSpickett
Copy link
Collaborator

Please include in the PR description a citation / standards reference / whatever as best you can for the specification of this operation. It doesn't look vendor specific but it doesn't appear in the DWARF 5 standard.

https://yurydelendik.github.io/webassembly-dwarf/ is from 2020 so is this part of a hypothetical DWARF6?

Anyway, citations, such as they are, plz :)

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits and kinda thought about the DWARF side with refernce to https://yurydelendik.github.io/webassembly-dwarf/, but I've not done a lot with the parsing code so I don't know about the details.

Nominate @Michael137 to also review, or pass it on to someone who can.

Add support for DW_OP_WASM_location in DWARFExpression. This PR rebases llvm#78977 and cleans up the unit test.
@JDevlieghere JDevlieghere force-pushed the DW_OP_WASM_location branch from dc29048 to c9cea26 Compare July 29, 2025 20:00
JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Jul 29, 2025
This PR implements a register context for Wasm, which uses virtual
registers to resolve Wasm local, globals and stack values. The registers
are used to implement supprot for `DW_OP_WASM_location` in the DWARF
expression evaluator (llvm#151010). This also adds a more comprehensive
test, showing that we can use this to show local variables.
Comment on lines 33 to 36
if (wasm_op == eWasmTagOperandStack)
data.GetU32(&offset);
else
data.GetULEB128(&offset);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be misreading https://yurydelendik.github.io/webassembly-dwarf/#DWARF-expressions-and-location-descriptions, but isn't the U32 encoding for the globals? And only in the 0x3 case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just re-use the logic in SymbolFileWasm::ParseVendorDWARFOpcode for this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh i see why this worked. The wasm_op value doesn't map to the WasmVirtualRegisterKinds values right? So wasm_op == eWasmTagOperandStack really means wasm_op is the 0x3 form of the Globals tag

Copy link
Member Author

@JDevlieghere JDevlieghere Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's right. I didn't realize I had reused the enum value here, that's confusing. Let's reuse the switch here.

Copy link
Member

@Michael137 Michael137 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

Modulo the comment in GetVendorDWARFOpcodeSize

@JDevlieghere JDevlieghere merged commit c2548a8 into llvm:main Jul 30, 2025
9 checks passed
@JDevlieghere JDevlieghere deleted the DW_OP_WASM_location branch July 30, 2025 16:20
rupprecht added a commit to rupprecht/llvm-project that referenced this pull request Jul 30, 2025
JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Jul 30, 2025
This PR implements a register context for Wasm, which uses virtual
registers to resolve Wasm local, globals and stack values. The registers
are used to implement supprot for `DW_OP_WASM_location` in the DWARF
expression evaluator (llvm#151010). This also adds a more comprehensive
test, showing that we can use this to show local variables.
JDevlieghere added a commit that referenced this pull request Jul 31, 2025
This PR implements a register context for Wasm, which uses virtual
registers to resolve Wasm local, globals and stack values. The registers
are used to implement support for `DW_OP_WASM_location` in the DWARF
expression evaluator (#151010). This also adds a more comprehensive
test, showing that we can use this to show local variables.
JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Jul 31, 2025
Add support for DW_OP_WASM_location in DWARFExpression. This PR rebases
at https://yurydelendik.github.io/webassembly-dwarf/ and supported by
LLVM-based toolchains such as Clang, Swift, Emscripten, and Rust.

(cherry picked from commit c2548a8)
JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Jul 31, 2025
This PR implements a register context for Wasm, which uses virtual
registers to resolve Wasm local, globals and stack values. The registers
are used to implement supprot for `DW_OP_WASM_location` in the DWARF
expression evaluator (llvm#151010). This also adds a more comprehensive
test, showing that we can use this to show local variables.

(cherry picked from commit 1c70fa8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants