-
Notifications
You must be signed in to change notification settings - Fork 17
feat(hugr-llvm)!: Add support for emitting debug locations #3026
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
base: main
Are you sure you want to change the base?
Changes from 19 commits
47b8f66
82fc4b2
435a456
5e9e3c7
e2f837f
5f3419f
1220995
26db7ea
cf9e51d
f848fdc
1485b15
54b5a97
27f4fd1
84226c6
a4bdfc8
3376256
8c3c2cf
2feac5a
18f1fb4
6a0e206
dae040b
4ebb9b8
3bd16c6
2d59311
08bddaf
6aa4bf1
5413ea7
e0b44be
60f1585
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
|
cgh-qtnm marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -179,6 +179,8 @@ pub enum NodeLabel<N: HugrNode = Node> { | |
| /// Display the node index as a number. | ||
| #[default] | ||
| Numeric, | ||
| /// Display the node index and JSON metadata for a given key. | ||
| MetadataKey(String), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
MetadataKey {
metadata_entries: Vec<String>,
}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I'll extend the functionality as you described.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, looks like adding the ability to enumerate the metadata map to print all entries would perturb a lot of interfaces so I won't do that. But taking a list of keys is easy. |
||
| /// Display the labels corresponding to the node indices. | ||
| Custom(HashMap<N, String>), | ||
| } | ||
|
|
@@ -196,6 +198,14 @@ pub(in crate::hugr) fn node_style<'a>( | |
| } | ||
| } | ||
|
|
||
| fn numeric_label(h: &Hugr, n: NodeIndex, is_entry: bool) -> String { | ||
| if is_entry { | ||
| format!("({}) [**{}**]", n.index(), node_name(h, n)) | ||
| } else { | ||
| format!("({}) {}", n.index(), node_name(h, n)) | ||
| } | ||
| } | ||
|
|
||
| let mut entrypoint_style = PresentationStyle::default(); | ||
| entrypoint_style.stroke = Some("#832561".to_string()); | ||
| entrypoint_style.stroke_width = Some("3px".to_string()); | ||
|
|
@@ -204,18 +214,9 @@ pub(in crate::hugr) fn node_style<'a>( | |
| match formatter.node_labels { | ||
| NodeLabel::Numeric => Box::new(move |n| { | ||
| if Some(n) == entrypoint { | ||
| NodeStyle::boxed(format!( | ||
| "({ni}) [**{name}**]", | ||
| ni = n.index(), | ||
| name = node_name(h, n) | ||
| )) | ||
| .with_attrs(entrypoint_style.clone()) | ||
| NodeStyle::boxed(numeric_label(h, n, true)).with_attrs(entrypoint_style.clone()) | ||
| } else { | ||
| NodeStyle::boxed(format!( | ||
| "({ni}) {name}", | ||
| ni = n.index(), | ||
| name = node_name(h, n) | ||
| )) | ||
| NodeStyle::boxed(numeric_label(h, n, false)) | ||
| } | ||
| }), | ||
| NodeLabel::None => Box::new(move |n| { | ||
|
|
@@ -226,6 +227,30 @@ pub(in crate::hugr) fn node_style<'a>( | |
| NodeStyle::boxed(node_name(h, n)) | ||
| } | ||
| }), | ||
| NodeLabel::MetadataKey(label) => Box::new(move |n| { | ||
| let metadata = serde_json::to_string( | ||
| h.get_metadata_any(Node::from_portgraph(n), label.clone()) | ||
|
cgh-qtnm marked this conversation as resolved.
Outdated
|
||
| .unwrap_or(&serde_json::Value::Null), | ||
| ) | ||
| .expect("Could not render JSON metadata"); | ||
| // mermaid renderer in portgraph does not like double quotes or newlines | ||
| let metadata_clean = metadata.replace('\n', " ").replace('"', "\'"); | ||
|
|
||
| if Some(n) == entrypoint { | ||
| NodeStyle::boxed(format!( | ||
| "{} <{}>", | ||
| numeric_label(h, n, true), | ||
| metadata_clean | ||
| )) | ||
| .with_attrs(entrypoint_style.clone()) | ||
| } else { | ||
| NodeStyle::boxed(format!( | ||
| "{} <{}>", | ||
| numeric_label(h, n, false), | ||
| metadata_clean | ||
| )) | ||
| } | ||
| }), | ||
| NodeLabel::Custom(labels) => Box::new(move |n| { | ||
| if Some(n) == entrypoint { | ||
| NodeStyle::boxed(format!( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| #![allow(missing_docs)] | ||
|
cgh-qtnm marked this conversation as resolved.
Outdated
|
||
|
|
||
| use std::any::type_name; | ||
| use std::fmt; | ||
|
|
||
| use crate::metadata::Metadata; | ||
| use crate::{HugrView, Node}; | ||
| use serde::{ | ||
| Deserialize, Serialize, | ||
| de::{DeserializeOwned, Deserializer, Error as DeError, Visitor}, | ||
| }; | ||
| use serde_json::{Error as JsonError, Value as JsonValue}; | ||
| use thiserror::Error; | ||
|
|
||
| pub const DEBUGINFO_META_KEY: &str = "core.debug_info"; | ||
|
|
||
| /// Visitor and wrapper function passed as "deserialize_with" attribute | ||
| /// in order to deserialize a usize from a string using serde_json | ||
| struct JsonStrToIntVisitor; | ||
|
cgh-qtnm marked this conversation as resolved.
Outdated
|
||
|
|
||
| impl<'de> Visitor<'de> for JsonStrToIntVisitor { | ||
| type Value = usize; | ||
|
|
||
| fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
| formatter.write_str("a usize or a string convertible with str::parse<usize>()") | ||
| } | ||
|
|
||
| fn visit_str<E: DeError>(self, s: &str) -> Result<Self::Value, E> { | ||
| s.parse::<usize>().map_err(E::custom) | ||
| } | ||
|
|
||
| fn visit_u64<E: DeError>(self, x: u64) -> Result<Self::Value, E> { | ||
| x.try_into().map_err(E::custom) | ||
| } | ||
| } | ||
|
|
||
| fn deserialize_usize_str<'de, D: Deserializer<'de>>(deserializer: D) -> Result<usize, D::Error> { | ||
| deserializer.deserialize_any(JsonStrToIntVisitor) | ||
| } | ||
|
|
||
| #[derive(Serialize, Deserialize)] | ||
| pub struct CompileUnitRecord { | ||
| pub directory: String, | ||
| #[serde(deserialize_with = "deserialize_usize_str")] | ||
| pub filename: usize, | ||
| pub file_table: Vec<String>, | ||
| } | ||
|
|
||
| impl Metadata for CompileUnitRecord { | ||
| type Type<'hugr> = CompileUnitRecord; | ||
| const KEY: &'static str = DEBUGINFO_META_KEY; | ||
| } | ||
|
|
||
| #[derive(Debug, Error)] | ||
| pub enum DebugInfoError { | ||
| /// There is a specific required mapping between HUGR nodes and debug record types, | ||
| /// if present | ||
| #[error("Debug metadata does not deserialize to {0}: {1}\n{2}")] | ||
| DRTypeMismatchError(&'static str, JsonError, JsonValue), | ||
| } | ||
|
|
||
| #[derive(Serialize, Deserialize)] | ||
| pub struct SubprogramRecord { | ||
| #[serde(deserialize_with = "deserialize_usize_str")] | ||
| pub file: usize, | ||
| #[serde(deserialize_with = "deserialize_usize_str")] | ||
| pub line_no: usize, | ||
| // TODO | ||
| //scope: Option<ScopeRecord>, | ||
| #[serde(deserialize_with = "deserialize_usize_str")] | ||
| pub scope_line: usize, | ||
| } | ||
|
|
||
| impl Metadata for SubprogramRecord { | ||
| type Type<'hugr> = SubprogramRecord; | ||
| const KEY: &'static str = DEBUGINFO_META_KEY; | ||
| } | ||
|
|
||
| #[derive(Serialize, Deserialize)] | ||
| pub struct LocationRecord { | ||
| #[serde(deserialize_with = "deserialize_usize_str")] | ||
| pub column: usize, | ||
| #[serde(deserialize_with = "deserialize_usize_str")] | ||
| pub line_no: usize, | ||
| } | ||
|
|
||
| impl Metadata for LocationRecord { | ||
| type Type<'hugr> = LocationRecord; | ||
| const KEY: &'static str = DEBUGINFO_META_KEY; | ||
| } | ||
|
|
||
| /// Inspect the debug metadata attached to the HUGR node. | ||
| /// | ||
| /// If there is no debug metadata, return Ok(None). If it is present but does not | ||
| /// deserialize into `T`, return DRTypeMismatchError. Otherwise, return the deserialized | ||
| /// Some(`T`). | ||
| pub fn try_get_debug_meta< | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can't we use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to distinguish between the "key not present" case and the "value does not deserialize into T" case, I believe
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can move this into the core HugrView implementation if you'd like, as
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, let's add a fn try_get_metadata<M: Metadata>(&self, node: Self::Node) -> Result<Option<...>, serde_json::Error> |
||
| 'h, | ||
| H: HugrView<Node = Node>, | ||
| T: Metadata<Type<'h> = T> + DeserializeOwned, | ||
| >( | ||
| hugr: &'h H, | ||
| node: Node, | ||
| ) -> Result<Option<T>, DebugInfoError> { | ||
| if let Some(json) = hugr.get_metadata_any(node, DEBUGINFO_META_KEY) { | ||
| serde_json::from_value::<T>(json.clone()) | ||
| .map_err(|e| DebugInfoError::DRTypeMismatchError(type_name::<T>(), e, json.clone())) | ||
| .map(|debug_record| Some(debug_record)) | ||
| } else { | ||
| Ok(None) | ||
| } | ||
| } | ||

Uh oh!
There was an error while loading. Please reload this page.