https://github.com/JDevlieghere updated 
https://github.com/llvm/llvm-project/pull/151010

>From fe6624eafac163aa5e62c0af72d63831a1ec3160 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jo...@devlieghere.com>
Date: Mon, 28 Jul 2025 10:38:02 -0700
Subject: [PATCH 1/2] [lldb] Support DW_OP_WASM_location in DWARFExpression

Add support for DW_OP_WASM_location in DWARFExpression. This PR rebases #78977 
and cleans up the unit test.
---
 .../include/lldb/Expression/DWARFExpression.h |  12 +-
 lldb/source/Expression/DWARFExpression.cpp    |  40 +-
 .../ObjectFile/wasm/ObjectFileWasm.cpp        |   6 +-
 .../Plugins/SymbolFile/DWARF/CMakeLists.txt   |   1 +
 .../Plugins/SymbolFile/DWARF/DWARFUnit.cpp    |   4 +-
 .../Plugins/SymbolFile/DWARF/DWARFUnit.h      |   8 +-
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      |   4 +
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |   3 +
 .../SymbolFile/DWARF/SymbolFileDWARFDwo.cpp   |   8 +-
 .../SymbolFile/DWARF/SymbolFileDWARFDwo.h     |   3 +-
 .../SymbolFile/DWARF/SymbolFileWasm.cpp       |  78 +++
 .../Plugins/SymbolFile/DWARF/SymbolFileWasm.h |  34 ++
 lldb/unittests/Expression/CMakeLists.txt      |   2 +
 .../Expression/DWARFExpressionTest.cpp        | 556 +++++++++++++-----
 14 files changed, 595 insertions(+), 164 deletions(-)
 create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp
 create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h

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..41ab8d1b75b34 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -41,6 +41,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 +328,9 @@ llvm::StringRef 
SymbolFileDWARF::GetPluginDescriptionStatic() {
 }
 
 SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFileSP objfile_sp) {
+  if (objfile_sp->GetArchitecture().GetTriple().isWasm())
+    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 ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+                                               uint32_t num) override {
+    return num;
+  }
+
+private:
+  RegisterInfo m_reg_info;
+  RegisterValue m_reg_value;
+};
+} // namespace
+
 static llvm::Expected<Scalar> Evaluate(llvm::ArrayRef<uint8_t> expr,
                                        lldb::ModuleSP module_sp = {},
                                        DWARFUnit *unit = nullptr,
-                                       ExecutionContext *exe_ctx = nullptr) {
+                                       ExecutionContext *exe_ctx = nullptr,
+                                       RegisterContext *reg_ctx = nullptr) {
   DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle,
                           /*addr_size*/ 4);
 
-  llvm::Expected<Value> result =
-      DWARFExpression::Evaluate(exe_ctx, /*reg_ctx*/ nullptr, module_sp,
-                                extractor, unit, lldb::eRegisterKindLLDB,
-                                /*initial_value_ptr*/ nullptr,
-                                /*object_address_ptr*/ nullptr);
+  llvm::Expected<Value> result = DWARFExpression::Evaluate(
+      exe_ctx, reg_ctx, module_sp, extractor, unit, lldb::eRegisterKindLLDB,
+      /*initial_value_ptr*/ nullptr,
+      /*object_address_ptr*/ nullptr);
   if (!result)
     return result.takeError();
 
@@ -53,7 +143,7 @@ static llvm::Expected<Scalar> 
Evaluate(llvm::ArrayRef<uint8_t> expr,
     if (buf.GetByteSize() <= 8) {
       uint64_t val = 0;
       memcpy(&val, buf.GetBytes(), buf.GetByteSize());
-      return Scalar(llvm::APInt(buf.GetByteSize()*8, val, false));
+      return Scalar(llvm::APInt(buf.GetByteSize() * 8, val, false));
     }
   }
     [[fallthrough]];
@@ -65,8 +155,8 @@ static llvm::Expected<Scalar> 
Evaluate(llvm::ArrayRef<uint8_t> expr,
 
 class DWARFExpressionTester : public YAMLModuleTester {
 public:
-  DWARFExpressionTester(llvm::StringRef yaml_data, size_t cu_index) :
-      YAMLModuleTester(yaml_data, cu_index) {}
+  DWARFExpressionTester(llvm::StringRef yaml_data, size_t cu_index)
+      : YAMLModuleTester(yaml_data, cu_index) {}
 
   using YAMLModuleTester::YAMLModuleTester;
   llvm::Expected<Scalar> Eval(llvm::ArrayRef<uint8_t> expr) {
@@ -377,30 +467,6 @@ TEST(DWARFExpression, DW_OP_unknown) {
 TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit0, DW_OP_deref}), llvm::Failed());
 
-  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;
-    }
-  };
-
   // Set up a mock process.
   ArchSpec arch("i386-pc-linux");
   Platform::SetHostPlatform(
@@ -421,9 +487,9 @@ TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {
 
   ExecutionContext exe_ctx(process_sp);
   // Implicit location: *0x4.
-  EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value},
-                                {}, {}, &exe_ctx),
-                       llvm::HasValue(GetScalar(32, 0x07060504, false)));
+  EXPECT_THAT_EXPECTED(
+      Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx),
+      llvm::HasValue(GetScalar(32, 0x07060504, false)));
   // Memory location: *(*0x4).
   // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref}, {}, {}, &exe_ctx),
@@ -618,10 +684,12 @@ class CustomSymbolFileDWARF : public SymbolFileDWARF {
     return offset - data_offset;
   }
 
-  bool
-  ParseVendorDWARFOpcode(uint8_t op, const lldb_private::DataExtractor 
&opcodes,
-                         lldb::offset_t &offset,
-                         std::vector<lldb_private::Value> &stack) const final {
+  virtual bool ParseVendorDWARFOpcode(
+      uint8_t op, const lldb_private::DataExtractor &opcodes,
+      lldb::offset_t &offset,
+
+      RegisterContext *reg_ctx, lldb::RegisterKind reg_kind,
+      std::vector<lldb_private::Value> &stack) const override {
     if (op != DW_OP_WASM_location) {
       return false;
     }
@@ -647,13 +715,14 @@ class CustomSymbolFileDWARF : public SymbolFileDWARF {
 char CustomSymbolFileDWARF::ID;
 
 static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp,
-                                           DWARFUnit &dwarf_unit) {
+                                           DWARFUnit &dwarf_unit,
+                                           RegisterContext *reg_ctx) {
   // Test that expression extensions can be evaluated, for example
   // DW_OP_WASM_location which is not currently handled by DWARFExpression:
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03
                                  0x04, 0x00, 0x00,          // index:u32
                                  0x00, DW_OP_stack_value},
-                                module_sp, &dwarf_unit),
+                                module_sp, &dwarf_unit, nullptr, reg_ctx),
                        llvm::HasValue(GetScalar(32, 42, false)));
 
   // Test that searches for opcodes work in the presence of extensions:
@@ -667,138 +736,331 @@ static auto 
testExpressionVendorExtensions(lldb::ModuleSP module_sp,
 
 TEST(DWARFExpression, Extensions) {
   const char *yamldata = R"(
---- !ELF
+--- !WASM
 FileHeader:
-  Class:   ELFCLASS64
-  Data:    ELFDATA2LSB
-  Type:    ET_EXEC
-  Machine: EM_386
-DWARF:
-  debug_abbrev:
-    - Table:
-        - Code:            0x00000001
-          Tag:             DW_TAG_compile_unit
-          Children:        DW_CHILDREN_no
-  debug_info:
-    - Version:         4
-      AddrSize:        4
-      Entries:
-        - AbbrCode:        0x1
-        - AbbrCode:        0x0
+  Version:         0x1
+Sections:
+  - Type:            TYPE
+    Signatures:
+      - Index:           0
+        ParamTypes:
+          - I32
+        ReturnTypes:
+          - I32
+  - Type:            FUNCTION
+    FunctionTypes:   [ 0 ]
+  - Type:            TABLE
+    Tables:
+      - Index:           0
+        ElemType:        FUNCREF
+        Limits:
+          Flags:           [ HAS_MAX ]
+          Minimum:         0x1
+          Maximum:         0x1
+  - Type:            MEMORY
+    Memories:
+      - Flags:           [ HAS_MAX ]
+        Minimum:         0x100
+        Maximum:         0x100
+  - Type:            GLOBAL
+    Globals:
+      - Index:           0
+        Type:            I32
+        Mutable:         true
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           65536
+  - Type:            EXPORT
+    Exports:
+      - Name:            memory
+        Kind:            MEMORY
+        Index:           0
+      - Name:            square
+        Kind:            FUNCTION
+        Index:           0
+      - Name:            __indirect_function_table
+        Kind:            TABLE
+        Index:           0
+  - Type:            CODE
+    Functions:
+      - Index:           0
+        Locals:
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+        Body:            
2300210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B
+  - Type:            CUSTOM
+    Name:            name
+    FunctionNames:
+      - Index:           0
+        Name:            square
+    GlobalNames:
+      - Index:           0
+        Name:            __stack_pointer
+  - Type:            CUSTOM
+    Name:            .debug_abbrev
+    Payload:         
011101250E1305030E10171B0E110112060000022E01110112064018030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042400030E3E0B0B0B000000
+  - Type:            CUSTOM
+    Name:            .debug_info
+    Payload:         
510000000400000000000401670000001D005E000000000000000A000000020000003C00000002020000003C00000004ED00039F5700000001014D0000000302910C0400000001014D000000000400000000050400
+  - Type:            CUSTOM
+    Name:            .debug_str
+    Payload:         
696E740076616C756500513A5C70616F6C6F7365764D5346545C6C6C766D2D70726F6A6563745C6C6C64625C746573745C4150495C66756E6374696F6E616C69746965735C6764625F72656D6F74655F636C69656E745C737175617265007371756172652E6300636C616E672076657273696F6E2031382E302E30202868747470733A2F2F6769746875622E636F6D2F6C6C766D2F6C6C766D2D70726F6A65637420373535303166353336323464653932616166636532663164613639386232343961373239336463372900
+  - Type:            CUSTOM
+    Name:            .debug_line
+    Payload:         
64000000040020000000010101FB0E0D000101010100000001000001007371756172652E6300000000000005020200000001000502250000000301050A0A010005022C00000005120601000502330000000510010005023A0000000503010005023E000000000101
 )";
 
-  SubsystemRAII<FileSystem, HostInfo, TypeSystemClang, ObjectFileELF,
-                CustomSymbolFileDWARF>
+  SubsystemRAII<FileSystem, HostInfo, ObjectFileWasm, SymbolVendorWasm>
       subsystems;
 
+  // Set up a wasm target
+  ArchSpec arch("wasm32-unknown-unknown-wasm");
+  lldb::PlatformSP host_platform_sp =
+      platform_linux::PlatformLinux::CreateInstance(true, &arch);
+  ASSERT_TRUE(host_platform_sp);
+  Platform::SetHostPlatform(host_platform_sp);
+  lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
+  ASSERT_TRUE(debugger_sp);
+  lldb::TargetSP target_sp;
+  lldb::PlatformSP platform_sp;
+  debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
+                                            lldb_private::eLoadDependentsNo,
+                                            platform_sp, target_sp);
+  // Set up a mock process and thread.
+  lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
+  lldb::ProcessSP process_sp =
+      std::make_shared<MockProcess>(target_sp, listener_sp);
+  ASSERT_TRUE(process_sp);
+  MockThread thread(*process_sp);
+  const uint32_t kExpectedValue = 42;
+  lldb::RegisterContextSP reg_ctx_sp = std::make_shared<MockRegisterContext>(
+      thread, RegisterValue(kExpectedValue));
+  thread.SetRegisterContext(reg_ctx_sp);
+
   llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata);
   EXPECT_THAT_EXPECTED(file, llvm::Succeeded());
-
   auto module_sp = std::make_shared<Module>(file->moduleSpec());
-  auto &symfile =
-      *llvm::cast<CustomSymbolFileDWARF>(module_sp->GetSymbolFile());
-  auto *dwarf_unit = symfile.DebugInfo().GetUnitAtIndex(0);
+  auto obj_file_sp = module_sp->GetObjectFile()->shared_from_this();
+  SymbolFileWasm sym_file_wasm(obj_file_sp, nullptr);
+  auto *dwarf_unit = sym_file_wasm.DebugInfo().GetUnitAtIndex(0);
 
-  testExpressionVendorExtensions(module_sp, *dwarf_unit);
+  testExpressionVendorExtensions(module_sp, *dwarf_unit, reg_ctx_sp.get());
 }
 
-TEST(DWARFExpression, ExtensionsDWO) {
+TEST(DWARFExpression, ExtensionsSplitSymbols) {
   const char *skeleton_yamldata = R"(
---- !ELF
+--- !WASM
 FileHeader:
-  Class:   ELFCLASS64
-  Data:    ELFDATA2LSB
-  Type:    ET_EXEC
-  Machine: EM_386
-DWARF:
-  debug_abbrev:
-    - Table:
-        - Code:            0x00000001
-          Tag:             DW_TAG_skeleton_unit
-          Children:        DW_CHILDREN_no
-          Attributes:
-            - Attribute:       DW_AT_dwo_name
-              Form:            DW_FORM_string
-            - Attribute:       DW_AT_dwo_id
-              Form:            DW_FORM_data4
-  debug_info:
-    - Version:         4
-      AddrSize:        4
-      Entries:
-        - AbbrCode:        0x1
-          Values:
-            - CStr:           "dwo_unit"
-            - Value:           0x01020304
-        - AbbrCode:        0x0
+  Version:         0x1
+Sections:
+  - Type:            TYPE
+    Signatures:
+      - Index:           0
+        ParamTypes:
+          - I32
+        ReturnTypes:
+          - I32
+  - Type:            FUNCTION
+    FunctionTypes:   [ 0 ]
+  - Type:            TABLE
+    Tables:
+      - Index:           0
+        ElemType:        FUNCREF
+        Limits:
+          Flags:           [ HAS_MAX ]
+          Minimum:         0x1
+          Maximum:         0x1
+  - Type:            MEMORY
+    Memories:
+      - Flags:           [ HAS_MAX ]
+        Minimum:         0x100
+        Maximum:         0x100
+  - Type:            GLOBAL
+    Globals:
+      - Index:           0
+        Type:            I32
+        Mutable:         true
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           65536
+  - Type:            EXPORT
+    Exports:
+      - Name:            memory
+        Kind:            MEMORY
+        Index:           0
+      - Name:            square
+        Kind:            FUNCTION
+        Index:           0
+      - Name:            __indirect_function_table
+        Kind:            TABLE
+        Index:           0
+  - Type:            CODE
+    Functions:
+      - Index:           0
+        Locals:
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+        Body:            
2300210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B
+  - Type:            CUSTOM
+    Name:            name
+    FunctionNames:
+      - Index:           0
+        Name:            square
+    GlobalNames:
+      - Index:           0
+        Name:            __stack_pointer
+  - Type:            CUSTOM
+    Name:            external_debug_info
+    Payload:         167371756172652E7761736D2E64656275672E7761736D
 )";
 
-  // .dwo sections aren't currently supported by dwarfyaml. The dwo_yamldata
-  // contents where generated by roundtripping the following yaml through
-  // yaml2obj | obj2yaml and renaming the sections. This works because the
-  // structure of the .dwo and non-.dwo sections is identical.
-  //
-  // --- !ELF
-  // FileHeader:
-  //   Class:   ELFCLASS64
-  //   Data:    ELFDATA2LSB
-  //   Type:    ET_EXEC
-  //   Machine: EM_386
-  // DWARF:
-  //   debug_abbrev: #.dwo
-  //     - Table:
-  //         - Code:            0x00000001
-  //           Tag:             DW_TAG_compile_unit
-  //           Children:        DW_CHILDREN_no
-  //           Attributes:
-  //             - Attribute:       DW_AT_dwo_id
-  //               Form:            DW_FORM_data4
-  //   debug_info: #.dwo
-  //     - Version:         4
-  //       AddrSize:        4
-  //       Entries:
-  //         - AbbrCode:        0x1
-  //           Values:
-  //             - Value:           0x0120304
-  //         - AbbrCode:        0x0
-  const char *dwo_yamldata = R"(
---- !ELF
+  const char *sym_yamldata = R"(
+--- !WASM
 FileHeader:
-  Class:           ELFCLASS64
-  Data:            ELFDATA2LSB
-  Type:            ET_EXEC
-  Machine:         EM_386
+  Version:         0x1
 Sections:
-  - Name:            .debug_abbrev.dwo
-    Type:            SHT_PROGBITS
-    AddressAlign:    0x1
-    Content:         '0111007506000000'
-  - Name:            .debug_info.dwo
-    Type:            SHT_PROGBITS
-    AddressAlign:    0x1
-    Content:         0D00000004000000000004010403020100
+  - Type:            TYPE
+    Signatures:
+      - Index:           0
+        ParamTypes:
+          - I32
+        ReturnTypes:
+          - I32
+  - Type:            FUNCTION
+    FunctionTypes:   [ 0 ]
+  - Type:            TABLE
+    Tables:
+      - Index:           0
+        ElemType:        FUNCREF
+        Limits:
+          Flags:           [ HAS_MAX ]
+          Minimum:         0x1
+          Maximum:         0x1
+  - Type:            MEMORY
+    Memories:
+      - Flags:           [ HAS_MAX ]
+        Minimum:         0x100
+        Maximum:         0x100
+  - Type:            GLOBAL
+    Globals:
+      - Index:           0
+        Type:            I32
+        Mutable:         true
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           65536
+  - Type:            EXPORT
+    Exports:
+      - Name:            memory
+        Kind:            MEMORY
+        Index:           0
+      - Name:            square
+        Kind:            FUNCTION
+        Index:           0
+      - Name:            __indirect_function_table
+        Kind:            TABLE
+        Index:           0
+  - Type:            CODE
+    Functions:
+      - Index:           0
+        Locals:
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+          - Type:            I32
+            Count:           1
+        Body:            
2300210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B
+  - Type:            CUSTOM
+    Name:            name
+    FunctionNames:
+      - Index:           0
+        Name:            square
+    GlobalNames:
+      - Index:           0
+        Name:            __stack_pointer
+  - Type:            CUSTOM
+    Name:            .debug_abbrev
+    Payload:         
011101250E1305030E10171B0E110112060000022E01110112064018030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042400030E3E0B0B0B000000
+  - Type:            CUSTOM
+    Name:            .debug_info
+    Payload:         
510000000400000000000401670000001D005E0000000000000004000000020000003C00000002020000003C00000004ED00039F5700000001014D0000000302910C5100000001014D000000000400000000050400
+  - Type:            CUSTOM
+    Name:            .debug_str
+    Payload:         
696E7400513A5C70616F6C6F7365764D5346545C6C6C766D2D70726F6A6563745C6C6C64625C746573745C4150495C66756E6374696F6E616C69746965735C6764625F72656D6F74655F636C69656E740076616C756500737175617265007371756172652E6300636C616E672076657273696F6E2031382E302E30202868747470733A2F2F6769746875622E636F6D2F6C6C766D2F6C6C766D2D70726F6A65637420373535303166353336323464653932616166636532663164613639386232343961373239336463372900
+  - Type:            CUSTOM
+    Name:            .debug_line
+    Payload:         
64000000040020000000010101FB0E0D000101010100000001000001007371756172652E6300000000000005020200000001000502250000000301050A0A010005022C00000005120601000502330000000510010005023A0000000503010005023E000000000101
 )";
 
