diff --git a/Cargo.lock b/Cargo.lock index af96de9386..4981ad7340 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,22 +13,22 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.2.16", + "getrandom", "once_cell", "serde", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -69,9 +69,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -84,36 +84,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] @@ -222,9 +222,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -232,7 +232,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -258,9 +258,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitmaps" @@ -327,15 +327,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "bytecount" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "byteorder" @@ -366,9 +366,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.20" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -440,9 +440,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -450,9 +450,9 @@ dependencies = [ [[package]] name = "clap-verbosity-flag" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678fade3b77aa3a8ff3aae87e9c008d3fb00473a41c71fbf74e91c8c7b37e84" +checksum = "eeab6a5cdfc795a05538422012f20a5496f050223c91be4e5420bfd13c641fb1" dependencies = [ "clap", "log", @@ -460,9 +460,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -472,9 +472,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", @@ -484,9 +484,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clio" @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "console" @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2364b9aa47e460ce9bca6ac1777d14c98eef7e274eb077beed49f3adc94183ed" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" dependencies = [ "proc-macro2", "quote", @@ -802,6 +802,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97af9b5f014e228b33e77d75ee0e6e87960124f0f4b16337b586a6bec91867b1" +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + [[package]] name = "either" version = "1.15.0" @@ -859,9 +865,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1042,20 +1048,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -1128,9 +1123,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -1145,9 +1140,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1206,7 +1201,7 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hugr" -version = "0.20.1" +version = "0.20.2" dependencies = [ "bumpalo", "criterion", @@ -1220,8 +1215,9 @@ dependencies = [ [[package]] name = "hugr-cli" -version = "0.20.1" +version = "0.20.2" dependencies = [ + "anyhow", "assert_cmd", "assert_fs", "clap", @@ -1233,11 +1229,12 @@ dependencies = [ "rstest", "serde_json", "tempfile", + "thiserror 2.0.12", ] [[package]] name = "hugr-core" -version = "0.20.1" +version = "0.20.2" dependencies = [ "cgmath", "cool_asserts", @@ -1255,7 +1252,7 @@ dependencies = [ "jsonschema", "lazy_static", "paste", - "petgraph 0.8.1", + "petgraph 0.8.2", "portgraph", "proptest", "proptest-derive", @@ -1277,7 +1274,7 @@ dependencies = [ [[package]] name = "hugr-llvm" -version = "0.20.1" +version = "0.20.2" dependencies = [ "anyhow", "delegate", @@ -1288,7 +1285,7 @@ dependencies = [ "insta", "itertools 0.14.0", "lazy_static", - "petgraph 0.8.1", + "petgraph 0.8.2", "portgraph", "rstest", "strum", @@ -1296,7 +1293,7 @@ dependencies = [ [[package]] name = "hugr-model" -version = "0.20.1" +version = "0.20.2" dependencies = [ "base64", "bumpalo", @@ -1320,7 +1317,7 @@ dependencies = [ [[package]] name = "hugr-passes" -version = "0.20.1" +version = "0.20.2" dependencies = [ "ascent", "derive_more 1.0.0", @@ -1328,7 +1325,7 @@ dependencies = [ "itertools 0.14.0", "lazy_static", "paste", - "petgraph 0.8.1", + "petgraph 0.8.2", "portgraph", "proptest", "proptest-recurse", @@ -1368,17 +1365,21 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -1412,21 +1413,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -1435,31 +1437,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -1467,67 +1449,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1547,9 +1516,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1578,7 +1547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core", + "rand_core 0.6.4", "rand_xoshiro", "sized-chunks", "typenum", @@ -1609,7 +1578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "serde", ] @@ -1680,6 +1649,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -1736,7 +1715,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom", "libc", ] @@ -1783,9 +1762,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" [[package]] name = "linux-raw-sys" @@ -1795,9 +1774,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "llvm-sys" @@ -1814,9 +1793,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1830,9 +1809,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -1843,30 +1822,24 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -1975,6 +1948,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "oorandom" version = "11.1.5" @@ -1998,9 +1977,9 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2008,15 +1987,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2033,9 +2012,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror 2.0.12", @@ -2044,9 +2023,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -2054,9 +2033,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", @@ -2067,11 +2046,10 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -2088,12 +2066,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" +checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" dependencies = [ "fixedbitset 0.5.7", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "indexmap 2.9.0", "serde", ] @@ -2146,9 +2124,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portgraph" @@ -2159,11 +2137,20 @@ dependencies = [ "bitvec", "delegate", "itertools 0.14.0", - "petgraph 0.8.1", + "petgraph 0.8.2", "serde", "thiserror 2.0.12", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2176,7 +2163,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.25", + "zerocopy", ] [[package]] @@ -2250,9 +2237,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", @@ -2369,9 +2356,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -2381,23 +2368,22 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ - "libc", "rand_chacha", - "rand_core", + "rand_core 0.9.3", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.3", ] [[package]] @@ -2405,17 +2391,23 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.2.16", + "getrandom", ] [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core", + "rand_core 0.9.3", ] [[package]] @@ -2424,7 +2416,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2449,9 +2441,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags", ] @@ -2527,23 +2519,24 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "relrc" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "036a8b094257e8a5bae0f9978044f2593619afd53476d534cfe1f31a5198ebeb" +checksum = "65b9e2100a2ee7d9efb575064b2f9a552d7f9f60289d7b95b9c22175101c7c4a" dependencies = [ "derive-where", "derive_more 0.99.20", "fxhash", "itertools 0.13.0", - "petgraph 0.8.1", + "petgraph 0.8.2", + "serde", "thiserror 1.0.69", ] [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64", "bytes", @@ -2555,11 +2548,8 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "serde", @@ -2568,12 +2558,12 @@ dependencies = [ "sync_wrapper", "tokio", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] @@ -2608,9 +2598,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -2629,9 +2619,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -2642,9 +2632,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rusty-fork" @@ -2673,6 +2663,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2734,15 +2736,16 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", "indexmap 2.9.0", + "schemars", "serde", "serde_derive", "serde_json", @@ -2752,9 +2755,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" dependencies = [ "darling", "proc-macro2", @@ -2777,9 +2780,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2810,18 +2813,15 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smol_str" @@ -2835,9 +2835,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2885,9 +2885,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", @@ -2905,9 +2905,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -2933,7 +2933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3018,9 +3018,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -3038,9 +3038,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "libc", @@ -3052,15 +3052,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.9.0", "toml_datetime", @@ -3082,6 +3082,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -3106,9 +3124,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -3214,12 +3232,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -3240,9 +3252,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "uuid-simd" @@ -3297,9 +3313,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -3402,15 +3418,15 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -3437,44 +3453,24 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-registry" -version = "0.4.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", -] +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -3500,7 +3496,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3509,7 +3505,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3521,29 +3517,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -3556,12 +3536,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3574,12 +3548,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3592,24 +3560,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3622,12 +3578,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3640,12 +3590,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3658,12 +3602,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3676,17 +3614,11 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -3700,17 +3632,11 @@ dependencies = [ "bitflags", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wyz" @@ -3729,9 +3655,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -3741,9 +3667,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -3751,33 +3677,13 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] - [[package]] name = "zerocopy" version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.25", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "zerocopy-derive", ] [[package]] @@ -3812,11 +3718,22 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -3825,9 +3742,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 68f3e8eee5..b123c9897d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ result_large_err = "allow" large_enum_variant = "allow" [workspace.dependencies] +anyhow = "1.0.98" insta = { version = "1.43.1" } bitvec = "1.0.1" capnp = "0.20.6" @@ -57,7 +58,7 @@ jsonschema = "0.29.1" lazy_static = "1.4.0" num-rational = "0.4.1" paste = "1.0" -proptest = "1.4.0" +proptest = "1.7.0" proptest-derive = "0.5.0" regex = "1.10.6" regex-syntax = "0.8.3" @@ -65,7 +66,7 @@ rstest = "0.24.0" semver = "1.0.26" serde = "1.0.219" serde_json = "1.0.140" -serde_with = "3.12.0" +serde_with = "3.13.0" serde_yaml = "0.9.34" smol_str = "0.3.1" static_assertions = "1.1.0" @@ -73,24 +74,24 @@ strum = "0.27.0" tempfile = "3.20" thiserror = "2.0.12" typetag = "0.2.20" -clap = { version = "4.5.38" } +clap = { version = "4.5.40" } clio = "0.3.5" -clap-verbosity-flag = "3.0.1" +clap-verbosity-flag = "3.0.3" assert_cmd = "2.0.17" assert_fs = "1.1.3" predicates = "3.1.0" indexmap = "2.9.0" fxhash = "0.2.1" -bumpalo = "3.16.0" +bumpalo = "3.18.1" pathsearch = "0.2.0" base64 = "0.22.1" ordered-float = "5.0.0" -pest = "2.8.0" -pest_derive = "2.8.0" +pest = "2.8.1" +pest_derive = "2.8.1" pretty = "0.12.4" pretty_assertions = "1.4.1" zstd = "0.13.2" -relrc = "0.4.1" +relrc = "0.4.6" # These public dependencies usually require breaking changes downstream, so we # try to be as permissive as possible. diff --git a/hugr-cli/CHANGELOG.md b/hugr-cli/CHANGELOG.md index 141b60f73e..5bbc26a608 100644 --- a/hugr-cli/CHANGELOG.md +++ b/hugr-cli/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-cli-v0.20.1...hugr-cli-v0.20.2) - 2025-06-25 + +### New Features + +- *(cli)* convert sub-command for converting envelope formats ([#2331](https://github.com/CQCL/hugr/pull/2331)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-cli-v0.20.0...hugr-cli-v0.20.1) - 2025-06-03 ### New Features diff --git a/hugr-cli/Cargo.toml b/hugr-cli/Cargo.toml index faa64c220d..a05666cc0a 100644 --- a/hugr-cli/Cargo.toml +++ b/hugr-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-cli" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } license = { workspace = true } @@ -19,9 +19,11 @@ bench = false clap = { workspace = true, features = ["derive", "cargo"] } clap-verbosity-flag.workspace = true derive_more = { workspace = true, features = ["display", "error", "from"] } -hugr = { path = "../hugr", version = "0.20.1" } +hugr = { path = "../hugr", version = "0.20.2" } serde_json.workspace = true clio = { workspace = true, features = ["clap-parse"] } +anyhow.workspace = true +thiserror.workspace = true [lints] workspace = true diff --git a/hugr-cli/src/convert.rs b/hugr-cli/src/convert.rs new file mode 100644 index 0000000000..d330f1b43c --- /dev/null +++ b/hugr-cli/src/convert.rs @@ -0,0 +1,85 @@ +//! Convert between different HUGR envelope formats. +use anyhow::Result; +use clap::Parser; +use clio::Output; +use hugr::envelope::{EnvelopeConfig, EnvelopeFormat, ZstdConfig}; + +use crate::CliError; +use crate::hugr_io::HugrInputArgs; + +/// Convert between different HUGR envelope formats. +#[derive(Parser, Debug)] +#[clap(version = "1.0", long_about = None)] +#[clap(about = "Convert a HUGR between different envelope formats.")] +#[group(id = "hugr")] +#[non_exhaustive] +pub struct ConvertArgs { + /// Hugr input. + #[command(flatten)] + pub input_args: HugrInputArgs, + + /// Output file. Use '-' for stdout. + #[clap(short, long, value_parser, default_value = "-")] + pub output: Output, + + /// Output format. One of: json, model, model-exts, model-text, model-text-exts + #[clap(short, long, value_name = "FORMAT")] + pub format: Option, + + /// Use default text-based envelope configuration. + /// Cannot be combined with --format or --binary. + #[clap(long, conflicts_with_all = ["format", "binary"])] + pub text: bool, + + /// Use default binary envelope configuration. + /// Cannot be combined with --format or --text. + #[clap(long, conflicts_with_all = ["format", "text"])] + pub binary: bool, + + /// Enable zstd compression for the output + #[clap(long)] + pub compress: bool, + + /// Zstd compression level (1-22, where 1 is fastest and 22 is best compression) + /// Uses the default level if not specified. + #[clap(long, value_name = "LEVEL", requires = "compress")] + pub compression_level: Option, +} + +impl ConvertArgs { + /// Convert a HUGR between different envelope formats + pub fn run_convert(&mut self) -> Result<()> { + let (env_config, package) = self.input_args.get_envelope()?; + + // Handle text and binary format flags, which override the format option + let mut config = if self.text { + EnvelopeConfig::text() + } else if self.binary { + EnvelopeConfig::binary() + } else { + // Parse the requested format + let format = match &self.format { + Some(fmt) => match fmt.as_str() { + "json" => EnvelopeFormat::PackageJson, + "model" => EnvelopeFormat::Model, + "model-exts" => EnvelopeFormat::ModelWithExtensions, + "model-text" => EnvelopeFormat::ModelText, + "model-text-exts" => EnvelopeFormat::ModelTextWithExtensions, + _ => Err(CliError::InvalidFormat(fmt.clone()))?, + }, + None => env_config.format, // Use input format if not specified + }; + EnvelopeConfig::new(format) + }; + + // Configure compression + if let Some(level) = self.compress.then_some(self.compression_level).flatten() { + config = config.with_zstd(ZstdConfig::new(level)); + } + + // Write the package with the requested format + hugr::envelope::write_envelope(&mut self.output, &package, config)?; + + Ok(()) + } +} diff --git a/hugr-cli/src/hugr_io.rs b/hugr-cli/src/hugr_io.rs index bc7da686c2..9ff4b0cf85 100644 --- a/hugr-cli/src/hugr_io.rs +++ b/hugr-cli/src/hugr_io.rs @@ -1,7 +1,7 @@ //! Input/output arguments for the HUGR CLI. use clio::Input; -use hugr::envelope::{EnvelopeError, read_envelope}; +use hugr::envelope::{EnvelopeConfig, EnvelopeError, read_envelope}; use hugr::extension::ExtensionRegistry; use hugr::package::Package; use hugr::{Extension, Hugr}; @@ -43,18 +43,29 @@ impl HugrInputArgs { /// Read a hugr envelope from the input and return the package encoded /// within. /// + /// # Errors + /// /// If [`HugrInputArgs::hugr_json`] is `true`, [`HugrInputArgs::get_hugr`] should be called instead as /// reading the input as a package will fail. pub fn get_package(&mut self) -> Result { + self.get_envelope().map(|(_, package)| package) + } + + /// Read a hugr envelope from the input and return the envelope + /// configuration and the package encoded within. + /// + /// # Errors + /// + /// If [`HugrInputArgs::hugr_json`] is `true`, [`HugrInputArgs::get_hugr`] should be called instead as + /// reading the input as a package will fail. + pub fn get_envelope(&mut self) -> Result<(EnvelopeConfig, Package), CliError> { let extensions = self.load_extensions()?; let buffer = BufReader::new(&mut self.input); - match read_envelope(buffer, &extensions) { - Ok((_, pkg)) => Ok(pkg), - Err(EnvelopeError::MagicNumber { .. }) => Err(CliError::NotAnEnvelope), - Err(e) => Err(CliError::Envelope(e)), - } + read_envelope(buffer, &extensions).map_err(|e| match e { + EnvelopeError::MagicNumber { .. } => CliError::NotAnEnvelope, + _ => CliError::Envelope(e), + }) } - /// Read a hugr JSON file from the input. /// /// This is a legacy option for reading old HUGR JSON files when the diff --git a/hugr-cli/src/lib.rs b/hugr-cli/src/lib.rs index d70519a2c5..0b91ed547b 100644 --- a/hugr-cli/src/lib.rs +++ b/hugr-cli/src/lib.rs @@ -63,6 +63,7 @@ use hugr::envelope::EnvelopeError; use hugr::package::PackageValidationError; use std::ffi::OsString; +pub mod convert; pub mod extensions; pub mod hugr_io; pub mod mermaid; @@ -81,13 +82,15 @@ pub enum CliArgs { GenExtensions(extensions::ExtArgs), /// Write HUGR as mermaid diagrams. Mermaid(mermaid::MermaidArgs), + /// Convert between different HUGR envelope formats. + Convert(convert::ConvertArgs), /// External commands #[command(external_subcommand)] External(Vec), } /// Error type for the CLI. -#[derive(Debug, derive_more::Display, derive_more::Error, derive_more::From)] +#[derive(Debug, derive_more::Display, thiserror::Error, derive_more::From)] #[non_exhaustive] pub enum CliError { /// Error reading input. @@ -107,6 +110,11 @@ pub enum CliError { "Input file is not a HUGR envelope. Invalid magic number.\n\nUse `--hugr-json` to read a raw HUGR JSON file instead." )] NotAnEnvelope, + /// Invalid format string for conversion. + #[display( + "Invalid format: '{_0}'. Valid formats are: json, model, model-exts, model-text, model-text-exts" + )] + InvalidFormat(String), } /// Other arguments affecting the HUGR CLI runtime. diff --git a/hugr-cli/src/main.rs b/hugr-cli/src/main.rs index fe405101db..8063f25916 100644 --- a/hugr-cli/src/main.rs +++ b/hugr-cli/src/main.rs @@ -2,7 +2,7 @@ use clap::Parser as _; -use hugr_cli::{CliArgs, mermaid, validate}; +use hugr_cli::{CliArgs, convert, mermaid, validate}; use clap_verbosity_flag::log::Level; @@ -11,6 +11,7 @@ fn main() { CliArgs::Validate(args) => run_validate(args), CliArgs::GenExtensions(args) => args.run_dump(&hugr::std_extensions::STD_REG), CliArgs::Mermaid(args) => run_mermaid(args), + CliArgs::Convert(args) => run_convert(args), CliArgs::External(args) => { // External subcommand support: invoke `hugr-` if args.is_empty() { @@ -71,3 +72,13 @@ fn run_mermaid(mut args: mermaid::MermaidArgs) { std::process::exit(1); } } + +/// Run the `convert` subcommand. +fn run_convert(mut args: convert::ConvertArgs) { + let result = args.run_convert(); + + if let Err(e) = result { + eprintln!("{e}"); + std::process::exit(1); + } +} diff --git a/hugr-cli/tests/convert.rs b/hugr-cli/tests/convert.rs new file mode 100644 index 0000000000..343e0bccd5 --- /dev/null +++ b/hugr-cli/tests/convert.rs @@ -0,0 +1,262 @@ +//! Tests for the convert subcommand +//! +//! Miri is globally disabled for these tests because they mostly involve +//! calling the CLI binary, which Miri doesn't support. +#![cfg(all(test, not(miri)))] + +use assert_cmd::Command; +use assert_fs::{NamedTempFile, fixture::FileWriteStr}; +use hugr::builder::{DataflowSubContainer, ModuleBuilder}; +use hugr::envelope::{EnvelopeConfig, EnvelopeFormat, read_envelope}; +use hugr::package::Package; +use hugr::types::Type; +use hugr::{ + builder::{Container, Dataflow}, + extension::ExtensionRegistry, + extension::prelude::bool_t, + types::Signature, +}; +use predicates::str::contains; +use rstest::{fixture, rstest}; +use std::io::BufReader; + +#[fixture] +fn cmd() -> Command { + Command::cargo_bin("hugr").unwrap() +} + +#[fixture] +fn convert_cmd(mut cmd: Command) -> Command { + cmd.arg("convert"); + cmd +} + +/// A test package, containing a module-rooted HUGR. +#[fixture] +fn test_package(#[default(bool_t())] id_type: Type) -> Package { + let mut module = ModuleBuilder::new(); + let df = module + .define_function("test", Signature::new_endo(id_type)) + .unwrap(); + let [i] = df.input_wires_arr(); + df.finish_with_outputs([i]).unwrap(); + let hugr = module.hugr().clone(); // unvalidated + + Package::new(vec![hugr]) +} + +#[fixture] +fn test_envelope_text(test_package: Package) -> (String, Package) { + let config = EnvelopeConfig::text(); + (test_package.store_str(config).unwrap(), test_package) +} + +#[fixture] +fn test_envelope_file(test_envelope_text: (String, Package)) -> NamedTempFile { + let file = assert_fs::NamedTempFile::new("sample.hugr").unwrap(); + file.write_str(&test_envelope_text.0).unwrap(); + file +} + +#[rstest] +fn test_convert_to_json(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "json", + ]); + + convert_cmd.assert().success(); + + // Verify the output exists and is a valid hugr envelope + let output_content = std::fs::read(output_file.path()).expect("Failed to read output file"); + let reader = BufReader::new(output_content.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify the format is correct + assert_eq!(config.format, EnvelopeFormat::PackageJson); +} + +#[rstest] +fn test_convert_to_model(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "model", + ]); + + convert_cmd.assert().success(); + + // Verify the output exists and is a valid hugr envelope + let output_content = std::fs::read(output_file.path()).expect("Failed to read output file"); + let reader = BufReader::new(output_content.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify the format is correct + assert_eq!(config.format, EnvelopeFormat::Model); +} + +#[rstest] +fn test_convert_invalid_format(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "invalid-format", + ]); + + // This should fail with an error message about the invalid format + convert_cmd + .assert() + .failure() + .stderr(contains("Invalid format")); +} + +#[rstest] +fn test_convert_with_compression(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--compress", + "--compression-level", + "5", + ]); + + convert_cmd.assert().success(); +} + +#[rstest] +fn test_convert_stdin_stdout(test_envelope_text: (String, Package), mut convert_cmd: Command) { + // Use stdin/stdout + convert_cmd.args(["-", "--format", "model-exts"]); + convert_cmd.write_stdin(test_envelope_text.0); + + // Should succeed and produce output to stdout + convert_cmd.assert().success(); +} + +#[rstest] +fn test_convert_model_text_format(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "model-text", + ]); + + convert_cmd.assert().success(); + + // Verify the output exists and is a valid hugr envelope + let output_content = std::fs::read(output_file.path()).expect("Failed to read output file"); + let reader = BufReader::new(output_content.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify the format is correct + assert_eq!(config.format, EnvelopeFormat::ModelText); +} + +#[rstest] +fn test_format_roundtrip(test_package: Package) { + // Test conversion between all formats in a roundtrip + // Start with JSON format + let config_json = EnvelopeConfig::new(EnvelopeFormat::PackageJson); + let mut json_data = Vec::new(); + hugr::envelope::write_envelope(&mut json_data, &test_package, config_json).unwrap(); + + // Convert to Model format + let config_model = EnvelopeConfig::new(EnvelopeFormat::Model); + let reader = BufReader::new(json_data.as_slice()); + let registry = ExtensionRegistry::default(); + let (_, package) = read_envelope(reader, ®istry).unwrap(); + + let mut model_data = Vec::new(); + hugr::envelope::write_envelope(&mut model_data, &package, config_model).unwrap(); + + // Convert back to JSON + let reader = BufReader::new(model_data.as_slice()); + let (_, package_back) = read_envelope(reader, ®istry).unwrap(); + + // Package should be the same after roundtrip conversion + assert_eq!(test_package, package_back); +} + +#[rstest] +fn test_convert_text_flag(test_envelope_text: (String, Package), mut convert_cmd: Command) { + convert_cmd.args(["-", "--text"]); + convert_cmd.write_stdin(test_envelope_text.0); + + let output = convert_cmd.assert().success().get_output().to_owned(); + let stdout = output.stdout.clone(); + + let reader = BufReader::new(stdout.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify it's a text-based format + assert!(config.format.ascii_printable()); +} + +#[rstest] +fn test_convert_binary_flag(test_envelope_text: (String, Package), mut convert_cmd: Command) { + convert_cmd.args(["-", "--binary"]); + convert_cmd.write_stdin(test_envelope_text.0); + + let output = convert_cmd.assert().success().get_output().to_owned(); + let stdout = output.stdout.clone(); + + let reader = BufReader::new(stdout.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify it's a binary format (not ASCII printable) + assert!(!config.format.ascii_printable()); + assert!(config.zstd.is_some()); +} + +#[rstest] +fn test_format_conflicts(mut convert_cmd: Command) { + // Test that --format and --text cannot be combined + convert_cmd.args(["-", "--format", "json", "--text"]); + + // Should fail due to conflicting options + convert_cmd + .assert() + .failure() + .stderr(contains("cannot be used with")); + + // Test that --text and --binary cannot be combined + let mut convert_cmd = Command::cargo_bin("hugr").unwrap(); + convert_cmd.arg("convert"); + convert_cmd.args(["-", "--text", "--binary"]); + + // Should fail due to conflicting options + convert_cmd + .assert() + .failure() + .stderr(contains("cannot be used with")); +} diff --git a/hugr-core/CHANGELOG.md b/hugr-core/CHANGELOG.md index f712d16633..9026431a7a 100644 --- a/hugr-core/CHANGELOG.md +++ b/hugr-core/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-core-v0.20.1...hugr-core-v0.20.2) - 2025-06-25 + +### Documentation + +- fix doc links in persistent + +### New Features + +- Add serial data types for SimpleReplacement and PersistentHugr ([#2300](https://github.com/CQCL/hugr/pull/2300)) +- Add MermaidFormatter to replace RenderConfig ([#2275](https://github.com/CQCL/hugr/pull/2275)) +- *(core)* builder pattern for EnvelopeConfig ([#2330](https://github.com/CQCL/hugr/pull/2330)) +- *(core, llvm)* add array unpack operations ([#2339](https://github.com/CQCL/hugr/pull/2339)) +- Deprecate invalidation_set, add invalidated_nodes and SimpleReplacement::invalidation_set ([#2358](https://github.com/CQCL/hugr/pull/2358)) +- Rewrite for peeling a TailLoop ([#2290](https://github.com/CQCL/hugr/pull/2290)) +- Create Module/FunctionBuilders from existing Hugrs ([#2359](https://github.com/CQCL/hugr/pull/2359)) +- better errors using metadata from generator ([#2368](https://github.com/CQCL/hugr/pull/2368)) +- use `core.` prefixes for generator metadata keys ([#2371](https://github.com/CQCL/hugr/pull/2371)) + +### Refactor + +- *(types.rs)* rm incorrect comment and unnecessary allow-unused ([#2340](https://github.com/CQCL/hugr/pull/2340)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-core-v0.20.0...hugr-core-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr-core/Cargo.toml b/hugr-core/Cargo.toml index c78e1d9dd2..a365c73e19 100644 --- a/hugr-core/Cargo.toml +++ b/hugr-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-core" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } @@ -30,7 +30,7 @@ name = "model" name = "persistent_walker_example" [dependencies] -hugr-model = { version = "0.20.1", path = "../hugr-model" } +hugr-model = { version = "0.20.2", path = "../hugr-model" } cgmath = { workspace = true, features = ["serde"] } delegate = { workspace = true } @@ -63,7 +63,7 @@ thiserror = { workspace = true } typetag = { workspace = true } semver = { workspace = true, features = ["serde"] } zstd = { workspace = true, optional = true } -relrc = { workspace = true, features = ["petgraph"] } +relrc = { workspace = true, features = ["petgraph", "serde"] } [dev-dependencies] rstest = { workspace = true } diff --git a/hugr-core/src/builder/dataflow.rs b/hugr-core/src/builder/dataflow.rs index 0f7f078ec7..2a0fdf9315 100644 --- a/hugr-core/src/builder/dataflow.rs +++ b/hugr-core/src/builder/dataflow.rs @@ -259,6 +259,31 @@ impl FunctionBuilder { } } +impl + AsRef> FunctionBuilder { + /// Initialize a new function definition on the root module of an existing HUGR. + /// + /// The HUGR's entrypoint will **not** be modified. + /// + /// # Errors + /// + /// Error in adding DFG child nodes. + pub fn with_hugr( + mut hugr: B, + name: impl Into, + signature: impl Into, + ) -> Result { + let signature: PolyFuncType = signature.into(); + let body = signature.body().clone(); + let op = ops::FuncDefn::new(name, signature); + + let module = hugr.as_ref().module_root(); + let func = hugr.as_mut().add_node_with_parent(module, op); + + let db = DFGBuilder::create_with_io(hugr, func, body)?; + Ok(Self::from_dfg_builder(db)) + } +} + impl + AsRef, T> Container for DFGWrapper { #[inline] fn container_node(&self) -> Node { diff --git a/hugr-core/src/builder/module.rs b/hugr-core/src/builder/module.rs index 47bba818d2..543b9f2c1e 100644 --- a/hugr-core/src/builder/module.rs +++ b/hugr-core/src/builder/module.rs @@ -57,6 +57,12 @@ impl HugrBuilder for ModuleBuilder { } impl + AsRef> ModuleBuilder { + /// Continue building a module from an existing hugr. + #[must_use] + pub fn with_hugr(hugr: T) -> Self { + ModuleBuilder(hugr) + } + /// Replace a [`ops::FuncDecl`] with [`ops::FuncDefn`] and return a builder for /// the defining graph. /// @@ -233,4 +239,19 @@ mod test { assert_matches!(build_result, Ok(_)); Ok(()) } + + #[test] + fn builder_from_existing() -> Result<(), BuildError> { + let hugr = Hugr::new(); + + let fn_builder = FunctionBuilder::with_hugr(hugr, "main", Signature::new_endo(vec![]))?; + let mut hugr = fn_builder.finish_hugr()?; + + let mut module_builder = ModuleBuilder::with_hugr(&mut hugr); + module_builder.declare("other", Signature::new_endo(vec![]).into())?; + + hugr.validate()?; + + Ok(()) + } } diff --git a/hugr-core/src/envelope.rs b/hugr-core/src/envelope.rs index 077980cc5f..0223267b85 100644 --- a/hugr-core/src/envelope.rs +++ b/hugr-core/src/envelope.rs @@ -47,12 +47,16 @@ pub mod serde_with; pub use header::{EnvelopeConfig, EnvelopeFormat, MAGIC_NUMBERS, ZstdConfig}; pub use package_json::PackageEncodingError; -use crate::Hugr; -use crate::{extension::ExtensionRegistry, package::Package}; +use crate::{Hugr, HugrView}; +use crate::{ + extension::{ExtensionRegistry, Version}, + package::Package, +}; use header::EnvelopeHeader; use std::io::BufRead; use std::io::Write; use std::str::FromStr; +use thiserror::Error; #[allow(unused_imports)] use itertools::Itertools as _; @@ -60,6 +64,53 @@ use itertools::Itertools as _; use crate::import::ImportError; use crate::{Extension, import::import_package}; +/// Key used to store the name of the generator that produced the envelope. +pub const GENERATOR_KEY: &str = "core.generator"; +/// Key used to store the list of used extensions in the metadata of a HUGR. +pub const USED_EXTENSIONS_KEY: &str = "core.used_extensions"; + +/// Get the name of the generator from the metadata of the HUGR modules. +/// If multiple modules have different generators, a comma-separated list is returned in +/// module order. +/// If no generator is found, `None` is returned. +fn get_generator(modules: &[H]) -> Option { + let generators: Vec = modules + .iter() + .filter_map(|hugr| hugr.get_metadata(hugr.module_root(), GENERATOR_KEY)) + .map(|v| v.to_string()) + .collect(); + if generators.is_empty() { + return None; + } + + Some(generators.join(", ")) +} + +fn gen_str(generator: &Option) -> String { + match generator { + Some(g) => format!("\ngenerated by {g}"), + None => String::new(), + } +} + +/// Wrap an error with a generator string. +#[derive(Error, Debug)] +#[error("{inner}{}", gen_str(&self.generator))] +pub struct WithGenerator { + inner: E, + /// The name of the generator that produced the envelope, if any. + generator: Option, +} + +impl WithGenerator { + fn new(err: E, modules: &[impl HugrView]) -> Self { + Self { + inner: err, + generator: get_generator(modules), + } + } +} + /// Read a HUGR envelope from a reader. /// /// Returns the deserialized package and the configuration used to encode it. @@ -210,6 +261,7 @@ pub enum EnvelopeError { ModelImport { /// The source error. source: ImportError, + // TODO add generator to model import errors }, /// Error reading a HUGR model payload. ModelRead { @@ -391,7 +443,7 @@ fn encode_model<'h>( _ => unreachable!(), } - // Apend extensions for binary model. + // Append extensions for binary model. if format == EnvelopeFormat::ModelWithExtensions { serde_json::to_writer(writer, &extensions.iter().collect_vec())?; } @@ -399,6 +451,85 @@ fn encode_model<'h>( Ok(()) } +#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)] +struct UsedExtension { + name: String, + version: Version, +} + +#[derive(Debug, Error)] +#[error( + "Extension '{name}' version mismatch: registered version is {registered}, but used version is {used}" +)] +/// Error raised when the reported used version of an extension +/// does not match the registered version in the extension registry. +pub struct ExtensionVersionMismatch { + name: String, + registered: Version, + used: Version, +} + +#[derive(Debug, Error)] +#[non_exhaustive] +/// Error raised when checking for breaking changes in used extensions. +pub enum ExtensionBreakingError { + /// The extension version in the metadata does not match the registered version. + #[error("{0}")] + ExtensionVersionMismatch(ExtensionVersionMismatch), + + /// Error deserializing the used extensions metadata. + #[error("Failed to deserialize used extensions metadata")] + Deserialization(#[from] serde_json::Error), +} +/// If HUGR metadata contains a list of used extensions, under the key [`USED_EXTENSIONS_KEY`], +/// and extension is registered in the given registry, check that the +/// version of the extension in the metadata matches the registered version (up to +/// MAJOR.MINOR). +fn check_breaking_extensions( + hugr: impl crate::HugrView, + registry: &ExtensionRegistry, +) -> Result<(), ExtensionBreakingError> { + let Some(exts) = hugr.get_metadata(hugr.module_root(), USED_EXTENSIONS_KEY) else { + return Ok(()); // No used extensions metadata, nothing to check + }; + let used_exts: Vec = serde_json::from_value(exts.clone())?; // TODO handle errors properly + + for ext in used_exts { + let Some(registered) = registry.get(ext.name.as_str()) else { + continue; // Extension not registered, ignore + }; + if !compatible_versions(registered.version(), &ext.version) { + // This is a breaking change, raise an error. + + return Err(ExtensionBreakingError::ExtensionVersionMismatch( + ExtensionVersionMismatch { + name: ext.name, + registered: registered.version().clone(), + used: ext.version, + }, + )); + } + } + + Ok(()) +} + +/// Check if two versions are compatible according to: +/// - Major version must match. +/// - If major version is 0, minor version must match. +fn compatible_versions(v1: &Version, v2: &Version) -> bool { + if v1.major != v2.major { + return false; // Major version mismatch + } + + if v1.major == 0 { + // For major version 0, we only allow minor version matches + return v1.minor == v2.minor; + } + + true +} + #[cfg(test)] pub(crate) mod test { use super::*; @@ -409,9 +540,13 @@ pub(crate) mod test { use crate::HugrView; use crate::builder::test::{multi_module_package, simple_package}; - use crate::extension::PRELUDE_REGISTRY; + use crate::extension::{Extension, ExtensionRegistry, Version}; + use crate::extension::{ExtensionId, PRELUDE_REGISTRY}; + use crate::hugr::HugrMut; use crate::hugr::test::check_hugr_equality; use crate::std_extensions::STD_REG; + use serde_json::json; + use std::sync::Arc; /// Returns an `ExtensionRegistry` with the extensions from both /// sets. Avoids cloning if the first one already contains all @@ -527,4 +662,147 @@ pub(crate) mod test { assert_eq!(package, new_package); } + + #[rstest] + #[case::simple(simple_package())] + fn test_check_breaking_extensions(#[case] mut package: Package) { + // extension with major version 0 + let test_ext_v0 = + Extension::new(ExtensionId::new_unchecked("test-v0"), Version::new(0, 2, 3)); + // extension with major version > 0 + let test_ext_v1 = + Extension::new(ExtensionId::new_unchecked("test-v1"), Version::new(1, 2, 3)); + + // Create a registry with the test extensions + let registry = + ExtensionRegistry::new([Arc::new(test_ext_v0.clone()), Arc::new(test_ext_v1.clone())]); + let mut hugr = package.modules.remove(0); + + // No metadata - should pass + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Matching version for v0 - should pass + let used_exts = json!([{ "name": "test-v0", "version": "0.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Matching major/minor but different patch for v0 - should pass + let used_exts = json!([{ "name": "test-v0", "version": "0.2.4" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + //Different minor version for v0 - should fail + let used_exts = json!([{ "name": "test-v0", "version": "0.3.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v0" && registered == Version::new(0, 2, 3) && used == Version::new(0, 3, 3) + ); + + // Different major version for v0 - should fail + let used_exts = json!([{ "name": "test-v0", "version": "1.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v0" && registered == Version::new(0, 2, 3) && used == Version::new(1, 2, 3) + ); + + // Matching version for v1 - should pass + let used_exts = json!([{ "name": "test-v1", "version": "1.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Different minor version for v1 - should pass + let used_exts = json!([{ "name": "test-v1", "version": "1.3.0" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Different patch for v1 - should pass + let used_exts = json!([{ "name": "test-v1", "version": "1.2.4" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Different major version for v1 - should fail + let used_exts = json!([{ "name": "test-v1", "version": "2.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v1" && registered == Version::new(1, 2, 3) && used == Version::new(2, 2, 3) + ); + + // Non-registered extension - should pass + let used_exts = json!([{ "name": "unknown", "version": "1.0.0" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Multiple extensions - one mismatch should fail + let used_exts = json!([ + { "name": "unknown", "version": "1.0.0" }, + { "name": "test-v1", "version": "2.0.0" } + ]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v1" && registered == Version::new(1, 2, 3) && used == Version::new(2, 0, 0) + ); + + // Invalid metadata format - should fail with deserialization error + hugr.set_metadata( + hugr.module_root(), + USED_EXTENSIONS_KEY, + json!("not an array"), + ); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::Deserialization(_)) + ); + + // Multiple extensions with all compatible versions - should pass + let used_exts = json!([ + { "name": "test-v0", "version": "0.2.5" }, + { "name": "test-v1", "version": "1.9.9" } + ]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + } + + #[test] + fn test_with_generator_error_message() { + let test_ext = Extension::new(ExtensionId::new_unchecked("test"), Version::new(1, 0, 0)); + let registry = ExtensionRegistry::new([Arc::new(test_ext)]); + + let mut hugr = simple_package().modules.remove(0); + + // Set a generator name in the metadata + let generator_name = json!({ "name": "TestGenerator", "version": "1.2.3" }); + hugr.set_metadata(hugr.module_root(), GENERATOR_KEY, generator_name.clone()); + + // Set incompatible extension version in metadata + let used_exts = json!([{ "name": "test", "version": "2.0.0" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + + // Create the error and wrap it with WithGenerator + let err = check_breaking_extensions(&hugr, ®istry).unwrap_err(); + let with_gen = WithGenerator::new(err, &[&hugr]); + + let err_msg = with_gen.to_string(); + assert!(err_msg.contains("Extension 'test' version mismatch")); + assert!(err_msg.contains(generator_name.to_string().as_str())); + } } diff --git a/hugr-core/src/envelope/header.rs b/hugr-core/src/envelope/header.rs index 99004b7efb..66af887454 100644 --- a/hugr-core/src/envelope/header.rs +++ b/hugr-core/src/envelope/header.rs @@ -95,7 +95,7 @@ impl EnvelopeFormat { } /// Configuration for encoding an envelope. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[non_exhaustive] pub struct EnvelopeConfig { /// The format to use for the payload. @@ -105,19 +105,29 @@ pub struct EnvelopeConfig { pub zstd: Option, } -impl Default for EnvelopeConfig { - fn default() -> Self { - let format = Default::default(); - let zstd = if cfg!(feature = "zstd") { - Some(ZstdConfig::default()) - } else { - None - }; - Self { format, zstd } +impl EnvelopeConfig { + /// Create a new envelope configuration with the specified format. + /// `zstd` compression is disabled by default. + pub fn new(format: EnvelopeFormat) -> Self { + Self { + format, + ..Default::default() + } + } + + /// Set the zstd compression configuration for the envelope. + pub fn with_zstd(self, zstd: ZstdConfig) -> Self { + Self { + zstd: Some(zstd), + ..self + } + } + + /// Disable zstd compression in the envelope configuration. + pub fn disable_compression(self) -> Self { + Self { zstd: None, ..self } } -} -impl EnvelopeConfig { /// Create a new envelope header with the specified configuration. pub(super) fn make_header(&self) -> EnvelopeHeader { EnvelopeHeader { @@ -162,6 +172,12 @@ pub struct ZstdConfig { } impl ZstdConfig { + /// Create a new zstd configuration with the specified compression level. + pub fn new(level: u8) -> Self { + Self { + level: NonZeroU8::new(level), + } + } /// Create a new zstd configuration with default compression level. #[must_use] pub const fn default_level() -> Self { diff --git a/hugr-core/src/envelope/package_json.rs b/hugr-core/src/envelope/package_json.rs index 6a2b62b75e..bbdf19d26e 100644 --- a/hugr-core/src/envelope/package_json.rs +++ b/hugr-core/src/envelope/package_json.rs @@ -3,6 +3,7 @@ use derive_more::{Display, Error, From}; use itertools::Itertools; use std::io; +use super::{ExtensionBreakingError, WithGenerator, check_breaking_extensions}; use crate::extension::ExtensionRegistry; use crate::extension::resolution::ExtensionResolutionError; use crate::hugr::ExtensionError; @@ -21,19 +22,24 @@ pub(super) fn from_json_reader( extensions: pkg_extensions, } = serde_json::from_value::(val.clone())?; let mut modules = modules.into_iter().map(|h| h.0).collect_vec(); - let pkg_extensions = ExtensionRegistry::new_with_extension_resolution( pkg_extensions, &extension_registry.into(), - )?; + ) + .map_err(|err| WithGenerator::new(err, &modules))?; // Resolve the operations in the modules using the defined registries. let mut combined_registry = extension_registry.clone(); combined_registry.extend(&pkg_extensions); - for module in &mut modules { - module.resolve_extension_defs(&combined_registry)?; + for module in &modules { + check_breaking_extensions(module, &combined_registry) + .map_err(|err| WithGenerator::new(err, &modules))?; } + modules + .iter_mut() + .try_for_each(|module| module.resolve_extension_defs(&combined_registry)) + .map_err(|err| WithGenerator::new(err, &modules))?; Ok(Package { modules, @@ -64,7 +70,9 @@ pub enum PackageEncodingError { /// Error raised while reading from a file. IOError(io::Error), /// Could not resolve the extension needed to encode the hugr. - ExtensionResolution(ExtensionResolutionError), + ExtensionResolution(WithGenerator), + /// Error raised while checking for breaking extension version mismatch. + ExtensionVersion(WithGenerator), /// Could not resolve the runtime extensions for the hugr. RuntimeExtensionResolution(ExtensionError), } diff --git a/hugr-core/src/hugr/patch.rs b/hugr-core/src/hugr/patch.rs index dac266205c..4273c9c8a7 100644 --- a/hugr-core/src/hugr/patch.rs +++ b/hugr-core/src/hugr/patch.rs @@ -6,6 +6,7 @@ pub mod inline_dfg; pub mod insert_cut; pub mod insert_identity; pub mod outline_cfg; +pub mod peel_loop; mod port_types; pub mod replace; pub mod simple_replace; @@ -33,12 +34,27 @@ pub trait PatchVerification { /// error. fn verify(&self, h: &impl HugrView) -> Result<(), Self::Error>; - /// Returns a set of nodes referenced by the rewrite. Modifying any of these - /// nodes will invalidate it. + /// The nodes invalidated by the rewrite. Deprecated: implement + /// [Self::invalidated_nodes] instead. The default returns the empty + /// iterator; this should be fine as there are no external calls. + #[deprecated(note = "Use/implement invalidated_nodes instead")] + fn invalidation_set(&self) -> impl Iterator { + std::iter::empty() + } + + /// Returns the nodes removed or altered by the rewrite. Modifying any of these + /// nodes will invalidate the rewrite. /// - /// Two `impl Rewrite`s can be composed if their invalidation sets are + /// Two `impl Rewrite`s can be composed if their `invalidated_nodes` are /// disjoint. - fn invalidation_set(&self) -> impl Iterator; + fn invalidated_nodes( + &self, + h: &impl HugrView, + ) -> impl Iterator { + let _ = h; + #[expect(deprecated)] + self.invalidation_set() + } } /// A patch that can be applied to a mutable Hugr of type `H`. @@ -142,8 +158,11 @@ impl PatchVerification for Transactional { } #[inline] - fn invalidation_set(&self) -> impl Iterator { - self.underlying.invalidation_set() + fn invalidated_nodes( + &self, + h: &impl HugrView, + ) -> impl Iterator { + self.underlying.invalidated_nodes(h) } } diff --git a/hugr-core/src/hugr/patch/consts.rs b/hugr-core/src/hugr/patch/consts.rs index 8e39fab906..dcdeea52f5 100644 --- a/hugr-core/src/hugr/patch/consts.rs +++ b/hugr-core/src/hugr/patch/consts.rs @@ -46,7 +46,10 @@ impl PatchVerification for RemoveLoadConstant { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.0) } } @@ -93,7 +96,10 @@ impl PatchVerification for RemoveConst { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.0) } } @@ -158,7 +164,7 @@ mod test { let remove_1 = RemoveLoadConstant(load_1_node); assert_eq!( - remove_1.invalidation_set().exactly_one().ok(), + remove_1.invalidated_nodes(&h).exactly_one().ok(), Some(load_1_node) ); @@ -166,7 +172,7 @@ mod test { let remove_con = RemoveConst(con_node); assert_eq!( - remove_con.invalidation_set().exactly_one().ok(), + remove_con.invalidated_nodes(&h).exactly_one().ok(), Some(con_node) ); diff --git a/hugr-core/src/hugr/patch/inline_call.rs b/hugr-core/src/hugr/patch/inline_call.rs index b6beb8d459..40eac06e0e 100644 --- a/hugr-core/src/hugr/patch/inline_call.rs +++ b/hugr-core/src/hugr/patch/inline_call.rs @@ -52,7 +52,7 @@ impl PatchVerification for InlineCall { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes(&self, _: &impl HugrView) -> impl Iterator { Some(self.0).into_iter() } } diff --git a/hugr-core/src/hugr/patch/inline_dfg.rs b/hugr-core/src/hugr/patch/inline_dfg.rs index a4ef76f2c5..bc14ae8af1 100644 --- a/hugr-core/src/hugr/patch/inline_dfg.rs +++ b/hugr-core/src/hugr/patch/inline_dfg.rs @@ -4,7 +4,7 @@ use super::{PatchHugrMut, PatchVerification}; use crate::ops::handle::{DfgID, NodeHandle}; -use crate::{IncomingPort, Node, OutgoingPort, PortIndex}; +use crate::{HugrView, IncomingPort, Node, OutgoingPort, PortIndex}; /// Structure identifying an `InlineDFG` rewrite from the spec pub struct InlineDFG(pub DfgID); @@ -43,7 +43,10 @@ impl PatchVerification for InlineDFG { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { [self.0.node()].into_iter() } } diff --git a/hugr-core/src/hugr/patch/insert_cut.rs b/hugr-core/src/hugr/patch/insert_cut.rs index f85348b4d8..518c75cc92 100644 --- a/hugr-core/src/hugr/patch/insert_cut.rs +++ b/hugr-core/src/hugr/patch/insert_cut.rs @@ -118,7 +118,10 @@ impl PatchVerification for InsertCut { } #[inline] - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.parent) .chain(self.targets.iter().map(|(n, _)| *n)) .unique() @@ -179,7 +182,7 @@ mod tests { let targets: Vec<_> = h.all_linked_inputs(i).collect(); let inserter = InsertCut::new(h.entrypoint(), targets, replacement); assert_eq!( - inserter.invalidation_set().collect::>(), + inserter.invalidated_nodes(&h).collect::>(), vec![h.entrypoint(), o] ); diff --git a/hugr-core/src/hugr/patch/insert_identity.rs b/hugr-core/src/hugr/patch/insert_identity.rs index f1e4048402..725a0dd3c0 100644 --- a/hugr-core/src/hugr/patch/insert_identity.rs +++ b/hugr-core/src/hugr/patch/insert_identity.rs @@ -67,7 +67,10 @@ impl PatchVerification for IdentityInsertion { } #[inline] - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.post_node) } } diff --git a/hugr-core/src/hugr/patch/outline_cfg.rs b/hugr-core/src/hugr/patch/outline_cfg.rs index 0e74b4e572..e0d5a27850 100644 --- a/hugr-core/src/hugr/patch/outline_cfg.rs +++ b/hugr-core/src/hugr/patch/outline_cfg.rs @@ -97,7 +97,10 @@ impl PatchVerification for OutlineCfg { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { self.blocks.iter().copied() } } diff --git a/hugr-core/src/hugr/patch/peel_loop.rs b/hugr-core/src/hugr/patch/peel_loop.rs new file mode 100644 index 0000000000..9cf61290b6 --- /dev/null +++ b/hugr-core/src/hugr/patch/peel_loop.rs @@ -0,0 +1,300 @@ +//! Rewrite to peel one iteration of a [TailLoop], creating a [DFG] containing a copy of +//! the loop body, and a [Conditional] containing the original `TailLoop` node. +use derive_more::{Display, Error}; + +use crate::core::HugrNode; +use crate::ops::{ + Case, Conditional, DFG, DataflowOpTrait, Input, OpTrait, OpType, Output, TailLoop, +}; +use crate::types::Signature; +use crate::{Direction, HugrView, Node}; + +use super::{HugrMut, PatchHugrMut, PatchVerification}; + +/// Rewrite that peels one iteration of a [TailLoop] by turning the +/// iteration test into a [Conditional]. +#[derive(Clone, Debug, PartialEq)] +pub struct PeelTailLoop(N); + +/// Error in performing [`PeelTailLoop`] rewrite. +#[derive(Clone, Debug, Display, Error, PartialEq)] +#[non_exhaustive] +pub enum PeelTailLoopError { + /// The specified Node was not a [`TailLoop`] + #[display("Node to peel {node} expected to be a TailLoop but actually {op}")] + NotTailLoop { + /// The node requested to peel + node: N, + /// The actual (non-tail-loop) operation + op: OpType, + }, +} + +impl PeelTailLoop { + /// Create a new instance that will peel the specified [TailLoop] node + pub fn new(node: N) -> Self { + Self(node) + } +} + +impl PatchVerification for PeelTailLoop { + type Error = PeelTailLoopError; + type Node = N; + fn verify(&self, h: &impl HugrView) -> Result<(), Self::Error> { + let opty = h.get_optype(self.0); + if !opty.is_tail_loop() { + return Err(PeelTailLoopError::NotTailLoop { + node: self.0, + op: opty.clone(), + }); + } + Ok(()) + } + + fn invalidated_nodes(&self, h: &impl HugrView) -> impl Iterator { + h.get_io(self.0) + .into_iter() + .flat_map(|[_, output]| [self.0, output].into_iter()) + } +} + +impl PatchHugrMut for PeelTailLoop { + type Outcome = (); + fn apply_hugr_mut(self, h: &mut impl HugrMut) -> Result<(), Self::Error> { + self.verify(h)?; // Now we know we have a TailLoop! + let loop_ty = h.optype_mut(self.0); + let signature = loop_ty.dataflow_signature().unwrap().into_owned(); + // Replace the TailLoop with a DFG - this maintains all external connections + let OpType::TailLoop(tl) = std::mem::replace(loop_ty, DFG { signature }.into()) else { + panic!("Wasn't a TailLoop ?!") + }; + let sum_rows = Vec::from(tl.control_variants()); + let rest = tl.rest.clone(); + let Signature { + input: loop_in, + output: loop_out, + } = tl.signature().into_owned(); + + // Copy the DFG (ex-TailLoop) children into a new TailLoop *before* we add any more + let new_loop = h.add_node_after(self.0, tl); // Temporary parent + h.copy_descendants(self.0, new_loop, None); + + // Add conditional inside DFG. + let [_, dfg_out] = h.get_io(self.0).unwrap(); + let cond = Conditional { + sum_rows, + other_inputs: rest, + outputs: loop_out.clone(), + }; + let case_in_rows = [0, 1].map(|i| cond.case_input_row(i).unwrap()); + // This preserves all edges from the end of the loop body to the conditional: + h.replace_op(dfg_out, cond); + let cond_n = dfg_out; + h.add_ports(cond_n, Direction::Outgoing, loop_out.len() as isize + 1); + let dfg_out = h.add_node_before( + cond_n, + Output { + types: loop_out.clone(), + }, + ); + for p in 0..loop_out.len() { + h.connect(cond_n, p, dfg_out, p) + } + + // Now wire up the internals of the Conditional + let cases = case_in_rows.map(|in_row| { + let signature = Signature::new(in_row.clone(), loop_out.clone()); + let n = h.add_node_with_parent(cond_n, Case { signature }); + h.add_node_with_parent(n, Input { types: in_row }); + let types = loop_out.clone(); + h.add_node_with_parent(n, Output { types }); + n + }); + + h.set_parent(new_loop, cases[TailLoop::CONTINUE_TAG]); + let [ctn_in, ctn_out] = h.get_io(cases[TailLoop::CONTINUE_TAG]).unwrap(); + let [brk_in, brk_out] = h.get_io(cases[TailLoop::BREAK_TAG]).unwrap(); + for p in 0..loop_out.len() { + h.connect(brk_in, p, brk_out, p); + h.connect(new_loop, p, ctn_out, p) + } + for p in 0..loop_in.len() { + h.connect(ctn_in, p, new_loop, p); + } + Ok(()) + } + + /// Failure only occurs if the node is not a [TailLoop]. + /// (Any later failure means an invalid Hugr and `panic`.) + const UNCHANGED_ON_FAILURE: bool = true; +} + +#[cfg(test)] +mod test { + use itertools::Itertools; + + use crate::builder::test::simple_dfg_hugr; + use crate::builder::{ + Container, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, + }; + use crate::extension::prelude::{bool_t, usize_t}; + use crate::ops::{OpTag, OpTrait, Tag, TailLoop, handle::NodeHandle}; + use crate::std_extensions::arithmetic::int_types::INT_TYPES; + use crate::types::{Signature, Type, TypeRow}; + use crate::{HugrView, hugr::HugrMut}; + + use super::{PeelTailLoop, PeelTailLoopError}; + + #[test] + fn bad_peel() { + let backup = simple_dfg_hugr(); + let op = backup.entrypoint_optype().clone(); + assert!(!op.is_tail_loop()); + let mut h = backup.clone(); + let r = h.apply_patch(PeelTailLoop::new(h.entrypoint())); + assert_eq!( + r, + Err(PeelTailLoopError::NotTailLoop { + node: backup.entrypoint(), + op + }) + ); + assert_eq!(h, backup); + } + + #[test] + fn peel_loop_incoming_edges() { + let i32_t = || INT_TYPES[5].clone(); + let mut mb = crate::builder::ModuleBuilder::new(); + let helper = mb + .declare( + "helper", + Signature::new( + vec![bool_t(), usize_t(), i32_t()], + vec![Type::new_sum([vec![bool_t(); 2], vec![]]), usize_t()], + ) + .into(), + ) + .unwrap(); + let mut fb = mb + .define_function( + "main", + Signature::new(vec![bool_t(), usize_t(), i32_t()], usize_t()), + ) + .unwrap(); + let [b, u, i] = fb.input_wires_arr(); + let (tl, call) = { + let mut tlb = fb + .tail_loop_builder( + [(bool_t(), b), (bool_t(), b)], + [(usize_t(), u)], + TypeRow::new(), + ) + .unwrap(); + let [b, _, u] = tlb.input_wires_arr(); + // Static edge from FuncDecl, and 'ext' edge from function Input: + let c = tlb.call(&helper, &[], [b, u, i]).unwrap(); + let [pred, other] = c.outputs_arr(); + (tlb.finish_with_outputs(pred, [other]).unwrap(), c.node()) + }; + let _ = fb.finish_with_outputs(tl.outputs()).unwrap(); + let mut h = mb.finish_hugr().unwrap(); + + h.apply_patch(PeelTailLoop::new(tl.node())).unwrap(); + h.validate().unwrap(); + + assert_eq!( + h.nodes() + .filter(|n| h.get_optype(*n).is_tail_loop()) + .count(), + 1 + ); + use OpTag::*; + assert_eq!(tags(&h, call), [FnCall, Dfg, FuncDefn, ModuleRoot]); + let [c1, c2] = h + .all_linked_inputs(helper.node()) + .map(|(n, _p)| n) + .collect_array() + .unwrap(); + assert!([c1, c2].contains(&call)); + let other = if call == c1 { c2 } else { c1 }; + assert_eq!( + tags(&h, other), + [ + FnCall, + TailLoop, + Case, + Conditional, + Dfg, + FuncDefn, + ModuleRoot + ] + ); + } + + fn tags(h: &H, n: H::Node) -> Vec { + let mut v = Vec::new(); + let mut o = Some(n); + while let Some(n) = o { + v.push(h.get_optype(n).tag()); + o = h.get_parent(n); + } + v + } + + #[test] + fn peel_loop_order_output() { + let i16_t = || INT_TYPES[4].clone(); + let mut fb = + FunctionBuilder::new("main", Signature::new(vec![i16_t(), bool_t()], i16_t())).unwrap(); + + let [i, b] = fb.input_wires_arr(); + let tl = { + let mut tlb = fb + .tail_loop_builder([(i16_t(), i), (bool_t(), b)], [], i16_t().into()) + .unwrap(); + let [i, _b] = tlb.input_wires_arr(); + // This loop only goes round once. However, we do not expect this to affect + // peeling: *dataflow analysis* can tell us that the conditional will always + // take one Case (that does not contain the TailLoop), we do not do that here. + let [cont] = tlb + .add_dataflow_op( + Tag::new( + TailLoop::BREAK_TAG, + tlb.loop_signature().unwrap().control_variants().into(), + ), + [i], + ) + .unwrap() + .outputs_arr(); + tlb.finish_with_outputs(cont, []).unwrap() + }; + let [i2] = tl.outputs_arr(); + // Create a DFG (no inputs, one output) that reads the result of the TailLoop via an 'ext` edge + let dfg = fb + .dfg_builder(Signature::new(vec![], i16_t()), []) + .unwrap() + .finish_with_outputs([i2]) + .unwrap(); + let mut h = fb.finish_hugr_with_outputs(dfg.outputs()).unwrap(); + let tl = tl.node(); + + h.apply_patch(PeelTailLoop::new(tl)).unwrap(); + h.validate().unwrap(); + let [tl] = h + .nodes() + .filter(|n| h.get_optype(*n).is_tail_loop()) + .collect_array() + .unwrap(); + { + use OpTag::*; + assert_eq!( + tags(&h, tl), + [TailLoop, Case, Conditional, Dfg, FuncDefn, ModuleRoot] + ); + } + let [out_n] = h.output_neighbours(tl).collect_array().unwrap(); + assert!(h.get_optype(out_n).is_output()); + assert_eq!(h.get_parent(tl), h.get_parent(out_n)); + } +} diff --git a/hugr-core/src/hugr/patch/replace.rs b/hugr-core/src/hugr/patch/replace.rs index 7f70ba2b60..ba4104ad9a 100644 --- a/hugr-core/src/hugr/patch/replace.rs +++ b/hugr-core/src/hugr/patch/replace.rs @@ -320,7 +320,10 @@ impl PatchVerification for Replacement { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { self.removal.iter().copied() } } diff --git a/hugr-core/src/hugr/patch/simple_replace.rs b/hugr-core/src/hugr/patch/simple_replace.rs index ee47e95f8b..8fb22febda 100644 --- a/hugr-core/src/hugr/patch/simple_replace.rs +++ b/hugr-core/src/hugr/patch/simple_replace.rs @@ -17,6 +17,8 @@ use thiserror::Error; use super::inline_dfg::InlineDFGError; use super::{BoundaryPort, HostPort, PatchHugrMut, PatchVerification, ReplacementPort}; +pub mod serial; + /// Specification of a simple replacement operation. /// /// # Type parameters @@ -517,6 +519,11 @@ impl SimpleReplacement { let subgraph = subgraph.map_nodes(node_map); SimpleReplacement::new_unchecked(subgraph, replacement.clone()) } + + /// Allows to get the [Self::invalidated_nodes] without requiring a [HugrView]. + pub fn invalidation_set(&self) -> impl Iterator { + self.subgraph.nodes().iter().copied() + } } impl PatchVerification for SimpleReplacement { @@ -528,8 +535,11 @@ impl PatchVerification for SimpleReplacement { } #[inline] - fn invalidation_set(&self) -> impl Iterator { - self.subgraph.nodes().iter().copied() + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { + self.invalidation_set() } } @@ -865,7 +875,7 @@ pub(in crate::hugr::patch) mod test { // Check invalidation set assert_eq!( - HashSet::<_>::from_iter(r.invalidation_set()), + HashSet::<_>::from_iter(r.invalidated_nodes(&h)), HashSet::<_>::from_iter([h_node_cx, h_node_h0, h_node_h1]), ); diff --git a/hugr-core/src/hugr/patch/simple_replace/serial.rs b/hugr-core/src/hugr/patch/simple_replace/serial.rs new file mode 100644 index 0000000000..387c012d21 --- /dev/null +++ b/hugr-core/src/hugr/patch/simple_replace/serial.rs @@ -0,0 +1,116 @@ +//! Serialisation of [`SimpleReplacement`] + +use super::*; + +/// Serialized format for [`SimpleReplacement`] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct SerialSimpleReplacement { + /// The subgraph to be replaced + pub subgraph: SiblingSubgraph, + /// The replacement Hugr + pub replacement: H, +} + +impl SimpleReplacement { + /// Create a new [`SimpleReplacement`] from its serialized format + pub fn from_serial>(value: SerialSimpleReplacement) -> Self { + let SerialSimpleReplacement { + subgraph, + replacement, + } = value; + SimpleReplacement { + subgraph, + replacement: replacement.into(), + } + } + + /// Convert a [`SimpleReplacement`] into its serialized format + pub fn into_serial>(self) -> SerialSimpleReplacement { + let SimpleReplacement { + subgraph, + replacement, + } = self; + SerialSimpleReplacement { + subgraph, + replacement: replacement.into(), + } + } + + /// Create its serialized format from a reference to [`SimpleReplacement`] + pub fn to_serial<'a, H>(&'a self) -> SerialSimpleReplacement + where + N: Clone, + H: From<&'a Hugr>, + { + let SimpleReplacement { + subgraph, + replacement, + } = self; + SerialSimpleReplacement { + subgraph: subgraph.clone(), + replacement: replacement.into(), + } + } +} + +impl> From> for SerialSimpleReplacement { + fn from(value: SimpleReplacement) -> Self { + value.into_serial() + } +} + +impl, N> From> for SimpleReplacement { + fn from(value: SerialSimpleReplacement) -> Self { + SimpleReplacement::from_serial(value) + } +} + +#[cfg(test)] +mod test { + use super::super::test::*; + use super::*; + use crate::{envelope::serde_with::AsStringEnvelope, utils::test_quantum_extension::cx_gate}; + + use derive_more::derive::{From, Into}; + use rstest::rstest; + use serde_with::serde_as; + + #[serde_as] + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, From, Into)] + struct WrappedHugr { + #[serde_as(as = "AsStringEnvelope")] + pub hugr: Hugr, + } + + impl<'h> From<&'h Hugr> for WrappedHugr { + fn from(value: &'h Hugr) -> Self { + WrappedHugr { + hugr: value.clone(), + } + } + } + + #[rstest] + fn test_serial(simple_hugr: Hugr, dfg_hugr: Hugr) { + let h: Hugr = simple_hugr; + // 1. Locate the CX and its successor H's in h + let h_node_cx: Node = h + .entry_descendants() + .find(|node: &Node| *h.get_optype(*node) == cx_gate().into()) + .unwrap(); + let (h_node_h0, h_node_h1) = h.output_neighbours(h_node_cx).collect_tuple().unwrap(); + let s: Vec = vec![h_node_cx, h_node_h0, h_node_h1].into_iter().collect(); + // 2. Construct a new DFG-rooted hugr for the replacement + let replacement = dfg_hugr; + // 4. Define the replacement + let r = SimpleReplacement { + subgraph: SiblingSubgraph::try_from_nodes(s, &h).unwrap(), + replacement, + }; + + let other_repl_serial = r.to_serial::(); + let repl_serial = r.into_serial::(); + + assert_eq!(repl_serial, other_repl_serial); + } +} diff --git a/hugr-core/src/hugr/persistent.rs b/hugr-core/src/hugr/persistent.rs index 3436523e1f..d2813e11b6 100644 --- a/hugr-core/src/hugr/persistent.rs +++ b/hugr-core/src/hugr/persistent.rs @@ -60,6 +60,9 @@ //! To obtain a [`PersistentHugr`] from your state space, use //! [`CommitStateSpace::try_extract_hugr`]. A [`PersistentHugr`] can always be //! materialized into a [`Hugr`] type using [`PersistentHugr::to_hugr`]. +//! +//! +//! [`PatchVerification`]: crate::hugr::patch::PatchVerification mod parents_view; mod resolver; @@ -85,7 +88,7 @@ pub use resolver::PointerEqResolver; use crate::{ Hugr, HugrView, IncomingPort, Node, OutgoingPort, Port, SimpleReplacement, - hugr::patch::{Patch, PatchVerification, simple_replace}, + hugr::patch::{Patch, simple_replace}, }; /// A replacement operation that can be applied to a [`PersistentHugr`]. @@ -264,6 +267,9 @@ impl<'a> From<&'a RelRc> for &'a Commit { /// /// Currently, only patches that apply to subgraphs within dataflow regions /// are supported. +/// +/// [`PatchVerification`]: crate::hugr::patch::PatchVerification + #[derive(Clone, Debug)] pub struct PersistentHugr { /// The state space of all commits. @@ -760,5 +766,12 @@ fn find_conflicting_node<'a>( }) } +pub mod serial { + //! Serialization formats of [`CommitStateSpace`](super::CommitStateSpace) + //! and related types + #[doc(inline)] + pub use super::state_space::serial::*; +} + #[cfg(test)] mod tests; diff --git a/hugr-core/src/hugr/persistent/parents_view.rs b/hugr-core/src/hugr/persistent/parents_view.rs index 639acefccc..b4aa076060 100644 --- a/hugr-core/src/hugr/persistent/parents_view.rs +++ b/hugr-core/src/hugr/persistent/parents_view.rs @@ -5,7 +5,7 @@ use crate::{ extension::ExtensionRegistry, hugr::{ internal::HugrInternals, - views::{ExtractionResult, render::RenderConfig}, + views::{ExtractionResult, render}, }, ops::OpType, }; @@ -196,7 +196,8 @@ impl HugrView for ParentsView<'_> { unimplemented!() } - fn mermaid_string_with_config(&self, _config: RenderConfig) -> String { + #[allow(deprecated)] + fn mermaid_string_with_config(&self, _config: render::RenderConfig) -> String { unimplemented!() } diff --git a/hugr-core/src/hugr/persistent/resolver.rs b/hugr-core/src/hugr/persistent/resolver.rs index 2390a51f20..0a0d140ee5 100644 --- a/hugr-core/src/hugr/persistent/resolver.rs +++ b/hugr-core/src/hugr/persistent/resolver.rs @@ -7,7 +7,7 @@ use relrc::EquivalenceResolver; /// /// This is a trivial resolver (to be expanded on later), that considers two /// patches equivalent if they point to the same data in memory. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PointerEqResolver; impl EquivalenceResolver for PointerEqResolver { diff --git a/hugr-core/src/hugr/persistent/state_space.rs b/hugr-core/src/hugr/persistent/state_space.rs index 55c54ff8c8..d710c1aaec 100644 --- a/hugr-core/src/hugr/persistent/state_space.rs +++ b/hugr-core/src/hugr/persistent/state_space.rs @@ -16,11 +16,15 @@ use crate::{ ops::OpType, }; +pub mod serial; + /// A copyable handle to a [`Commit`] vertex within a [`CommitStateSpace`] pub type CommitId = relrc::NodeId; /// A HUGR node within a commit of the commit state space -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] +#[derive( + Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash, serde::Serialize, serde::Deserialize, +)] pub struct PatchNode(pub CommitId, pub Node); impl std::fmt::Display for PatchNode { diff --git a/hugr-core/src/hugr/persistent/state_space/serial.rs b/hugr-core/src/hugr/persistent/state_space/serial.rs new file mode 100644 index 0000000000..c345308b8b --- /dev/null +++ b/hugr-core/src/hugr/persistent/state_space/serial.rs @@ -0,0 +1,144 @@ +use relrc::serialization::SerializedHistoryGraph; + +use super::*; +use crate::hugr::patch::simple_replace::serial::SerialSimpleReplacement; + +/// Serialized format for [`PersistentReplacement`] +pub type SerialPersistentReplacement = SerialSimpleReplacement; + +/// Serialized format for CommitData +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum SerialCommitData { + /// Base commit containing a Hugr + Base(H), + /// Replacement commit containing a serialized replacement + Replacement(SerialPersistentReplacement), +} + +impl CommitData { + /// Create a new [`CommitData`]` from its serialized format + pub fn from_serial>(value: SerialCommitData) -> Self { + match value { + SerialCommitData::Base(h) => CommitData::Base(h.into()), + SerialCommitData::Replacement(replacement) => { + CommitData::Replacement(replacement.into()) + } + } + } + + /// Convert this [`CommitData`] into its serialized format + pub fn into_serial>(self) -> SerialCommitData { + match self { + CommitData::Base(h) => SerialCommitData::Base(h.into()), + CommitData::Replacement(replacement) => { + SerialCommitData::Replacement(replacement.into_serial()) + } + } + } +} + +impl> From for SerialCommitData { + fn from(value: CommitData) -> Self { + value.into_serial() + } +} + +impl> From> for CommitData { + fn from(value: SerialCommitData) -> Self { + CommitData::from_serial(value) + } +} + +/// Serialized format for commit state space +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct SerialCommitStateSpace { + /// The serialized history graph containing commit data + pub graph: SerializedHistoryGraph, (), PointerEqResolver>, + /// The base commit ID + pub base_commit: CommitId, +} + +impl CommitStateSpace { + /// Create a new [`CommitStateSpace`] from its serialized format + pub fn from_serial + Clone>(value: SerialCommitStateSpace) -> Self { + let SerialCommitStateSpace { graph, base_commit } = value; + + // Deserialize the SerializedHistoryGraph into a HistoryGraph with CommitData + let graph = graph.map_nodes(|n| CommitData::from_serial(n)); + let graph = HistoryGraph::try_from_serialized(graph, PointerEqResolver) + .expect("failed to deserialize history graph"); + + Self { graph, base_commit } + } + + /// Convert a [`CommitStateSpace`] into its serialized format + pub fn into_serial>(self) -> SerialCommitStateSpace { + let Self { graph, base_commit } = self; + let graph = graph.to_serialized(); + let graph = graph.map_nodes(|n| n.into_serial()); + SerialCommitStateSpace { graph, base_commit } + } + + /// Create a serialized format from a reference to [`CommitStateSpace`] + pub fn to_serial(&self) -> SerialCommitStateSpace + where + H: From, + { + let Self { graph, base_commit } = self; + let graph = graph.to_serialized(); + let graph = graph.map_nodes(|n| n.into_serial()); + SerialCommitStateSpace { + graph, + base_commit: *base_commit, + } + } +} + +impl> From for SerialCommitStateSpace { + fn from(value: CommitStateSpace) -> Self { + value.into_serial() + } +} + +impl> From> for CommitStateSpace { + fn from(value: SerialCommitStateSpace) -> Self { + CommitStateSpace::from_serial(value) + } +} + +#[cfg(test)] +mod tests { + use derive_more::derive::Into; + use rstest::rstest; + use serde_with::serde_as; + + use super::*; + use crate::{ + envelope::serde_with::AsStringEnvelope, hugr::persistent::tests::test_state_space, + }; + + #[serde_as] + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, From, Into)] + struct WrappedHugr { + #[serde_as(as = "AsStringEnvelope")] + pub hugr: Hugr, + } + + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + #[rstest] + fn test_serialize_state_space(test_state_space: (CommitStateSpace, [CommitId; 4])) { + let (state_space, _) = test_state_space; + let serialized = state_space.to_serial::(); + + let deser = CommitStateSpace::from_serial(serialized); + let _serialized_2 = deser.to_serial::(); + + // TODO: add this once PointerEqResolver is replaced by a deterministic resolver + // insta::assert_snapshot!(serde_json::to_string_pretty(&serialized).unwrap()); + // see https://github.com/CQCL/hugr/issues/2299 + // assert_eq!( + // serde_json::to_string(&serialized), + // serde_json::to_string(&serialized_2) + // ); + } +} diff --git a/hugr-core/src/hugr/persistent/trait_impls.rs b/hugr-core/src/hugr/persistent/trait_impls.rs index c11949db9a..6c68762029 100644 --- a/hugr-core/src/hugr/persistent/trait_impls.rs +++ b/hugr-core/src/hugr/persistent/trait_impls.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use itertools::{Either, Itertools}; -use portgraph::render::{DotFormat, MermaidFormat}; +use portgraph::render::MermaidFormat; use crate::{ Direction, Hugr, HugrView, Node, Port, @@ -10,7 +10,7 @@ use crate::{ internal::HugrInternals, views::{ ExtractionResult, - render::{self, RenderConfig}, + render::{self, MermaidFormatter, NodeLabel}, }, }, }; @@ -242,35 +242,49 @@ impl HugrView for PersistentHugr { .flat_map(move |port| self.linked_ports(node, port).map(|(opp_node, _)| opp_node)) } - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + #[allow(deprecated)] + fn mermaid_string_with_config(&self, config: render::RenderConfig) -> String { + self.mermaid_string_with_formatter(MermaidFormatter::from_render_config(config, self)) } - fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { // Extract a concrete HUGR for displaying let (hugr, node_map) = self.apply_all(); - // Map config accordingly - let config = RenderConfig { - entrypoint: config.entrypoint.map(|n| node_map[&n]), - node_indices: config.node_indices, - port_offsets_in_edges: config.port_offsets_in_edges, - type_labels_in_edges: config.type_labels_in_edges, - }; - // Render the extracted HUGR but map the node indices back to the // original patch node IDs - let inv_node_map: HashMap<_, _> = node_map.into_iter().map(|(k, v)| (v, k)).collect(); - let fmt_node_index = |n: portgraph::NodeIndex| format!("{:?}", inv_node_map[&n.into()]); + let entrypoint = formatter.entrypoint().map(|n| node_map[&n]); + let node_labels = match formatter.node_labels() { + NodeLabel::None => NodeLabel::None, + NodeLabel::Numeric => { + // replace node labels with patch node IDs + let node_labels_map: HashMap<_, _> = node_map + .into_iter() + .map(|(k, v)| (v, format!("{:?}", k))) + .collect(); + NodeLabel::Custom(node_labels_map) + } + NodeLabel::Custom(labels) => { + // rekey labels to the extracted HUGR node IDs + let labels = labels + .iter() + .map(|(k, v)| (node_map[k], v.clone())) + .collect(); + NodeLabel::Custom(labels) + } + }; + + // Map config accordingly + let config = MermaidFormatter::new(&hugr) + .with_entrypoint(entrypoint) + .with_node_labels(node_labels) + .with_port_offsets(formatter.port_offsets()) + .with_type_labels(formatter.type_labels()); + hugr.graph .mermaid_format() .with_hierarchy(&hugr.hierarchy) - .with_node_style(render::node_style(&hugr, config, fmt_node_index)) + .with_node_style(render::node_style(&hugr, config.clone())) .with_edge_style(render::edge_style(&hugr, config)) .finish() } @@ -279,26 +293,7 @@ impl HugrView for PersistentHugr { where Self: Sized, { - // Extract a concrete HUGR for displaying - let (hugr, node_map) = self.apply_all(); - - // Map config accordingly - let config = RenderConfig { - entrypoint: Some(node_map[&self.entrypoint()]), - ..RenderConfig::default() - }; - - // Render the extracted HUGR but map the node indices back to the - // original patch node IDs - let inv_node_map: HashMap<_, _> = node_map.into_iter().map(|(k, v)| (v, k)).collect(); - let fmt_node_index = |n: portgraph::NodeIndex| format!("{:?}", inv_node_map[&n.into()]); - hugr.graph - .dot_format() - .with_hierarchy(&hugr.hierarchy) - .with_node_style(render::node_style(&hugr, config, fmt_node_index)) - .with_port_style(render::port_style(&hugr, config)) - .with_edge_style(render::edge_style(&hugr, config)) - .finish() + unimplemented!("use mermaid_string instead") } fn extensions(&self) -> &crate::extension::ExtensionRegistry { @@ -351,19 +346,15 @@ mod tests { .try_extract_hugr([commit1, commit2, commit4]) .unwrap(); - let mermaid_str = hugr.mermaid_string_with_config(RenderConfig { - node_indices: false, - entrypoint: Some(hugr.entrypoint()), - ..Default::default() - }); + let mermaid_str = hugr + .mermaid_format() + .with_node_labels(NodeLabel::None) + .finish(); let extracted_hugr = hugr.to_hugr(); let exp_str = extracted_hugr - .mermaid_string_with_config(RenderConfig { - node_indices: false, - entrypoint: Some(extracted_hugr.entrypoint()), - ..Default::default() - }) - .to_string(); + .mermaid_format() + .with_node_labels(NodeLabel::None) + .finish(); assert_eq!(mermaid_str, exp_str); } diff --git a/hugr-core/src/hugr/views.rs b/hugr-core/src/hugr/views.rs index e86b024eb3..1704d79f65 100644 --- a/hugr-core/src/hugr/views.rs +++ b/hugr-core/src/hugr/views.rs @@ -14,7 +14,8 @@ use std::borrow::Cow; use std::collections::HashMap; pub use self::petgraph::PetgraphWrapper; -use self::render::RenderConfig; +#[allow(deprecated)] +use self::render::{MermaidFormatter, RenderConfig}; pub use rerooted::Rerooted; pub use root_checked::{InvalidSignature, RootCheckable, RootChecked, check_tag}; pub use sibling_subgraph::SiblingSubgraph; @@ -385,7 +386,9 @@ pub trait HugrView: HugrInternals { /// /// For a more detailed representation, use the [`HugrView::dot_string`] /// format instead. - fn mermaid_string(&self) -> String; + fn mermaid_string(&self) -> String { + self.mermaid_string_with_formatter(self.mermaid_format()) + } /// Return the mermaid representation of the underlying hierarchical graph. /// @@ -394,8 +397,51 @@ pub trait HugrView: HugrInternals { /// /// For a more detailed representation, use the [`HugrView::dot_string`] /// format instead. + #[deprecated(note = "Use `mermaid_format` instead")] + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: RenderConfig) -> String; + /// Return the mermaid representation of the underlying hierarchical graph + /// according to the provided [`MermaidFormatter`] formatting options. + /// + /// The hierarchy is represented using subgraphs. Edges are labelled with + /// their source and target ports. + /// + /// For a more detailed representation, use the [`HugrView::dot_string`] + /// format instead. + /// + /// ## Deprecation of [`RenderConfig`] + /// While the deprecated [HugrView::mermaid_string_with_config] exists, this + /// will by default try to convert the formatter options to a [`RenderConfig`], + /// but this may panic if the configuration is not supported. Users are + /// encouraged to provide an implementation of this method overriding the default + /// and no longer rely on [HugrView::mermaid_string_with_config]. + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { + #[allow(deprecated)] + let config = match RenderConfig::try_from(formatter) { + Ok(config) => config, + Err(e) => { + panic!("Unsupported format option: {}", e); + } + }; + #[allow(deprecated)] + self.mermaid_string_with_config(config) + } + + /// Construct a mermaid representation of the underlying hierarchical graph. + /// + /// Options can be set on the returned [`MermaidFormatter`] struct, before + /// generating the String with [`MermaidFormatter::finish`]. + /// + /// The hierarchy is represented using subgraphs. Edges are labelled with + /// their source and target ports. + /// + /// For a more detailed representation, use the [`HugrView::dot_string`] + /// format instead. + fn mermaid_format(&self) -> MermaidFormatter { + MermaidFormatter::new(self).with_entrypoint(self.entrypoint()) + } + /// Return the graphviz representation of the underlying graph and hierarchy side by side. /// /// For a simpler representation, use the [`HugrView::mermaid_string`] format instead. @@ -629,21 +675,17 @@ impl HugrView for Hugr { self.graph.all_neighbours(node.into_portgraph()).map_into() } - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + #[allow(deprecated)] + fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + self.mermaid_string_with_formatter(MermaidFormatter::from_render_config(config, self)) } - fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { self.graph .mermaid_format() .with_hierarchy(&self.hierarchy) - .with_node_style(render::node_style(self, config, |n| n.index().to_string())) - .with_edge_style(render::edge_style(self, config)) + .with_node_style(render::node_style(self, formatter.clone())) + .with_edge_style(render::edge_style(self, formatter)) .finish() } @@ -651,16 +693,13 @@ impl HugrView for Hugr { where Self: Sized, { - let config = RenderConfig { - entrypoint: Some(self.entrypoint()), - ..RenderConfig::default() - }; + let formatter = MermaidFormatter::new(self).with_entrypoint(self.entrypoint()); self.graph .dot_format() .with_hierarchy(&self.hierarchy) - .with_node_style(render::node_style(self, config, |n| n.index().to_string())) - .with_port_style(render::port_style(self, config)) - .with_edge_style(render::edge_style(self, config)) + .with_node_style(render::node_style(self, formatter.clone())) + .with_port_style(render::port_style(self)) + .with_edge_style(render::edge_style(self, formatter)) .finish() } diff --git a/hugr-core/src/hugr/views/impls.rs b/hugr-core/src/hugr/views/impls.rs index 09c4475be8..75311f2f73 100644 --- a/hugr-core/src/hugr/views/impls.rs +++ b/hugr-core/src/hugr/views/impls.rs @@ -58,7 +58,9 @@ macro_rules! hugr_view_methods { fn neighbours(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator + Clone; fn all_neighbours(&self, node: Self::Node) -> impl Iterator + Clone; fn mermaid_string(&self) -> String; + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: crate::hugr::views::render::RenderConfig) -> String; + fn mermaid_string_with_formatter(&self, #[into] formatter: crate::hugr::views::render::MermaidFormatter) -> String; fn dot_string(&self) -> String; fn static_source(&self, node: Self::Node) -> Option; fn static_targets(&self, node: Self::Node) -> Option>; diff --git a/hugr-core/src/hugr/views/render.rs b/hugr-core/src/hugr/views/render.rs index 9d597f1c04..b787a9e383 100644 --- a/hugr-core/src/hugr/views/render.rs +++ b/hugr-core/src/hugr/views/render.rs @@ -1,16 +1,23 @@ //! Helper methods to compute the node/edge/port style when rendering a HUGR //! into dot or mermaid format. +use std::collections::HashMap; + use portgraph::render::{EdgeStyle, NodeStyle, PortStyle, PresentationStyle}; use portgraph::{LinkView, MultiPortGraph, NodeIndex, PortIndex, PortView}; +use crate::core::HugrNode; +use crate::hugr::internal::HugrInternals; use crate::ops::{NamedOp, OpType}; use crate::types::EdgeKind; use crate::{Hugr, HugrView, Node}; -/// Configuration for rendering a HUGR graph. +/// Reduced configuration for rendering a HUGR graph. +/// +/// Additional options are available in the [`MermaidFormatter`] struct. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] +#[deprecated(note = "Use `MermaidFormatter` instead")] pub struct RenderConfig { /// Show the node index in the graph nodes. pub node_indices: bool, @@ -22,6 +29,213 @@ pub struct RenderConfig { pub entrypoint: Option, } +/// Configuration for rendering a HUGR graph. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MermaidFormatter<'h, H: HugrInternals + ?Sized = Hugr> { + /// The HUGR to render. + hugr: &'h H, + /// How to display the node indices. + node_labels: NodeLabel, + /// Show port offsets in the graph edges. + port_offsets_in_edges: bool, + /// Show type labels on edges. + type_labels_in_edges: bool, + /// A node to highlight as the graph entrypoint. + entrypoint: Option, +} + +impl<'h, H: HugrInternals + ?Sized> MermaidFormatter<'h, H> { + /// Create a new [`MermaidFormatter`] from a [`RenderConfig`]. + #[allow(deprecated)] + pub fn from_render_config(config: RenderConfig, hugr: &'h H) -> Self { + let node_labels = if config.node_indices { + NodeLabel::Numeric + } else { + NodeLabel::None + }; + Self { + hugr, + node_labels, + port_offsets_in_edges: config.port_offsets_in_edges, + type_labels_in_edges: config.type_labels_in_edges, + entrypoint: config.entrypoint, + } + } + + /// Create a new [`MermaidFormatter`] for the given [`Hugr`]. + pub fn new(hugr: &'h H) -> Self { + Self { + hugr, + node_labels: NodeLabel::Numeric, + port_offsets_in_edges: true, + type_labels_in_edges: true, + entrypoint: None, + } + } + + /// The entrypoint to highlight in the rendered graph. + pub fn entrypoint(&self) -> Option { + self.entrypoint + } + + /// The rendering style of the node labels. + pub fn node_labels(&self) -> &NodeLabel { + &self.node_labels + } + + /// Whether to show port offsets on edges. + pub fn port_offsets(&self) -> bool { + self.port_offsets_in_edges + } + + /// Whether to show type labels on edges. + pub fn type_labels(&self) -> bool { + self.type_labels_in_edges + } + + /// Set the node labels style. + pub fn with_node_labels(mut self, node_labels: NodeLabel) -> Self { + self.node_labels = node_labels; + self + } + + /// Set whether to show port offsets in edges. + pub fn with_port_offsets(mut self, show: bool) -> Self { + self.port_offsets_in_edges = show; + self + } + + /// Set whether to show type labels in edges. + pub fn with_type_labels(mut self, show: bool) -> Self { + self.type_labels_in_edges = show; + self + } + + /// Set the entrypoint node to highlight. + pub fn with_entrypoint(mut self, entrypoint: impl Into>) -> Self { + self.entrypoint = entrypoint.into(); + self + } + + /// Render the graph into a Mermaid string. + pub fn finish(self) -> String + where + H: HugrView, + { + self.hugr.mermaid_string_with_formatter(self) + } + + pub(crate) fn with_hugr>( + self, + hugr: &NewH, + ) -> MermaidFormatter<'_, NewH> { + let MermaidFormatter { + hugr: _, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = self; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } +} + +/// An error that occurs when trying to convert a `FullRenderConfig` into a +/// `RenderConfig`. +#[derive(Debug, thiserror::Error)] +pub enum UnsupportedRenderConfig { + /// Custom node labels are not supported in the `RenderConfig` struct. + #[error("Custom node labels are not supported in the `RenderConfig` struct")] + CustomNodeLabels, +} + +#[allow(deprecated)] +impl<'h, H: HugrInternals + ?Sized> TryFrom> for RenderConfig { + type Error = UnsupportedRenderConfig; + + fn try_from(value: MermaidFormatter<'h, H>) -> Result { + if matches!(value.node_labels, NodeLabel::Custom(_)) { + return Err(UnsupportedRenderConfig::CustomNodeLabels); + } + let node_indices = matches!(value.node_labels, NodeLabel::Numeric); + Ok(Self { + node_indices, + port_offsets_in_edges: value.port_offsets_in_edges, + type_labels_in_edges: value.type_labels_in_edges, + entrypoint: value.entrypoint, + }) + } +} + +macro_rules! impl_mermaid_formatter_from { + ($t:ty, $($lifetime:tt)?) => { + impl<'h, $($lifetime,)? H: HugrView> From> for MermaidFormatter<'h, H> { + fn from(value: MermaidFormatter<'h, $t>) -> Self { + let MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = value; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } + } + }; +} + +impl_mermaid_formatter_from!(&'hh H, 'hh); +impl_mermaid_formatter_from!(&'hh mut H, 'hh); +impl_mermaid_formatter_from!(std::rc::Rc,); +impl_mermaid_formatter_from!(std::sync::Arc,); +impl_mermaid_formatter_from!(Box,); + +impl<'h, H: HugrView + ToOwned> From>> + for MermaidFormatter<'h, H> +{ + fn from(value: MermaidFormatter<'h, std::borrow::Cow<'_, H>>) -> Self { + let MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = value; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } +} + +/// How to display the node indices. +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub enum NodeLabel { + /// Do not display the node index. + None, + /// Display the node index as a number. + #[default] + Numeric, + /// Display the labels corresponding to the node indices. + Custom(HashMap), +} + +#[allow(deprecated)] impl Default for RenderConfig { fn default() -> Self { Self { @@ -36,8 +250,7 @@ impl Default for RenderConfig { /// Formatter method to compute a node style. pub(in crate::hugr) fn node_style<'a>( h: &'a Hugr, - config: RenderConfig, - fmt_node_index: impl Fn(NodeIndex) -> String + 'a, + formatter: MermaidFormatter<'a>, ) -> Box NodeStyle + 'a> { fn node_name(h: &Hugr, n: NodeIndex) -> String { match h.get_optype(n.into()) { @@ -50,42 +263,54 @@ pub(in crate::hugr) fn node_style<'a>( let mut entrypoint_style = PresentationStyle::default(); entrypoint_style.stroke = Some("#832561".to_string()); entrypoint_style.stroke_width = Some("3px".to_string()); - let entrypoint = config.entrypoint.map(Node::into_portgraph); + let entrypoint = formatter.entrypoint.map(Node::into_portgraph); - if config.node_indices { - Box::new(move |n| { + match formatter.node_labels { + NodeLabel::Numeric => Box::new(move |n| { if Some(n) == entrypoint { NodeStyle::boxed(format!( "({ni}) [**{name}**]", - ni = fmt_node_index(n), + ni = n.index(), name = node_name(h, n) )) .with_attrs(entrypoint_style.clone()) } else { NodeStyle::boxed(format!( "({ni}) {name}", - ni = fmt_node_index(n), + ni = n.index(), name = node_name(h, n) )) } - }) - } else { - Box::new(move |n| { + }), + NodeLabel::None => Box::new(move |n| { if Some(n) == entrypoint { NodeStyle::boxed(format!("[**{name}**]", name = node_name(h, n))) .with_attrs(entrypoint_style.clone()) } else { NodeStyle::boxed(node_name(h, n)) } - }) + }), + NodeLabel::Custom(labels) => Box::new(move |n| { + if Some(n) == entrypoint { + NodeStyle::boxed(format!( + "({label}) [**{name}**]", + label = labels.get(&n.into()).unwrap_or(&n.index().to_string()), + name = node_name(h, n) + )) + .with_attrs(entrypoint_style.clone()) + } else { + NodeStyle::boxed(format!( + "({label}) {name}", + label = labels.get(&n.into()).unwrap_or(&n.index().to_string()), + name = node_name(h, n) + )) + } + }), } } /// Formatter method to compute a port style. -pub(in crate::hugr) fn port_style( - h: &Hugr, - _config: RenderConfig, -) -> Box PortStyle + '_> { +pub(in crate::hugr) fn port_style(h: &Hugr) -> Box PortStyle + '_> { let graph = &h.graph; Box::new(move |port| { let node = graph.port_node(port).unwrap(); @@ -110,15 +335,15 @@ pub(in crate::hugr) fn port_style( /// Formatter method to compute an edge style. #[allow(clippy::type_complexity)] -pub(in crate::hugr) fn edge_style( - h: &Hugr, - config: RenderConfig, +pub(in crate::hugr) fn edge_style<'a>( + h: &'a Hugr, + config: MermaidFormatter<'_>, ) -> Box< dyn FnMut( ::LinkEndpoint, ::LinkEndpoint, ) -> EdgeStyle - + '_, + + 'a, > { let graph = &h.graph; Box::new(move |src, tgt| { @@ -162,3 +387,45 @@ pub(in crate::hugr) fn edge_style( style.with_label(label) }) } + +#[cfg(test)] +mod tests { + use crate::{NodeIndex, builder::test::simple_dfg_hugr}; + + use super::*; + + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + #[test] + fn test_custom_node_labels() { + let h = simple_dfg_hugr(); + let node_labels = h + .nodes() + .map(|n| (n, format!("node_{}", n.index()))) + .collect(); + let config = h + .mermaid_format() + .with_node_labels(NodeLabel::Custom(node_labels)); + insta::assert_snapshot!(h.mermaid_string_with_formatter(config)); + } + + #[test] + fn convert_full_render_config_to_render_config() { + let h = simple_dfg_hugr(); + let config: MermaidFormatter = + MermaidFormatter::new(&h).with_node_labels(NodeLabel::Custom(HashMap::new())); + #[allow(deprecated)] + { + assert!(RenderConfig::try_from(config).is_err()); + } + + #[allow(deprecated)] + let config = RenderConfig { + entrypoint: Some(h.entrypoint()), + ..Default::default() + }; + assert_eq!( + MermaidFormatter::from_render_config(config, &h), + h.mermaid_format() + ) + } +} diff --git a/hugr-core/src/hugr/views/rerooted.rs b/hugr-core/src/hugr/views/rerooted.rs index ea849372db..8c84abdc71 100644 --- a/hugr-core/src/hugr/views/rerooted.rs +++ b/hugr-core/src/hugr/views/rerooted.rs @@ -4,7 +4,6 @@ use crate::hugr::HugrMut; use crate::hugr::internal::{HugrInternals, HugrMutInternals}; -use super::render::RenderConfig; use super::{HugrView, panic_invalid_node}; /// A HUGR wrapper with a modified entrypoint node. @@ -60,14 +59,12 @@ impl HugrView for Rerooted { self.hugr.get_optype(self.entrypoint) } - #[inline] - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + fn mermaid_string_with_formatter( + &self, + formatter: crate::hugr::views::render::MermaidFormatter, + ) -> String { + self.hugr + .mermaid_string_with_formatter(formatter.with_hugr(&self.hugr)) } delegate::delegate! { @@ -103,6 +100,7 @@ impl HugrView for Rerooted { fn first_child(&self, node: Self::Node) -> Option; fn neighbours(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator + Clone; fn all_neighbours(&self, node: Self::Node) -> impl Iterator + Clone; + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: crate::hugr::views::render::RenderConfig) -> String; fn dot_string(&self) -> String; fn static_source(&self, node: Self::Node) -> Option; @@ -235,4 +233,12 @@ mod test { ); assert!(extracted_hugr.get_optype(extracted_bb).is_dataflow_block()); } + + #[test] + fn mermaid_format() { + let h = simple_cfg_hugr(); + let rerooted = h.with_entrypoint(h.entrypoint()); + let mermaid_str = rerooted.mermaid_format().finish(); + assert_eq!(mermaid_str, h.mermaid_format().finish()); + } } diff --git a/hugr-core/src/hugr/views/sibling_subgraph.rs b/hugr-core/src/hugr/views/sibling_subgraph.rs index f31265884f..03dff67f51 100644 --- a/hugr-core/src/hugr/views/sibling_subgraph.rs +++ b/hugr-core/src/hugr/views/sibling_subgraph.rs @@ -50,7 +50,7 @@ use super::root_checked::RootCheckable; /// The `boundary_port` and `signature` methods will panic if any are found. /// State order edges are also unsupported in replacements in /// `create_simple_replacement`. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct SiblingSubgraph { /// The nodes of the induced subgraph. nodes: Vec, diff --git a/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap b/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap new file mode 100644 index 0000000000..22b2ed8a51 --- /dev/null +++ b/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap @@ -0,0 +1,22 @@ +--- +source: hugr-core/src/hugr/views/render.rs +expression: h.mermaid_string_with_formatter(config) +--- +graph LR + subgraph 0 ["(node_0) Module"] + direction LR + subgraph 1 ["(node_1) FuncDefn: #quot;main#quot;"] + direction LR + 2["(node_2) Input"] + 3["(node_3) Output"] + subgraph 4 ["(node_4) [**DFG**]"] + direction LR + style 4 stroke:#832561,stroke-width:3px + 5["(node_5) Input"] + 6["(node_6) Output"] + 5--"0:0
Bool"-->6 + end + 2--"0:0
Bool"-->4 + 4--"0:0
Bool"-->3 + end + end diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 9c05740a5c..ca358c624b 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -59,12 +59,16 @@ impl TailLoop { /// Build the output `TypeRow` of the child graph of a `TailLoop` node. pub(crate) fn body_output_row(&self) -> TypeRow { - let sum_type = Type::new_sum([self.just_inputs.clone(), self.just_outputs.clone()]); - let mut outputs = vec![sum_type]; + let mut outputs = vec![Type::new_sum(self.control_variants())]; outputs.extend_from_slice(&self.rest); outputs.into() } + /// The variants (continue / break) of the first output from the child graph + pub(crate) fn control_variants(&self) -> [TypeRow; 2] { + [self.just_inputs.clone(), self.just_outputs.clone()] + } + /// Build the input `TypeRow` of the child graph of a `TailLoop` node. pub(crate) fn body_input_row(&self) -> TypeRow { self.just_inputs.extend(self.rest.iter()) diff --git a/hugr-core/src/std_extensions/collections/array.rs b/hugr-core/src/std_extensions/collections/array.rs index 04360ba345..eb31441453 100644 --- a/hugr-core/src/std_extensions/collections/array.rs +++ b/hugr-core/src/std_extensions/collections/array.rs @@ -43,7 +43,7 @@ pub const ARRAY_VALUENAME: TypeName = TypeName::new_inline("array"); /// Reported unique name of the extension pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("collections.array"); /// Extension version. -pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); +pub const VERSION: semver::Version = semver::Version::new(0, 1, 1); /// A linear, fixed-length collection of values. /// @@ -197,7 +197,31 @@ pub trait ArrayOpBuilder: GenericArrayOpBuilder { ) -> Result { self.add_new_generic_array::(elem_ty, values) } - + /// Adds an array unpack operation to the dataflow graph. + /// + /// This operation unpacks an array into individual elements. + /// + /// # Arguments + /// + /// * `elem_ty` - The type of the elements in the array. + /// * `size` - The size of the array. + /// * `input` - The wire representing the array to unpack. + /// + /// # Errors + /// + /// If building the operation fails. + /// + /// # Returns + /// + /// A vector of wires representing the individual elements from the array. + fn add_array_unpack( + &mut self, + elem_ty: Type, + size: u64, + input: Wire, + ) -> Result, BuildError> { + self.add_generic_array_unpack::(elem_ty, size, input) + } /// Adds an array clone operation to the dataflow graph and return the wires /// representing the originala and cloned array. /// diff --git a/hugr-core/src/std_extensions/collections/array/array_op.rs b/hugr-core/src/std_extensions/collections/array/array_op.rs index 3df679d591..915603c1da 100644 --- a/hugr-core/src/std_extensions/collections/array/array_op.rs +++ b/hugr-core/src/std_extensions/collections/array/array_op.rs @@ -21,7 +21,7 @@ use crate::utils::Never; use super::array_kind::ArrayKind; -/// Array operation definitions. Generic over the conrete array implementation. +/// Array operation definitions. Generic over the concrete array implementation. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, IntoStaticStr, EnumIter, EnumString)] #[allow(non_camel_case_types)] #[non_exhaustive] @@ -58,6 +58,10 @@ pub enum GenericArrayOpDef { /// references `AK` to ensure that the type parameter is used. #[strum(disabled)] _phantom(PhantomData, Never), + /// Unpacks an array into its individual elements: + /// `unpack: array -> (elemty)^SIZE` + /// where `SIZE` must be statically known (not a variable) + unpack, } /// Static parameters for array operations. Includes array size. Type is part of the type scheme. @@ -76,6 +80,10 @@ impl SignatureFromArgs for GenericArrayOpDef { params, FuncValueType::new(vec![elem_ty_var.clone(); n as usize], array_ty), ), + GenericArrayOpDef::unpack => PolyFuncTypeRV::new( + params, + FuncValueType::new(array_ty, vec![elem_ty_var.clone(); n as usize]), + ), GenericArrayOpDef::pop_left | GenericArrayOpDef::pop_right => { let popped_array_ty = AK::ty(n - 1, elem_ty_var.clone()); PolyFuncTypeRV::new( @@ -124,9 +132,9 @@ impl GenericArrayOpDef { _extension_ref: &Weak, ) -> SignatureFunc { use GenericArrayOpDef::{ - _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, + _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, unpack, }; - if let new_array | pop_left | pop_right = self { + if let new_array | unpack | pop_left | pop_right = self { // implements SignatureFromArgs // signature computed dynamically, so can rely on type definition in extension. (*self).into() @@ -184,7 +192,7 @@ impl GenericArrayOpDef { ), ), _phantom(_, never) => match *never {}, - new_array | pop_left | pop_right => unreachable!(), + new_array | unpack | pop_left | pop_right => unreachable!(), } .into() } @@ -218,6 +226,7 @@ impl MakeOpDef for GenericArrayOpDef { fn description(&self) -> String { match self { GenericArrayOpDef::new_array => "Create a new array from elements", + GenericArrayOpDef::unpack => "Unpack an array into its elements", GenericArrayOpDef::get => "Get an element from an array", GenericArrayOpDef::set => "Set an element in an array", GenericArrayOpDef::swap => "Swap two elements in an array", @@ -250,7 +259,7 @@ impl MakeOpDef for GenericArrayOpDef { } #[derive(Clone, Debug, PartialEq)] -/// Concrete array operation. Generic over the actual array implemenation. +/// Concrete array operation. Generic over the actual array implementation. pub struct GenericArrayOp { /// The operation definition. pub def: GenericArrayOpDef, @@ -275,7 +284,7 @@ impl MakeExtensionOp for GenericArrayOp { fn type_args(&self) -> Vec { use GenericArrayOpDef::{ - _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, + _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, unpack, }; let ty_arg = TypeArg::Type { ty: self.elem_ty.clone(), @@ -288,7 +297,7 @@ impl MakeExtensionOp for GenericArrayOp { ); vec![ty_arg] } - new_array | pop_left | pop_right | get | set | swap => { + new_array | unpack | pop_left | pop_right | get | set | swap => { vec![TypeArg::BoundedNat { n: self.size }, ty_arg] } _phantom(_, never) => match never {}, @@ -379,6 +388,22 @@ mod tests { b.finish_hugr_with_outputs(out.outputs()).unwrap(); } + #[rstest] + #[case(Array)] + #[case(ValueArray)] + /// Test building a HUGR involving an unpack operation. + fn test_unpack(#[case] _kind: AK) { + let mut b = DFGBuilder::new(inout_sig(AK::ty(2, qb_t()), vec![qb_t(), qb_t()])).unwrap(); + + let [array] = b.input_wires_arr(); + + let op = GenericArrayOpDef::::unpack.to_concrete(qb_t(), 2); + + let out = b.add_dataflow_op(op, [array]).unwrap(); + + b.finish_hugr_with_outputs(out.outputs()).unwrap(); + } + #[rstest] #[case(Array)] #[case(ValueArray)] diff --git a/hugr-core/src/std_extensions/collections/array/op_builder.rs b/hugr-core/src/std_extensions/collections/array/op_builder.rs index 9f913e753d..2740673f80 100644 --- a/hugr-core/src/std_extensions/collections/array/op_builder.rs +++ b/hugr-core/src/std_extensions/collections/array/op_builder.rs @@ -49,6 +49,32 @@ pub trait GenericArrayOpBuilder: Dataflow { Ok(out) } + /// Adds an array unpack operation to the dataflow graph. + /// + /// This operation unpacks an array into individual elements. + /// + /// # Arguments + /// + /// * `elem_ty` - The type of the elements in the array. + /// * `size` - The size of the array. + /// * `input` - The wire representing the array. + /// + /// # Errors + /// + /// Returns an error if building the operation fails. + /// + /// # Returns + /// + /// A vector of wires representing the individual elements of the array. + fn add_generic_array_unpack( + &mut self, + elem_ty: Type, + size: u64, + input: Wire, + ) -> Result, BuildError> { + let op = GenericArrayOpDef::::unpack.instantiate(&[size.into(), elem_ty.into()])?; + Ok(self.add_dataflow_op(op, vec![input])?.outputs().collect()) + } /// Adds an array clone operation to the dataflow graph and return the wires /// representing the originala and cloned array. /// @@ -283,6 +309,17 @@ pub fn build_all_array_ops_generic(mut builder: B) - let us0 = builder.add_load_value(ConstUsize::new(0)); let us1 = builder.add_load_value(ConstUsize::new(1)); let us2 = builder.add_load_value(ConstUsize::new(2)); + let arr = builder + .add_new_generic_array::(usize_t(), [us1, us2]) + .unwrap(); + + // Add array unpack operation + let [_us1, _us2] = builder + .add_generic_array_unpack::(usize_t(), 2, arr) + .unwrap() + .try_into() + .unwrap(); + let arr = builder .add_new_generic_array::(usize_t(), [us1, us2]) .unwrap(); diff --git a/hugr-core/src/std_extensions/collections/value_array.rs b/hugr-core/src/std_extensions/collections/value_array.rs index a9668931cd..fe89824d77 100644 --- a/hugr-core/src/std_extensions/collections/value_array.rs +++ b/hugr-core/src/std_extensions/collections/value_array.rs @@ -31,7 +31,7 @@ pub const VALUE_ARRAY_VALUENAME: TypeName = TypeName::new_inline("value_array"); /// Reported unique name of the extension pub const EXTENSION_ID: ExtensionId = ExtensionId::new_static_unchecked("collections.value_array"); /// Extension version. -pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); +pub const VERSION: semver::Version = semver::Version::new(0, 1, 1); /// A fixed-length collection of values. /// diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 6fa0596237..2b36233133 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -25,8 +25,6 @@ use smol_str::SmolStr; pub use type_param::TypeArg; pub use type_row::{TypeRow, TypeRowRV}; -// Unused in --no-features -#[allow(unused_imports)] pub(crate) use poly_func::PolyFuncTypeBase; use itertools::FoldWhile::{Continue, Done}; diff --git a/hugr-llvm/CHANGELOG.md b/hugr-llvm/CHANGELOG.md index 4584d7246d..0ed47be85a 100644 --- a/hugr-llvm/CHANGELOG.md +++ b/hugr-llvm/CHANGELOG.md @@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-llvm-v0.20.1...hugr-llvm-v0.20.2) - 2025-06-25 + +### New Features + +- *(core, llvm)* add array unpack operations ([#2339](https://github.com/CQCL/hugr/pull/2339)) + +### Refactor + +- *(llvm)* replace HashMap with BTreeMap ([#2313](https://github.com/CQCL/hugr/pull/2313)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-llvm-v0.20.0...hugr-llvm-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr-llvm/Cargo.toml b/hugr-llvm/Cargo.toml index ac8dcbd958..31bd533960 100644 --- a/hugr-llvm/Cargo.toml +++ b/hugr-llvm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-llvm" -version = "0.20.1" +version = "0.20.2" description = "A general and extensible crate for lowering HUGRs into LLVM IR" edition.workspace = true @@ -26,7 +26,7 @@ workspace = true [dependencies] inkwell = { version = "0.6.0", default-features = false } -hugr-core = { path = "../hugr-core", version = "0.20.1" } +hugr-core = { path = "../hugr-core", version = "0.20.2" } anyhow = "1.0.98" itertools.workspace = true delegate.workspace = true diff --git a/hugr-llvm/src/custom/extension_op.rs b/hugr-llvm/src/custom/extension_op.rs index 4afef24e3e..640b43bcb5 100644 --- a/hugr-llvm/src/custom/extension_op.rs +++ b/hugr-llvm/src/custom/extension_op.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{collections::BTreeMap, rc::Rc}; use hugr_core::{ HugrView, Node, @@ -56,7 +56,7 @@ impl< /// /// Those callbacks may hold references with lifetimes older than `'a`. #[derive(Default)] -pub struct ExtensionOpMap<'a, H>(HashMap<(ExtensionId, OpName), Box>>); +pub struct ExtensionOpMap<'a, H>(BTreeMap<(ExtensionId, OpName), Box>>); impl<'a, H: HugrView> ExtensionOpMap<'a, H> { /// Register a callback to emit a [`ExtensionOp`], keyed by fully diff --git a/hugr-llvm/src/custom/load_constant.rs b/hugr-llvm/src/custom/load_constant.rs index 0ea964d160..0d84a99ae8 100644 --- a/hugr-llvm/src/custom/load_constant.rs +++ b/hugr-llvm/src/custom/load_constant.rs @@ -1,5 +1,5 @@ //! Provides the implementation for a collection of [`CustomConst`] callbacks. -use std::{any::TypeId, collections::HashMap}; +use std::{any::TypeId, collections::BTreeMap}; use hugr_core::{HugrView, Node, ops::constant::CustomConst}; use inkwell::values::BasicValueEnum; @@ -36,7 +36,7 @@ impl< /// The callbacks are keyed by the [`TypeId`] of those impls. #[derive(Default)] pub struct LoadConstantsMap<'a, H>( - HashMap>>, + BTreeMap>>, ); impl<'a, H: HugrView> LoadConstantsMap<'a, H> { diff --git a/hugr-llvm/src/emit/func.rs b/hugr-llvm/src/emit/func.rs index e6ce4c9c4e..77d865540f 100644 --- a/hugr-llvm/src/emit/func.rs +++ b/hugr-llvm/src/emit/func.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{collections::BTreeMap, rc::Rc}; use anyhow::{Result, anyhow}; use hugr_core::{ @@ -51,7 +51,7 @@ where emit_context: EmitModuleContext<'c, 'a, H>, todo: EmissionSet, func: FunctionValue<'c>, - env: HashMap>, + env: BTreeMap>, builder: Builder<'c>, prologue_bb: BasicBlock<'c>, launch_bb: BasicBlock<'c>, diff --git a/hugr-llvm/src/emit/ops/cfg.rs b/hugr-llvm/src/emit/ops/cfg.rs index 744427a1c6..bdf27389a2 100644 --- a/hugr-llvm/src/emit/ops/cfg.rs +++ b/hugr-llvm/src/emit/ops/cfg.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::BTreeMap; use anyhow::{Result, anyhow}; use hugr_core::{ @@ -21,7 +21,7 @@ use crate::{ use super::emit_dataflow_parent; pub struct CfgEmitter<'c, 'hugr, H> { - bbs: HashMap, (BasicBlock<'c>, RowMailBox<'c>)>, + bbs: BTreeMap, (BasicBlock<'c>, RowMailBox<'c>)>, inputs: Option>>, outputs: Option>, node: FatNode<'hugr, CFG, H>, @@ -47,7 +47,7 @@ impl<'c, 'hugr, H: HugrView> CfgEmitter<'c, 'hugr, H> { // to crate the other blocks immediately before it. This is just for // nice block ordering. let exit_block = context.new_basic_block("", None); - let mut bbs = HashMap::new(); + let mut bbs = BTreeMap::new(); for child in node.children() { if child.is_exit_block() { let output_row = { diff --git a/hugr-llvm/src/extension/collections/array.rs b/hugr-llvm/src/extension/collections/array.rs index 2a602e291c..725fbe2724 100644 --- a/hugr-llvm/src/extension/collections/array.rs +++ b/hugr-llvm/src/extension/collections/array.rs @@ -455,6 +455,24 @@ pub fn emit_array_op<'c, H: HugrView>( } outputs.finish(ctx.builder(), [array_v.into()]) } + ArrayOpDef::unpack => { + let [array_v] = inputs + .try_into() + .map_err(|_| anyhow!("ArrayOpDef::unpack expects one argument"))?; + let (array_ptr, array_offset) = decompose_array_fat_pointer(builder, array_v)?; + + let mut result = Vec::with_capacity(size as usize); + let usize_t = usize_ty(&ctx.typing_session()); + + for i in 0..size { + let idx = builder.build_int_add(array_offset, usize_t.const_int(i, false), "")?; + let elem_addr = unsafe { builder.build_in_bounds_gep(array_ptr, &[idx], "")? }; + let elem_v = builder.build_load(elem_addr, "")?; + result.push(elem_v); + } + + outputs.finish(ctx.builder(), result) + } ArrayOpDef::get => { let [array_v, index_v] = inputs .try_into() @@ -999,6 +1017,41 @@ mod test { check_emission!(hugr, llvm_ctx); } + // #[rstest] + // #[case(1, 2, 3)] + // #[case(0, 0, 0)] + // #[case(10, 20, 30)] + // fn exec_unpack_and_sum(mut exec_ctx: TestContext, #[case] a: u64, #[case] b: u64, #[case] expected: u64) { + // let hugr = SimpleHugrConfig::new() + // .with_extensions(exec_registry()) + // .with_outs(vec![usize_t()]) + // .finish(|mut builder| { + // // Create an array with the test values + // let values = vec![ConstUsize::new(a).into(), ConstUsize::new(b).into()]; + // let arr = builder.add_load_value(array::ArrayValue::new(usize_t(), values)); + + // // Unpack the array + // let [val_a, val_b] = builder.add_array_unpack(usize_t(), 2, arr).unwrap().try_into().unwrap(); + + // // Add the values + // let sum = { + // let int_ty = int_type(6); + // let a_int = builder.cast(val_a, int_ty.clone()).unwrap(); + // let b_int = builder.cast(val_b, int_ty.clone()).unwrap(); + // let sum_int = builder.add_iadd(6, a_int, b_int).unwrap(); + // builder.cast(sum_int, usize_t()).unwrap() + // }; + + // builder.finish_hugr_with_outputs([sum]).unwrap() + // }); + // exec_ctx.add_extensions(|cge| { + // cge.add_default_prelude_extensions() + // .add_default_array_extensions() + // .add_default_int_extensions() + // }); + // assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); + // } + fn exec_registry() -> ExtensionRegistry { ExtensionRegistry::new([ int_types::EXTENSION.to_owned(), @@ -1398,6 +1451,51 @@ mod test { assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); } + #[rstest] + #[case(&[], 0)] + #[case(&[1, 2], 3)] + #[case(&[6, 6, 6], 18)] + fn exec_unpack( + mut exec_ctx: TestContext, + #[case] array_contents: &[u64], + #[case] expected: u64, + ) { + // We build a HUGR that: + // - Loads an array with the given contents + // - Unpacks all the elements + // - Returns the sum of the elements + + let int_ty = int_type(6); + let hugr = SimpleHugrConfig::new() + .with_outs(int_ty.clone()) + .with_extensions(exec_registry()) + .finish(|mut builder| { + let array = array::ArrayValue::new( + int_ty.clone(), + array_contents + .iter() + .map(|&i| ConstInt::new_u(6, i).unwrap().into()) + .collect_vec(), + ); + let array = builder.add_load_value(array); + let unpacked = builder + .add_array_unpack(int_ty.clone(), array_contents.len() as u64, array) + .unwrap(); + let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap()); + for elem in unpacked { + r = builder.add_iadd(6, r, elem).unwrap(); + } + + builder.finish_with_outputs([r]).unwrap() + }); + exec_ctx.add_extensions(|cge| { + cge.add_default_prelude_extensions() + .add_default_array_extensions() + .add_default_int_extensions() + }); + assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); + } + #[rstest] #[case(5, 42, 0)] #[case(5, 42, 1)] diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap index 3f4d8cecb2..c2ed6a10df 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap @@ -29,220 +29,236 @@ entry_block: ; preds = %alloca_block store i64 1, i64* %4, align 4 %5 = getelementptr inbounds i64, i64* %1, i64 1 store i64 2, i64* %5, align 4 - %array_ptr = extractvalue { i64*, i64 } %3, 0 - %array_offset = extractvalue { i64*, i64 } %3, 1 - %6 = icmp ult i64 0, 2 - %7 = icmp ult i64 1, 2 - %8 = and i1 %6, %7 - br i1 %8, label %11, label %9 - -9: ; preds = %entry_block - %10 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %3, 1 - br label %19 - -11: ; preds = %entry_block - %12 = add i64 0, %array_offset - %13 = add i64 1, %array_offset - %14 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 - %15 = load i64, i64* %14, align 4 - %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %13 + %6 = call i8* @malloc(i64 mul (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2)) + %7 = bitcast i8* %6 to i64* + %8 = insertvalue { i64*, i64 } poison, i64* %7, 0 + %9 = insertvalue { i64*, i64 } %8, i64 0, 1 + %10 = getelementptr inbounds i64, i64* %7, i64 0 + store i64 1, i64* %10, align 4 + %11 = getelementptr inbounds i64, i64* %7, i64 1 + store i64 2, i64* %11, align 4 + %array_ptr = extractvalue { i64*, i64 } %9, 0 + %array_offset = extractvalue { i64*, i64 } %9, 1 + %12 = add i64 %array_offset, 0 + %13 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 + %14 = load i64, i64* %13, align 4 + %15 = add i64 %array_offset, 1 + %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %15 %17 = load i64, i64* %16, align 4 - store i64 %17, i64* %14, align 4 - store i64 %15, i64* %16, align 4 - %18 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %3, 1 - br label %19 - -19: ; preds = %9, %11 - %"0.0" = phi { i1, { i64*, i64 } } [ %18, %11 ], [ %10, %9 ] - %20 = extractvalue { i1, { i64*, i64 } } %"0.0", 0 - switch i1 %20, label %21 [ - i1 true, label %23 + %array_ptr9 = extractvalue { i64*, i64 } %3, 0 + %array_offset10 = extractvalue { i64*, i64 } %3, 1 + %18 = icmp ult i64 0, 2 + %19 = icmp ult i64 1, 2 + %20 = and i1 %18, %19 + br i1 %20, label %23, label %21 + +21: ; preds = %entry_block + %22 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %3, 1 + br label %31 + +23: ; preds = %entry_block + %24 = add i64 0, %array_offset10 + %25 = add i64 1, %array_offset10 + %26 = getelementptr inbounds i64, i64* %array_ptr9, i64 %24 + %27 = load i64, i64* %26, align 4 + %28 = getelementptr inbounds i64, i64* %array_ptr9, i64 %25 + %29 = load i64, i64* %28, align 4 + store i64 %29, i64* %26, align 4 + store i64 %27, i64* %28, align 4 + %30 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %3, 1 + br label %31 + +31: ; preds = %21, %23 + %"0.0" = phi { i1, { i64*, i64 } } [ %30, %23 ], [ %22, %21 ] + %32 = extractvalue { i1, { i64*, i64 } } %"0.0", 0 + switch i1 %32, label %33 [ + i1 true, label %35 ] -21: ; preds = %19 - %22 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 - br label %cond_16_case_0 +33: ; preds = %31 + %34 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 + br label %cond_19_case_0 -23: ; preds = %19 - %24 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 - br label %cond_16_case_1 +35: ; preds = %31 + %36 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 + br label %cond_19_case_1 -cond_16_case_0: ; preds = %21 - %25 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 - %26 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 - %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) +cond_19_case_0: ; preds = %33 + %37 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 + %38 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 + %39 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %37, i8* %38) call void @abort() - br label %cond_exit_16 - -cond_16_case_1: ; preds = %23 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"08.0" = phi { i64*, i64 } [ zeroinitializer, %cond_16_case_0 ], [ %24, %cond_16_case_1 ] - %array_ptr20 = extractvalue { i64*, i64 } %"08.0", 0 - %array_offset21 = extractvalue { i64*, i64 } %"08.0", 1 - %28 = icmp ult i64 0, 2 - br i1 %28, label %30, label %29 - -29: ; preds = %cond_exit_16 - br label %35 - -30: ; preds = %cond_exit_16 - %31 = add i64 0, %array_offset21 - %32 = getelementptr inbounds i64, i64* %array_ptr20, i64 %31 - %33 = load i64, i64* %32, align 4 - %34 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %33, 1 - br label %35 - -35: ; preds = %29, %30 - %"022.0" = phi { i1, i64 } [ %34, %30 ], [ { i1 false, i64 poison }, %29 ] - %36 = extractvalue { i1, i64 } %"022.0", 0 - switch i1 %36, label %37 [ - i1 true, label %38 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %35 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"013.0" = phi { i64*, i64 } [ zeroinitializer, %cond_19_case_0 ], [ %36, %cond_19_case_1 ] + %array_ptr25 = extractvalue { i64*, i64 } %"013.0", 0 + %array_offset26 = extractvalue { i64*, i64 } %"013.0", 1 + %40 = icmp ult i64 0, 2 + br i1 %40, label %42, label %41 + +41: ; preds = %cond_exit_19 + br label %47 + +42: ; preds = %cond_exit_19 + %43 = add i64 0, %array_offset26 + %44 = getelementptr inbounds i64, i64* %array_ptr25, i64 %43 + %45 = load i64, i64* %44, align 4 + %46 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %45, 1 + br label %47 + +47: ; preds = %41, %42 + %"027.0" = phi { i1, i64 } [ %46, %42 ], [ { i1 false, i64 poison }, %41 ] + %48 = extractvalue { i1, i64 } %"027.0", 0 + switch i1 %48, label %49 [ + i1 true, label %50 ] -37: ; preds = %35 - br label %cond_28_case_0 +49: ; preds = %47 + br label %cond_31_case_0 -38: ; preds = %35 - %39 = extractvalue { i1, i64 } %"022.0", 1 - br label %cond_28_case_1 +50: ; preds = %47 + %51 = extractvalue { i1, i64 } %"027.0", 1 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %37 - %40 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 - %41 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 - %42 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %40, i8* %41) +cond_31_case_0: ; preds = %49 + %52 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 + %53 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 + %54 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %52, i8* %53) call void @abort() - br label %cond_exit_28 - -cond_28_case_1: ; preds = %38 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"026.0" = phi i64 [ 0, %cond_28_case_0 ], [ %39, %cond_28_case_1 ] - %array_ptr36 = extractvalue { i64*, i64 } %"08.0", 0 - %array_offset37 = extractvalue { i64*, i64 } %"08.0", 1 - %43 = icmp ult i64 1, 2 - br i1 %43, label %47, label %44 - -44: ; preds = %cond_exit_28 - %45 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"026.0", 2 - %46 = insertvalue { i1, { i64*, i64 }, i64 } %45, { i64*, i64 } %"08.0", 1 - br label %53 - -47: ; preds = %cond_exit_28 - %48 = add i64 1, %array_offset37 - %49 = getelementptr inbounds i64, i64* %array_ptr36, i64 %48 - %50 = load i64, i64* %49, align 4 - store i64 %"026.0", i64* %49, align 4 - %51 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %50, 2 - %52 = insertvalue { i1, { i64*, i64 }, i64 } %51, { i64*, i64 } %"08.0", 1 - br label %53 - -53: ; preds = %44, %47 - %"038.0" = phi { i1, { i64*, i64 }, i64 } [ %52, %47 ], [ %46, %44 ] - %54 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 0 - switch i1 %54, label %55 [ - i1 true, label %58 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %50 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 + %"031.0" = phi i64 [ 0, %cond_31_case_0 ], [ %51, %cond_31_case_1 ] + %array_ptr41 = extractvalue { i64*, i64 } %"013.0", 0 + %array_offset42 = extractvalue { i64*, i64 } %"013.0", 1 + %55 = icmp ult i64 1, 2 + br i1 %55, label %59, label %56 + +56: ; preds = %cond_exit_31 + %57 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"031.0", 2 + %58 = insertvalue { i1, { i64*, i64 }, i64 } %57, { i64*, i64 } %"013.0", 1 + br label %65 + +59: ; preds = %cond_exit_31 + %60 = add i64 1, %array_offset42 + %61 = getelementptr inbounds i64, i64* %array_ptr41, i64 %60 + %62 = load i64, i64* %61, align 4 + store i64 %"031.0", i64* %61, align 4 + %63 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %62, 2 + %64 = insertvalue { i1, { i64*, i64 }, i64 } %63, { i64*, i64 } %"013.0", 1 + br label %65 + +65: ; preds = %56, %59 + %"043.0" = phi { i1, { i64*, i64 }, i64 } [ %64, %59 ], [ %58, %56 ] + %66 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 0 + switch i1 %66, label %67 [ + i1 true, label %70 ] -55: ; preds = %53 - %56 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 2 - %57 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 1 - br label %cond_39_case_0 +67: ; preds = %65 + %68 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 2 + %69 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 1 + br label %cond_42_case_0 -58: ; preds = %53 - %59 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 2 - %60 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 1 - br label %cond_39_case_1 +70: ; preds = %65 + %71 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 2 + %72 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 1 + br label %cond_42_case_1 -cond_39_case_0: ; preds = %55 - %61 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 - %62 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 - %63 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %61, i8* %62) +cond_42_case_0: ; preds = %67 + %73 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 + %74 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 + %75 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %73, i8* %74) call void @abort() - br label %cond_exit_39 - -cond_39_case_1: ; preds = %58 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"041.0" = phi i64 [ 0, %cond_39_case_0 ], [ %59, %cond_39_case_1 ] - %"142.0" = phi { i64*, i64 } [ zeroinitializer, %cond_39_case_0 ], [ %60, %cond_39_case_1 ] - %array_ptr61 = extractvalue { i64*, i64 } %"142.0", 0 - %array_offset62 = extractvalue { i64*, i64 } %"142.0", 1 - %new_offset = add i64 %array_offset62, 1 - %64 = getelementptr inbounds i64, i64* %array_ptr61, i64 %array_offset62 - %65 = load i64, i64* %64, align 4 - %66 = insertvalue { i64*, i64 } poison, i64* %array_ptr61, 0 - %67 = insertvalue { i64*, i64 } %66, i64 %new_offset, 1 - %68 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %65, 2 - %69 = insertvalue { i1, { i64*, i64 }, i64 } %68, { i64*, i64 } %67, 1 - %70 = extractvalue { i1, { i64*, i64 }, i64 } %69, 0 - switch i1 %70, label %71 [ - i1 true, label %72 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %70 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 + %"046.0" = phi i64 [ 0, %cond_42_case_0 ], [ %71, %cond_42_case_1 ] + %"147.0" = phi { i64*, i64 } [ zeroinitializer, %cond_42_case_0 ], [ %72, %cond_42_case_1 ] + %array_ptr66 = extractvalue { i64*, i64 } %"147.0", 0 + %array_offset67 = extractvalue { i64*, i64 } %"147.0", 1 + %new_offset = add i64 %array_offset67, 1 + %76 = getelementptr inbounds i64, i64* %array_ptr66, i64 %array_offset67 + %77 = load i64, i64* %76, align 4 + %78 = insertvalue { i64*, i64 } poison, i64* %array_ptr66, 0 + %79 = insertvalue { i64*, i64 } %78, i64 %new_offset, 1 + %80 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %77, 2 + %81 = insertvalue { i1, { i64*, i64 }, i64 } %80, { i64*, i64 } %79, 1 + %82 = extractvalue { i1, { i64*, i64 }, i64 } %81, 0 + switch i1 %82, label %83 [ + i1 true, label %84 ] -71: ; preds = %cond_exit_39 - br label %cond_50_case_0 +83: ; preds = %cond_exit_42 + br label %cond_53_case_0 -72: ; preds = %cond_exit_39 - %73 = extractvalue { i1, { i64*, i64 }, i64 } %69, 2 - %74 = extractvalue { i1, { i64*, i64 }, i64 } %69, 1 - br label %cond_50_case_1 +84: ; preds = %cond_exit_42 + %85 = extractvalue { i1, { i64*, i64 }, i64 } %81, 2 + %86 = extractvalue { i1, { i64*, i64 }, i64 } %81, 1 + br label %cond_53_case_1 -cond_50_case_0: ; preds = %71 - %75 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 - %76 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 - %77 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %75, i8* %76) +cond_53_case_0: ; preds = %83 + %87 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 + %88 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 + %89 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %87, i8* %88) call void @abort() - br label %cond_exit_50 - -cond_50_case_1: ; preds = %72 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"064.0" = phi i64 [ 0, %cond_50_case_0 ], [ %73, %cond_50_case_1 ] - %"165.0" = phi { i64*, i64 } [ zeroinitializer, %cond_50_case_0 ], [ %74, %cond_50_case_1 ] - %array_ptr78 = extractvalue { i64*, i64 } %"165.0", 0 - %array_offset79 = extractvalue { i64*, i64 } %"165.0", 1 - %78 = add i64 %array_offset79, 0 - %79 = getelementptr inbounds i64, i64* %array_ptr78, i64 %78 - %80 = load i64, i64* %79, align 4 - %81 = insertvalue { i64*, i64 } poison, i64* %array_ptr78, 0 - %82 = insertvalue { i64*, i64 } %81, i64 %array_offset79, 1 - %83 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %80, 2 - %84 = insertvalue { i1, { i64*, i64 }, i64 } %83, { i64*, i64 } %82, 1 - %85 = extractvalue { i1, { i64*, i64 }, i64 } %84, 0 - switch i1 %85, label %86 [ - i1 true, label %87 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %84 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"069.0" = phi i64 [ 0, %cond_53_case_0 ], [ %85, %cond_53_case_1 ] + %"170.0" = phi { i64*, i64 } [ zeroinitializer, %cond_53_case_0 ], [ %86, %cond_53_case_1 ] + %array_ptr83 = extractvalue { i64*, i64 } %"170.0", 0 + %array_offset84 = extractvalue { i64*, i64 } %"170.0", 1 + %90 = add i64 %array_offset84, 0 + %91 = getelementptr inbounds i64, i64* %array_ptr83, i64 %90 + %92 = load i64, i64* %91, align 4 + %93 = insertvalue { i64*, i64 } poison, i64* %array_ptr83, 0 + %94 = insertvalue { i64*, i64 } %93, i64 %array_offset84, 1 + %95 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %92, 2 + %96 = insertvalue { i1, { i64*, i64 }, i64 } %95, { i64*, i64 } %94, 1 + %97 = extractvalue { i1, { i64*, i64 }, i64 } %96, 0 + switch i1 %97, label %98 [ + i1 true, label %99 ] -86: ; preds = %cond_exit_50 - br label %cond_61_case_0 +98: ; preds = %cond_exit_53 + br label %cond_64_case_0 -87: ; preds = %cond_exit_50 - %88 = extractvalue { i1, { i64*, i64 }, i64 } %84, 2 - %89 = extractvalue { i1, { i64*, i64 }, i64 } %84, 1 - br label %cond_61_case_1 +99: ; preds = %cond_exit_53 + %100 = extractvalue { i1, { i64*, i64 }, i64 } %96, 2 + %101 = extractvalue { i1, { i64*, i64 }, i64 } %96, 1 + br label %cond_64_case_1 -cond_61_case_0: ; preds = %86 - %90 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 - %91 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 - %92 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %90, i8* %91) +cond_64_case_0: ; preds = %98 + %102 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 + %103 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 + %104 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %102, i8* %103) call void @abort() - br label %cond_exit_61 - -cond_61_case_1: ; preds = %87 - br label %cond_exit_61 - -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"081.0" = phi i64 [ 0, %cond_61_case_0 ], [ %88, %cond_61_case_1 ] - %"182.0" = phi { i64*, i64 } [ zeroinitializer, %cond_61_case_0 ], [ %89, %cond_61_case_1 ] - %array_ptr95 = extractvalue { i64*, i64 } %"182.0", 0 - %array_offset96 = extractvalue { i64*, i64 } %"182.0", 1 - %93 = bitcast i64* %array_ptr95 to i8* - call void @free(i8* %93) + br label %cond_exit_64 + +cond_64_case_1: ; preds = %99 + br label %cond_exit_64 + +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"086.0" = phi i64 [ 0, %cond_64_case_0 ], [ %100, %cond_64_case_1 ] + %"187.0" = phi { i64*, i64 } [ zeroinitializer, %cond_64_case_0 ], [ %101, %cond_64_case_1 ] + %array_ptr100 = extractvalue { i64*, i64 } %"187.0", 0 + %array_offset101 = extractvalue { i64*, i64 } %"187.0", 1 + %105 = bitcast i64* %array_ptr100 to i8* + call void @free(i8* %105) ret void } diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap index c4b46a02a8..8224af5a5c 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap @@ -20,69 +20,72 @@ define void @_hl.main.1() { alloca_block: %"12_0" = alloca i64, align 8 %"10_0" = alloca i64, align 8 + %"15_0" = alloca { i64*, i64 }, align 8 %"13_0" = alloca { i64*, i64 }, align 8 + %"14_0" = alloca i64, align 8 + %"14_1" = alloca i64, align 8 %"8_0" = alloca i64, align 8 - %"14_0" = alloca { i1, { i64*, i64 } }, align 8 + %"18_0" = alloca { i1, { i64*, i64 } }, align 8 %"0" = alloca { i1, { i64*, i64 } }, align 8 - %"16_0" = alloca { i64*, i64 }, align 8 - %"08" = alloca { i64*, i64 }, align 8 - %"010" = alloca { i64*, i64 }, align 8 - %"21_0" = alloca { i32, i8* }, align 8 - %"18_0" = alloca { i64*, i64 }, align 8 - %"22_0" = alloca { i64*, i64 }, align 8 + %"19_0" = alloca { i64*, i64 }, align 8 + %"013" = alloca { i64*, i64 }, align 8 %"015" = alloca { i64*, i64 }, align 8 - %"24_0" = alloca { i64*, i64 }, align 8 - %"26_0" = alloca { i1, i64 }, align 8 - %"26_1" = alloca { i64*, i64 }, align 8 - %"022" = alloca { i1, i64 }, align 8 + %"24_0" = alloca { i32, i8* }, align 8 + %"21_0" = alloca { i64*, i64 }, align 8 + %"25_0" = alloca { i64*, i64 }, align 8 + %"020" = alloca { i64*, i64 }, align 8 + %"27_0" = alloca { i64*, i64 }, align 8 + %"29_0" = alloca { i1, i64 }, align 8 + %"29_1" = alloca { i64*, i64 }, align 8 + %"027" = alloca { i1, i64 }, align 8 %"1" = alloca { i64*, i64 }, align 8 - %"28_0" = alloca i64, align 8 - %"026" = alloca i64, align 8 - %"33_0" = alloca { i32, i8* }, align 8 - %"34_0" = alloca i64, align 8 - %"030" = alloca i64, align 8 - %"36_0" = alloca i64, align 8 - %"38_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 - %"038" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"31_0" = alloca i64, align 8 + %"031" = alloca i64, align 8 + %"36_0" = alloca { i32, i8* }, align 8 + %"37_0" = alloca i64, align 8 + %"035" = alloca i64, align 8 %"39_0" = alloca i64, align 8 - %"39_1" = alloca { i64*, i64 }, align 8 - %"041" = alloca i64, align 8 - %"142" = alloca { i64*, i64 }, align 8 - %"045" = alloca i64, align 8 - %"146" = alloca { i64*, i64 }, align 8 - %"44_0" = alloca { i32, i8* }, align 8 - %"41_0" = alloca i64, align 8 - %"41_1" = alloca { i64*, i64 }, align 8 - %"45_0" = alloca i64, align 8 - %"45_1" = alloca { i64*, i64 }, align 8 - %"054" = alloca i64, align 8 - %"155" = alloca { i64*, i64 }, align 8 - %"47_0" = alloca i64, align 8 - %"47_1" = alloca { i64*, i64 }, align 8 - %"49_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"41_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"043" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"42_0" = alloca i64, align 8 + %"42_1" = alloca { i64*, i64 }, align 8 + %"046" = alloca i64, align 8 + %"147" = alloca { i64*, i64 }, align 8 + %"050" = alloca i64, align 8 + %"151" = alloca { i64*, i64 }, align 8 + %"47_0" = alloca { i32, i8* }, align 8 + %"44_0" = alloca i64, align 8 + %"44_1" = alloca { i64*, i64 }, align 8 + %"48_0" = alloca i64, align 8 + %"48_1" = alloca { i64*, i64 }, align 8 + %"059" = alloca i64, align 8 + %"160" = alloca { i64*, i64 }, align 8 %"50_0" = alloca i64, align 8 %"50_1" = alloca { i64*, i64 }, align 8 - %"064" = alloca i64, align 8 - %"165" = alloca { i64*, i64 }, align 8 - %"55_0" = alloca { i32, i8* }, align 8 - %"56_0" = alloca i64, align 8 - %"56_1" = alloca { i64*, i64 }, align 8 - %"071" = alloca i64, align 8 - %"172" = alloca { i64*, i64 }, align 8 - %"58_0" = alloca i64, align 8 - %"58_1" = alloca { i64*, i64 }, align 8 - %"60_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"52_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"53_0" = alloca i64, align 8 + %"53_1" = alloca { i64*, i64 }, align 8 + %"069" = alloca i64, align 8 + %"170" = alloca { i64*, i64 }, align 8 + %"58_0" = alloca { i32, i8* }, align 8 + %"59_0" = alloca i64, align 8 + %"59_1" = alloca { i64*, i64 }, align 8 + %"076" = alloca i64, align 8 + %"177" = alloca { i64*, i64 }, align 8 %"61_0" = alloca i64, align 8 %"61_1" = alloca { i64*, i64 }, align 8 - %"081" = alloca i64, align 8 - %"182" = alloca { i64*, i64 }, align 8 - %"66_0" = alloca { i32, i8* }, align 8 - %"67_0" = alloca i64, align 8 - %"67_1" = alloca { i64*, i64 }, align 8 - %"088" = alloca i64, align 8 - %"189" = alloca { i64*, i64 }, align 8 - %"69_0" = alloca i64, align 8 - %"69_1" = alloca { i64*, i64 }, align 8 + %"63_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"64_0" = alloca i64, align 8 + %"64_1" = alloca { i64*, i64 }, align 8 + %"086" = alloca i64, align 8 + %"187" = alloca { i64*, i64 }, align 8 + %"69_0" = alloca { i32, i8* }, align 8 + %"70_0" = alloca i64, align 8 + %"70_1" = alloca { i64*, i64 }, align 8 + %"093" = alloca i64, align 8 + %"194" = alloca { i64*, i64 }, align 8 + %"72_0" = alloca i64, align 8 + %"72_1" = alloca { i64*, i64 }, align 8 br label %entry_block entry_block: ; preds = %alloca_block @@ -98,347 +101,369 @@ entry_block: ; preds = %alloca_block store i64 %"10_01", i64* %4, align 4 %5 = getelementptr inbounds i64, i64* %1, i64 1 store i64 %"12_02", i64* %5, align 4 - store { i64*, i64 } %3, { i64*, i64 }* %"13_0", align 8 - store i64 0, i64* %"8_0", align 4 - %"13_03" = load { i64*, i64 }, { i64*, i64 }* %"13_0", align 8 - %"8_04" = load i64, i64* %"8_0", align 4 - %"10_05" = load i64, i64* %"10_0", align 4 - %array_ptr = extractvalue { i64*, i64 } %"13_03", 0 - %array_offset = extractvalue { i64*, i64 } %"13_03", 1 - %6 = icmp ult i64 %"8_04", 2 - %7 = icmp ult i64 %"10_05", 2 - %8 = and i1 %6, %7 - br i1 %8, label %11, label %9 - -9: ; preds = %entry_block - %10 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %"13_03", 1 - store { i1, { i64*, i64 } } %10, { i1, { i64*, i64 } }* %"0", align 8 - br label %19 - -11: ; preds = %entry_block - %12 = add i64 %"8_04", %array_offset - %13 = add i64 %"10_05", %array_offset - %14 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 - %15 = load i64, i64* %14, align 4 - %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %13 + store { i64*, i64 } %3, { i64*, i64 }* %"15_0", align 8 + %"10_03" = load i64, i64* %"10_0", align 4 + %"12_04" = load i64, i64* %"12_0", align 4 + %6 = call i8* @malloc(i64 mul (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2)) + %7 = bitcast i8* %6 to i64* + %8 = insertvalue { i64*, i64 } poison, i64* %7, 0 + %9 = insertvalue { i64*, i64 } %8, i64 0, 1 + %10 = getelementptr inbounds i64, i64* %7, i64 0 + store i64 %"10_03", i64* %10, align 4 + %11 = getelementptr inbounds i64, i64* %7, i64 1 + store i64 %"12_04", i64* %11, align 4 + store { i64*, i64 } %9, { i64*, i64 }* %"13_0", align 8 + %"13_05" = load { i64*, i64 }, { i64*, i64 }* %"13_0", align 8 + %array_ptr = extractvalue { i64*, i64 } %"13_05", 0 + %array_offset = extractvalue { i64*, i64 } %"13_05", 1 + %12 = add i64 %array_offset, 0 + %13 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 + %14 = load i64, i64* %13, align 4 + %15 = add i64 %array_offset, 1 + %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %15 %17 = load i64, i64* %16, align 4 - store i64 %17, i64* %14, align 4 - store i64 %15, i64* %16, align 4 - %18 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %"13_03", 1 - store { i1, { i64*, i64 } } %18, { i1, { i64*, i64 } }* %"0", align 8 - br label %19 - -19: ; preds = %9, %11 - %"06" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"0", align 8 - store { i1, { i64*, i64 } } %"06", { i1, { i64*, i64 } }* %"14_0", align 8 - %"14_07" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"14_0", align 8 - %20 = extractvalue { i1, { i64*, i64 } } %"14_07", 0 - switch i1 %20, label %21 [ - i1 true, label %23 + store i64 %14, i64* %"14_0", align 4 + store i64 %17, i64* %"14_1", align 4 + store i64 0, i64* %"8_0", align 4 + %"15_06" = load { i64*, i64 }, { i64*, i64 }* %"15_0", align 8 + %"8_07" = load i64, i64* %"8_0", align 4 + %"10_08" = load i64, i64* %"10_0", align 4 + %array_ptr9 = extractvalue { i64*, i64 } %"15_06", 0 + %array_offset10 = extractvalue { i64*, i64 } %"15_06", 1 + %18 = icmp ult i64 %"8_07", 2 + %19 = icmp ult i64 %"10_08", 2 + %20 = and i1 %18, %19 + br i1 %20, label %23, label %21 + +21: ; preds = %entry_block + %22 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %"15_06", 1 + store { i1, { i64*, i64 } } %22, { i1, { i64*, i64 } }* %"0", align 8 + br label %31 + +23: ; preds = %entry_block + %24 = add i64 %"8_07", %array_offset10 + %25 = add i64 %"10_08", %array_offset10 + %26 = getelementptr inbounds i64, i64* %array_ptr9, i64 %24 + %27 = load i64, i64* %26, align 4 + %28 = getelementptr inbounds i64, i64* %array_ptr9, i64 %25 + %29 = load i64, i64* %28, align 4 + store i64 %29, i64* %26, align 4 + store i64 %27, i64* %28, align 4 + %30 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %"15_06", 1 + store { i1, { i64*, i64 } } %30, { i1, { i64*, i64 } }* %"0", align 8 + br label %31 + +31: ; preds = %21, %23 + %"011" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"0", align 8 + store { i1, { i64*, i64 } } %"011", { i1, { i64*, i64 } }* %"18_0", align 8 + %"18_012" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"18_0", align 8 + %32 = extractvalue { i1, { i64*, i64 } } %"18_012", 0 + switch i1 %32, label %33 [ + i1 true, label %35 ] -21: ; preds = %19 - %22 = extractvalue { i1, { i64*, i64 } } %"14_07", 1 - store { i64*, i64 } %22, { i64*, i64 }* %"010", align 8 - br label %cond_16_case_0 - -23: ; preds = %19 - %24 = extractvalue { i1, { i64*, i64 } } %"14_07", 1 - store { i64*, i64 } %24, { i64*, i64 }* %"015", align 8 - br label %cond_16_case_1 - -cond_16_case_0: ; preds = %21 - %"011" = load { i64*, i64 }, { i64*, i64 }* %"010", align 8 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"21_0", align 8 - store { i64*, i64 } %"011", { i64*, i64 }* %"18_0", align 8 - %"21_012" = load { i32, i8* }, { i32, i8* }* %"21_0", align 8 - %"18_013" = load { i64*, i64 }, { i64*, i64 }* %"18_0", align 8 - %25 = extractvalue { i32, i8* } %"21_012", 0 - %26 = extractvalue { i32, i8* } %"21_012", 1 - %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) - call void @abort() - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"22_0", align 8 - %"22_014" = load { i64*, i64 }, { i64*, i64 }* %"22_0", align 8 - store { i64*, i64 } %"22_014", { i64*, i64 }* %"08", align 8 - br label %cond_exit_16 +33: ; preds = %31 + %34 = extractvalue { i1, { i64*, i64 } } %"18_012", 1 + store { i64*, i64 } %34, { i64*, i64 }* %"015", align 8 + br label %cond_19_case_0 -cond_16_case_1: ; preds = %23 +35: ; preds = %31 + %36 = extractvalue { i1, { i64*, i64 } } %"18_012", 1 + store { i64*, i64 } %36, { i64*, i64 }* %"020", align 8 + br label %cond_19_case_1 + +cond_19_case_0: ; preds = %33 %"016" = load { i64*, i64 }, { i64*, i64 }* %"015", align 8 - store { i64*, i64 } %"016", { i64*, i64 }* %"24_0", align 8 - %"24_017" = load { i64*, i64 }, { i64*, i64 }* %"24_0", align 8 - store { i64*, i64 } %"24_017", { i64*, i64 }* %"08", align 8 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"09" = load { i64*, i64 }, { i64*, i64 }* %"08", align 8 - store { i64*, i64 } %"09", { i64*, i64 }* %"16_0", align 8 - %"16_018" = load { i64*, i64 }, { i64*, i64 }* %"16_0", align 8 - %"8_019" = load i64, i64* %"8_0", align 4 - %array_ptr20 = extractvalue { i64*, i64 } %"16_018", 0 - %array_offset21 = extractvalue { i64*, i64 } %"16_018", 1 - %28 = icmp ult i64 %"8_019", 2 - br i1 %28, label %30, label %29 - -29: ; preds = %cond_exit_16 - store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"022", align 4 - store { i64*, i64 } %"16_018", { i64*, i64 }* %"1", align 8 - br label %35 - -30: ; preds = %cond_exit_16 - %31 = add i64 %"8_019", %array_offset21 - %32 = getelementptr inbounds i64, i64* %array_ptr20, i64 %31 - %33 = load i64, i64* %32, align 4 - %34 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %33, 1 - store { i1, i64 } %34, { i1, i64 }* %"022", align 4 - store { i64*, i64 } %"16_018", { i64*, i64 }* %"1", align 8 - br label %35 - -35: ; preds = %29, %30 - %"023" = load { i1, i64 }, { i1, i64 }* %"022", align 4 - %"124" = load { i64*, i64 }, { i64*, i64 }* %"1", align 8 - store { i1, i64 } %"023", { i1, i64 }* %"26_0", align 4 - store { i64*, i64 } %"124", { i64*, i64 }* %"26_1", align 8 - %"26_025" = load { i1, i64 }, { i1, i64 }* %"26_0", align 4 - %36 = extractvalue { i1, i64 } %"26_025", 0 - switch i1 %36, label %37 [ - i1 true, label %38 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"24_0", align 8 + store { i64*, i64 } %"016", { i64*, i64 }* %"21_0", align 8 + %"24_017" = load { i32, i8* }, { i32, i8* }* %"24_0", align 8 + %"21_018" = load { i64*, i64 }, { i64*, i64 }* %"21_0", align 8 + %37 = extractvalue { i32, i8* } %"24_017", 0 + %38 = extractvalue { i32, i8* } %"24_017", 1 + %39 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %37, i8* %38) + call void @abort() + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"25_0", align 8 + %"25_019" = load { i64*, i64 }, { i64*, i64 }* %"25_0", align 8 + store { i64*, i64 } %"25_019", { i64*, i64 }* %"013", align 8 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %35 + %"021" = load { i64*, i64 }, { i64*, i64 }* %"020", align 8 + store { i64*, i64 } %"021", { i64*, i64 }* %"27_0", align 8 + %"27_022" = load { i64*, i64 }, { i64*, i64 }* %"27_0", align 8 + store { i64*, i64 } %"27_022", { i64*, i64 }* %"013", align 8 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"014" = load { i64*, i64 }, { i64*, i64 }* %"013", align 8 + store { i64*, i64 } %"014", { i64*, i64 }* %"19_0", align 8 + %"19_023" = load { i64*, i64 }, { i64*, i64 }* %"19_0", align 8 + %"8_024" = load i64, i64* %"8_0", align 4 + %array_ptr25 = extractvalue { i64*, i64 } %"19_023", 0 + %array_offset26 = extractvalue { i64*, i64 } %"19_023", 1 + %40 = icmp ult i64 %"8_024", 2 + br i1 %40, label %42, label %41 + +41: ; preds = %cond_exit_19 + store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"027", align 4 + store { i64*, i64 } %"19_023", { i64*, i64 }* %"1", align 8 + br label %47 + +42: ; preds = %cond_exit_19 + %43 = add i64 %"8_024", %array_offset26 + %44 = getelementptr inbounds i64, i64* %array_ptr25, i64 %43 + %45 = load i64, i64* %44, align 4 + %46 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %45, 1 + store { i1, i64 } %46, { i1, i64 }* %"027", align 4 + store { i64*, i64 } %"19_023", { i64*, i64 }* %"1", align 8 + br label %47 + +47: ; preds = %41, %42 + %"028" = load { i1, i64 }, { i1, i64 }* %"027", align 4 + %"129" = load { i64*, i64 }, { i64*, i64 }* %"1", align 8 + store { i1, i64 } %"028", { i1, i64 }* %"29_0", align 4 + store { i64*, i64 } %"129", { i64*, i64 }* %"29_1", align 8 + %"29_030" = load { i1, i64 }, { i1, i64 }* %"29_0", align 4 + %48 = extractvalue { i1, i64 } %"29_030", 0 + switch i1 %48, label %49 [ + i1 true, label %50 ] -37: ; preds = %35 - br label %cond_28_case_0 +49: ; preds = %47 + br label %cond_31_case_0 -38: ; preds = %35 - %39 = extractvalue { i1, i64 } %"26_025", 1 - store i64 %39, i64* %"030", align 4 - br label %cond_28_case_1 +50: ; preds = %47 + %51 = extractvalue { i1, i64 } %"29_030", 1 + store i64 %51, i64* %"035", align 4 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %37 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"33_0", align 8 - %"33_028" = load { i32, i8* }, { i32, i8* }* %"33_0", align 8 - %40 = extractvalue { i32, i8* } %"33_028", 0 - %41 = extractvalue { i32, i8* } %"33_028", 1 - %42 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %40, i8* %41) +cond_31_case_0: ; preds = %49 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"36_0", align 8 + %"36_033" = load { i32, i8* }, { i32, i8* }* %"36_0", align 8 + %52 = extractvalue { i32, i8* } %"36_033", 0 + %53 = extractvalue { i32, i8* } %"36_033", 1 + %54 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %52, i8* %53) call void @abort() - store i64 0, i64* %"34_0", align 4 - %"34_029" = load i64, i64* %"34_0", align 4 - store i64 %"34_029", i64* %"026", align 4 - br label %cond_exit_28 - -cond_28_case_1: ; preds = %38 - %"031" = load i64, i64* %"030", align 4 - store i64 %"031", i64* %"36_0", align 4 - %"36_032" = load i64, i64* %"36_0", align 4 - store i64 %"36_032", i64* %"026", align 4 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"027" = load i64, i64* %"026", align 4 - store i64 %"027", i64* %"28_0", align 4 - %"26_133" = load { i64*, i64 }, { i64*, i64 }* %"26_1", align 8 - %"10_034" = load i64, i64* %"10_0", align 4 - %"28_035" = load i64, i64* %"28_0", align 4 - %array_ptr36 = extractvalue { i64*, i64 } %"26_133", 0 - %array_offset37 = extractvalue { i64*, i64 } %"26_133", 1 - %43 = icmp ult i64 %"10_034", 2 - br i1 %43, label %47, label %44 - -44: ; preds = %cond_exit_28 - %45 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"28_035", 2 - %46 = insertvalue { i1, { i64*, i64 }, i64 } %45, { i64*, i64 } %"26_133", 1 - store { i1, { i64*, i64 }, i64 } %46, { i1, { i64*, i64 }, i64 }* %"038", align 8 - br label %53 - -47: ; preds = %cond_exit_28 - %48 = add i64 %"10_034", %array_offset37 - %49 = getelementptr inbounds i64, i64* %array_ptr36, i64 %48 - %50 = load i64, i64* %49, align 4 - store i64 %"28_035", i64* %49, align 4 - %51 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %50, 2 - %52 = insertvalue { i1, { i64*, i64 }, i64 } %51, { i64*, i64 } %"26_133", 1 - store { i1, { i64*, i64 }, i64 } %52, { i1, { i64*, i64 }, i64 }* %"038", align 8 - br label %53 - -53: ; preds = %44, %47 - %"039" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"038", align 8 - store { i1, { i64*, i64 }, i64 } %"039", { i1, { i64*, i64 }, i64 }* %"38_0", align 8 - %"38_040" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"38_0", align 8 - %54 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 0 - switch i1 %54, label %55 [ - i1 true, label %58 + store i64 0, i64* %"37_0", align 4 + %"37_034" = load i64, i64* %"37_0", align 4 + store i64 %"37_034", i64* %"031", align 4 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %50 + %"036" = load i64, i64* %"035", align 4 + store i64 %"036", i64* %"39_0", align 4 + %"39_037" = load i64, i64* %"39_0", align 4 + store i64 %"39_037", i64* %"031", align 4 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 + %"032" = load i64, i64* %"031", align 4 + store i64 %"032", i64* %"31_0", align 4 + %"29_138" = load { i64*, i64 }, { i64*, i64 }* %"29_1", align 8 + %"10_039" = load i64, i64* %"10_0", align 4 + %"31_040" = load i64, i64* %"31_0", align 4 + %array_ptr41 = extractvalue { i64*, i64 } %"29_138", 0 + %array_offset42 = extractvalue { i64*, i64 } %"29_138", 1 + %55 = icmp ult i64 %"10_039", 2 + br i1 %55, label %59, label %56 + +56: ; preds = %cond_exit_31 + %57 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"31_040", 2 + %58 = insertvalue { i1, { i64*, i64 }, i64 } %57, { i64*, i64 } %"29_138", 1 + store { i1, { i64*, i64 }, i64 } %58, { i1, { i64*, i64 }, i64 }* %"043", align 8 + br label %65 + +59: ; preds = %cond_exit_31 + %60 = add i64 %"10_039", %array_offset42 + %61 = getelementptr inbounds i64, i64* %array_ptr41, i64 %60 + %62 = load i64, i64* %61, align 4 + store i64 %"31_040", i64* %61, align 4 + %63 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %62, 2 + %64 = insertvalue { i1, { i64*, i64 }, i64 } %63, { i64*, i64 } %"29_138", 1 + store { i1, { i64*, i64 }, i64 } %64, { i1, { i64*, i64 }, i64 }* %"043", align 8 + br label %65 + +65: ; preds = %56, %59 + %"044" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"043", align 8 + store { i1, { i64*, i64 }, i64 } %"044", { i1, { i64*, i64 }, i64 }* %"41_0", align 8 + %"41_045" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"41_0", align 8 + %66 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 0 + switch i1 %66, label %67 [ + i1 true, label %70 ] -55: ; preds = %53 - %56 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 2 - %57 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 1 - store i64 %56, i64* %"045", align 4 - store { i64*, i64 } %57, { i64*, i64 }* %"146", align 8 - br label %cond_39_case_0 - -58: ; preds = %53 - %59 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 2 - %60 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 1 - store i64 %59, i64* %"054", align 4 - store { i64*, i64 } %60, { i64*, i64 }* %"155", align 8 - br label %cond_39_case_1 - -cond_39_case_0: ; preds = %55 - %"047" = load i64, i64* %"045", align 4 - %"148" = load { i64*, i64 }, { i64*, i64 }* %"146", align 8 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"44_0", align 8 - store i64 %"047", i64* %"41_0", align 4 - store { i64*, i64 } %"148", { i64*, i64 }* %"41_1", align 8 - %"44_049" = load { i32, i8* }, { i32, i8* }* %"44_0", align 8 - %"41_050" = load i64, i64* %"41_0", align 4 - %"41_151" = load { i64*, i64 }, { i64*, i64 }* %"41_1", align 8 - %61 = extractvalue { i32, i8* } %"44_049", 0 - %62 = extractvalue { i32, i8* } %"44_049", 1 - %63 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %61, i8* %62) +67: ; preds = %65 + %68 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 2 + %69 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 1 + store i64 %68, i64* %"050", align 4 + store { i64*, i64 } %69, { i64*, i64 }* %"151", align 8 + br label %cond_42_case_0 + +70: ; preds = %65 + %71 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 2 + %72 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 1 + store i64 %71, i64* %"059", align 4 + store { i64*, i64 } %72, { i64*, i64 }* %"160", align 8 + br label %cond_42_case_1 + +cond_42_case_0: ; preds = %67 + %"052" = load i64, i64* %"050", align 4 + %"153" = load { i64*, i64 }, { i64*, i64 }* %"151", align 8 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"47_0", align 8 + store i64 %"052", i64* %"44_0", align 4 + store { i64*, i64 } %"153", { i64*, i64 }* %"44_1", align 8 + %"47_054" = load { i32, i8* }, { i32, i8* }* %"47_0", align 8 + %"44_055" = load i64, i64* %"44_0", align 4 + %"44_156" = load { i64*, i64 }, { i64*, i64 }* %"44_1", align 8 + %73 = extractvalue { i32, i8* } %"47_054", 0 + %74 = extractvalue { i32, i8* } %"47_054", 1 + %75 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %73, i8* %74) call void @abort() - store i64 0, i64* %"45_0", align 4 - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"45_1", align 8 - %"45_052" = load i64, i64* %"45_0", align 4 - %"45_153" = load { i64*, i64 }, { i64*, i64 }* %"45_1", align 8 - store i64 %"45_052", i64* %"041", align 4 - store { i64*, i64 } %"45_153", { i64*, i64 }* %"142", align 8 - br label %cond_exit_39 - -cond_39_case_1: ; preds = %58 - %"056" = load i64, i64* %"054", align 4 - %"157" = load { i64*, i64 }, { i64*, i64 }* %"155", align 8 - store i64 %"056", i64* %"47_0", align 4 - store { i64*, i64 } %"157", { i64*, i64 }* %"47_1", align 8 - %"47_058" = load i64, i64* %"47_0", align 4 - %"47_159" = load { i64*, i64 }, { i64*, i64 }* %"47_1", align 8 - store i64 %"47_058", i64* %"041", align 4 - store { i64*, i64 } %"47_159", { i64*, i64 }* %"142", align 8 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"043" = load i64, i64* %"041", align 4 - %"144" = load { i64*, i64 }, { i64*, i64 }* %"142", align 8 - store i64 %"043", i64* %"39_0", align 4 - store { i64*, i64 } %"144", { i64*, i64 }* %"39_1", align 8 - %"39_160" = load { i64*, i64 }, { i64*, i64 }* %"39_1", align 8 - %array_ptr61 = extractvalue { i64*, i64 } %"39_160", 0 - %array_offset62 = extractvalue { i64*, i64 } %"39_160", 1 - %new_offset = add i64 %array_offset62, 1 - %64 = getelementptr inbounds i64, i64* %array_ptr61, i64 %array_offset62 - %65 = load i64, i64* %64, align 4 - %66 = insertvalue { i64*, i64 } poison, i64* %array_ptr61, 0 - %67 = insertvalue { i64*, i64 } %66, i64 %new_offset, 1 - %68 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %65, 2 - %69 = insertvalue { i1, { i64*, i64 }, i64 } %68, { i64*, i64 } %67, 1 - store { i1, { i64*, i64 }, i64 } %69, { i1, { i64*, i64 }, i64 }* %"49_0", align 8 - %"49_063" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"49_0", align 8 - %70 = extractvalue { i1, { i64*, i64 }, i64 } %"49_063", 0 - switch i1 %70, label %71 [ - i1 true, label %72 + store i64 0, i64* %"48_0", align 4 + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"48_1", align 8 + %"48_057" = load i64, i64* %"48_0", align 4 + %"48_158" = load { i64*, i64 }, { i64*, i64 }* %"48_1", align 8 + store i64 %"48_057", i64* %"046", align 4 + store { i64*, i64 } %"48_158", { i64*, i64 }* %"147", align 8 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %70 + %"061" = load i64, i64* %"059", align 4 + %"162" = load { i64*, i64 }, { i64*, i64 }* %"160", align 8 + store i64 %"061", i64* %"50_0", align 4 + store { i64*, i64 } %"162", { i64*, i64 }* %"50_1", align 8 + %"50_063" = load i64, i64* %"50_0", align 4 + %"50_164" = load { i64*, i64 }, { i64*, i64 }* %"50_1", align 8 + store i64 %"50_063", i64* %"046", align 4 + store { i64*, i64 } %"50_164", { i64*, i64 }* %"147", align 8 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 + %"048" = load i64, i64* %"046", align 4 + %"149" = load { i64*, i64 }, { i64*, i64 }* %"147", align 8 + store i64 %"048", i64* %"42_0", align 4 + store { i64*, i64 } %"149", { i64*, i64 }* %"42_1", align 8 + %"42_165" = load { i64*, i64 }, { i64*, i64 }* %"42_1", align 8 + %array_ptr66 = extractvalue { i64*, i64 } %"42_165", 0 + %array_offset67 = extractvalue { i64*, i64 } %"42_165", 1 + %new_offset = add i64 %array_offset67, 1 + %76 = getelementptr inbounds i64, i64* %array_ptr66, i64 %array_offset67 + %77 = load i64, i64* %76, align 4 + %78 = insertvalue { i64*, i64 } poison, i64* %array_ptr66, 0 + %79 = insertvalue { i64*, i64 } %78, i64 %new_offset, 1 + %80 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %77, 2 + %81 = insertvalue { i1, { i64*, i64 }, i64 } %80, { i64*, i64 } %79, 1 + store { i1, { i64*, i64 }, i64 } %81, { i1, { i64*, i64 }, i64 }* %"52_0", align 8 + %"52_068" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"52_0", align 8 + %82 = extractvalue { i1, { i64*, i64 }, i64 } %"52_068", 0 + switch i1 %82, label %83 [ + i1 true, label %84 ] -71: ; preds = %cond_exit_39 - br label %cond_50_case_0 - -72: ; preds = %cond_exit_39 - %73 = extractvalue { i1, { i64*, i64 }, i64 } %"49_063", 2 - %74 = extractvalue { i1, { i64*, i64 }, i64 } %"49_063", 1 - store i64 %73, i64* %"071", align 4 - store { i64*, i64 } %74, { i64*, i64 }* %"172", align 8 - br label %cond_50_case_1 - -cond_50_case_0: ; preds = %71 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"55_0", align 8 - %"55_068" = load { i32, i8* }, { i32, i8* }* %"55_0", align 8 - %75 = extractvalue { i32, i8* } %"55_068", 0 - %76 = extractvalue { i32, i8* } %"55_068", 1 - %77 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %75, i8* %76) +83: ; preds = %cond_exit_42 + br label %cond_53_case_0 + +84: ; preds = %cond_exit_42 + %85 = extractvalue { i1, { i64*, i64 }, i64 } %"52_068", 2 + %86 = extractvalue { i1, { i64*, i64 }, i64 } %"52_068", 1 + store i64 %85, i64* %"076", align 4 + store { i64*, i64 } %86, { i64*, i64 }* %"177", align 8 + br label %cond_53_case_1 + +cond_53_case_0: ; preds = %83 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"58_0", align 8 + %"58_073" = load { i32, i8* }, { i32, i8* }* %"58_0", align 8 + %87 = extractvalue { i32, i8* } %"58_073", 0 + %88 = extractvalue { i32, i8* } %"58_073", 1 + %89 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %87, i8* %88) call void @abort() - store i64 0, i64* %"56_0", align 4 - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"56_1", align 8 - %"56_069" = load i64, i64* %"56_0", align 4 - %"56_170" = load { i64*, i64 }, { i64*, i64 }* %"56_1", align 8 - store i64 %"56_069", i64* %"064", align 4 - store { i64*, i64 } %"56_170", { i64*, i64 }* %"165", align 8 - br label %cond_exit_50 - -cond_50_case_1: ; preds = %72 - %"073" = load i64, i64* %"071", align 4 - %"174" = load { i64*, i64 }, { i64*, i64 }* %"172", align 8 - store i64 %"073", i64* %"58_0", align 4 - store { i64*, i64 } %"174", { i64*, i64 }* %"58_1", align 8 - %"58_075" = load i64, i64* %"58_0", align 4 - %"58_176" = load { i64*, i64 }, { i64*, i64 }* %"58_1", align 8 - store i64 %"58_075", i64* %"064", align 4 - store { i64*, i64 } %"58_176", { i64*, i64 }* %"165", align 8 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"066" = load i64, i64* %"064", align 4 - %"167" = load { i64*, i64 }, { i64*, i64 }* %"165", align 8 - store i64 %"066", i64* %"50_0", align 4 - store { i64*, i64 } %"167", { i64*, i64 }* %"50_1", align 8 - %"50_177" = load { i64*, i64 }, { i64*, i64 }* %"50_1", align 8 - %array_ptr78 = extractvalue { i64*, i64 } %"50_177", 0 - %array_offset79 = extractvalue { i64*, i64 } %"50_177", 1 - %78 = add i64 %array_offset79, 0 - %79 = getelementptr inbounds i64, i64* %array_ptr78, i64 %78 - %80 = load i64, i64* %79, align 4 - %81 = insertvalue { i64*, i64 } poison, i64* %array_ptr78, 0 - %82 = insertvalue { i64*, i64 } %81, i64 %array_offset79, 1 - %83 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %80, 2 - %84 = insertvalue { i1, { i64*, i64 }, i64 } %83, { i64*, i64 } %82, 1 - store { i1, { i64*, i64 }, i64 } %84, { i1, { i64*, i64 }, i64 }* %"60_0", align 8 - %"60_080" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"60_0", align 8 - %85 = extractvalue { i1, { i64*, i64 }, i64 } %"60_080", 0 - switch i1 %85, label %86 [ - i1 true, label %87 + store i64 0, i64* %"59_0", align 4 + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"59_1", align 8 + %"59_074" = load i64, i64* %"59_0", align 4 + %"59_175" = load { i64*, i64 }, { i64*, i64 }* %"59_1", align 8 + store i64 %"59_074", i64* %"069", align 4 + store { i64*, i64 } %"59_175", { i64*, i64 }* %"170", align 8 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %84 + %"078" = load i64, i64* %"076", align 4 + %"179" = load { i64*, i64 }, { i64*, i64 }* %"177", align 8 + store i64 %"078", i64* %"61_0", align 4 + store { i64*, i64 } %"179", { i64*, i64 }* %"61_1", align 8 + %"61_080" = load i64, i64* %"61_0", align 4 + %"61_181" = load { i64*, i64 }, { i64*, i64 }* %"61_1", align 8 + store i64 %"61_080", i64* %"069", align 4 + store { i64*, i64 } %"61_181", { i64*, i64 }* %"170", align 8 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"071" = load i64, i64* %"069", align 4 + %"172" = load { i64*, i64 }, { i64*, i64 }* %"170", align 8 + store i64 %"071", i64* %"53_0", align 4 + store { i64*, i64 } %"172", { i64*, i64 }* %"53_1", align 8 + %"53_182" = load { i64*, i64 }, { i64*, i64 }* %"53_1", align 8 + %array_ptr83 = extractvalue { i64*, i64 } %"53_182", 0 + %array_offset84 = extractvalue { i64*, i64 } %"53_182", 1 + %90 = add i64 %array_offset84, 0 + %91 = getelementptr inbounds i64, i64* %array_ptr83, i64 %90 + %92 = load i64, i64* %91, align 4 + %93 = insertvalue { i64*, i64 } poison, i64* %array_ptr83, 0 + %94 = insertvalue { i64*, i64 } %93, i64 %array_offset84, 1 + %95 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %92, 2 + %96 = insertvalue { i1, { i64*, i64 }, i64 } %95, { i64*, i64 } %94, 1 + store { i1, { i64*, i64 }, i64 } %96, { i1, { i64*, i64 }, i64 }* %"63_0", align 8 + %"63_085" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"63_0", align 8 + %97 = extractvalue { i1, { i64*, i64 }, i64 } %"63_085", 0 + switch i1 %97, label %98 [ + i1 true, label %99 ] -86: ; preds = %cond_exit_50 - br label %cond_61_case_0 - -87: ; preds = %cond_exit_50 - %88 = extractvalue { i1, { i64*, i64 }, i64 } %"60_080", 2 - %89 = extractvalue { i1, { i64*, i64 }, i64 } %"60_080", 1 - store i64 %88, i64* %"088", align 4 - store { i64*, i64 } %89, { i64*, i64 }* %"189", align 8 - br label %cond_61_case_1 - -cond_61_case_0: ; preds = %86 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"66_0", align 8 - %"66_085" = load { i32, i8* }, { i32, i8* }* %"66_0", align 8 - %90 = extractvalue { i32, i8* } %"66_085", 0 - %91 = extractvalue { i32, i8* } %"66_085", 1 - %92 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %90, i8* %91) +98: ; preds = %cond_exit_53 + br label %cond_64_case_0 + +99: ; preds = %cond_exit_53 + %100 = extractvalue { i1, { i64*, i64 }, i64 } %"63_085", 2 + %101 = extractvalue { i1, { i64*, i64 }, i64 } %"63_085", 1 + store i64 %100, i64* %"093", align 4 + store { i64*, i64 } %101, { i64*, i64 }* %"194", align 8 + br label %cond_64_case_1 + +cond_64_case_0: ; preds = %98 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"69_0", align 8 + %"69_090" = load { i32, i8* }, { i32, i8* }* %"69_0", align 8 + %102 = extractvalue { i32, i8* } %"69_090", 0 + %103 = extractvalue { i32, i8* } %"69_090", 1 + %104 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %102, i8* %103) call void @abort() - store i64 0, i64* %"67_0", align 4 - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"67_1", align 8 - %"67_086" = load i64, i64* %"67_0", align 4 - %"67_187" = load { i64*, i64 }, { i64*, i64 }* %"67_1", align 8 - store i64 %"67_086", i64* %"081", align 4 - store { i64*, i64 } %"67_187", { i64*, i64 }* %"182", align 8 - br label %cond_exit_61 - -cond_61_case_1: ; preds = %87 - %"090" = load i64, i64* %"088", align 4 - %"191" = load { i64*, i64 }, { i64*, i64 }* %"189", align 8 - store i64 %"090", i64* %"69_0", align 4 - store { i64*, i64 } %"191", { i64*, i64 }* %"69_1", align 8 - %"69_092" = load i64, i64* %"69_0", align 4 - %"69_193" = load { i64*, i64 }, { i64*, i64 }* %"69_1", align 8 - store i64 %"69_092", i64* %"081", align 4 - store { i64*, i64 } %"69_193", { i64*, i64 }* %"182", align 8 - br label %cond_exit_61 - -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"083" = load i64, i64* %"081", align 4 - %"184" = load { i64*, i64 }, { i64*, i64 }* %"182", align 8 - store i64 %"083", i64* %"61_0", align 4 - store { i64*, i64 } %"184", { i64*, i64 }* %"61_1", align 8 - %"61_194" = load { i64*, i64 }, { i64*, i64 }* %"61_1", align 8 - %array_ptr95 = extractvalue { i64*, i64 } %"61_194", 0 - %array_offset96 = extractvalue { i64*, i64 } %"61_194", 1 - %93 = bitcast i64* %array_ptr95 to i8* - call void @free(i8* %93) + store i64 0, i64* %"70_0", align 4 + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"70_1", align 8 + %"70_091" = load i64, i64* %"70_0", align 4 + %"70_192" = load { i64*, i64 }, { i64*, i64 }* %"70_1", align 8 + store i64 %"70_091", i64* %"086", align 4 + store { i64*, i64 } %"70_192", { i64*, i64 }* %"187", align 8 + br label %cond_exit_64 + +cond_64_case_1: ; preds = %99 + %"095" = load i64, i64* %"093", align 4 + %"196" = load { i64*, i64 }, { i64*, i64 }* %"194", align 8 + store i64 %"095", i64* %"72_0", align 4 + store { i64*, i64 } %"196", { i64*, i64 }* %"72_1", align 8 + %"72_097" = load i64, i64* %"72_0", align 4 + %"72_198" = load { i64*, i64 }, { i64*, i64 }* %"72_1", align 8 + store i64 %"72_097", i64* %"086", align 4 + store { i64*, i64 } %"72_198", { i64*, i64 }* %"187", align 8 + br label %cond_exit_64 + +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"088" = load i64, i64* %"086", align 4 + %"189" = load { i64*, i64 }, { i64*, i64 }* %"187", align 8 + store i64 %"088", i64* %"64_0", align 4 + store { i64*, i64 } %"189", { i64*, i64 }* %"64_1", align 8 + %"64_199" = load { i64*, i64 }, { i64*, i64 }* %"64_1", align 8 + %array_ptr100 = extractvalue { i64*, i64 } %"64_199", 0 + %array_offset101 = extractvalue { i64*, i64 } %"64_199", 1 + %105 = bitcast i64* %array_ptr100 to i8* + call void @free(i8* %105) ret void } diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap index 0bab21f5d3..6798b9ab55 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap @@ -23,216 +23,220 @@ alloca_block: entry_block: ; preds = %alloca_block %0 = insertvalue [2 x i64] undef, i64 1, 0 %1 = insertvalue [2 x i64] %0, i64 2, 1 - %2 = icmp ult i64 0, 2 - %3 = icmp ult i64 1, 2 - %4 = and i1 %2, %3 - br i1 %4, label %7, label %5 - -5: ; preds = %entry_block - %6 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %1, 1 - br label %17 + %2 = insertvalue [2 x i64] undef, i64 1, 0 + %3 = insertvalue [2 x i64] %2, i64 2, 1 + %extract = extractvalue [2 x i64] %3, 0 + %extract6 = extractvalue [2 x i64] %3, 1 + %4 = icmp ult i64 0, 2 + %5 = icmp ult i64 1, 2 + %6 = and i1 %4, %5 + br i1 %6, label %9, label %7 7: ; preds = %entry_block - %8 = alloca i64, i32 2, align 8 - %9 = bitcast i64* %8 to [2 x i64]* - store [2 x i64] %1, [2 x i64]* %9, align 4 - %10 = getelementptr inbounds i64, i64* %8, i64 0 - %11 = load i64, i64* %10, align 4 - %12 = getelementptr inbounds i64, i64* %8, i64 1 + %8 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %1, 1 + br label %19 + +9: ; preds = %entry_block + %10 = alloca i64, i32 2, align 8 + %11 = bitcast i64* %10 to [2 x i64]* + store [2 x i64] %1, [2 x i64]* %11, align 4 + %12 = getelementptr inbounds i64, i64* %10, i64 0 %13 = load i64, i64* %12, align 4 - store i64 %13, i64* %10, align 4 - store i64 %11, i64* %12, align 4 - %14 = bitcast i64* %8 to [2 x i64]* - %15 = load [2 x i64], [2 x i64]* %14, align 4 - %16 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %15, 1 - br label %17 - -17: ; preds = %5, %7 - %"0.0" = phi { i1, [2 x i64] } [ %16, %7 ], [ %6, %5 ] - %18 = extractvalue { i1, [2 x i64] } %"0.0", 0 - switch i1 %18, label %19 [ - i1 true, label %21 + %14 = getelementptr inbounds i64, i64* %10, i64 1 + %15 = load i64, i64* %14, align 4 + store i64 %15, i64* %12, align 4 + store i64 %13, i64* %14, align 4 + %16 = bitcast i64* %10 to [2 x i64]* + %17 = load [2 x i64], [2 x i64]* %16, align 4 + %18 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %17, 1 + br label %19 + +19: ; preds = %7, %9 + %"0.0" = phi { i1, [2 x i64] } [ %18, %9 ], [ %8, %7 ] + %20 = extractvalue { i1, [2 x i64] } %"0.0", 0 + switch i1 %20, label %21 [ + i1 true, label %23 ] -19: ; preds = %17 - %20 = extractvalue { i1, [2 x i64] } %"0.0", 1 - br label %cond_16_case_0 - -21: ; preds = %17 +21: ; preds = %19 %22 = extractvalue { i1, [2 x i64] } %"0.0", 1 - br label %cond_16_case_1 + br label %cond_19_case_0 + +23: ; preds = %19 + %24 = extractvalue { i1, [2 x i64] } %"0.0", 1 + br label %cond_19_case_1 -cond_16_case_0: ; preds = %19 - %23 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 - %24 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 - %25 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %23, i8* %24) +cond_19_case_0: ; preds = %21 + %25 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 + %26 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 + %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) call void @abort() - br label %cond_exit_16 - -cond_16_case_1: ; preds = %21 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"08.0" = phi [2 x i64] [ zeroinitializer, %cond_16_case_0 ], [ %22, %cond_16_case_1 ] - %26 = icmp ult i64 0, 2 - br i1 %26, label %28, label %27 - -27: ; preds = %cond_exit_16 - br label %34 - -28: ; preds = %cond_exit_16 - %29 = alloca i64, i32 2, align 8 - %30 = bitcast i64* %29 to [2 x i64]* - store [2 x i64] %"08.0", [2 x i64]* %30, align 4 - %31 = getelementptr inbounds i64, i64* %29, i64 0 - %32 = load i64, i64* %31, align 4 - %33 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %32, 1 - br label %34 - -34: ; preds = %27, %28 - %"020.0" = phi { i1, i64 } [ %33, %28 ], [ { i1 false, i64 poison }, %27 ] - %35 = extractvalue { i1, i64 } %"020.0", 0 - switch i1 %35, label %36 [ - i1 true, label %37 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %23 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"012.0" = phi [2 x i64] [ zeroinitializer, %cond_19_case_0 ], [ %24, %cond_19_case_1 ] + %28 = icmp ult i64 0, 2 + br i1 %28, label %30, label %29 + +29: ; preds = %cond_exit_19 + br label %36 + +30: ; preds = %cond_exit_19 + %31 = alloca i64, i32 2, align 8 + %32 = bitcast i64* %31 to [2 x i64]* + store [2 x i64] %"012.0", [2 x i64]* %32, align 4 + %33 = getelementptr inbounds i64, i64* %31, i64 0 + %34 = load i64, i64* %33, align 4 + %35 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %34, 1 + br label %36 + +36: ; preds = %29, %30 + %"024.0" = phi { i1, i64 } [ %35, %30 ], [ { i1 false, i64 poison }, %29 ] + %37 = extractvalue { i1, i64 } %"024.0", 0 + switch i1 %37, label %38 [ + i1 true, label %39 ] -36: ; preds = %34 - br label %cond_28_case_0 +38: ; preds = %36 + br label %cond_31_case_0 -37: ; preds = %34 - %38 = extractvalue { i1, i64 } %"020.0", 1 - br label %cond_28_case_1 +39: ; preds = %36 + %40 = extractvalue { i1, i64 } %"024.0", 1 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %36 - %39 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 - %40 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 - %41 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %39, i8* %40) +cond_31_case_0: ; preds = %38 + %41 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 + %42 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 + %43 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %41, i8* %42) call void @abort() - br label %cond_exit_28 - -cond_28_case_1: ; preds = %37 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"024.0" = phi i64 [ 0, %cond_28_case_0 ], [ %38, %cond_28_case_1 ] - %42 = icmp ult i64 1, 2 - br i1 %42, label %46, label %43 - -43: ; preds = %cond_exit_28 - %44 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"024.0", 1 - %45 = insertvalue { i1, i64, [2 x i64] } %44, [2 x i64] %"08.0", 2 - br label %55 - -46: ; preds = %cond_exit_28 - %47 = alloca i64, i32 2, align 8 - %48 = bitcast i64* %47 to [2 x i64]* - store [2 x i64] %"08.0", [2 x i64]* %48, align 4 - %49 = getelementptr inbounds i64, i64* %47, i64 1 - %50 = load i64, i64* %49, align 4 - store i64 %"024.0", i64* %49, align 4 - %51 = bitcast i64* %47 to [2 x i64]* - %52 = load [2 x i64], [2 x i64]* %51, align 4 - %53 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %50, 1 - %54 = insertvalue { i1, i64, [2 x i64] } %53, [2 x i64] %52, 2 - br label %55 - -55: ; preds = %43, %46 - %"034.0" = phi { i1, i64, [2 x i64] } [ %54, %46 ], [ %45, %43 ] - %56 = extractvalue { i1, i64, [2 x i64] } %"034.0", 0 - switch i1 %56, label %57 [ - i1 true, label %60 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %39 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 + %"028.0" = phi i64 [ 0, %cond_31_case_0 ], [ %40, %cond_31_case_1 ] + %44 = icmp ult i64 1, 2 + br i1 %44, label %48, label %45 + +45: ; preds = %cond_exit_31 + %46 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"028.0", 1 + %47 = insertvalue { i1, i64, [2 x i64] } %46, [2 x i64] %"012.0", 2 + br label %57 + +48: ; preds = %cond_exit_31 + %49 = alloca i64, i32 2, align 8 + %50 = bitcast i64* %49 to [2 x i64]* + store [2 x i64] %"012.0", [2 x i64]* %50, align 4 + %51 = getelementptr inbounds i64, i64* %49, i64 1 + %52 = load i64, i64* %51, align 4 + store i64 %"028.0", i64* %51, align 4 + %53 = bitcast i64* %49 to [2 x i64]* + %54 = load [2 x i64], [2 x i64]* %53, align 4 + %55 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %52, 1 + %56 = insertvalue { i1, i64, [2 x i64] } %55, [2 x i64] %54, 2 + br label %57 + +57: ; preds = %45, %48 + %"038.0" = phi { i1, i64, [2 x i64] } [ %56, %48 ], [ %47, %45 ] + %58 = extractvalue { i1, i64, [2 x i64] } %"038.0", 0 + switch i1 %58, label %59 [ + i1 true, label %62 ] -57: ; preds = %55 - %58 = extractvalue { i1, i64, [2 x i64] } %"034.0", 1 - %59 = extractvalue { i1, i64, [2 x i64] } %"034.0", 2 - br label %cond_39_case_0 +59: ; preds = %57 + %60 = extractvalue { i1, i64, [2 x i64] } %"038.0", 1 + %61 = extractvalue { i1, i64, [2 x i64] } %"038.0", 2 + br label %cond_42_case_0 -60: ; preds = %55 - %61 = extractvalue { i1, i64, [2 x i64] } %"034.0", 1 - %62 = extractvalue { i1, i64, [2 x i64] } %"034.0", 2 - br label %cond_39_case_1 +62: ; preds = %57 + %63 = extractvalue { i1, i64, [2 x i64] } %"038.0", 1 + %64 = extractvalue { i1, i64, [2 x i64] } %"038.0", 2 + br label %cond_42_case_1 -cond_39_case_0: ; preds = %57 - %63 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 - %64 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 - %65 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %63, i8* %64) +cond_42_case_0: ; preds = %59 + %65 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 + %66 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 + %67 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %65, i8* %66) call void @abort() - br label %cond_exit_39 - -cond_39_case_1: ; preds = %60 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"037.0" = phi i64 [ 0, %cond_39_case_0 ], [ %61, %cond_39_case_1 ] - %"138.0" = phi [2 x i64] [ zeroinitializer, %cond_39_case_0 ], [ %62, %cond_39_case_1 ] - %66 = alloca i64, i32 2, align 8 - %67 = bitcast i64* %66 to [2 x i64]* - store [2 x i64] %"138.0", [2 x i64]* %67, align 4 - %68 = getelementptr i64, i64* %66, i32 1 - %69 = load i64, i64* %66, align 4 - %70 = bitcast i64* %68 to [1 x i64]* - %71 = load [1 x i64], [1 x i64]* %70, align 4 - %72 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %69, 1 - %73 = insertvalue { i1, i64, [1 x i64] } %72, [1 x i64] %71, 2 - %74 = extractvalue { i1, i64, [1 x i64] } %73, 0 - switch i1 %74, label %75 [ - i1 true, label %76 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %62 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 + %"041.0" = phi i64 [ 0, %cond_42_case_0 ], [ %63, %cond_42_case_1 ] + %"142.0" = phi [2 x i64] [ zeroinitializer, %cond_42_case_0 ], [ %64, %cond_42_case_1 ] + %68 = alloca i64, i32 2, align 8 + %69 = bitcast i64* %68 to [2 x i64]* + store [2 x i64] %"142.0", [2 x i64]* %69, align 4 + %70 = getelementptr i64, i64* %68, i32 1 + %71 = load i64, i64* %68, align 4 + %72 = bitcast i64* %70 to [1 x i64]* + %73 = load [1 x i64], [1 x i64]* %72, align 4 + %74 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %71, 1 + %75 = insertvalue { i1, i64, [1 x i64] } %74, [1 x i64] %73, 2 + %76 = extractvalue { i1, i64, [1 x i64] } %75, 0 + switch i1 %76, label %77 [ + i1 true, label %78 ] -75: ; preds = %cond_exit_39 - br label %cond_50_case_0 +77: ; preds = %cond_exit_42 + br label %cond_53_case_0 -76: ; preds = %cond_exit_39 - %77 = extractvalue { i1, i64, [1 x i64] } %73, 1 - %78 = extractvalue { i1, i64, [1 x i64] } %73, 2 - br label %cond_50_case_1 +78: ; preds = %cond_exit_42 + %79 = extractvalue { i1, i64, [1 x i64] } %75, 1 + %80 = extractvalue { i1, i64, [1 x i64] } %75, 2 + br label %cond_53_case_1 -cond_50_case_0: ; preds = %75 - %79 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 - %80 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 - %81 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %79, i8* %80) +cond_53_case_0: ; preds = %77 + %81 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 + %82 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 + %83 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %81, i8* %82) call void @abort() - br label %cond_exit_50 - -cond_50_case_1: ; preds = %76 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"058.0" = phi i64 [ 0, %cond_50_case_0 ], [ %77, %cond_50_case_1 ] - %"159.0" = phi [1 x i64] [ zeroinitializer, %cond_50_case_0 ], [ %78, %cond_50_case_1 ] - %82 = alloca i64, align 8 - %83 = bitcast i64* %82 to [1 x i64]* - store [1 x i64] %"159.0", [1 x i64]* %83, align 4 - %84 = getelementptr i64, i64* %82, i32 0 - %85 = load i64, i64* %84, align 4 - %86 = bitcast i64* %82 to [0 x i64]* - %87 = load [0 x i64], [0 x i64]* %86, align 4 - %88 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %85, 1 - %89 = extractvalue { i1, i64 } %88, 0 - switch i1 %89, label %90 [ - i1 true, label %91 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %78 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"062.0" = phi i64 [ 0, %cond_53_case_0 ], [ %79, %cond_53_case_1 ] + %"163.0" = phi [1 x i64] [ zeroinitializer, %cond_53_case_0 ], [ %80, %cond_53_case_1 ] + %84 = alloca i64, align 8 + %85 = bitcast i64* %84 to [1 x i64]* + store [1 x i64] %"163.0", [1 x i64]* %85, align 4 + %86 = getelementptr i64, i64* %84, i32 0 + %87 = load i64, i64* %86, align 4 + %88 = bitcast i64* %84 to [0 x i64]* + %89 = load [0 x i64], [0 x i64]* %88, align 4 + %90 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %87, 1 + %91 = extractvalue { i1, i64 } %90, 0 + switch i1 %91, label %92 [ + i1 true, label %93 ] -90: ; preds = %cond_exit_50 - br label %cond_61_case_0 +92: ; preds = %cond_exit_53 + br label %cond_64_case_0 -91: ; preds = %cond_exit_50 - %92 = extractvalue { i1, i64 } %88, 1 - br label %cond_61_case_1 +93: ; preds = %cond_exit_53 + %94 = extractvalue { i1, i64 } %90, 1 + br label %cond_64_case_1 -cond_61_case_0: ; preds = %90 - %93 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 - %94 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 - %95 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %93, i8* %94) +cond_64_case_0: ; preds = %92 + %95 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 + %96 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 + %97 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %95, i8* %96) call void @abort() - br label %cond_exit_61 + br label %cond_exit_64 -cond_61_case_1: ; preds = %91 - br label %cond_exit_61 +cond_64_case_1: ; preds = %93 + br label %cond_exit_64 -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"073.0" = phi i64 [ 0, %cond_61_case_0 ], [ %92, %cond_61_case_1 ] +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"077.0" = phi i64 [ 0, %cond_64_case_0 ], [ %94, %cond_64_case_1 ] ret void } diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap index d8aaf790b8..c2cb8ed5a4 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap @@ -20,69 +20,72 @@ define void @_hl.main.1() { alloca_block: %"12_0" = alloca i64, align 8 %"10_0" = alloca i64, align 8 + %"15_0" = alloca [2 x i64], align 8 %"13_0" = alloca [2 x i64], align 8 + %"14_0" = alloca i64, align 8 + %"14_1" = alloca i64, align 8 %"8_0" = alloca i64, align 8 - %"14_0" = alloca { i1, [2 x i64] }, align 8 + %"18_0" = alloca { i1, [2 x i64] }, align 8 %"0" = alloca { i1, [2 x i64] }, align 8 - %"16_0" = alloca [2 x i64], align 8 - %"08" = alloca [2 x i64], align 8 - %"010" = alloca [2 x i64], align 8 - %"21_0" = alloca { i32, i8* }, align 8 - %"18_0" = alloca [2 x i64], align 8 - %"22_0" = alloca [2 x i64], align 8 - %"015" = alloca [2 x i64], align 8 - %"24_0" = alloca [2 x i64], align 8 - %"26_0" = alloca { i1, i64 }, align 8 - %"26_1" = alloca [2 x i64], align 8 - %"020" = alloca { i1, i64 }, align 8 + %"19_0" = alloca [2 x i64], align 8 + %"012" = alloca [2 x i64], align 8 + %"014" = alloca [2 x i64], align 8 + %"24_0" = alloca { i32, i8* }, align 8 + %"21_0" = alloca [2 x i64], align 8 + %"25_0" = alloca [2 x i64], align 8 + %"019" = alloca [2 x i64], align 8 + %"27_0" = alloca [2 x i64], align 8 + %"29_0" = alloca { i1, i64 }, align 8 + %"29_1" = alloca [2 x i64], align 8 + %"024" = alloca { i1, i64 }, align 8 %"1" = alloca [2 x i64], align 8 - %"28_0" = alloca i64, align 8 - %"024" = alloca i64, align 8 - %"33_0" = alloca { i32, i8* }, align 8 - %"34_0" = alloca i64, align 8 + %"31_0" = alloca i64, align 8 %"028" = alloca i64, align 8 - %"36_0" = alloca i64, align 8 - %"38_0" = alloca { i1, i64, [2 x i64] }, align 8 - %"034" = alloca { i1, i64, [2 x i64] }, align 8 + %"36_0" = alloca { i32, i8* }, align 8 + %"37_0" = alloca i64, align 8 + %"032" = alloca i64, align 8 %"39_0" = alloca i64, align 8 - %"39_1" = alloca [2 x i64], align 8 - %"037" = alloca i64, align 8 - %"138" = alloca [2 x i64], align 8 + %"41_0" = alloca { i1, i64, [2 x i64] }, align 8 + %"038" = alloca { i1, i64, [2 x i64] }, align 8 + %"42_0" = alloca i64, align 8 + %"42_1" = alloca [2 x i64], align 8 %"041" = alloca i64, align 8 %"142" = alloca [2 x i64], align 8 - %"44_0" = alloca { i32, i8* }, align 8 - %"41_0" = alloca i64, align 8 - %"41_1" = alloca [2 x i64], align 8 - %"45_0" = alloca i64, align 8 - %"45_1" = alloca [2 x i64], align 8 - %"050" = alloca i64, align 8 - %"151" = alloca [2 x i64], align 8 - %"47_0" = alloca i64, align 8 - %"47_1" = alloca [2 x i64], align 8 - %"49_0" = alloca { i1, i64, [1 x i64] }, align 8 + %"045" = alloca i64, align 8 + %"146" = alloca [2 x i64], align 8 + %"47_0" = alloca { i32, i8* }, align 8 + %"44_0" = alloca i64, align 8 + %"44_1" = alloca [2 x i64], align 8 + %"48_0" = alloca i64, align 8 + %"48_1" = alloca [2 x i64], align 8 + %"054" = alloca i64, align 8 + %"155" = alloca [2 x i64], align 8 %"50_0" = alloca i64, align 8 - %"50_1" = alloca [1 x i64], align 8 - %"058" = alloca i64, align 8 - %"159" = alloca [1 x i64], align 8 - %"55_0" = alloca { i32, i8* }, align 8 - %"56_0" = alloca i64, align 8 - %"56_1" = alloca [1 x i64], align 8 - %"065" = alloca i64, align 8 - %"166" = alloca [1 x i64], align 8 - %"58_0" = alloca i64, align 8 - %"58_1" = alloca [1 x i64], align 8 - %"60_0" = alloca { i1, i64 }, align 8 + %"50_1" = alloca [2 x i64], align 8 + %"52_0" = alloca { i1, i64, [1 x i64] }, align 8 + %"53_0" = alloca i64, align 8 + %"53_1" = alloca [1 x i64], align 8 + %"062" = alloca i64, align 8 + %"163" = alloca [1 x i64], align 8 + %"58_0" = alloca { i32, i8* }, align 8 + %"59_0" = alloca i64, align 8 + %"59_1" = alloca [1 x i64], align 8 + %"069" = alloca i64, align 8 + %"170" = alloca [1 x i64], align 8 %"61_0" = alloca i64, align 8 - %"61_1" = alloca [0 x i64], align 8 - %"073" = alloca i64, align 8 - %"174" = alloca [0 x i64], align 8 - %"66_0" = alloca { i32, i8* }, align 8 - %"67_0" = alloca i64, align 8 - %"67_1" = alloca [0 x i64], align 8 - %"080" = alloca i64, align 8 - %"181" = alloca [0 x i64], align 8 - %"69_0" = alloca i64, align 8 - %"69_1" = alloca [0 x i64], align 8 + %"61_1" = alloca [1 x i64], align 8 + %"63_0" = alloca { i1, i64 }, align 8 + %"64_0" = alloca i64, align 8 + %"64_1" = alloca [0 x i64], align 8 + %"077" = alloca i64, align 8 + %"178" = alloca [0 x i64], align 8 + %"69_0" = alloca { i32, i8* }, align 8 + %"70_0" = alloca i64, align 8 + %"70_1" = alloca [0 x i64], align 8 + %"084" = alloca i64, align 8 + %"185" = alloca [0 x i64], align 8 + %"72_0" = alloca i64, align 8 + %"72_1" = alloca [0 x i64], align 8 br label %entry_block entry_block: ; preds = %alloca_block @@ -92,344 +95,354 @@ entry_block: ; preds = %alloca_block %"12_02" = load i64, i64* %"12_0", align 4 %0 = insertvalue [2 x i64] undef, i64 %"10_01", 0 %1 = insertvalue [2 x i64] %0, i64 %"12_02", 1 - store [2 x i64] %1, [2 x i64]* %"13_0", align 4 + store [2 x i64] %1, [2 x i64]* %"15_0", align 4 + %"10_03" = load i64, i64* %"10_0", align 4 + %"12_04" = load i64, i64* %"12_0", align 4 + %2 = insertvalue [2 x i64] undef, i64 %"10_03", 0 + %3 = insertvalue [2 x i64] %2, i64 %"12_04", 1 + store [2 x i64] %3, [2 x i64]* %"13_0", align 4 + %"13_05" = load [2 x i64], [2 x i64]* %"13_0", align 4 + %extract = extractvalue [2 x i64] %"13_05", 0 + %extract6 = extractvalue [2 x i64] %"13_05", 1 + store i64 %extract, i64* %"14_0", align 4 + store i64 %extract6, i64* %"14_1", align 4 store i64 0, i64* %"8_0", align 4 - %"13_03" = load [2 x i64], [2 x i64]* %"13_0", align 4 - %"8_04" = load i64, i64* %"8_0", align 4 - %"10_05" = load i64, i64* %"10_0", align 4 - %2 = icmp ult i64 %"8_04", 2 - %3 = icmp ult i64 %"10_05", 2 - %4 = and i1 %2, %3 - br i1 %4, label %7, label %5 - -5: ; preds = %entry_block - %6 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %"13_03", 1 - store { i1, [2 x i64] } %6, { i1, [2 x i64] }* %"0", align 4 - br label %17 + %"15_07" = load [2 x i64], [2 x i64]* %"15_0", align 4 + %"8_08" = load i64, i64* %"8_0", align 4 + %"10_09" = load i64, i64* %"10_0", align 4 + %4 = icmp ult i64 %"8_08", 2 + %5 = icmp ult i64 %"10_09", 2 + %6 = and i1 %4, %5 + br i1 %6, label %9, label %7 7: ; preds = %entry_block - %8 = alloca i64, i32 2, align 8 - %9 = bitcast i64* %8 to [2 x i64]* - store [2 x i64] %"13_03", [2 x i64]* %9, align 4 - %10 = getelementptr inbounds i64, i64* %8, i64 %"8_04" - %11 = load i64, i64* %10, align 4 - %12 = getelementptr inbounds i64, i64* %8, i64 %"10_05" + %8 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %"15_07", 1 + store { i1, [2 x i64] } %8, { i1, [2 x i64] }* %"0", align 4 + br label %19 + +9: ; preds = %entry_block + %10 = alloca i64, i32 2, align 8 + %11 = bitcast i64* %10 to [2 x i64]* + store [2 x i64] %"15_07", [2 x i64]* %11, align 4 + %12 = getelementptr inbounds i64, i64* %10, i64 %"8_08" %13 = load i64, i64* %12, align 4 - store i64 %13, i64* %10, align 4 - store i64 %11, i64* %12, align 4 - %14 = bitcast i64* %8 to [2 x i64]* - %15 = load [2 x i64], [2 x i64]* %14, align 4 - %16 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %15, 1 - store { i1, [2 x i64] } %16, { i1, [2 x i64] }* %"0", align 4 - br label %17 - -17: ; preds = %5, %7 - %"06" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"0", align 4 - store { i1, [2 x i64] } %"06", { i1, [2 x i64] }* %"14_0", align 4 - %"14_07" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"14_0", align 4 - %18 = extractvalue { i1, [2 x i64] } %"14_07", 0 - switch i1 %18, label %19 [ - i1 true, label %21 + %14 = getelementptr inbounds i64, i64* %10, i64 %"10_09" + %15 = load i64, i64* %14, align 4 + store i64 %15, i64* %12, align 4 + store i64 %13, i64* %14, align 4 + %16 = bitcast i64* %10 to [2 x i64]* + %17 = load [2 x i64], [2 x i64]* %16, align 4 + %18 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %17, 1 + store { i1, [2 x i64] } %18, { i1, [2 x i64] }* %"0", align 4 + br label %19 + +19: ; preds = %7, %9 + %"010" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"0", align 4 + store { i1, [2 x i64] } %"010", { i1, [2 x i64] }* %"18_0", align 4 + %"18_011" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"18_0", align 4 + %20 = extractvalue { i1, [2 x i64] } %"18_011", 0 + switch i1 %20, label %21 [ + i1 true, label %23 ] -19: ; preds = %17 - %20 = extractvalue { i1, [2 x i64] } %"14_07", 1 - store [2 x i64] %20, [2 x i64]* %"010", align 4 - br label %cond_16_case_0 - -21: ; preds = %17 - %22 = extractvalue { i1, [2 x i64] } %"14_07", 1 - store [2 x i64] %22, [2 x i64]* %"015", align 4 - br label %cond_16_case_1 - -cond_16_case_0: ; preds = %19 - %"011" = load [2 x i64], [2 x i64]* %"010", align 4 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"21_0", align 8 - store [2 x i64] %"011", [2 x i64]* %"18_0", align 4 - %"21_012" = load { i32, i8* }, { i32, i8* }* %"21_0", align 8 - %"18_013" = load [2 x i64], [2 x i64]* %"18_0", align 4 - %23 = extractvalue { i32, i8* } %"21_012", 0 - %24 = extractvalue { i32, i8* } %"21_012", 1 - %25 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %23, i8* %24) +21: ; preds = %19 + %22 = extractvalue { i1, [2 x i64] } %"18_011", 1 + store [2 x i64] %22, [2 x i64]* %"014", align 4 + br label %cond_19_case_0 + +23: ; preds = %19 + %24 = extractvalue { i1, [2 x i64] } %"18_011", 1 + store [2 x i64] %24, [2 x i64]* %"019", align 4 + br label %cond_19_case_1 + +cond_19_case_0: ; preds = %21 + %"015" = load [2 x i64], [2 x i64]* %"014", align 4 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"24_0", align 8 + store [2 x i64] %"015", [2 x i64]* %"21_0", align 4 + %"24_016" = load { i32, i8* }, { i32, i8* }* %"24_0", align 8 + %"21_017" = load [2 x i64], [2 x i64]* %"21_0", align 4 + %25 = extractvalue { i32, i8* } %"24_016", 0 + %26 = extractvalue { i32, i8* } %"24_016", 1 + %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) call void @abort() - store [2 x i64] zeroinitializer, [2 x i64]* %"22_0", align 4 - %"22_014" = load [2 x i64], [2 x i64]* %"22_0", align 4 - store [2 x i64] %"22_014", [2 x i64]* %"08", align 4 - br label %cond_exit_16 - -cond_16_case_1: ; preds = %21 - %"016" = load [2 x i64], [2 x i64]* %"015", align 4 - store [2 x i64] %"016", [2 x i64]* %"24_0", align 4 - %"24_017" = load [2 x i64], [2 x i64]* %"24_0", align 4 - store [2 x i64] %"24_017", [2 x i64]* %"08", align 4 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"09" = load [2 x i64], [2 x i64]* %"08", align 4 - store [2 x i64] %"09", [2 x i64]* %"16_0", align 4 - %"16_018" = load [2 x i64], [2 x i64]* %"16_0", align 4 - %"8_019" = load i64, i64* %"8_0", align 4 - %26 = icmp ult i64 %"8_019", 2 - br i1 %26, label %28, label %27 - -27: ; preds = %cond_exit_16 - store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"020", align 4 - store [2 x i64] %"16_018", [2 x i64]* %"1", align 4 - br label %34 - -28: ; preds = %cond_exit_16 - %29 = alloca i64, i32 2, align 8 - %30 = bitcast i64* %29 to [2 x i64]* - store [2 x i64] %"16_018", [2 x i64]* %30, align 4 - %31 = getelementptr inbounds i64, i64* %29, i64 %"8_019" - %32 = load i64, i64* %31, align 4 - %33 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %32, 1 - store { i1, i64 } %33, { i1, i64 }* %"020", align 4 - store [2 x i64] %"16_018", [2 x i64]* %"1", align 4 - br label %34 - -34: ; preds = %27, %28 - %"021" = load { i1, i64 }, { i1, i64 }* %"020", align 4 - %"122" = load [2 x i64], [2 x i64]* %"1", align 4 - store { i1, i64 } %"021", { i1, i64 }* %"26_0", align 4 - store [2 x i64] %"122", [2 x i64]* %"26_1", align 4 - %"26_023" = load { i1, i64 }, { i1, i64 }* %"26_0", align 4 - %35 = extractvalue { i1, i64 } %"26_023", 0 - switch i1 %35, label %36 [ - i1 true, label %37 + store [2 x i64] zeroinitializer, [2 x i64]* %"25_0", align 4 + %"25_018" = load [2 x i64], [2 x i64]* %"25_0", align 4 + store [2 x i64] %"25_018", [2 x i64]* %"012", align 4 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %23 + %"020" = load [2 x i64], [2 x i64]* %"019", align 4 + store [2 x i64] %"020", [2 x i64]* %"27_0", align 4 + %"27_021" = load [2 x i64], [2 x i64]* %"27_0", align 4 + store [2 x i64] %"27_021", [2 x i64]* %"012", align 4 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"013" = load [2 x i64], [2 x i64]* %"012", align 4 + store [2 x i64] %"013", [2 x i64]* %"19_0", align 4 + %"19_022" = load [2 x i64], [2 x i64]* %"19_0", align 4 + %"8_023" = load i64, i64* %"8_0", align 4 + %28 = icmp ult i64 %"8_023", 2 + br i1 %28, label %30, label %29 + +29: ; preds = %cond_exit_19 + store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"024", align 4 + store [2 x i64] %"19_022", [2 x i64]* %"1", align 4 + br label %36 + +30: ; preds = %cond_exit_19 + %31 = alloca i64, i32 2, align 8 + %32 = bitcast i64* %31 to [2 x i64]* + store [2 x i64] %"19_022", [2 x i64]* %32, align 4 + %33 = getelementptr inbounds i64, i64* %31, i64 %"8_023" + %34 = load i64, i64* %33, align 4 + %35 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %34, 1 + store { i1, i64 } %35, { i1, i64 }* %"024", align 4 + store [2 x i64] %"19_022", [2 x i64]* %"1", align 4 + br label %36 + +36: ; preds = %29, %30 + %"025" = load { i1, i64 }, { i1, i64 }* %"024", align 4 + %"126" = load [2 x i64], [2 x i64]* %"1", align 4 + store { i1, i64 } %"025", { i1, i64 }* %"29_0", align 4 + store [2 x i64] %"126", [2 x i64]* %"29_1", align 4 + %"29_027" = load { i1, i64 }, { i1, i64 }* %"29_0", align 4 + %37 = extractvalue { i1, i64 } %"29_027", 0 + switch i1 %37, label %38 [ + i1 true, label %39 ] -36: ; preds = %34 - br label %cond_28_case_0 +38: ; preds = %36 + br label %cond_31_case_0 -37: ; preds = %34 - %38 = extractvalue { i1, i64 } %"26_023", 1 - store i64 %38, i64* %"028", align 4 - br label %cond_28_case_1 +39: ; preds = %36 + %40 = extractvalue { i1, i64 } %"29_027", 1 + store i64 %40, i64* %"032", align 4 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %36 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"33_0", align 8 - %"33_026" = load { i32, i8* }, { i32, i8* }* %"33_0", align 8 - %39 = extractvalue { i32, i8* } %"33_026", 0 - %40 = extractvalue { i32, i8* } %"33_026", 1 - %41 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %39, i8* %40) +cond_31_case_0: ; preds = %38 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"36_0", align 8 + %"36_030" = load { i32, i8* }, { i32, i8* }* %"36_0", align 8 + %41 = extractvalue { i32, i8* } %"36_030", 0 + %42 = extractvalue { i32, i8* } %"36_030", 1 + %43 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %41, i8* %42) call void @abort() - store i64 0, i64* %"34_0", align 4 - %"34_027" = load i64, i64* %"34_0", align 4 - store i64 %"34_027", i64* %"024", align 4 - br label %cond_exit_28 - -cond_28_case_1: ; preds = %37 + store i64 0, i64* %"37_0", align 4 + %"37_031" = load i64, i64* %"37_0", align 4 + store i64 %"37_031", i64* %"028", align 4 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %39 + %"033" = load i64, i64* %"032", align 4 + store i64 %"033", i64* %"39_0", align 4 + %"39_034" = load i64, i64* %"39_0", align 4 + store i64 %"39_034", i64* %"028", align 4 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 %"029" = load i64, i64* %"028", align 4 - store i64 %"029", i64* %"36_0", align 4 - %"36_030" = load i64, i64* %"36_0", align 4 - store i64 %"36_030", i64* %"024", align 4 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"025" = load i64, i64* %"024", align 4 - store i64 %"025", i64* %"28_0", align 4 - %"26_131" = load [2 x i64], [2 x i64]* %"26_1", align 4 - %"10_032" = load i64, i64* %"10_0", align 4 - %"28_033" = load i64, i64* %"28_0", align 4 - %42 = icmp ult i64 %"10_032", 2 - br i1 %42, label %46, label %43 - -43: ; preds = %cond_exit_28 - %44 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"28_033", 1 - %45 = insertvalue { i1, i64, [2 x i64] } %44, [2 x i64] %"26_131", 2 - store { i1, i64, [2 x i64] } %45, { i1, i64, [2 x i64] }* %"034", align 4 - br label %55 - -46: ; preds = %cond_exit_28 - %47 = alloca i64, i32 2, align 8 - %48 = bitcast i64* %47 to [2 x i64]* - store [2 x i64] %"26_131", [2 x i64]* %48, align 4 - %49 = getelementptr inbounds i64, i64* %47, i64 %"10_032" - %50 = load i64, i64* %49, align 4 - store i64 %"28_033", i64* %49, align 4 - %51 = bitcast i64* %47 to [2 x i64]* - %52 = load [2 x i64], [2 x i64]* %51, align 4 - %53 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %50, 1 - %54 = insertvalue { i1, i64, [2 x i64] } %53, [2 x i64] %52, 2 - store { i1, i64, [2 x i64] } %54, { i1, i64, [2 x i64] }* %"034", align 4 - br label %55 - -55: ; preds = %43, %46 - %"035" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"034", align 4 - store { i1, i64, [2 x i64] } %"035", { i1, i64, [2 x i64] }* %"38_0", align 4 - %"38_036" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"38_0", align 4 - %56 = extractvalue { i1, i64, [2 x i64] } %"38_036", 0 - switch i1 %56, label %57 [ - i1 true, label %60 + store i64 %"029", i64* %"31_0", align 4 + %"29_135" = load [2 x i64], [2 x i64]* %"29_1", align 4 + %"10_036" = load i64, i64* %"10_0", align 4 + %"31_037" = load i64, i64* %"31_0", align 4 + %44 = icmp ult i64 %"10_036", 2 + br i1 %44, label %48, label %45 + +45: ; preds = %cond_exit_31 + %46 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"31_037", 1 + %47 = insertvalue { i1, i64, [2 x i64] } %46, [2 x i64] %"29_135", 2 + store { i1, i64, [2 x i64] } %47, { i1, i64, [2 x i64] }* %"038", align 4 + br label %57 + +48: ; preds = %cond_exit_31 + %49 = alloca i64, i32 2, align 8 + %50 = bitcast i64* %49 to [2 x i64]* + store [2 x i64] %"29_135", [2 x i64]* %50, align 4 + %51 = getelementptr inbounds i64, i64* %49, i64 %"10_036" + %52 = load i64, i64* %51, align 4 + store i64 %"31_037", i64* %51, align 4 + %53 = bitcast i64* %49 to [2 x i64]* + %54 = load [2 x i64], [2 x i64]* %53, align 4 + %55 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %52, 1 + %56 = insertvalue { i1, i64, [2 x i64] } %55, [2 x i64] %54, 2 + store { i1, i64, [2 x i64] } %56, { i1, i64, [2 x i64] }* %"038", align 4 + br label %57 + +57: ; preds = %45, %48 + %"039" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"038", align 4 + store { i1, i64, [2 x i64] } %"039", { i1, i64, [2 x i64] }* %"41_0", align 4 + %"41_040" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"41_0", align 4 + %58 = extractvalue { i1, i64, [2 x i64] } %"41_040", 0 + switch i1 %58, label %59 [ + i1 true, label %62 ] -57: ; preds = %55 - %58 = extractvalue { i1, i64, [2 x i64] } %"38_036", 1 - %59 = extractvalue { i1, i64, [2 x i64] } %"38_036", 2 - store i64 %58, i64* %"041", align 4 - store [2 x i64] %59, [2 x i64]* %"142", align 4 - br label %cond_39_case_0 - -60: ; preds = %55 - %61 = extractvalue { i1, i64, [2 x i64] } %"38_036", 1 - %62 = extractvalue { i1, i64, [2 x i64] } %"38_036", 2 - store i64 %61, i64* %"050", align 4 - store [2 x i64] %62, [2 x i64]* %"151", align 4 - br label %cond_39_case_1 - -cond_39_case_0: ; preds = %57 +59: ; preds = %57 + %60 = extractvalue { i1, i64, [2 x i64] } %"41_040", 1 + %61 = extractvalue { i1, i64, [2 x i64] } %"41_040", 2 + store i64 %60, i64* %"045", align 4 + store [2 x i64] %61, [2 x i64]* %"146", align 4 + br label %cond_42_case_0 + +62: ; preds = %57 + %63 = extractvalue { i1, i64, [2 x i64] } %"41_040", 1 + %64 = extractvalue { i1, i64, [2 x i64] } %"41_040", 2 + store i64 %63, i64* %"054", align 4 + store [2 x i64] %64, [2 x i64]* %"155", align 4 + br label %cond_42_case_1 + +cond_42_case_0: ; preds = %59 + %"047" = load i64, i64* %"045", align 4 + %"148" = load [2 x i64], [2 x i64]* %"146", align 4 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"47_0", align 8 + store i64 %"047", i64* %"44_0", align 4 + store [2 x i64] %"148", [2 x i64]* %"44_1", align 4 + %"47_049" = load { i32, i8* }, { i32, i8* }* %"47_0", align 8 + %"44_050" = load i64, i64* %"44_0", align 4 + %"44_151" = load [2 x i64], [2 x i64]* %"44_1", align 4 + %65 = extractvalue { i32, i8* } %"47_049", 0 + %66 = extractvalue { i32, i8* } %"47_049", 1 + %67 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %65, i8* %66) + call void @abort() + store i64 0, i64* %"48_0", align 4 + store [2 x i64] zeroinitializer, [2 x i64]* %"48_1", align 4 + %"48_052" = load i64, i64* %"48_0", align 4 + %"48_153" = load [2 x i64], [2 x i64]* %"48_1", align 4 + store i64 %"48_052", i64* %"041", align 4 + store [2 x i64] %"48_153", [2 x i64]* %"142", align 4 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %62 + %"056" = load i64, i64* %"054", align 4 + %"157" = load [2 x i64], [2 x i64]* %"155", align 4 + store i64 %"056", i64* %"50_0", align 4 + store [2 x i64] %"157", [2 x i64]* %"50_1", align 4 + %"50_058" = load i64, i64* %"50_0", align 4 + %"50_159" = load [2 x i64], [2 x i64]* %"50_1", align 4 + store i64 %"50_058", i64* %"041", align 4 + store [2 x i64] %"50_159", [2 x i64]* %"142", align 4 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 %"043" = load i64, i64* %"041", align 4 %"144" = load [2 x i64], [2 x i64]* %"142", align 4 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"44_0", align 8 - store i64 %"043", i64* %"41_0", align 4 - store [2 x i64] %"144", [2 x i64]* %"41_1", align 4 - %"44_045" = load { i32, i8* }, { i32, i8* }* %"44_0", align 8 - %"41_046" = load i64, i64* %"41_0", align 4 - %"41_147" = load [2 x i64], [2 x i64]* %"41_1", align 4 - %63 = extractvalue { i32, i8* } %"44_045", 0 - %64 = extractvalue { i32, i8* } %"44_045", 1 - %65 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %63, i8* %64) - call void @abort() - store i64 0, i64* %"45_0", align 4 - store [2 x i64] zeroinitializer, [2 x i64]* %"45_1", align 4 - %"45_048" = load i64, i64* %"45_0", align 4 - %"45_149" = load [2 x i64], [2 x i64]* %"45_1", align 4 - store i64 %"45_048", i64* %"037", align 4 - store [2 x i64] %"45_149", [2 x i64]* %"138", align 4 - br label %cond_exit_39 - -cond_39_case_1: ; preds = %60 - %"052" = load i64, i64* %"050", align 4 - %"153" = load [2 x i64], [2 x i64]* %"151", align 4 - store i64 %"052", i64* %"47_0", align 4 - store [2 x i64] %"153", [2 x i64]* %"47_1", align 4 - %"47_054" = load i64, i64* %"47_0", align 4 - %"47_155" = load [2 x i64], [2 x i64]* %"47_1", align 4 - store i64 %"47_054", i64* %"037", align 4 - store [2 x i64] %"47_155", [2 x i64]* %"138", align 4 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"039" = load i64, i64* %"037", align 4 - %"140" = load [2 x i64], [2 x i64]* %"138", align 4 - store i64 %"039", i64* %"39_0", align 4 - store [2 x i64] %"140", [2 x i64]* %"39_1", align 4 - %"39_156" = load [2 x i64], [2 x i64]* %"39_1", align 4 - %66 = alloca i64, i32 2, align 8 - %67 = bitcast i64* %66 to [2 x i64]* - store [2 x i64] %"39_156", [2 x i64]* %67, align 4 - %68 = getelementptr i64, i64* %66, i32 1 - %69 = load i64, i64* %66, align 4 - %70 = bitcast i64* %68 to [1 x i64]* - %71 = load [1 x i64], [1 x i64]* %70, align 4 - %72 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %69, 1 - %73 = insertvalue { i1, i64, [1 x i64] } %72, [1 x i64] %71, 2 - store { i1, i64, [1 x i64] } %73, { i1, i64, [1 x i64] }* %"49_0", align 4 - %"49_057" = load { i1, i64, [1 x i64] }, { i1, i64, [1 x i64] }* %"49_0", align 4 - %74 = extractvalue { i1, i64, [1 x i64] } %"49_057", 0 - switch i1 %74, label %75 [ - i1 true, label %76 + store i64 %"043", i64* %"42_0", align 4 + store [2 x i64] %"144", [2 x i64]* %"42_1", align 4 + %"42_160" = load [2 x i64], [2 x i64]* %"42_1", align 4 + %68 = alloca i64, i32 2, align 8 + %69 = bitcast i64* %68 to [2 x i64]* + store [2 x i64] %"42_160", [2 x i64]* %69, align 4 + %70 = getelementptr i64, i64* %68, i32 1 + %71 = load i64, i64* %68, align 4 + %72 = bitcast i64* %70 to [1 x i64]* + %73 = load [1 x i64], [1 x i64]* %72, align 4 + %74 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %71, 1 + %75 = insertvalue { i1, i64, [1 x i64] } %74, [1 x i64] %73, 2 + store { i1, i64, [1 x i64] } %75, { i1, i64, [1 x i64] }* %"52_0", align 4 + %"52_061" = load { i1, i64, [1 x i64] }, { i1, i64, [1 x i64] }* %"52_0", align 4 + %76 = extractvalue { i1, i64, [1 x i64] } %"52_061", 0 + switch i1 %76, label %77 [ + i1 true, label %78 ] -75: ; preds = %cond_exit_39 - br label %cond_50_case_0 - -76: ; preds = %cond_exit_39 - %77 = extractvalue { i1, i64, [1 x i64] } %"49_057", 1 - %78 = extractvalue { i1, i64, [1 x i64] } %"49_057", 2 - store i64 %77, i64* %"065", align 4 - store [1 x i64] %78, [1 x i64]* %"166", align 4 - br label %cond_50_case_1 - -cond_50_case_0: ; preds = %75 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"55_0", align 8 - %"55_062" = load { i32, i8* }, { i32, i8* }* %"55_0", align 8 - %79 = extractvalue { i32, i8* } %"55_062", 0 - %80 = extractvalue { i32, i8* } %"55_062", 1 - %81 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %79, i8* %80) +77: ; preds = %cond_exit_42 + br label %cond_53_case_0 + +78: ; preds = %cond_exit_42 + %79 = extractvalue { i1, i64, [1 x i64] } %"52_061", 1 + %80 = extractvalue { i1, i64, [1 x i64] } %"52_061", 2 + store i64 %79, i64* %"069", align 4 + store [1 x i64] %80, [1 x i64]* %"170", align 4 + br label %cond_53_case_1 + +cond_53_case_0: ; preds = %77 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"58_0", align 8 + %"58_066" = load { i32, i8* }, { i32, i8* }* %"58_0", align 8 + %81 = extractvalue { i32, i8* } %"58_066", 0 + %82 = extractvalue { i32, i8* } %"58_066", 1 + %83 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %81, i8* %82) call void @abort() - store i64 0, i64* %"56_0", align 4 - store [1 x i64] zeroinitializer, [1 x i64]* %"56_1", align 4 - %"56_063" = load i64, i64* %"56_0", align 4 - %"56_164" = load [1 x i64], [1 x i64]* %"56_1", align 4 - store i64 %"56_063", i64* %"058", align 4 - store [1 x i64] %"56_164", [1 x i64]* %"159", align 4 - br label %cond_exit_50 - -cond_50_case_1: ; preds = %76 - %"067" = load i64, i64* %"065", align 4 - %"168" = load [1 x i64], [1 x i64]* %"166", align 4 - store i64 %"067", i64* %"58_0", align 4 - store [1 x i64] %"168", [1 x i64]* %"58_1", align 4 - %"58_069" = load i64, i64* %"58_0", align 4 - %"58_170" = load [1 x i64], [1 x i64]* %"58_1", align 4 - store i64 %"58_069", i64* %"058", align 4 - store [1 x i64] %"58_170", [1 x i64]* %"159", align 4 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"060" = load i64, i64* %"058", align 4 - %"161" = load [1 x i64], [1 x i64]* %"159", align 4 - store i64 %"060", i64* %"50_0", align 4 - store [1 x i64] %"161", [1 x i64]* %"50_1", align 4 - %"50_171" = load [1 x i64], [1 x i64]* %"50_1", align 4 - %82 = alloca i64, align 8 - %83 = bitcast i64* %82 to [1 x i64]* - store [1 x i64] %"50_171", [1 x i64]* %83, align 4 - %84 = getelementptr i64, i64* %82, i32 0 - %85 = load i64, i64* %84, align 4 - %86 = bitcast i64* %82 to [0 x i64]* - %87 = load [0 x i64], [0 x i64]* %86, align 4 - %88 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %85, 1 - store { i1, i64 } %88, { i1, i64 }* %"60_0", align 4 - %"60_072" = load { i1, i64 }, { i1, i64 }* %"60_0", align 4 - %89 = extractvalue { i1, i64 } %"60_072", 0 - switch i1 %89, label %90 [ - i1 true, label %91 + store i64 0, i64* %"59_0", align 4 + store [1 x i64] zeroinitializer, [1 x i64]* %"59_1", align 4 + %"59_067" = load i64, i64* %"59_0", align 4 + %"59_168" = load [1 x i64], [1 x i64]* %"59_1", align 4 + store i64 %"59_067", i64* %"062", align 4 + store [1 x i64] %"59_168", [1 x i64]* %"163", align 4 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %78 + %"071" = load i64, i64* %"069", align 4 + %"172" = load [1 x i64], [1 x i64]* %"170", align 4 + store i64 %"071", i64* %"61_0", align 4 + store [1 x i64] %"172", [1 x i64]* %"61_1", align 4 + %"61_073" = load i64, i64* %"61_0", align 4 + %"61_174" = load [1 x i64], [1 x i64]* %"61_1", align 4 + store i64 %"61_073", i64* %"062", align 4 + store [1 x i64] %"61_174", [1 x i64]* %"163", align 4 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"064" = load i64, i64* %"062", align 4 + %"165" = load [1 x i64], [1 x i64]* %"163", align 4 + store i64 %"064", i64* %"53_0", align 4 + store [1 x i64] %"165", [1 x i64]* %"53_1", align 4 + %"53_175" = load [1 x i64], [1 x i64]* %"53_1", align 4 + %84 = alloca i64, align 8 + %85 = bitcast i64* %84 to [1 x i64]* + store [1 x i64] %"53_175", [1 x i64]* %85, align 4 + %86 = getelementptr i64, i64* %84, i32 0 + %87 = load i64, i64* %86, align 4 + %88 = bitcast i64* %84 to [0 x i64]* + %89 = load [0 x i64], [0 x i64]* %88, align 4 + %90 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %87, 1 + store { i1, i64 } %90, { i1, i64 }* %"63_0", align 4 + %"63_076" = load { i1, i64 }, { i1, i64 }* %"63_0", align 4 + %91 = extractvalue { i1, i64 } %"63_076", 0 + switch i1 %91, label %92 [ + i1 true, label %93 ] -90: ; preds = %cond_exit_50 - br label %cond_61_case_0 - -91: ; preds = %cond_exit_50 - %92 = extractvalue { i1, i64 } %"60_072", 1 - store i64 %92, i64* %"080", align 4 - store [0 x i64] undef, [0 x i64]* %"181", align 4 - br label %cond_61_case_1 - -cond_61_case_0: ; preds = %90 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"66_0", align 8 - %"66_077" = load { i32, i8* }, { i32, i8* }* %"66_0", align 8 - %93 = extractvalue { i32, i8* } %"66_077", 0 - %94 = extractvalue { i32, i8* } %"66_077", 1 - %95 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %93, i8* %94) +92: ; preds = %cond_exit_53 + br label %cond_64_case_0 + +93: ; preds = %cond_exit_53 + %94 = extractvalue { i1, i64 } %"63_076", 1 + store i64 %94, i64* %"084", align 4 + store [0 x i64] undef, [0 x i64]* %"185", align 4 + br label %cond_64_case_1 + +cond_64_case_0: ; preds = %92 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"69_0", align 8 + %"69_081" = load { i32, i8* }, { i32, i8* }* %"69_0", align 8 + %95 = extractvalue { i32, i8* } %"69_081", 0 + %96 = extractvalue { i32, i8* } %"69_081", 1 + %97 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %95, i8* %96) call void @abort() - store i64 0, i64* %"67_0", align 4 - store [0 x i64] zeroinitializer, [0 x i64]* %"67_1", align 4 - %"67_078" = load i64, i64* %"67_0", align 4 - %"67_179" = load [0 x i64], [0 x i64]* %"67_1", align 4 - store i64 %"67_078", i64* %"073", align 4 - store [0 x i64] %"67_179", [0 x i64]* %"174", align 4 - br label %cond_exit_61 - -cond_61_case_1: ; preds = %91 - %"082" = load i64, i64* %"080", align 4 - %"183" = load [0 x i64], [0 x i64]* %"181", align 4 - store i64 %"082", i64* %"69_0", align 4 - store [0 x i64] %"183", [0 x i64]* %"69_1", align 4 - %"69_084" = load i64, i64* %"69_0", align 4 - %"69_185" = load [0 x i64], [0 x i64]* %"69_1", align 4 - store i64 %"69_084", i64* %"073", align 4 - store [0 x i64] %"69_185", [0 x i64]* %"174", align 4 - br label %cond_exit_61 - -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"075" = load i64, i64* %"073", align 4 - %"176" = load [0 x i64], [0 x i64]* %"174", align 4 - store i64 %"075", i64* %"61_0", align 4 - store [0 x i64] %"176", [0 x i64]* %"61_1", align 4 - %"61_186" = load [0 x i64], [0 x i64]* %"61_1", align 4 + store i64 0, i64* %"70_0", align 4 + store [0 x i64] zeroinitializer, [0 x i64]* %"70_1", align 4 + %"70_082" = load i64, i64* %"70_0", align 4 + %"70_183" = load [0 x i64], [0 x i64]* %"70_1", align 4 + store i64 %"70_082", i64* %"077", align 4 + store [0 x i64] %"70_183", [0 x i64]* %"178", align 4 + br label %cond_exit_64 + +cond_64_case_1: ; preds = %93 + %"086" = load i64, i64* %"084", align 4 + %"187" = load [0 x i64], [0 x i64]* %"185", align 4 + store i64 %"086", i64* %"72_0", align 4 + store [0 x i64] %"187", [0 x i64]* %"72_1", align 4 + %"72_088" = load i64, i64* %"72_0", align 4 + %"72_189" = load [0 x i64], [0 x i64]* %"72_1", align 4 + store i64 %"72_088", i64* %"077", align 4 + store [0 x i64] %"72_189", [0 x i64]* %"178", align 4 + br label %cond_exit_64 + +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"079" = load i64, i64* %"077", align 4 + %"180" = load [0 x i64], [0 x i64]* %"178", align 4 + store i64 %"079", i64* %"64_0", align 4 + store [0 x i64] %"180", [0 x i64]* %"64_1", align 4 + %"64_190" = load [0 x i64], [0 x i64]* %"64_1", align 4 ret void } diff --git a/hugr-llvm/src/extension/collections/stack_array.rs b/hugr-llvm/src/extension/collections/stack_array.rs index 50410d9630..9361a298cd 100644 --- a/hugr-llvm/src/extension/collections/stack_array.rs +++ b/hugr-llvm/src/extension/collections/stack_array.rs @@ -324,6 +324,18 @@ fn emit_array_op<'c, H: HugrView>( } outputs.finish(builder, [array_v.as_basic_value_enum()]) } + ArrayOpDef::unpack => { + let [array_v] = inputs + .try_into() + .map_err(|_| anyhow!("ArrayOpDef::unpack expects one argument"))?; + let array_v = array_v.into_array_value(); + + let result = (0..size) + .map(|i| builder.build_extract_value(array_v, i as u32, "extract")) + .collect::, _>>()?; + + outputs.finish(builder, result) + } ArrayOpDef::get => { let [array_v, index_v] = inputs .try_into() @@ -1222,6 +1234,51 @@ mod test { assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); } + #[rstest] + #[case(&[], 0)] + #[case(&[1, 2], 3)] + #[case(&[6, 6, 6], 18)] + fn exec_unpack( + mut exec_ctx: TestContext, + #[case] array_contents: &[u64], + #[case] expected: u64, + ) { + // We build a HUGR that: + // - Loads an array with the given contents + // - Unpacks all the elements + // - Returns the sum of the elements + + let int_ty = int_type(6); + let hugr = SimpleHugrConfig::new() + .with_outs(int_ty.clone()) + .with_extensions(exec_registry()) + .finish(|mut builder| { + let array = array::ArrayValue::new( + int_ty.clone(), + array_contents + .iter() + .map(|&i| ConstInt::new_u(6, i).unwrap().into()) + .collect_vec(), + ); + let array = builder.add_load_value(array); + let unpacked = builder + .add_array_unpack(int_ty.clone(), array_contents.len() as u64, array) + .unwrap(); + let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap()); + for elem in unpacked { + r = builder.add_iadd(6, r, elem).unwrap(); + } + + builder.finish_with_outputs([r]).unwrap() + }); + exec_ctx.add_extensions(|cge| { + cge.add_default_prelude_extensions() + .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen)) + .add_default_int_extensions() + }); + assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); + } + #[rstest] #[case(5, 42, 0)] #[case(5, 42, 1)] diff --git a/hugr-llvm/src/utils/type_map.rs b/hugr-llvm/src/utils/type_map.rs index 020d567a67..c4e64d90e8 100644 --- a/hugr-llvm/src/utils/type_map.rs +++ b/hugr-llvm/src/utils/type_map.rs @@ -1,5 +1,5 @@ //! Provides a generic mapping from [`HugrType`] into some domain. -use std::collections::HashMap; +use std::collections::BTreeMap; use hugr_core::{ extension::ExtensionId, @@ -94,7 +94,7 @@ pub type CustomTypeKey = (ExtensionId, TypeName); #[derive(Default)] pub struct TypeMap<'a, TM: TypeMapping> { type_map: TM, - custom_hooks: HashMap + 'a>>, + custom_hooks: BTreeMap + 'a>>, } impl<'a, TM: TypeMapping + 'a> TypeMap<'a, TM> { diff --git a/hugr-model/CHANGELOG.md b/hugr-model/CHANGELOG.md index b17ebd1774..901f077d14 100644 --- a/hugr-model/CHANGELOG.md +++ b/hugr-model/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-model-v0.20.1...hugr-model-v0.20.2) - 2025-06-25 + +### New Features + +- better errors using metadata from generator ([#2368](https://github.com/CQCL/hugr/pull/2368)) + ## [0.20.0](https://github.com/CQCL/hugr/compare/hugr-model-v0.19.0...hugr-model-v0.20.0) - 2025-05-14 ### New Features diff --git a/hugr-model/Cargo.toml b/hugr-model/Cargo.toml index 9f9b046b5e..24c1f9e42e 100644 --- a/hugr-model/Cargo.toml +++ b/hugr-model/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-model" -version = "0.20.1" +version = "0.20.2" readme = "README.md" documentation = "https://docs.rs/hugr-model/" description = "Data model for Quantinuum's HUGR intermediate representation" diff --git a/hugr-model/src/v0/binary/read.rs b/hugr-model/src/v0/binary/read.rs index 5d16089947..001a805cc9 100644 --- a/hugr-model/src/v0/binary/read.rs +++ b/hugr-model/src/v0/binary/read.rs @@ -5,12 +5,12 @@ use bumpalo::Bump; use bumpalo::collections::Vec as BumpVec; use std::io::BufRead; -/// An error encounted while deserialising a model. +/// An error encountered while deserialising a model. #[derive(Debug, derive_more::From, derive_more::Display, derive_more::Error)] #[non_exhaustive] pub enum ReadError { #[from(forward)] - /// An error encounted while decoding a model from a `capnproto` buffer. + /// An error encountered while decoding a model from a `capnproto` buffer. DecodingError(capnp::Error), } diff --git a/hugr-passes/CHANGELOG.md b/hugr-passes/CHANGELOG.md index 44af68ad29..d5f2921845 100644 --- a/hugr-passes/CHANGELOG.md +++ b/hugr-passes/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-passes-v0.20.1...hugr-passes-v0.20.2) - 2025-06-25 + +### Bug Fixes + +- update CallGraph and remove_dead_funcs for module-only FuncDefns ([#2336](https://github.com/CQCL/hugr/pull/2336)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-passes-v0.20.0...hugr-passes-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr-passes/Cargo.toml b/hugr-passes/Cargo.toml index d751d82d57..8c1daafbcd 100644 --- a/hugr-passes/Cargo.toml +++ b/hugr-passes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-passes" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } license = { workspace = true } @@ -19,7 +19,7 @@ workspace = true bench = false [dependencies] -hugr-core = { path = "../hugr-core", version = "0.20.1" } +hugr-core = { path = "../hugr-core", version = "0.20.2" } portgraph = { workspace = true } ascent = { version = "0.8.0" } derive_more = { workspace = true, features = ["display", "error", "from"] } diff --git a/hugr-passes/src/call_graph.rs b/hugr-passes/src/call_graph.rs index 6bee5eaa0c..e33881b1f7 100644 --- a/hugr-passes/src/call_graph.rs +++ b/hugr-passes/src/call_graph.rs @@ -26,13 +26,17 @@ pub enum CallGraphNode { } /// Details the [`Call`]s and [`LoadFunction`]s in a Hugr. -/// Each node in the `CallGraph` corresponds to a [`FuncDefn`] in the Hugr; each edge corresponds -/// to a [`Call`]/[`LoadFunction`] of the edge's target, contained in the edge's source. /// -/// For Hugrs whose root is neither a [Module](OpType::Module) nor a [`FuncDefn`], the call graph -/// will have an additional [`CallGraphNode::NonFuncRoot`] corresponding to the Hugr's root, with no incoming edges. +/// Each node in the `CallGraph` corresponds to a [`FuncDefn`] or [`FuncDecl`] in the Hugr; +/// each edge corresponds to a [`Call`]/[`LoadFunction`] of the edge's target, contained in +/// the edge's source. +/// +/// For Hugrs whose entrypoint is neither a [Module](OpType::Module) nor a [`FuncDefn`], the +/// call graph will have an additional [`CallGraphNode::NonFuncRoot`] corresponding to the Hugr's +/// entrypoint, with no incoming edges. /// /// [`Call`]: OpType::Call +/// [`FuncDecl`]: OpType::FuncDecl /// [`FuncDefn`]: OpType::FuncDefn /// [`LoadFunction`]: OpType::LoadFunction pub struct CallGraph { @@ -41,14 +45,13 @@ pub struct CallGraph { } impl CallGraph { - /// Makes a new `CallGraph` for a specified (subview) of a Hugr. - /// Calls to functions outside the view will be dropped. + /// Makes a new `CallGraph` for a Hugr. pub fn new(hugr: &impl HugrView) -> Self { let mut g = Graph::default(); let non_func_root = (!hugr.get_optype(hugr.entrypoint()).is_module()).then_some(hugr.entrypoint()); let node_to_g = hugr - .entry_descendants() + .children(hugr.module_root()) .filter_map(|n| { let weight = match hugr.get_optype(n) { OpType::FuncDecl(_) => CallGraphNode::FuncDecl(n), @@ -94,7 +97,7 @@ impl CallGraph { /// Convert a Hugr [Node] into a petgraph node index. /// Result will be `None` if `n` is not a [`FuncDefn`](OpType::FuncDefn), - /// [`FuncDecl`](OpType::FuncDecl) or the hugr root. + /// [`FuncDecl`](OpType::FuncDecl) or the [HugrView::entrypoint]. pub fn node_index(&self, n: N) -> Option> { self.node_to_g.get(&n).copied() } diff --git a/hugr-passes/src/dead_funcs.rs b/hugr-passes/src/dead_funcs.rs index 7109a141f3..e09b6a3b43 100644 --- a/hugr-passes/src/dead_funcs.rs +++ b/hugr-passes/src/dead_funcs.rs @@ -21,7 +21,7 @@ use super::call_graph::{CallGraph, CallGraphNode}; #[non_exhaustive] /// Errors produced by [`RemoveDeadFuncsPass`]. pub enum RemoveDeadFuncsError { - /// The specified entry point is not a `FuncDefn` node or is not a child of the root. + /// The specified entry point is not a `FuncDefn` node #[error( "Entrypoint for RemoveDeadFuncsPass {node} was not a function definition in the root module" )] @@ -35,30 +35,17 @@ fn reachable_funcs<'a, H: HugrView>( cg: &'a CallGraph, h: &'a H, entry_points: impl IntoIterator, -) -> Result + 'a, RemoveDeadFuncsError> { +) -> impl Iterator + 'a { let g = cg.graph(); - let mut entry_points = entry_points.into_iter(); - let searcher = if h.get_optype(h.entrypoint()).is_module() { - let mut d = Dfs::new(g, 0.into()); - d.stack.clear(); - for n in entry_points { - if !h.get_optype(n).is_func_defn() || h.get_parent(n) != Some(h.entrypoint()) { - return Err(RemoveDeadFuncsError::InvalidEntryPoint { node: n }); - } - d.stack.push(cg.node_index(n).unwrap()); - } - d - } else { - if let Some(n) = entry_points.next() { - // Can't be a child of the module root as there isn't a module root! - return Err(RemoveDeadFuncsError::InvalidEntryPoint { node: n }); - } - Dfs::new(g, cg.node_index(h.entrypoint()).unwrap()) - }; - Ok(searcher.iter(g).map(|i| match g.node_weight(i).unwrap() { + let mut d = Dfs::new(g, 0.into()); + d.stack.clear(); // Remove the fake 0 + for n in entry_points { + d.stack.push(cg.node_index(n).unwrap()); + } + d.iter(g).map(|i| match g.node_weight(i).unwrap() { CallGraphNode::FuncDefn(n) | CallGraphNode::FuncDecl(n) => *n, CallGraphNode::NonFuncRoot => h.entrypoint(), - })) + }) } #[derive(Debug, Clone, Default)] @@ -86,14 +73,31 @@ impl> ComposablePass for RemoveDeadFuncsPass { type Error = RemoveDeadFuncsError; type Result = (); fn run(&self, hugr: &mut H) -> Result<(), RemoveDeadFuncsError> { - let reachable = reachable_funcs( - &CallGraph::new(hugr), - hugr, - self.entry_points.iter().copied(), - )? - .collect::>(); + let mut entry_points = Vec::new(); + for &n in self.entry_points.iter() { + if !hugr.get_optype(n).is_func_defn() { + return Err(RemoveDeadFuncsError::InvalidEntryPoint { node: n }); + } + debug_assert_eq!(hugr.get_parent(n), Some(hugr.module_root())); + entry_points.push(n); + } + if hugr.entrypoint() != hugr.module_root() { + entry_points.push(hugr.entrypoint()) + } + + let mut reachable = + reachable_funcs(&CallGraph::new(hugr), hugr, entry_points).collect::>(); + // Also prevent removing the entrypoint itself + let mut n = Some(hugr.entrypoint()); + while let Some(n2) = n { + n = hugr.get_parent(n2); + if n == Some(hugr.module_root()) { + reachable.insert(n2); + } + } + let unreachable = hugr - .entry_descendants() + .children(hugr.module_root()) .filter(|n| { OpTag::Function.is_superset(hugr.get_optype(*n).tag()) && !reachable.contains(n) }) @@ -108,17 +112,13 @@ impl> ComposablePass for RemoveDeadFuncsPass { /// Deletes from the Hugr any functions that are not used by either [`Call`] or /// [`LoadFunction`] nodes in reachable parts. /// -/// For [`Module`]-rooted Hugrs, `entry_points` may provide a list of entry points, -/// which must be children of the root. Note that if `entry_points` is empty, this will -/// result in all functions in the module being removed. -/// -/// For non-[`Module`]-rooted Hugrs, `entry_points` must be empty; the root node is used. +/// `entry_points` may provide a list of entry points, which must be [`FuncDefn`]s (children of the root). +/// The [HugrView::entrypoint] will also be used unless it is the [HugrView::module_root]. +/// Note that for a [`Module`]-rooted Hugr with no `entry_points` provided, this will remove +/// all functions from the module. /// /// # Errors -/// * If there are any `entry_points` but the root of the hugr is not a [`Module`] -/// * If any node in `entry_points` is -/// * not a [`FuncDefn`], or -/// * not a child of the root +/// * If any node in `entry_points` is not a [`FuncDefn`] /// /// [`Call`]: hugr_core::ops::OpType::Call /// [`FuncDefn`]: hugr_core::ops::OpType::FuncDefn @@ -138,24 +138,28 @@ pub fn remove_dead_funcs( mod test { use std::collections::HashMap; + use hugr_core::ops::handle::NodeHandle; use itertools::Itertools; use rstest::rstest; use hugr_core::builder::{ Container, Dataflow, DataflowSubContainer, HugrBuilder, ModuleBuilder, }; + use hugr_core::hugr::hugrmut::HugrMut; use hugr_core::{HugrView, extension::prelude::usize_t, types::Signature}; use super::remove_dead_funcs; #[rstest] - #[case([], vec![])] // No entry_points removes everything! - #[case(["main"], vec!["from_main", "main"])] - #[case(["from_main"], vec!["from_main"])] - #[case(["other1"], vec!["other1", "other2"])] - #[case(["other2"], vec!["other2"])] - #[case(["other1", "other2"], vec!["other1", "other2"])] + #[case(false, [], vec![])] // No entry_points removes everything! + #[case(true, [], vec!["from_main", "main"])] + #[case(false, ["main"], vec!["from_main", "main"])] + #[case(false, ["from_main"], vec!["from_main"])] + #[case(false, ["other1"], vec!["other1", "other2"])] + #[case(true, ["other2"], vec!["from_main", "main", "other2"])] + #[case(false, ["other1", "other2"], vec!["other1", "other2"])] fn remove_dead_funcs_entry_points( + #[case] use_hugr_entrypoint: bool, #[case] entry_points: impl IntoIterator, #[case] retained_funcs: Vec<&'static str>, ) -> Result<(), Box> { @@ -173,12 +177,15 @@ mod test { let fm = fm.finish_with_outputs(f_inp)?; let mut m = hb.define_function("main", Signature::new_endo(usize_t()))?; let mc = m.call(fm.handle(), &[], m.input_wires())?; - m.finish_with_outputs(mc.outputs())?; + let m = m.finish_with_outputs(mc.outputs())?; let mut hugr = hb.finish_hugr()?; + if use_hugr_entrypoint { + hugr.set_entrypoint(m.node()); + } let avail_funcs = hugr - .entry_descendants() + .children(hugr.module_root()) .filter_map(|n| { hugr.get_optype(n) .as_func_defn() diff --git a/hugr-py/Cargo.toml b/hugr-py/Cargo.toml index b47f054641..27020c8122 100644 --- a/hugr-py/Cargo.toml +++ b/hugr-py/Cargo.toml @@ -21,6 +21,6 @@ bench = false [dependencies] bumpalo = { workspace = true, features = ["collections"] } -hugr-model = { version = "0.20.1", path = "../hugr-model", features = ["pyo3"] } +hugr-model = { version = "0.20.2", path = "../hugr-model", features = ["pyo3"] } paste.workspace = true pyo3 = { workspace = true, features = ["extension-module", "abi3-py310"] } diff --git a/hugr-py/src/hugr/model/export.py b/hugr-py/src/hugr/model/export.py index eec5e99958..d93713d2a9 100644 --- a/hugr-py/src/hugr/model/export.py +++ b/hugr-py/src/hugr/model/export.py @@ -62,18 +62,7 @@ def export_node(self, node: Node) -> model.Node | None: inputs = [self.link_name(InPort(node, i)) for i in range(node_data._num_inps)] outputs = [self.link_name(OutPort(node, i)) for i in range(node_data._num_outs)] - meta = [] - - # Export JSON metadata - for meta_name, meta_value in node_data.metadata.items(): - # TODO: Is this the correct way to convert the metadata as JSON? - meta_json = json.dumps(meta_value) - meta.append( - model.Apply( - "compat.meta_json", - [model.Literal(meta_name), model.Literal(meta_json)], - ) - ) + meta = self.export_json_meta(node) # Add an order hint key to the node if necessary if _needs_order_key(self.hugr, node): @@ -373,9 +362,27 @@ def export_node(self, node: Node) -> model.Node | None: error = f"Unknown operation: {op}" raise ValueError(error) + def export_json_meta(self, node: Node) -> list[model.Term]: + """Export the metadata of the node via the JSON compatibility constructor.""" + node_data = self.hugr[node] + meta: list[model.Term] = [] + + for meta_name, meta_value in node_data.metadata.items(): + # TODO: Is this the correct way to convert the metadata as JSON? + meta_json = json.dumps(meta_value) + meta.append( + model.Apply( + "compat.meta_json", + [model.Literal(meta_name), model.Literal(meta_json)], + ) + ) + + return meta + def export_region_module(self, node: Node) -> model.Region: """Export a module node as a module region.""" node_data = self.hugr[node] + meta = self.export_json_meta(node) children = [] for child in node_data.children: @@ -384,7 +391,7 @@ def export_region_module(self, node: Node) -> model.Region: if child_node is not None: children.append(child_node) - return model.Region(kind=model.RegionKind.MODULE, children=children) + return model.Region(kind=model.RegionKind.MODULE, children=children, meta=meta) def export_region_dfg(self, node: Node) -> model.Region: """Export the children of a node as a dataflow region.""" @@ -439,6 +446,7 @@ def export_region_dfg(self, node: Node) -> model.Region: children=children, sources=sources, targets=targets, + meta=meta, ) def export_region_cfg(self, node: Node) -> model.Region: @@ -539,7 +547,7 @@ def find_func_input(self, node: Node) -> str | None: case _: return None - return _mangle_name(node, name) + return _mangle_name(func_node, name) def find_const_input(self, node: Node) -> model.Term | None: """Find and export the constant that a node is connected to, if any.""" diff --git a/hugr-py/src/hugr/std/_json_defs/collections/array.json b/hugr-py/src/hugr/std/_json_defs/collections/array.json index 641ed9a6a9..3d4e5c3829 100644 --- a/hugr-py/src/hugr/std/_json_defs/collections/array.json +++ b/hugr-py/src/hugr/std/_json_defs/collections/array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.array", "types": { "array": { @@ -729,6 +729,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } } diff --git a/hugr-py/src/hugr/std/_json_defs/collections/value_array.json b/hugr-py/src/hugr/std/_json_defs/collections/value_array.json index 064a3bf9c3..e2904d0330 100644 --- a/hugr-py/src/hugr/std/_json_defs/collections/value_array.json +++ b/hugr-py/src/hugr/std/_json_defs/collections/value_array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.value_array", "types": { "value_array": { @@ -732,6 +732,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.value_array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } } diff --git a/hugr-py/src/hugr/std/prelude.py b/hugr-py/src/hugr/std/prelude.py index 1818bd9d1b..9534549538 100644 --- a/hugr-py/src/hugr/std/prelude.py +++ b/hugr-py/src/hugr/std/prelude.py @@ -22,11 +22,10 @@ class StringVal(val.ExtensionValue): def to_value(self) -> val.Extension: name = "ConstString" - payload = {"value": self.v} return val.Extension( name, typ=STRING_T, - val=payload, + val=self.v, ) def __str__(self) -> str: diff --git a/hugr-py/tests/conftest.py b/hugr-py/tests/conftest.py index f3ce57336f..f4b2617a01 100644 --- a/hugr-py/tests/conftest.py +++ b/hugr-py/tests/conftest.py @@ -21,7 +21,7 @@ from hugr.ops import ComWire -QUANTUM_EXT = ext.Extension("pytest.quantum,", ext.Version(0, 1, 0)) +QUANTUM_EXT = ext.Extension("pytest.quantum", ext.Version(0, 1, 0)) QUANTUM_EXT.add_op_def( ext.OpDef( name="H", diff --git a/hugr-py/tests/test_prelude.py b/hugr-py/tests/test_prelude.py index a0f47ac5a4..c2ef2cbeec 100644 --- a/hugr-py/tests/test_prelude.py +++ b/hugr-py/tests/test_prelude.py @@ -1,8 +1,18 @@ +from hugr.build.dfg import Dfg from hugr.std.prelude import STRING_T, StringVal +from .conftest import validate + def test_string_val(): - ext_val = StringVal("test").to_value() + val = StringVal("test") + ext_val = val.to_value() assert ext_val.name == "ConstString" assert ext_val.typ == STRING_T - assert ext_val.val == {"value": "test"} + assert ext_val.val == "test" + + dfg = Dfg() + v = dfg.load(val) + dfg.set_outputs(v) + + validate(dfg.hugr) diff --git a/hugr/CHANGELOG.md b/hugr/CHANGELOG.md index 412a04cf7d..46439ca695 100644 --- a/hugr/CHANGELOG.md +++ b/hugr/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-v0.20.1...hugr-v0.20.2) - 2025-06-25 + +### Bug Fixes + +- update CallGraph and remove_dead_funcs for module-only FuncDefns ([#2336](https://github.com/CQCL/hugr/pull/2336)) + +### Documentation + +- fix doc links in persistent + +### New Features + +- Add serial data types for SimpleReplacement and PersistentHugr ([#2300](https://github.com/CQCL/hugr/pull/2300)) +- Add MermaidFormatter to replace RenderConfig ([#2275](https://github.com/CQCL/hugr/pull/2275)) +- *(core, llvm)* add array unpack operations ([#2339](https://github.com/CQCL/hugr/pull/2339)) +- Deprecate invalidation_set, add invalidated_nodes and SimpleReplacement::invalidation_set ([#2358](https://github.com/CQCL/hugr/pull/2358)) +- Rewrite for peeling a TailLoop ([#2290](https://github.com/CQCL/hugr/pull/2290)) +- Create Module/FunctionBuilders from existing Hugrs ([#2359](https://github.com/CQCL/hugr/pull/2359)) +- better errors using metadata from generator ([#2368](https://github.com/CQCL/hugr/pull/2368)) +- use `core.` prefixes for generator metadata keys ([#2371](https://github.com/CQCL/hugr/pull/2371)) +- *(core)* builder pattern for EnvelopeConfig ([#2330](https://github.com/CQCL/hugr/pull/2330)) + +### Refactor + +- *(types.rs)* rm incorrect comment and unnecessary allow-unused ([#2340](https://github.com/CQCL/hugr/pull/2340)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-v0.20.0...hugr-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr/Cargo.toml b/hugr/Cargo.toml index 5cd388586d..d43ee4ae90 100644 --- a/hugr/Cargo.toml +++ b/hugr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } @@ -30,10 +30,10 @@ llvm-test = ["hugr-llvm/llvm14-0", "hugr-llvm/test-utils"] zstd = ["hugr-core/zstd"] [dependencies] -hugr-model = { path = "../hugr-model", version = "0.20.1" } -hugr-core = { path = "../hugr-core", version = "0.20.1" } -hugr-passes = { path = "../hugr-passes", version = "0.20.1" } -hugr-llvm = { path = "../hugr-llvm", version = "0.20.1", optional = true } +hugr-model = { path = "../hugr-model", version = "0.20.2" } +hugr-core = { path = "../hugr-core", version = "0.20.2" } +hugr-passes = { path = "../hugr-passes", version = "0.20.2" } +hugr-llvm = { path = "../hugr-llvm", version = "0.20.2", optional = true } [dev-dependencies] lazy_static = { workspace = true } diff --git a/hugr/benches/benchmarks/hugr.rs b/hugr/benches/benchmarks/hugr.rs index 3616e1ed5f..e5f2d4de2b 100644 --- a/hugr/benches/benchmarks/hugr.rs +++ b/hugr/benches/benchmarks/hugr.rs @@ -19,9 +19,7 @@ trait Serializer { struct JsonSer; impl Serializer for JsonSer { fn serialize(&self, hugr: &Hugr) -> Vec { - let mut cfg = EnvelopeConfig::default(); - cfg.format = EnvelopeFormat::PackageJson; - cfg.zstd = None; + let cfg = EnvelopeConfig::new(EnvelopeFormat::PackageJson).disable_compression(); let mut bytes = Vec::new(); hugr.store(&mut bytes, cfg).unwrap(); @@ -36,9 +34,8 @@ struct CapnpSer; impl Serializer for CapnpSer { fn serialize(&self, hugr: &Hugr) -> Vec { - let mut cfg = EnvelopeConfig::default(); - cfg.format = EnvelopeFormat::ModelWithExtensions; - cfg.zstd = Some(Default::default()); + let cfg = + EnvelopeConfig::new(EnvelopeFormat::ModelWithExtensions).with_zstd(Default::default()); let mut bytes = Vec::new(); hugr.store(&mut bytes, cfg).unwrap(); diff --git a/specification/hugr.md b/specification/hugr.md index 3bd22b8efd..dc8251f30a 100644 --- a/specification/hugr.md +++ b/specification/hugr.md @@ -778,6 +778,40 @@ existing metadata, given the node ID. `Ports` (for port metadata) or `History` (for use by the rewrite engine)? +Reserved metadata keys used by the HUGR tooling are prefixed with `core.`. +Use of this prefix by external tooling may cause issues. + +#### Generator Metadata +Tooling generating HUGR can specify some reserved metadata keys to be used for debugging +purposes. + +The key `core.generator` when used on the module root node is +used to specify the tooling used to generate the module. +The associated value must be an object/dictionary containing the fields `name` +and `version`, each with string values. Extra fields may be used to include +additional data about generating tooling that may be useful for debugging. Example: + +```json +{ + "core.generator": { "name": "my_compiler", "version": "1.0.0" } +} +``` + +The key `core.used_extensions` when used on the module root node is +used to specify the names and versions of all the extensions used in the module. +Some of these may correspond to extensions packaged with the module, but they +may also be extensions the consuming tooling has pre-loaded. They can be used by the +tooling to check for extension version mismatches. The value associated with the key +must be an array of objects/dictionaries containing the keys `name` and `version`, each +with string values. Example: +```json +{ + "core.used_extensions": [{ "name": "my_ext", "version": "2.2.3" }] +} +``` + + + **TODO** Do we allow per-port metadata (using the same mechanism?) **TODO** What about references to ports? Should we add a list of port diff --git a/specification/std_extensions/collections/array.json b/specification/std_extensions/collections/array.json index 641ed9a6a9..3d4e5c3829 100644 --- a/specification/std_extensions/collections/array.json +++ b/specification/std_extensions/collections/array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.array", "types": { "array": { @@ -729,6 +729,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } } diff --git a/specification/std_extensions/collections/value_array.json b/specification/std_extensions/collections/value_array.json index 064a3bf9c3..e2904d0330 100644 --- a/specification/std_extensions/collections/value_array.json +++ b/specification/std_extensions/collections/value_array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.value_array", "types": { "value_array": { @@ -732,6 +732,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.value_array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } }