diff --git a/crates/debug/src/transform/unit.rs b/crates/debug/src/transform/unit.rs index 01835ccdf7..d8711b5f1e 100644 --- a/crates/debug/src/transform/unit.rs +++ b/crates/debug/src/transform/unit.rs @@ -82,20 +82,31 @@ where Ok(String::from("??")) } +enum WebAssemblyPtrKind { + Reference, + Pointer, +} + /// Replaces WebAssembly pointer type DIE with the wrapper /// which natively represented by offset in a Wasm memory. /// -/// `pointer_type_entry` is an DW_TAG_pointer_type entry (e.g. `T*`), -/// which refers its base type (e.g. `T`). +/// `pointer_type_entry` is a DW_TAG_pointer_type entry (e.g. `T*`), +/// which refers its base type (e.g. `T`), or is a +/// DW_TAG_reference_type (e.g. `T&`). /// /// The generated wrapper is a structure that contains only the /// `__ptr` field. The utility operators overloads is added to /// provide better debugging experience. /// +/// Wrappers of pointer and reference types are identical except for +/// their name -- they are formatted and accessed from a debugger +/// the same way. +/// /// Notice that "resolve_vmctx_memory_ptr" is external/builtin /// subprogram that is not part of Wasm code. fn replace_pointer_type( parent_id: write::UnitEntryId, + kind: WebAssemblyPtrKind, comp_unit: &mut write::Unit, wp_die_id: write::UnitEntryId, pointer_type_entry: &DebuggingInformationEntry, @@ -121,11 +132,18 @@ where // Build DW_TAG_structure_type for the wrapper: // .. DW_AT_name = "WebAssemblyPtrWrapper", // .. DW_AT_byte_size = 4, - add_tag!(parent_id, gimli::DW_TAG_structure_type => wrapper_die as wrapper_die_id { - gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add(format!( + let name = match kind { + WebAssemblyPtrKind::Pointer => format!( "WebAssemblyPtrWrapper<{}>", get_base_type_name(pointer_type_entry, unit, context)? - ).as_str())), + ), + WebAssemblyPtrKind::Reference => format!( + "WebAssemblyRefWrapper<{}>", + get_base_type_name(pointer_type_entry, unit, context)? + ), + }; + add_tag!(parent_id, gimli::DW_TAG_structure_type => wrapper_die as wrapper_die_id { + gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add(name.as_str())), gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN) }); @@ -393,11 +411,17 @@ where let parent = stack.last().unwrap(); - if entry.tag() == gimli::DW_TAG_pointer_type { + if entry.tag() == gimli::DW_TAG_pointer_type || entry.tag() == gimli::DW_TAG_reference_type + { // Wrap pointer types. - // TODO reference types? + let pointer_kind = match entry.tag() { + gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer, + gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference, + _ => panic!(), + }; let die_id = replace_pointer_type( *parent, + pointer_kind, comp_unit, wp_die_id, entry, diff --git a/tests/all/debug/lldb.rs b/tests/all/debug/lldb.rs index 6677acfb44..c29e757b31 100644 --- a/tests/all/debug/lldb.rs +++ b/tests/all/debug/lldb.rs @@ -166,3 +166,37 @@ check: resuming )?; Ok(()) } + +#[test] +#[ignore] +#[cfg(all( + any(target_os = "linux", target_os = "macos"), + target_pointer_width = "64" +))] +pub fn test_debug_dwarf_ref() -> Result<()> { + let output = lldb_with_script( + &[ + "-g", + "--opt-level", + "0", + "tests/all/debug/testsuite/fraction-norm.wasm", + ], + r#"b fraction-norm.cc:26 +r +p __vmctx->set(),n->denominator +c"#, + )?; + + check_lldb_output( + &output, + r#" +check: Breakpoint 1: no locations (pending) +check: stop reason = breakpoint 1.1 +check: frame #0 +sameln: norm(n=(__ptr = +check: = 27 +check: resuming +"#, + )?; + Ok(()) +} diff --git a/tests/all/debug/testsuite/fraction-norm.cc b/tests/all/debug/testsuite/fraction-norm.cc new file mode 100644 index 0000000000..c896c38238 --- /dev/null +++ b/tests/all/debug/testsuite/fraction-norm.cc @@ -0,0 +1,40 @@ +// Compile with: +// clang++ --target=wasm32 fraction-norm.cc -o fraction-norm.wasm -g \ +// -O0 -nostdlib -fdebug-prefix-map=$PWD=. + +struct Fraction { + long numerator; + long denominator; +}; + +inline long abs(long x) +{ + return x >= 0 ? x : -x; +} + +extern "C" +void norm(Fraction &n) +{ + long a = abs(n.numerator), b = abs(n.denominator); + if (a == 0 || b == 0) return; + do { + a %= b; + if (a == 0) break; + b %= a; + } while (b > 0); + long gcd = a + b; + if (n.denominator > 0) { + n.numerator /= gcd; + n.denominator /= gcd; + } else { + n.numerator /= -gcd; + n.denominator /= -gcd; + } +} + +extern "C" +void _start() +{ + Fraction c = {6, 27}; + norm(c); +} diff --git a/tests/all/debug/testsuite/fraction-norm.wasm b/tests/all/debug/testsuite/fraction-norm.wasm new file mode 100755 index 0000000000..23f34181a5 Binary files /dev/null and b/tests/all/debug/testsuite/fraction-norm.wasm differ