-  SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, CustomSymbolFileDWARF>
+  SubsystemRAII<FileSystem, HostInfo, ObjectFileWasm, SymbolVendorWasm>
       subsystems;
 
+  // Set up a wasm target
+  ArchSpec arch("wasm32-unknown-unknown-wasm");
+  lldb::PlatformSP host_platform_sp =
+      platform_linux::PlatformLinux::CreateInstance(true, &arch);
+  ASSERT_TRUE(host_platform_sp);
+  Platform::SetHostPlatform(host_platform_sp);
+  lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
+  ASSERT_TRUE(debugger_sp);
+  lldb::TargetSP target_sp;
+  lldb::PlatformSP platform_sp;
+  debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
+                                            lldb_private::eLoadDependentsNo,
+                                            platform_sp, target_sp);
+  // Set up a mock process and thread.
+  lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
+  lldb::ProcessSP process_sp =
+      std::make_shared<MockProcess>(target_sp, listener_sp);
+  ASSERT_TRUE(process_sp);
+  MockThread thread(*process_sp);
+  const uint32_t kExpectedValue = 42;
+  lldb::RegisterContextSP reg_ctx_sp = std::make_shared<MockRegisterContext>(
+      thread, RegisterValue(kExpectedValue));
+  thread.SetRegisterContext(reg_ctx_sp);
+
   llvm::Expected<TestFile> skeleton_file =
       TestFile::fromYaml(skeleton_yamldata);
   EXPECT_THAT_EXPECTED(skeleton_file, llvm::Succeeded());
