From ac90d9e352d79bb98f5c5cf74e27a809d0f8c841 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Wed, 21 Oct 2026 21:38:52 -0700 Subject: [PATCH] Preserve inline scopes in line-table debug info Retain the source-scope map when line tables are enabled so native debug metadata includes inline call chains without variable debug info. Signed-off-by: Trent Nelson --- .../mir-importer/src/translator/body.rs | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/cuda-oxide/crates/mir-importer/src/translator/body.rs b/cuda-oxide/crates/mir-importer/src/translator/body.rs index cd49e86c91..251ff03ed5 100644 --- a/cuda-oxide/crates/mir-importer/src/translator/body.rs +++ b/cuda-oxide/crates/mir-importer/src/translator/body.rs @@ -2375,7 +2375,7 @@ pub fn translate_body( } if let Some(scope_map) = debug_source_scopes - && debug_kind.variables_enabled() + && debug_kind.line_tables_enabled() { llvm_export::ops::set_debug_source_scope_map(ctx, op_ptr, scope_map); } @@ -2742,6 +2742,138 @@ pub fn cuda_oxide_device_generated_kernel(mut wrapped: Wrapper) -> u32 { } } + /// Import a real MIR body with the source-scope sidecar supplied by the + /// codegen bridge, then verify its inline provenance survives MIR lowering. + /// Line tables need these scopes even when variable debug info is disabled. + #[test] + fn imported_inline_scopes_survive_lowering_in_line_table_and_full_modes() { + use llvm_export::ops::{DebugInlinedScope, DebugSourceScope}; + + let unique = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos(); + let root = std::env::temp_dir().join(format!( + "cuda_oxide_inline_scopes_{}_{}", + std::process::id(), + unique + )); + std::fs::create_dir_all(&root).unwrap(); + let fixture = root.join("entry.rs"); + std::fs::write(&fixture, "pub fn entry() {}\n").unwrap(); + let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); + let sysroot = std::process::Command::new(rustc) + .args(["--print", "sysroot"]) + .output() + .unwrap(); + assert!(sysroot.status.success()); + let args = vec![ + "rustc".to_string(), + "--edition=2024".to_string(), + "--crate-type=rlib".to_string(), + "--emit=metadata".to_string(), + format!("--out-dir={}", root.display()), + format!( + "--sysroot={}", + String::from_utf8(sysroot.stdout).unwrap().trim() + ), + fixture.display().to_string(), + ]; + let worker_root = root.clone(); + std::thread::Builder::new() + .stack_size(64 * 1024 * 1024) + .spawn(move || { + rustc_public::run!(&args, || { + let item = rustc_public::all_local_items() + .into_iter() + .find(|item| item.name().ends_with("::entry")) + .unwrap(); + let instance = mono::Instance::try_from(item).unwrap(); + let body = instance.body().unwrap(); + let successors: Vec<_> = body + .blocks + .iter() + .map(|block| block.terminator.successors()) + .collect(); + let caller = DebugSourcePosition { + file: fixture, + line: 1, + column: 1, + }; + let scopes = DebugSourceScopeMap { + scopes: vec![ + DebugSourceScope { + id: 0, + parent: None, + span: Some(caller.clone()), + inlined: None, + }, + DebugSourceScope { + id: 1, + parent: Some(0), + span: Some(DebugSourcePosition { + file: worker_root.join("helper.rs"), + line: 279, + column: 5, + }), + inlined: Some(DebugInlinedScope { + callee_name: "helper::group_rank".to_string(), + callsite: Some(caller), + }), + }, + ], + locations: vec![], + }; + for kind in [DebugKind::Off, DebugKind::LineTables, DebugKind::Full] { + let mut ctx = Context::new(); + crate::translator::register_dialects(&mut ctx); + let module = ModuleOp::new(&mut ctx, "test".try_into().unwrap()); + let region = module.get_operation().deref(&ctx).get_region(0); + let block = region.deref(&ctx).iter(&ctx).next().unwrap(); + let function = translate_body( + &mut ctx, + &body, + &instance, + body.blocks.len(), + &successors, + false, + None, + Some("entry"), + &mut Legaliser::default(), + kind, + Some(&scopes), + None, + ) + .unwrap(); + function.insert_at_back(block, &ctx); + mir_lower::register(&mut ctx); + mir_lower::lower_mir_to_llvm(&mut ctx, module.get_operation()).unwrap(); + let lowered = block + .deref(&ctx) + .iter(&ctx) + .find(|op| { + Operation::get_op::(*op, &ctx).is_some() + }) + .unwrap(); + let expected = match kind { + DebugKind::Off => None, + DebugKind::LineTables | DebugKind::Full => Some(scopes.clone()), + }; + assert_eq!( + llvm_export::ops::debug_source_scope_map(&ctx, lowered), + expected + ); + } + std::ops::ControlFlow::<(), _>::Continue(()) + }) + .expect("fixture compilation succeeds"); + }) + .unwrap() + .join() + .unwrap(); + std::fs::remove_dir_all(root).ok(); + } + #[test] fn inline_intent_reaches_llvm_func_attr_before_export() { for (intent, keyword) in [