-  llvm::Expected<TestFile> dwo_file = TestFile::fromYaml(dwo_yamldata);
-  EXPECT_THAT_EXPECTED(dwo_file, llvm::Succeeded());
-
   auto skeleton_module_sp =
       std::make_shared<Module>(skeleton_file->moduleSpec());
-  auto &skeleton_symfile =
-      *llvm::cast<CustomSymbolFileDWARF>(skeleton_module_sp->GetSymbolFile());
 
-  auto dwo_module_sp = std::make_shared<Module>(dwo_file->moduleSpec());
-  SymbolFileDWARFDwo dwo_symfile(
-      skeleton_symfile, dwo_module_sp->GetObjectFile()->shared_from_this(),
-      0x0120304);
-  auto *dwo_dwarf_unit = dwo_symfile.DebugInfo().GetUnitAtIndex(0);
+  llvm::Expected<TestFile> sym_file = TestFile::fromYaml(sym_yamldata);
+  EXPECT_THAT_EXPECTED(sym_file, llvm::Succeeded());
+  auto sym_module_sp = std::make_shared<Module>(sym_file->moduleSpec());
+
+  auto obj_file_sp = sym_module_sp->GetObjectFile()->shared_from_this();
+  SymbolFileWasm sym_file_wasm(obj_file_sp, nullptr);
+  auto *dwarf_unit = sym_file_wasm.DebugInfo().GetUnitAtIndex(0);
 
-  testExpressionVendorExtensions(dwo_module_sp, *dwo_dwarf_unit);
+  testExpressionVendorExtensions(sym_module_sp, *dwarf_unit, reg_ctx_sp.get());
 }
 
 TEST_F(DWARFExpressionMockProcessTest, DW_OP_piece_file_addr) {

>From dc290487a1e163c2c5f609e4a9c2068b835072af Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jo...@devlieghere.com>
Date: Tue, 29 Jul 2025 11:42:52 -0700
Subject: [PATCH 2/2] Address review feedback

---
 lldb/source/Expression/DWARFExpression.cpp    | 30 ---------
 .../SymbolFile/DWARF/SymbolFileWasm.cpp       | 65 +++++++++++--------
 lldb/source/Utility/WasmVirtualRegisters.h    | 53 +++++++++++++++
 .../Expression/DWARFExpressionTest.cpp        | 41 ++++++++----
 4 files changed, 119 insertions(+), 70 deletions(-)
 create mode 100644 lldb/source/Utility/WasmVirtualRegisters.h

diff --git a/lldb/source/Expression/DWARFExpression.cpp 
b/lldb/source/Expression/DWARFExpression.cpp
index 68093e9710a28..391e27704b63d 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -2301,36 +2301,6 @@ 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, reg_ctx,
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp
index 94250d30b787c..eee76a4411a77 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp
@@ -7,10 +7,14 @@
 
//===----------------------------------------------------------------------===//
 
 #include "SymbolFileWasm.h"
+#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
+#include "Utility/WasmVirtualRegisters.h"
+#include "lldb/Utility/LLDBLog.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) {}
@@ -21,17 +25,15 @@ 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) {
+  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) {
+  const uint8_t wasm_op = data.GetU8(&offset);
+  if (wasm_op == eWasmTagOperandStack)
     data.GetU32(&offset);
-  } else {
+  else
     data.GetULEB128(&offset);
-  }
   return offset - data_offset;
 }
 
@@ -41,37 +43,48 @@ bool SymbolFileWasm::ParseVendorDWARFOpcode(uint8_t op,
                                             RegisterContext *reg_ctx,
                                             lldb::RegisterKind reg_kind,
                                             std::vector<Value> &stack) const {
-  if (op != llvm::dwarf::DW_OP_WASM_location) {
+  if (op != llvm::dwarf::DW_OP_WASM_location)
     return false;
-  }
 
-  uint8_t wasm_op = opcodes.GetU8(&offset);
+  uint32_t index = 0;
+  uint8_t tag = eWasmTagNotAWasmLocation;
 
-  /* 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 {
+  /// |DWARF Location Index | WebAssembly Construct |
+  /// |---------------------|-----------------------|
+  /// |0                    | Local                 |
+  /// |1 or 3               | Global                |
+  /// |2                    | Operand Stack         |
+  const uint8_t wasm_op = opcodes.GetU8(&offset);
+  switch (wasm_op) {
+  case 0: // LOCAL
     index = opcodes.GetULEB128(&offset);
+    tag = eWasmTagLocal;
+    break;
+  case 1: // GLOBAL_FIXED
+    index = opcodes.GetULEB128(&offset);
+    tag = eWasmTagGlobal;
+    break;
+  case 2: // OPERAND_STACK
+    index = opcodes.GetULEB128(&offset);
+    tag = eWasmTagOperandStack;
+    break;
+  case 3: // GLOBAL_RELOC
+    index = opcodes.GetU32(&offset);
+    tag = eWasmTagGlobal;
+    break;
+  default:
+    return false;
   }
 
-  uint32_t reg_num = (((wasm_op + 1) & 0x03) << 30) | (index & 0x3fffffff);
+  const uint32_t reg_num = GetWasmRegister(tag, index);
 
   Value tmp;
   llvm::Error error = DWARFExpression::ReadRegisterValueAsScalar(
       reg_ctx, reg_kind, reg_num, tmp);
-  if (error)
+  if (error) {
+    LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), std::move(error), "{0}");
     return false;
+  }
 
   stack.push_back(tmp);
   return true;
diff --git a/lldb/source/Utility/WasmVirtualRegisters.h 
b/lldb/source/Utility/WasmVirtualRegisters.h
new file mode 100644
index 0000000000000..404a5ae35dc91
--- /dev/null
+++ b/lldb/source/Utility/WasmVirtualRegisters.h
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_UTILITY_WASM_VIRTUAL_REGISTERS_H
+#define LLDB_SOURCE_UTILITY_WASM_VIRTUAL_REGISTERS_H
+
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+// 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 Wasm location
+//    1: Local
+//    2: Global
+//    3: Operand stack value
+enum WasmVirtualRegisterKinds {
+  eWasmTagNotAWasmLocation = 0,
+  eWasmTagLocal = 1,
+  eWasmTagGlobal = 2,
+  eWasmTagOperandStack = 3,
+};
+
+static const uint32_t kWasmVirtualRegisterTagMask = 0x03;
+static const uint32_t kWasmVirtualRegisterIndexMask = 0x3fffffff;
+static const uint32_t kWasmVirtualRegisterTagShift = 30;
+
+inline uint32_t GetWasmVirtualRegisterTag(size_t reg) {
+  return (reg >> kWasmVirtualRegisterTagShift) & kWasmVirtualRegisterTagMask;
+}
+
+inline uint32_t GetWasmVirtualRegisterIndex(size_t reg) {
+  return reg & kWasmVirtualRegisterIndexMask;
+}
+
+inline uint32_t GetWasmRegister(uint8_t tag, uint32_t index) {
+  return ((tag & kWasmVirtualRegisterTagMask) << kWasmVirtualRegisterTagShift) 
|
+         (index & kWasmVirtualRegisterIndexMask);
+}
+
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp 
b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index a5d59ecac7bfc..8b1b9336190a2 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -39,15 +39,20 @@ struct MockProcess : Process {
       : 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)
@@ -59,15 +64,18 @@ struct MockProcess : Process {
 
 class MockThread : public Thread {
 public:
-  MockThread(Process &process) : Thread(process, 1 /* tid */), m_reg_ctx_sp() 
{}
+  MockThread(Process &process) : Thread(process, /*tid=*/1), 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) {
@@ -85,27 +93,32 @@ class MockRegisterContext : public RegisterContext {
         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; */
+    return &m_reg_info;
   }
+
   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 ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
                                                uint32_t num) override {
     return num;
@@ -127,8 +140,8 @@ static llvm::Expected<Scalar> 
Evaluate(llvm::ArrayRef<uint8_t> expr,
 
   llvm::Expected<Value> result = DWARFExpression::Evaluate(
       exe_ctx, reg_ctx, module_sp, extractor, unit, lldb::eRegisterKindLLDB,
-      /*initial_value_ptr*/ nullptr,
-      /*object_address_ptr*/ nullptr);
+      /*initial_value_ptr=*/nullptr,
+      /*object_address_ptr=*/nullptr);
   if (!result)
     return result.takeError();
 
@@ -823,7 +836,7 @@ TEST(DWARFExpression, Extensions) {
   SubsystemRAII<FileSystem, HostInfo, ObjectFileWasm, SymbolVendorWasm>
       subsystems;
 
-  // Set up a wasm target
+  // Set up a wasm target.
   ArchSpec arch("wasm32-unknown-unknown-wasm");
   lldb::PlatformSP host_platform_sp =
       platform_linux::PlatformLinux::CreateInstance(true, &arch);
@@ -1022,7 +1035,7 @@ TEST(DWARFExpression, ExtensionsSplitSymbols) {
   SubsystemRAII<FileSystem, HostInfo, ObjectFileWasm, SymbolVendorWasm>
       subsystems;
 
-  // Set up a wasm target
+  // Set up a wasm target.
   ArchSpec arch("wasm32-unknown-unknown-wasm");
   lldb::PlatformSP host_platform_sp =
       platform_linux::PlatformLinux::CreateInstance(true, &arch);
@@ -1090,12 +1103,12 @@ TEST_F(DWARFExpressionMockProcessTest, 
DW_OP_piece_file_addr) {
   uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0, DW_OP_piece, 1,
                     DW_OP_addr, 0x50, 0x0, 0x0, 0x0, DW_OP_piece, 1};
   DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
-                          /*addr_size*/ 4);
+                          /*addr_size=*/4);
   llvm::Expected<Value> result = DWARFExpression::Evaluate(
-      &exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor,
-      /*unit*/ nullptr, lldb::eRegisterKindLLDB,
-      /*initial_value_ptr*/ nullptr,
-      /*object_address_ptr*/ nullptr);
+      &exe_ctx, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor,
+      /*unit=*/nullptr, lldb::eRegisterKindLLDB,
+      /*initial_value_ptr=*/nullptr,
+      /*object_address_ptr=*/nullptr);
 
   ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
   ASSERT_EQ(result->GetValueType(), Value::ValueType::HostAddress);

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to