diff --git a/.github/workflows/ci-llzk.yml b/.github/workflows/ci-llzk.yml new file mode 100644 index 000000000..98b9b5f81 --- /dev/null +++ b/.github/workflows/ci-llzk.yml @@ -0,0 +1,57 @@ +name: CI (circom-to-llzk) +on: + push: + branches: [ llzk ] + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: cachix/install-nix-action@9280e7aca88deada44c930f1e2c78e21c3ae3edd # v31.7.0 + with: + install_url: https://releases.nixos.org/nix/nix-2.31.2/install + github_access_token: "${{ secrets.GITHUB_TOKEN }}" + - name: Set Git credentials + run: | + git config --global 'url.https://api@github.com/'.insteadOf 'https://github.com/' + git config --global 'url.https://ssh@github.com/'.insteadOf 'ssh://git@github.com/' + git config --global 'url.https://git@github.com/'.insteadOf 'git@github.com:' + - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16 + with: + name: veridise-public + authToken: '${{ secrets.CACHIX_PUBLIC_TOKEN }}' + - name: Build and test + run: nix --print-build-logs build + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: cachix/install-nix-action@9280e7aca88deada44c930f1e2c78e21c3ae3edd # v31.7.0 + with: + install_url: https://releases.nixos.org/nix/nix-2.31.2/install + github_access_token: "${{ secrets.GITHUB_TOKEN }}" + - name: Set Git credentials + run: | + git config --global 'url.https://api@github.com/'.insteadOf 'https://github.com/' + git config --global 'url.https://ssh@github.com/'.insteadOf 'ssh://git@github.com/' + git config --global 'url.https://git@github.com/'.insteadOf 'git@github.com:' + - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16 + with: + name: veridise-public + authToken: '${{ secrets.CACHIX_PUBLIC_TOKEN }}' + - name: Set nix environment + uses: nicknovitski/nix-develop@v1 + env: + CI_BOT_PAT: "${{ secrets.CI_BUILD_TOKEN }}" + - name: Run clippy + run: cargo clippy -p llzk_backend -- --no-deps diff --git a/.gitignore b/.gitignore index b400bb37e..f9b59dc91 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ type_analysis/target/ .idea/ .vscode/ .DS_Store + +# Nix package build +/result +/build-tools diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..a6e4f35b2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,14 @@ +# AGENTS.md + +This repository expects coding agents to use `nix` for builds and command execution so all necessary dependencies are available. + +## Build and command guidance + +- Preferred build command: `nix build -L` +- When you need to run a project command inside the development environment, use: + `nix develop --command bash -c "[command]"` + +## Completion requirement + +- Always ensure the build is successful before you stop working. +- If you make changes, finish by running a successful build with `nix`. diff --git a/Cargo.lock b/Cargo.lock index bf95bb67d..51d214b80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -20,6 +20,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -29,6 +41,37 @@ dependencies = [ "term", ] +[[package]] +name = "assert_cmd" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_fs" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652f6cb1f516886fcfee5e7a5c078b9ade62cfcb889524efe5a64d682dd27a9" +dependencies = [ + "anstyle", + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + [[package]] name = "atty" version = "0.2.14" @@ -46,6 +89,46 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.9.4", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.106", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.9.4", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.106", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -67,6 +150,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + [[package]] name = "block-buffer" version = "0.10.2" @@ -76,17 +165,56 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "caseless" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6fd507454086c8edfd769ca6ada439193cdb209c7681712ef6275cccbfe5d8" +dependencies = [ + "unicode-normalization", +] + [[package]] name = "cc" -version = "1.0.79" +version = "1.2.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -99,14 +227,19 @@ name = "circom" version = "2.2.2" dependencies = [ "ansi_term", + "assert_cmd", + "assert_fs", "clap", "compiler", "constraint_generation", "constraint_writers", "dag", - "exitcode", + "glob", + "lazy_static", + "llzk_backend", "parser", "program_structure", + "regex", "type_analysis", "wast", ] @@ -120,6 +253,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "2.34.0" @@ -128,7 +272,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim", "textwrap", "unicode-width", @@ -176,6 +320,20 @@ dependencies = [ "program_structure", ] +[[package]] +name = "comrak" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccde7f074aafedd0dfa210b7df42326927e3b64f499e0ce8dcc5c132ef08baba" +dependencies = [ + "caseless", + "entities", + "memchr", + "slug", + "typed-arena", + "unicode_categories", +] + [[package]] name = "constant_tracking" version = "2.0.0" @@ -215,6 +373,15 @@ dependencies = [ "json", ] +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cpufeatures" version = "0.2.2" @@ -224,6 +391,31 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.2" @@ -251,12 +443,24 @@ dependencies = [ "program_structure", ] +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.3" @@ -288,6 +492,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "either" version = "1.7.0" @@ -303,32 +513,33 @@ dependencies = [ "log", ] +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" + [[package]] name = "errno" -version = "0.3.1" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys 0.61.0", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "exitcode" -version = "1.1.2" +name = "find-msvc-tools" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" +checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" [[package]] name = "fixedbitset" @@ -354,7 +565,49 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "globset" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax 0.8.6", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.9.4", + "ignore", + "walkdir", ] [[package]] @@ -368,7 +621,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.31", ] [[package]] @@ -392,6 +645,22 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -421,7 +690,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix", + "rustix 0.37.19", "windows-sys 0.48.0", ] @@ -461,7 +730,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax", + "regex-syntax 0.6.27", "string_cache", "term", "tiny-keccak", @@ -479,9 +748,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ "spin", ] @@ -494,9 +763,19 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] [[package]] name = "libm" @@ -510,6 +789,74 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "llzk" +version = "0.4.0" +source = "git+https://github.com/project-llzk/llzk-rs#5c35089f7b0fefa909a1b6eda90e6d0c7c7f5815" +dependencies = [ + "llzk-macro", + "llzk-sys", + "log", + "melior", + "melior-macro", + "mlir-sys", + "paste", +] + +[[package]] +name = "llzk-macro" +version = "0.3.0" +source = "git+https://github.com/project-llzk/llzk-rs#5c35089f7b0fefa909a1b6eda90e6d0c7c7f5815" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.106", + "thiserror 2.0.17", +] + +[[package]] +name = "llzk-sys" +version = "20.0.0" +source = "git+https://github.com/project-llzk/llzk-rs#5c35089f7b0fefa909a1b6eda90e6d0c7c7f5815" +dependencies = [ + "anyhow", + "llzk-sys-build-support", + "mlir-sys", +] + +[[package]] +name = "llzk-sys-build-support" +version = "1.0.1" +source = "git+https://github.com/project-llzk/llzk-rs#5c35089f7b0fefa909a1b6eda90e6d0c7c7f5815" +dependencies = [ + "anyhow", + "bindgen 0.71.1", + "cc", +] + +[[package]] +name = "llzk_backend" +version = "0.1.0" +dependencies = [ + "ansi_term", + "anyhow", + "compiler", + "llzk", + "melior", + "melior-macro", + "mlir-sys", + "num-bigint-dig", + "num-traits", + "program_structure", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -522,12 +869,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lz_fnv" @@ -535,11 +879,52 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bbb1b0dbe51f0976eaa466f4e0bdc11856fe8008aee26f30ccec8de15b28e38" +[[package]] +name = "melior" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd600336f22a76d4de41c54c27561a6be90c7a10f4586aab4c564b8da28f2f2a" +dependencies = [ + "melior-macro", + "mlir-sys", +] + +[[package]] +name = "melior-macro" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ad461682befe0344538c5ee53ad1da53eef55e1db17071a7e748e74bb7f637" +dependencies = [ + "comrak", + "convert_case", + "proc-macro2", + "quote", + "regex", + "syn 2.0.106", + "tblgen", + "unindent", +] + [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mlir-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9348dd263d2680d657635d9dd084cdcd785d7486a41332ffa4c926d5882b54" +dependencies = [ + "bindgen 0.71.1", +] [[package]] name = "new_debug_unreachable" @@ -547,6 +932,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -605,9 +1000,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "parking_lot" @@ -647,13 +1042,19 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pest" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69486e2b8c2d2aeb9762db7b4e00b0331156393555cff467f4163ff06821eef8" dependencies = [ - "thiserror", + "thiserror 1.0.31", "ucd-trie", ] @@ -677,7 +1078,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 1.0.98", ] [[package]] @@ -722,11 +1123,48 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "difflib", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.106", +] + [[package]] name = "proc-macro2" -version = "1.0.42" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -747,13 +1185,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.20" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -781,7 +1225,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.9", ] [[package]] @@ -790,7 +1234,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -799,20 +1243,32 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom", + "getrandom 0.2.9", "redox_syscall", - "thiserror", + "thiserror 1.0.31", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.8.6", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.6", ] [[package]] @@ -821,6 +1277,18 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -833,14 +1301,27 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.7", "windows-sys 0.48.0", ] +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.9.4", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.0", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -853,6 +1334,15 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -873,7 +1363,7 @@ checksum = "75743a150d003dd863b51dc809bcad0d73f2102c53632f1e954e738192a3413f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.98", ] [[package]] @@ -898,12 +1388,28 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "siphasher" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +[[package]] +name = "slug" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -912,9 +1418,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "string_cache" @@ -946,6 +1452,42 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tblgen" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7364c3b3e7036cebffcc65895bff60ca160e1c9d98a84fb24f723e8c90b02a60" +dependencies = [ + "bindgen 0.72.1", + "cc", + "paste", + "thiserror 2.0.17", +] + +[[package]] +name = "tempfile" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix 1.1.2", + "windows-sys 0.61.0", +] + [[package]] name = "term" version = "0.7.0" @@ -966,6 +1508,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "textwrap" version = "0.11.0" @@ -981,7 +1529,16 @@ version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.31", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", ] [[package]] @@ -992,7 +1549,18 @@ checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.98", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -1013,6 +1581,21 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "type_analysis" version = "2.2.2" @@ -1022,6 +1605,12 @@ dependencies = [ "program_structure", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.15.0" @@ -1040,6 +1629,21 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.9" @@ -1052,6 +1656,18 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + [[package]] name = "vec_map" version = "0.8.2" @@ -1064,12 +1680,103 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + [[package]] name = "wast" version = "39.0.0" @@ -1112,6 +1819,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-sys" version = "0.45.0" @@ -1130,6 +1843,15 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -1243,3 +1965,9 @@ name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/Cargo.toml b/Cargo.toml index 2df8912ae..0935e1359 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,6 @@ members = [ "constraint_writers", "constant_tracking", "code_producers", + "llzk_backend", "dag" ] \ No newline at end of file diff --git a/README.md b/README.md index 2d5f31b13..413d7ef59 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Basic background on Zero-knowledge proofs can be found on [Background section](h # Install +Set path variables for the [LLZK dependency](https://github.com/project-llzk/llzk-rs?tab=readme-ov-file#building-tips) Refer to [Installation section](https://docs.circom.io/getting-started/installation/) ## :warning: Deprecation note diff --git a/circom/Cargo.toml b/circom/Cargo.toml index 7fbd1ae28..6e678b440 100644 --- a/circom/Cargo.toml +++ b/circom/Cargo.toml @@ -20,7 +20,16 @@ constraint_generation = { path = "../constraint_generation" } constraint_writers = { path = "../constraint_writers" } compiler = { path = "../compiler" } dag = { path = "../dag" } +llzk_backend = { path = "../llzk_backend" } clap = "2.33.0" ansi_term = "0.12.1" wast = "39.0.0" -exitcode = "1.1.2" + +[dev-dependencies] +assert_cmd = "2.0" +assert_fs = "1.1" +regex = "1.11" +lazy_static = "1.5" + +[build-dependencies] +glob = "0.3.3" diff --git a/circom/build.rs b/circom/build.rs new file mode 100644 index 000000000..04c33b603 --- /dev/null +++ b/circom/build.rs @@ -0,0 +1,50 @@ +use std::env; +use std::fs::File; +use std::path::Path; +use std::io::Write; +use std::fs::{copy, create_dir_all}; +use glob::glob; + +/// Generates the file `discovered_tests.in` in the output directory, containing +/// test functions for each `.circom` file found in the `tests/` directory. +/// Each test function is named based on the file path, with slashes replaced +/// by underscores, and is set up to call `lit_test` with the file's contents. +fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("discovered_tests.in"); + let mut test_code = "".to_string(); + + for entry in glob("tests/**/*.circom").expect("Failed to read glob pattern") { + let path = entry.unwrap(); + create_dir_all(Path::new(&out_dir).join(path.parent().unwrap())).unwrap(); + copy(path.clone(), Path::new(&out_dir).join(path.clone())).unwrap(); + let test_name = path + .to_str() + .unwrap() + .replace('/', "_") + .replace(".circom", "") + .replace('-', "_") + .to_lowercase(); + + test_code = format!( + " + {} + + #[test] + fn {}() -> LitResult<()> {{ + lit_test(include_str!(\"{}\"), \"{}\") + }}", + test_code, + test_name, + path.to_str().unwrap(), + test_name + ); + } + + generate_file(dest_path, test_code.as_bytes()); +} + +fn generate_file>(path: P, text: &[u8]) { + let mut f = File::create(path).unwrap(); + f.write_all(text).unwrap() +} diff --git a/circom/src/input_user.rs b/circom/src/input_user.rs index f89d04c26..ae2f446aa 100644 --- a/circom/src/input_user.rs +++ b/circom/src/input_user.rs @@ -2,9 +2,12 @@ use std::path::PathBuf; pub struct Input { pub input_program: PathBuf, + pub out_dump_parse: PathBuf, + pub out_dump_vcp: PathBuf, pub out_r1cs: PathBuf, pub out_json_constraints: PathBuf, pub out_json_substitutions: PathBuf, + pub out_llzk_code: PathBuf, pub out_wat_code: PathBuf, pub out_wasm_code: PathBuf, pub out_wasm_name: String, @@ -15,7 +18,10 @@ pub struct Input { pub out_c_dat: PathBuf, pub out_sym: PathBuf, //pub field: &'static str, + pub dump_parse_flag: bool, + pub dump_vcp_flag: bool, pub c_flag: bool, + pub llzk_flag: Option, pub wasm_flag: bool, pub wat_flag: bool, pub no_asm_flag: bool, @@ -32,9 +38,12 @@ pub struct Input { pub flag_old_heuristics: bool, pub inspect_constraints_flag: bool, pub no_rounds: usize, + pub flag_stabilize: bool, pub flag_verbose: bool, pub flag_no_init: bool, pub prime: String, + pub llzk_plaintext_out_flag: bool, + pub llzk_pass_pipeline: String, pub link_libraries : Vec } @@ -43,11 +52,14 @@ const R1CS: &'static str = "r1cs"; const WAT: &'static str = "wat"; const WASM: &'static str = "wasm"; const CPP: &'static str = "cpp"; +const LLZK: &'static str = "llzk"; const JS: &'static str = "js"; const DAT: &'static str = "dat"; const SYM: &'static str = "sym"; const JSON: &'static str = "json"; +pub const LLZK_KIND_CONCRETE: &'static str = "concrete"; +pub const LLZK_KIND_TEMPLATED: &'static str = "templated"; impl Input { pub fn new() -> Result { @@ -65,12 +77,15 @@ impl Input { file_name = format!("{}_c", file_name) }; let output_c_path = Input::build_folder(&output_path, &file_name, CPP); + let output_llzk_path = Input::build_folder(&output_path, &file_name, LLZK); let output_js_path = Input::build_folder(&output_path, &file_name, JS); let o_style = input_processing::get_simplification_style(&matches)?; let link_libraries = input_processing::get_link_libraries(&matches); Result::Ok(Input { //field: P_BN128, input_program: input, + out_dump_parse: Input::build_output(&output_path, &file_name, "parse.txt"), + out_dump_vcp: Input::build_output(&output_path, &file_name, "vcp.txt"), out_r1cs: Input::build_output(&output_path, &file_name, R1CS), out_wat_code: Input::build_output(&output_js_path, &file_name, WAT), out_wasm_code: Input::build_output(&output_js_path, &file_name, WASM), @@ -80,6 +95,7 @@ impl Input { out_c_run_name: file_name.clone(), out_c_code: Input::build_output(&output_c_path, &file_name, CPP), out_c_dat: Input::build_output(&output_c_path, &file_name, DAT), + out_llzk_code: Input::build_output(&output_llzk_path, &file_name, LLZK), out_sym: Input::build_output(&output_path, &file_name, SYM), out_json_constraints: Input::build_output( &output_path, @@ -91,9 +107,14 @@ impl Input { &format!("{}_substitutions", file_name), JSON, ), + dump_parse_flag: input_processing::get_dump_parse(&matches), + dump_vcp_flag: input_processing::get_dump_vcp(&matches), wat_flag:input_processing::get_wat(&matches), wasm_flag: input_processing::get_wasm(&matches), c_flag: c_flag, + llzk_flag: input_processing::get_llzk(&matches), + llzk_plaintext_out_flag: input_processing::get_llzk_plaintext(&matches), + llzk_pass_pipeline: input_processing::get_llzk_pass_pipeline(&matches)?, no_asm_flag:input_processing::get_no_asm(&matches), r1cs_flag: input_processing::get_r1cs(&matches), sym_flag: input_processing::get_sym(&matches), @@ -110,6 +131,7 @@ impl Input { flag_old_heuristics: input_processing::get_flag_old_heuristics(&matches), flag_verbose: input_processing::get_flag_verbose(&matches), flag_no_init: input_processing::get_flag_no_init(&matches), + flag_stabilize: input_processing::get_flag_stabilize(&matches), prime: input_processing::get_prime(&matches)?, link_libraries }) @@ -161,18 +183,33 @@ impl Input { self.out_c_run_name.clone() } + pub fn dump_parse_file(&self) -> &str { + self.out_dump_parse.to_str().unwrap() + } + pub fn dump_vcp_file(&self) -> &str { + self.out_dump_vcp.to_str().unwrap() + } pub fn c_file(&self) -> &str { self.out_c_code.to_str().unwrap() } pub fn dat_file(&self) -> &str { self.out_c_dat.to_str().unwrap() } + pub fn llzk_file(&self) -> &str { + self.out_llzk_code.to_str().unwrap() + } pub fn json_constraints_file(&self) -> &str { self.out_json_constraints.to_str().unwrap() } pub fn json_substitutions_file(&self) -> &str { self.out_json_substitutions.to_str().unwrap() } + pub fn dump_parse_flag(&self) -> bool { + self.dump_parse_flag + } + pub fn dump_vcp_flag(&self) -> bool { + self.dump_vcp_flag + } pub fn wasm_flag(&self) -> bool { self.wasm_flag } @@ -182,6 +219,9 @@ impl Input { pub fn c_flag(&self) -> bool { self.c_flag } + pub fn llzk_flag(&self) -> &Option { + &self.llzk_flag + } pub fn no_asm_flag(&self) -> bool { self.no_asm_flag } @@ -209,6 +249,9 @@ impl Input { pub fn inspect_constraints_flag(&self) -> bool { self.inspect_constraints_flag } + pub fn flag_stabilize(&self) -> bool { + self.flag_stabilize + } pub fn flag_verbose(&self) -> bool { self.flag_verbose } @@ -233,6 +276,30 @@ impl Input { pub fn prime(&self) -> String{ self.prime.clone() } + pub fn llzk_pass_pipeline(&self) -> String { + self.llzk_pass_pipeline.clone() + } + pub fn llzk_plaintext_out_flag(&self) -> bool { + self.llzk_plaintext_out_flag + } + pub fn to_llzk_config(&self) -> Result { + use program_structure::constants::UsefulConstants; + let prime = UsefulConstants::new(&self.prime).get_p().to_biguint().ok_or_else(|| { + eprintln!( + "{} prime should be convertible to unsigned", + ansi_term::Colour::Red.paint("LLZK config error:"), + ); + })?; + Ok(llzk_backend::LlzkConfig { + filename: self.llzk_file().to_string(), + pass_pipeline: self.llzk_pass_pipeline(), + prime_str: self.prime(), + prime, + verbose: self.flag_verbose(), + stabilize: self.flag_stabilize(), + emit_plaintext: self.llzk_plaintext_out_flag(), + }) + } } mod input_processing { use ansi_term::Colour; @@ -311,10 +378,28 @@ mod input_processing { matches.is_present("no_asm") } + pub fn get_dump_parse(matches: &ArgMatches) -> bool { + matches.is_present("print_dump_parse") + } + + pub fn get_dump_vcp(matches: &ArgMatches) -> bool { + matches.is_present("print_dump_vcp") + } + pub fn get_c(matches: &ArgMatches) -> bool { matches.is_present("print_c") } + pub fn get_llzk(matches: &ArgMatches) -> Option { + // Since there's a default value, `matches.is_present()` is + // always true so `occurrences_of()` must be used instead. + if matches.occurrences_of("gen_llzk_ir") > 0 { + matches.value_of("gen_llzk_ir").map(String::from) + } else { + None + } + } + pub fn get_main_inputs_log(matches: &ArgMatches) -> bool { matches.is_present("main_inputs_log") } @@ -334,6 +419,10 @@ mod input_processing { matches.is_present("inspect_constraints") } + pub fn get_flag_stabilize(matches: &ArgMatches) -> bool { + matches.is_present("flag_stabilize") + } + pub fn get_flag_verbose(matches: &ArgMatches) -> bool { matches.is_present("flag_verbose") } @@ -371,6 +460,17 @@ mod input_processing { } } + pub fn get_llzk_pass_pipeline(matches: &ArgMatches) -> Result { + match matches.is_present("mlir_pass_pipeline") { + true => Ok(String::from(matches.value_of("mlir_pass_pipeline").unwrap())), + false => Ok(String::from("")), + } + } + + pub fn get_llzk_plaintext(matches: &ArgMatches) -> bool { + matches.is_present("llzk_plaintext") + } + pub fn view() -> ArgMatches<'static> { App::new("circom compiler") .version(VERSION) @@ -496,6 +596,21 @@ mod input_processing { .display_order(330) .help("Adds directory to library search path"), ) + .arg( + Arg::with_name("print_dump_parse") + .long("dump_parse") + .takes_value(false) + .display_order(10) + .help("Parses the circuit and dumps the program archive"), + ) + .arg( + Arg::with_name("print_dump_vcp") + .long("dump_vcp") + .takes_value(false) + .display_order(11) + .help("Parses the circuit and dumps the VCP (very concrete program)") + .long_help("Parses the circuit and dumps the VCP (very concrete program). This may take a while for very large circuits."), + ) .arg( Arg::with_name("print_c") .long("c") @@ -504,6 +619,15 @@ mod input_processing { .display_order(150) .help("Compiles the circuit to C++"), ) + .arg( + Arg::with_name("gen_llzk_ir") + .long("llzk") + .takes_value(true) + .possible_values(&[super::LLZK_KIND_CONCRETE, super::LLZK_KIND_TEMPLATED]) + .default_value(super::LLZK_KIND_TEMPLATED) + .display_order(5001) + .help("Compiles the circuit to LLZK-IR"), + ) .arg( Arg::with_name("parallel_simplification") .long("parallel") @@ -528,6 +652,13 @@ mod input_processing { .display_order(210) .help("Produces a log_inputs.txt file"), ) + .arg( + Arg::with_name("flag_stabilize") + .long("stabilize") + .takes_value(false) + .display_order(790) + .help("Attempt to stabilize operation ordering in LLZK IR output"), + ) .arg( Arg::with_name("flag_verbose") .long("verbose") @@ -558,6 +689,21 @@ mod input_processing { .display_order(300) .help("To choose the prime number to use to generate the circuit. Receives the name of the curve (bn128, bls12377, bls12381, goldilocks, grumpkin, pallas, secq256r1, vesta)"), ) + .arg ( + Arg::with_name("mlir_pass_pipeline") + .long("llzk_passes") + .takes_value(true) + .default_value("") + .display_order(5002) + .help("Specify an MLIR pass pipeline to apply on the LLZK IR output"), + ) + .arg( + Arg::with_name("llzk_plaintext") + .long("llzk_plaintext") + .takes_value(false) + .display_order(5003) + .help("Emit LLZK IR in plaintext/assembly format instead of bytecode (default is bytecode)"), + ) .get_matches() } diff --git a/circom/src/main.rs b/circom/src/main.rs index 40e2b2a01..a8987d2d2 100644 --- a/circom/src/main.rs +++ b/circom/src/main.rs @@ -27,6 +27,16 @@ fn start() -> Result<(), ()> { let mut program_archive = parser_user::parse_project(&user_input)?; type_analysis_user::analyse_project(&mut program_archive)?; + // Dump the ProgramArchive if requested + if user_input.dump_parse_flag() { + write_to_file(format!("{:#?}", program_archive), user_input.dump_parse_file())?; + } + // If requested, generate LLZK IR output with generic templates + let llzk_gen_opt = user_input.llzk_flag().as_deref(); + if Some(crate::input_user::LLZK_KIND_TEMPLATED) == llzk_gen_opt { + return llzk_backend::generate_llzk(&program_archive, user_input.to_llzk_config()?); + } + let config = ExecutionConfig { no_rounds: user_input.no_rounds(), flag_p: user_input.parallel_simplification_flag(), @@ -45,7 +55,28 @@ fn start() -> Result<(), ()> { json_substitutions: user_input.json_substitutions_file().to_string(), prime: user_input.prime(), }; + let parse_info = + if llzk_gen_opt.is_some_and(|kind| kind == crate::input_user::LLZK_KIND_CONCRETE) { + Some(llzk_backend::CachedParseInfo::from(&program_archive)) + } else { + None + }; + let circuit = execution_user::execute_project(program_archive, config)?; + + // Dump the VCP if requested + if user_input.dump_vcp_flag() { + println!("Dumping VCP to: {}", user_input.dump_vcp_file()); + println!("{}", Colour::Yellow.paint("Dumping VCP may take a while for large circuits.")); + write_to_file(format!("{:#?}", circuit), user_input.dump_vcp_file())?; + } + + // If requested, generate LLZK IR output after templates have been made concrete + if let Some(parse_info) = parse_info { + let vcp_plus = llzk_backend::VCPPlus { vcp: &circuit, parse_info }; + return llzk_backend::generate_llzk(&vcp_plus, user_input.to_llzk_config()?); + } + let compilation_config = CompilerConfig { vcp: circuit, debug_output: user_input.print_ir_flag(), @@ -69,3 +100,14 @@ fn start() -> Result<(), ()> { compilation_user::compile(compilation_config)?; Result::Ok(()) } + +fn write_to_file>(contents: C, filename: &str) -> Result<(), ()> { + let out_path = std::path::Path::new(filename); + // Ensure parent directories exist + if let Some(parent) = out_path.parent() { + std::fs::create_dir_all(parent).map_err(|_err| {})?; + } + std::fs::write(out_path, contents).map_err(|_err| {})?; + println!("{} {}", Colour::Green.paint("Written successfully:"), filename); + Result::Ok(()) +} diff --git a/circom/tests/arrays/array_copy1_loop.circom b/circom/tests/arrays/array_copy1_loop.circom new file mode 100644 index 000000000..89fbf7dca --- /dev/null +++ b/circom/tests/arrays/array_copy1_loop.circom @@ -0,0 +1,19 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +// Scalar copy version of `array_copy1_vec.circom` test. Output is identical except for basic blocks. +template Array1(n, S) { + signal output out[n]; + + for (var i = 0; i < n; i++) { + out[i] <== S[i]; + } +} + +component main = Array1(5, [11,22,33,44,55]); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/array_copy1_vec.circom b/circom/tests/arrays/array_copy1_vec.circom new file mode 100644 index 000000000..f256026c0 --- /dev/null +++ b/circom/tests/arrays/array_copy1_vec.circom @@ -0,0 +1,17 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +// Vector copy version of `array_copy1_loop.circom` test. Output is identical except for basic blocks. +template Array1(n, S) { + signal output out[n]; + + out <== S; +} + +component main = Array1(5, [11,22,33,44,55]); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/array_copy2_loop.circom b/circom/tests/arrays/array_copy2_loop.circom new file mode 100644 index 000000000..94f5c75cf --- /dev/null +++ b/circom/tests/arrays/array_copy2_loop.circom @@ -0,0 +1,67 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Scalar copy version of `array_copy2_vec.circom` test. Output is identical except for basic blocks. +template Array2(n) { + signal input a[n]; + signal output b[n]; + + for (var i = 0; i < n; i++) { + b[i] <== a[i]; + } +} + +component main = Array2(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array2::@Array2<[5]>>} { +// CHECK-NEXT: poly.template @Array2 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Array2 { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Array2::@Array2<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array2::@Array2<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_9]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@Array2::@Array2<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array2::@Array2<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@Array2::@Array2<[@n]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@b] : <@Array2::@Array2<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_18]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_15]]{{\[}}%[[VAL_23]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_25]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_copy2_vec.circom b/circom/tests/arrays/array_copy2_vec.circom new file mode 100644 index 000000000..6fdb611bc --- /dev/null +++ b/circom/tests/arrays/array_copy2_vec.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Vector copy version of `array_copy2_loop.circom` test. Output is identical except for basic blocks. +template Array2(n) { + signal input inp[n]; + signal output out[n]; + + out <== inp; +} + +component main = Array2(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array2::@Array2<[5]>>} { +// CHECK-NEXT: poly.template @Array2 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Array2 { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Array2::@Array2<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array2::@Array2<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@Array2::@Array2<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array2::@Array2<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Array2::@Array2<[@n]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@out] : <@Array2::@Array2<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !array.type<@n x !felt.type<"bn128">>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_copy3_loop.circom b/circom/tests/arrays/array_copy3_loop.circom new file mode 100644 index 000000000..643dd574c --- /dev/null +++ b/circom/tests/arrays/array_copy3_loop.circom @@ -0,0 +1,93 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Vector copy version of `array_copy3_vec.circom` test. +template Array3(n) { + signal input inp[n][n]; + signal output out[n][n]; + + for(var i = 0; i < n; i++) { + for(var j = 0; j < n; j++) { + out[i][j] <== inp[i][j]; + } + } +} + +component main = Array3(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array3::@Array3<[5]>>} { +// CHECK-NEXT: poly.template @Array3 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Array3 { +// CHECK-NEXT: struct.member @out : !array.type<@n,@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type<"bn128">>) -> !struct.type<@Array3::@Array3<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array3::@Array3<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_9]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_11]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_12]]) %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_14]], %[[VAL_15]]] : <@n,@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_17]], %[[VAL_18]]] = %[[VAL_16]] : <@n,@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Array3::@Array3<[@n]>>, !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array3::@Array3<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !struct.type<@Array3::@Array3<[@n]>>, %[[VAL_24:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_23]][@out] : <@Array3::@Array3<[@n]>>, !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_29:[0-9a-zA-Z_\.]+]] = %[[VAL_27]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_29]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_30]]) %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_32]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_34]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_35]]) %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_37]], %[[VAL_38]]] : <@n,@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_40]], %[[VAL_41]]] : <@n,@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_42]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_36]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_44]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_31]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_46]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_copy3_partial.circom b/circom/tests/arrays/array_copy3_partial.circom new file mode 100644 index 000000000..f5bc00ab1 --- /dev/null +++ b/circom/tests/arrays/array_copy3_partial.circom @@ -0,0 +1,68 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Parital vector copy version of `array_copy3_loop.circom` test. The outer dimension is traversed +// explicitly but the inner dimension is treated as a vector copy. Output is identical. +template Array3(n) { + signal input inp[n][n]; + signal output out[n][n]; + + for (var i = 0; i < n; i++) { + out[i] <== inp[i]; + } +} + +component main = Array3(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array3::@Array3<[5]>>} { +// CHECK-NEXT: poly.template @Array3 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Array3 { +// CHECK-NEXT: struct.member @out : !array.type<@n,@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type<"bn128">>) -> !struct.type<@Array3::@Array3<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array3::@Array3<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_0]]{{\[}}%[[VAL_9]]] : <@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <@n,@n x !felt.type<"bn128">>, <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Array3::@Array3<[@n]>>, !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array3::@Array3<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@Array3::@Array3<[@n]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@out] : <@Array3::@Array3<[@n]>>, !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_18]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_15]]{{\[}}%[[VAL_23]]] : <@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_17]]{{\[}}%[[VAL_25]]] : <@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_24]] : !array.type<@n x !felt.type<"bn128">>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_copy3_vec.circom b/circom/tests/arrays/array_copy3_vec.circom new file mode 100644 index 000000000..64c0500b7 --- /dev/null +++ b/circom/tests/arrays/array_copy3_vec.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Vector copy version of `array_copy3_loop.circom` test. +template Array3(n) { + signal input inp[n][n]; + signal output out[n][n]; + + out <== inp; +} + +component main = Array3(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array3::@Array3<[5]>>} { +// CHECK-NEXT: poly.template @Array3 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Array3 { +// CHECK-NEXT: struct.member @out : !array.type<@n,@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type<"bn128">>) -> !struct.type<@Array3::@Array3<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array3::@Array3<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@Array3::@Array3<[@n]>>, !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array3::@Array3<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Array3::@Array3<[@n]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@out] : <@Array3::@Array3<[@n]>>, !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !array.type<@n,@n x !felt.type<"bn128">>, !array.type<@n,@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_copy4_vec.circom b/circom/tests/arrays/array_copy4_vec.circom new file mode 100644 index 000000000..0edcad9f1 --- /dev/null +++ b/circom/tests/arrays/array_copy4_vec.circom @@ -0,0 +1,17 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +// Vector copy version of `array_copy1_loop.circom` test. Output is identical except for basic blocks. +template Array4(n, m, S) { + signal output out[n][m]; + + out <== S; +} + +component main = Array4(3, 2, [[11,22],[33,44],[55,66]]); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/array_dimensions_00.circom b/circom/tests/arrays/array_dimensions_00.circom new file mode 100644 index 000000000..66ef0b1b0 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_00.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims() { + var arr2[2 + 2]; +} + +component main = ArrayDims(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]] : <4 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]] : <4 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_01.circom b/circom/tests/arrays/array_dimensions_01.circom new file mode 100644 index 000000000..c18054d03 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_01.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims() { + var arr[0 > 1 ? 7 : 9]; +} + +component main = ArrayDims(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_02.circom b/circom/tests/arrays/array_dimensions_02.circom new file mode 100644 index 000000000..dcf1c48a4 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_02.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims() { + // (prime + 3) - (prime + 1) = 2 + var arr[21888242871839275222246405745257275088548364400416034343698204186575808495620 - 21888242871839275222246405745257275088548364400416034343698204186575808495618]; +} + +component main = ArrayDims(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_4]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_03.circom b/circom/tests/arrays/array_dimensions_03.circom new file mode 100644 index 000000000..a61fdfe7c --- /dev/null +++ b/circom/tests/arrays/array_dimensions_03.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims() { + // Equals 14 + var arr[(7 ** ((2 << ~(~1)) > 2 ? !(5) : (1 <= 2))) ^ 15]; +} + +component main = ArrayDims(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]] : <14 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]] : <14 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_04.circom b/circom/tests/arrays/array_dimensions_04.circom new file mode 100644 index 000000000..846186337 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_04.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims(N) { + signal input inp[N]; +} + +component main = ArrayDims(7); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[7]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) -> !struct.type<@ArrayDims::@ArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[@N]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_05.circom b/circom/tests/arrays/array_dimensions_05.circom new file mode 100644 index 000000000..39c04ea02 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_05.circom @@ -0,0 +1,43 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims(N) { + var M = N + 1; + signal output outp[M]; +} + +component main = ArrayDims(7); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[7]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.expr @"M@300" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_1]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: struct.member @outp : !array.type<@"M@300" x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @"M@300" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = poly.read_const @"M@300" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_8]][@outp] : <@ArrayDims::@ArrayDims<[@N]>>, !array.type<@"M@300" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_06.circom b/circom/tests/arrays/array_dimensions_06.circom new file mode 100644 index 000000000..432b4095c --- /dev/null +++ b/circom/tests/arrays/array_dimensions_06.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims(N) { + signal input arr[N + 1]; +} + +component main = ArrayDims(7); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[7]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.expr @"N_Add_1@279" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_1]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !array.type<@"N_Add_1@279" x !felt.type<"bn128">>) -> !struct.type<@ArrayDims::@ArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_1@279" : !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_4]] : !struct.type<@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[@N]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !array.type<@"N_Add_1@279" x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_1@279" : !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_07.circom b/circom/tests/arrays/array_dimensions_07.circom new file mode 100644 index 000000000..ab30fbd54 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_07.circom @@ -0,0 +1,46 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims(N) { + var arr[N]; +} + +component main = ArrayDims(7); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[7]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_3]], %[[VAL_4]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_6]] to %[[VAL_5]] step %[[VAL_7]] { +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_8]]] = %[[VAL_2]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims::@ArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_12]], %[[VAL_13]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_15]] to %[[VAL_14]] step %[[VAL_16]] { +// CHECK-NEXT: array.write %[[VAL_12]]{{\[}}%[[VAL_17]]] = %[[VAL_11]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_08.circom b/circom/tests/arrays/array_dimensions_08.circom new file mode 100644 index 000000000..06e11b9d6 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_08.circom @@ -0,0 +1,65 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims(N, M) { + var arr[N][M]; +} + +component main = ArrayDims(7, 2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[7, 2]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.param @M +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[@N, @M]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[@N, @M]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_4]], %[[VAL_5]] : <@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] { +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <@M x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.new : <@N,@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_10]], %[[VAL_11]] : <@N,@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_13]] to %[[VAL_12]] step %[[VAL_14]] { +// CHECK-NEXT: array.insert %[[VAL_10]]{{\[}}%[[VAL_15]]] = %[[VAL_4]] : <@N,@M x !felt.type<"bn128">>, <@M x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims::@ArrayDims<[@N, @M]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[@N, @M]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.new : <@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_20]], %[[VAL_21]] : <@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_25:[0-9a-zA-Z_\.]+]] = %[[VAL_23]] to %[[VAL_22]] step %[[VAL_24]] { +// CHECK-NEXT: array.write %[[VAL_20]]{{\[}}%[[VAL_25]]] = %[[VAL_19]] : <@M x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.new : <@N,@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_26]], %[[VAL_27]] : <@N,@M x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_31:[0-9a-zA-Z_\.]+]] = %[[VAL_29]] to %[[VAL_28]] step %[[VAL_30]] { +// CHECK-NEXT: array.insert %[[VAL_26]]{{\[}}%[[VAL_31]]] = %[[VAL_20]] : <@N,@M x !felt.type<"bn128">>, <@M x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_09.circom b/circom/tests/arrays/array_dimensions_09.circom new file mode 100644 index 000000000..81aa49194 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_09.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims(N, M) { + var D = N + M; + var arr[D]; +} + +component main = ArrayDims(7, 2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[7, 2]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.param @M +// CHECK-NEXT: poly.expr @"D@292" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_1]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[@N, @M]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[@N, @M]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @"D@292" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_6]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = array.new : <@"D@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_9]], %[[VAL_10]] : <@"D@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_14:[0-9a-zA-Z_\.]+]] = %[[VAL_12]] to %[[VAL_11]] step %[[VAL_13]] { +// CHECK-NEXT: array.write %[[VAL_9]]{{\[}}%[[VAL_14]]] = %[[VAL_8]] : <@"D@292" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@ArrayDims::@ArrayDims<[@N, @M]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[@N, @M]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @"D@292" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_18]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.new : <@"D@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_21]], %[[VAL_22]] : <@"D@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_26:[0-9a-zA-Z_\.]+]] = %[[VAL_24]] to %[[VAL_23]] step %[[VAL_25]] { +// CHECK-NEXT: array.write %[[VAL_21]]{{\[}}%[[VAL_26]]] = %[[VAL_20]] : <@"D@292" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_10.circom b/circom/tests/arrays/array_dimensions_10.circom new file mode 100644 index 000000000..ed2ae3534 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_10.circom @@ -0,0 +1,49 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayDims() { + var a[2] = [123, 675]; + signal output outp[a[1]]; +} + +component main = ArrayDims(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayDims::@ArrayDims<[]>>} { +// CHECK-NEXT: poly.template @ArrayDims { +// CHECK-NEXT: poly.expr @"a[1]@307" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 123 : <"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 675 : <"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_0]], %[[VAL_1]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_4]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @ArrayDims { +// CHECK-NEXT: struct.member @outp : !array.type<@"a[1]@307" x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims::@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @"a[1]@307" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_11]], %[[VAL_11]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 123 : <"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 675 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_13]], %[[VAL_14]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@ArrayDims::@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims::@ArrayDims<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @"a[1]@307" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@outp] : <@ArrayDims::@ArrayDims<[]>>, !array.type<@"a[1]@307" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_19]], %[[VAL_19]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 123 : <"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 675 : <"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_21]], %[[VAL_22]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_11.circom b/circom/tests/arrays/array_dimensions_11.circom new file mode 100644 index 000000000..4adf0e258 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_11.circom @@ -0,0 +1,109 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template EvilArrayDims(N) { + var x = 12; + var a[2] = [123, 675]; + x = 6; + signal input in[x]; + a[1] = N + x; + signal output out1[a[1]]; + signal output out2[N > 12 ? a[0] + a[1] : 0]; +} + +component main = EvilArrayDims(7); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EvilArrayDims::@EvilArrayDims<[7]>>} { +// CHECK-NEXT: poly.template @EvilArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.expr @"N_Greater_12?a[0]_Add_a[1]:0@411" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 12 : <"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 123 : <"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 675 : <"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_3]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_6]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_7]]{{\[}}%[[VAL_9]]] = %[[VAL_8]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_6]], %[[VAL_0]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_10]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_7]]{{\[}}%[[VAL_12]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_7]]{{\[}}%[[VAL_14]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.yield %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"a[1]@381" { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 123 : <"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 675 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_17]], %[[VAL_18]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_21]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_22]]{{\[}}%[[VAL_24]]] = %[[VAL_23]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_22]]{{\[}}%[[VAL_25]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"x@336" { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EvilArrayDims { +// CHECK-NEXT: struct.member @out1 : !array.type<@"a[1]@381" x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @out2 : !array.type<@"N_Greater_12?a[0]_Add_a[1]:0@411" x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !array.type<@"x@336" x !felt.type<"bn128">>) -> !struct.type<@EvilArrayDims::@EvilArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.new : <@EvilArrayDims::@EvilArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Greater_12?a[0]_Add_a[1]:0@411" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = poly.read_const @"a[1]@381" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = poly.read_const @"x@336" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 12 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_35]], %[[VAL_35]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 123 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 675 : <"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_37]], %[[VAL_38]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_30]], %[[VAL_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_42]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_39]]{{\[}}%[[VAL_43]]] = %[[VAL_41]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_29]] : !struct.type<@EvilArrayDims::@EvilArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !struct.type<@EvilArrayDims::@EvilArrayDims<[@N]>>, %[[VAL_45:[0-9a-zA-Z_\.]+]]: !array.type<@"x@336" x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Greater_12?a[0]_Add_a[1]:0@411" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = poly.read_const @"a[1]@381" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = poly.read_const @"x@336" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@out1] : <@EvilArrayDims::@EvilArrayDims<[@N]>>, !array.type<@"a[1]@381" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@out2] : <@EvilArrayDims::@EvilArrayDims<[@N]>>, !array.type<@"N_Greater_12?a[0]_Add_a[1]:0@411" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 12 : <"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_53]], %[[VAL_53]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 123 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 675 : <"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_55]], %[[VAL_56]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_46]], %[[VAL_58]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_60]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_57]]{{\[}}%[[VAL_61]]] = %[[VAL_59]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_dimensions_12.circom b/circom/tests/arrays/array_dimensions_12.circom new file mode 100644 index 000000000..aa15ad9ea --- /dev/null +++ b/circom/tests/arrays/array_dimensions_12.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* +// COM: LLZK does not (currently) allow `function.call` in `poly.expr` which is generated to define the dimension of `out2` here. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + var i = 0; + while (i < 6) { + return in; + assert(0 == 1); // Unreachable because of the early return above + i++; + } + return -1; +} + +template EvilArrayDims(N) { + var x = 12; + var a[2] = [123, 675]; + x = 6; + signal input in[x]; + a[1] = N + x; + signal output out1[a[1]]; + if (earlyReturnFn(N) > 0) { + a[1] = N + 2; + } else { + a[1] = N * 2; + } + signal output out2[N > 12 ? a[0] + a[1] : 0]; +} + +component main = EvilArrayDims(7); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/array_dimensions_13.circom b/circom/tests/arrays/array_dimensions_13.circom new file mode 100644 index 000000000..2c59efd93 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_13.circom @@ -0,0 +1,144 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template EvilArrayDims(N, M) { + var x; + if (N > 99) { + var D = N + M; + var arr[D]; + arr[D-1] = 99; + x = arr[D-1]; + } else { + var D = N * M + 1; + var arr[D]; + arr[D-1] = 77; + x = arr[D-1]; + } +} + +component main = EvilArrayDims(7, 2); + +// CHECK: #[[$ATTR_0:[0-9a-zA-Z_\.]+]] = affine_map<()[s0] -> (s0)> +// +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EvilArrayDims::@EvilArrayDims<[7, 2]>>} { +// CHECK-NEXT: poly.template @EvilArrayDims { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.param @M +// CHECK-NEXT: struct.def @EvilArrayDims { +// CHECK-NEXT: function.def @compute() -> !struct.type<@EvilArrayDims::@EvilArrayDims<[@N, @M]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@EvilArrayDims::@EvilArrayDims<[@N, @M]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 99 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_2]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_2]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.new{(){{\[}}%[[VAL_9]]]} : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_10]], %[[VAL_11]] : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_13]] to %[[VAL_12]] step %[[VAL_14]] { +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_15]]] = %[[VAL_8]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 99 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_7]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_19]]] = %[[VAL_16]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_7]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_22]]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_2]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_24]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.new{(){{\[}}%[[VAL_28]]]} : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_29]], %[[VAL_30]] : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_32]] to %[[VAL_31]] step %[[VAL_33]] { +// CHECK-NEXT: array.write %[[VAL_29]]{{\[}}%[[VAL_34]]] = %[[VAL_27]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 77 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_29]]{{\[}}%[[VAL_38]]] = %[[VAL_35]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_42]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@EvilArrayDims::@EvilArrayDims<[@N, @M]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !struct.type<@EvilArrayDims::@EvilArrayDims<[@N, @M]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 99 : <"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_45]], %[[VAL_47]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_48]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_44]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.new{(){{\[}}%[[VAL_52]]]} : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_53]], %[[VAL_54]] : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_58:[0-9a-zA-Z_\.]+]] = %[[VAL_56]] to %[[VAL_55]] step %[[VAL_57]] { +// CHECK-NEXT: array.write %[[VAL_53]]{{\[}}%[[VAL_58]]] = %[[VAL_51]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 99 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_50]], %[[VAL_60]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_53]]{{\[}}%[[VAL_62]]] = %[[VAL_59]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_50]], %[[VAL_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_64]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_53]]{{\[}}%[[VAL_65]]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_66]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_45]], %[[VAL_44]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_67]], %[[VAL_68]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_69]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.new{(){{\[}}%[[VAL_71]]]} : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_72]], %[[VAL_73]] : <#[[$ATTR_0]] x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_77:[0-9a-zA-Z_\.]+]] = %[[VAL_75]] to %[[VAL_74]] step %[[VAL_76]] { +// CHECK-NEXT: array.write %[[VAL_72]]{{\[}}%[[VAL_77]]] = %[[VAL_70]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 77 : <"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_69]], %[[VAL_79]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_72]]{{\[}}%[[VAL_81]]] = %[[VAL_78]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_69]], %[[VAL_82]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_72]]{{\[}}%[[VAL_84]]] : <#[[$ATTR_0]] x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_85]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_extract_01.circom b/circom/tests/arrays/array_extract_01.circom new file mode 100644 index 000000000..951500d17 --- /dev/null +++ b/circom/tests/arrays/array_extract_01.circom @@ -0,0 +1,78 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal input in[17][13]; + signal output out; + + var sum = 0; + for(var i = 0; i < 17; i++) { + var tmp[13]; + tmp = in[i]; + + sum += tmp[i % 13]; + } +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<17,13 x !felt.type<"bn128">>) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_2]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 17 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_5]], %[[VAL_7]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]], %[[VAL_11]] : <13 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_0]]{{\[}}%[[VAL_13]]] : <17,13 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_9]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_17]]] : <13 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_21]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !array.type<17,13 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_28:[0-9a-zA-Z_\.]+]] = %[[VAL_26]], %[[VAL_29:[0-9a-zA-Z_\.]+]] = %[[VAL_25]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 17 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_28]], %[[VAL_30]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_31]]) %[[VAL_28]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_32:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_33:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]], %[[VAL_34]] : <13 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_23]]{{\[}}%[[VAL_36]]] : <17,13 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_32]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_37]]{{\[}}%[[VAL_40]]] : <13 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_33]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_32]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_44]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_insert_in_function.circom b/circom/tests/arrays/array_insert_in_function.circom new file mode 100644 index 000000000..0a064101e --- /dev/null +++ b/circom/tests/arrays/array_insert_in_function.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function lookup() { + var arr[2][2]; + arr[0] = [0, 1]; + arr[1] = [2, 3]; + return arr[1][1]; +} + +template Caller() { + signal input in; + signal output out <== lookup(); +} + +component main = Caller(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller::@Caller<[]>>} { +// CHECK-NEXT: function.def @lookup() -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_0]], %[[VAL_0]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_2]]{{\[}}%[[VAL_3]]] = %[[VAL_1]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_2]]{{\[}}%[[VAL_4]]] = %[[VAL_1]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_5]], %[[VAL_6]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_2]]{{\[}}%[[VAL_9]]] = %[[VAL_7]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_10]], %[[VAL_11]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_2]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_16]], %[[VAL_18]]] : <2,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Caller { +// CHECK-NEXT: struct.def @Caller { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Caller::@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller::@Caller<[]>> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = function.call @lookup() : () -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_21]][@out] = %[[VAL_22]] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_21]] : !struct.type<@Caller::@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller::@Caller<[]>>, %[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_23]][@out] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = function.call @lookup() : () -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_25]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_insert_in_template.circom b/circom/tests/arrays/array_insert_in_template.circom new file mode 100644 index 000000000..3857af47b --- /dev/null +++ b/circom/tests/arrays/array_insert_in_template.circom @@ -0,0 +1,19 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function default_init() { + var out[3][2]; + return out; +} + +template Main() { + var a[3][2] = default_init(); +} + +component main = Main(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Main::@Main<[]>>} { diff --git a/circom/tests/arrays/array_size_mismatch_return.circom b/circom/tests/arrays/array_size_mismatch_return.circom new file mode 100644 index 000000000..64fe8bc34 --- /dev/null +++ b/circom/tests/arrays/array_size_mismatch_return.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +// The circom compiler only gives a warning for this: +// warning[T3001]: Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, the remaining positions are assigned to 0. + +function smaller() { + return [99, 98, 97, 96, 95]; +} + +template ImplicitExtension() { + signal output out[10]; + var temp[10] = smaller(); + out[0] <-- temp[0]; + out[4] <-- temp[4]; + out[5] <-- temp[5]; + out[9] <-- temp[9]; +} + +component main = ImplicitExtension(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/array_size_mismatch_store_0.circom b/circom/tests/arrays/array_size_mismatch_store_0.circom new file mode 100644 index 000000000..cbbd75977 --- /dev/null +++ b/circom/tests/arrays/array_size_mismatch_store_0.circom @@ -0,0 +1,106 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// The circom compiler only gives a warning for this: +// warning[T3001]: Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, +// the remaining positions are not modified. Initially all variables are initialized to 0. + +template ImplicitExtension() { + signal output out[10]; + var temp[10] = [99, 98, 97, 96, 95]; + out[0] <-- temp[0]; + out[4] <-- temp[4]; + out[5] <-- temp[5]; + out[9] <-- temp[9]; +} + +component main = ImplicitExtension(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ImplicitExtension::@ImplicitExtension<[]>>} { +// CHECK-NEXT: poly.template @ImplicitExtension { +// CHECK-NEXT: struct.def @ImplicitExtension { +// CHECK-NEXT: struct.member @out : !array.type<10 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ImplicitExtension::@ImplicitExtension<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ImplicitExtension::@ImplicitExtension<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 97 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 96 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 95 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant 10 : index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_10]], %[[VAL_11]] : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_12]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_10]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_11]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_14]] to %[[VAL_13]] step %[[VAL_15]] { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_16]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_16]]] = %[[VAL_17]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_22]]] = %[[VAL_20]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_24]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_27]]] = %[[VAL_25]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_29]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_32]]] = %[[VAL_30]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_34]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_37]]] = %[[VAL_35]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_1]] : <@ImplicitExtension::@ImplicitExtension<[]>>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ImplicitExtension::@ImplicitExtension<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !struct.type<@ImplicitExtension::@ImplicitExtension<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_38]][@out] : <@ImplicitExtension::@ImplicitExtension<[]>>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_40]], %[[VAL_40]], %[[VAL_40]], %[[VAL_40]], %[[VAL_40]], %[[VAL_40]], %[[VAL_40]], %[[VAL_40]], %[[VAL_40]], %[[VAL_40]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 97 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 96 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 95 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_42]], %[[VAL_43]], %[[VAL_44]], %[[VAL_45]], %[[VAL_46]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = arith.constant 10 : index +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_48]], %[[VAL_49]] : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_50]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_48]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_49]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_54:[0-9a-zA-Z_\.]+]] = %[[VAL_52]] to %[[VAL_51]] step %[[VAL_53]] { +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_54]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_41]]{{\[}}%[[VAL_54]]] = %[[VAL_55]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_size_mismatch_store_1.circom b/circom/tests/arrays/array_size_mismatch_store_1.circom new file mode 100644 index 000000000..8af872cc5 --- /dev/null +++ b/circom/tests/arrays/array_size_mismatch_store_1.circom @@ -0,0 +1,118 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArrayShenanigans() { + var x[2][2]; + var y[1][3] = [[9,8,7]]; + x = y; + signal output outp[2][2] <== x; +} + +component main = ArrayShenanigans(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayShenanigans::@ArrayShenanigans<[]>>} { +// CHECK-NEXT: poly.template @ArrayShenanigans { +// CHECK-NEXT: struct.def @ArrayShenanigans { +// CHECK-NEXT: struct.member @outp : !array.type<2,2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayShenanigans::@ArrayShenanigans<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayShenanigans::@ArrayShenanigans<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_4]]] = %[[VAL_2]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_5]]] = %[[VAL_2]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_6]], %[[VAL_6]], %[[VAL_6]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.new : <1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_8]]{{\[}}%[[VAL_9]]] = %[[VAL_7]] : <1,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_10]], %[[VAL_11]], %[[VAL_12]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new : <1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_14]]{{\[}}%[[VAL_15]]] = %[[VAL_13]] : <1,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_16]], %[[VAL_17]] : index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_18]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_16]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_17]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 3 : index +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_20]], %[[VAL_21]] : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_22]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_20]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_21]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_26:[0-9a-zA-Z_\.]+]] = %[[VAL_24]] to %[[VAL_19]] step %[[VAL_25]] { +// CHECK-NEXT: scf.for %[[VAL_27:[0-9a-zA-Z_\.]+]] = %[[VAL_24]] to %[[VAL_23]] step %[[VAL_25]] { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_26]], %[[VAL_27]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_26]], %[[VAL_27]]] = %[[VAL_28]] : <2,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@outp] = %[[VAL_3]] : <@ArrayShenanigans::@ArrayShenanigans<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayShenanigans::@ArrayShenanigans<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayShenanigans::@ArrayShenanigans<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_29]][@outp] : <@ArrayShenanigans::@ArrayShenanigans<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_31]], %[[VAL_31]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_33]]{{\[}}%[[VAL_34]]] = %[[VAL_32]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_33]]{{\[}}%[[VAL_35]]] = %[[VAL_32]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_36]], %[[VAL_36]], %[[VAL_36]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.new : <1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_38]]{{\[}}%[[VAL_39]]] = %[[VAL_37]] : <1,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_40]], %[[VAL_41]], %[[VAL_42]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.new : <1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_44]]{{\[}}%[[VAL_45]]] = %[[VAL_43]] : <1,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_46]], %[[VAL_47]] : index +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_48]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_46]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_47]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.constant 3 : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_50]], %[[VAL_51]] : index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_52]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_50]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_51]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_56:[0-9a-zA-Z_\.]+]] = %[[VAL_54]] to %[[VAL_49]] step %[[VAL_55]] { +// CHECK-NEXT: scf.for %[[VAL_57:[0-9a-zA-Z_\.]+]] = %[[VAL_54]] to %[[VAL_53]] step %[[VAL_55]] { +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_44]]{{\[}}%[[VAL_56]], %[[VAL_57]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_33]]{{\[}}%[[VAL_56]], %[[VAL_57]]] = %[[VAL_58]] : <2,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_33]] : !array.type<2,2 x !felt.type<"bn128">>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_size_mismatch_store_2.circom b/circom/tests/arrays/array_size_mismatch_store_2.circom new file mode 100644 index 000000000..9345e516a --- /dev/null +++ b/circom/tests/arrays/array_size_mismatch_store_2.circom @@ -0,0 +1,263 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// COM: Only works for llzk=concrete mode for now, pending larger template support + +pragma circom 2.0.0; + +function arr() { + var x[1][2][3] = [[[2,3,4], [5,6,7]]]; + var y[1][1][1] = x; + return y; +} + +template ArrayShenanigans() { + var z[2][2][2] = arr(); + signal output outp[2][2][2] <== z; +} + +component main = ArrayShenanigans(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArrayShenanigans_0::@ArrayShenanigans_0<[]>>} { +// CHECK-NEXT: function.def @arr_0() -> !array.type<1,1,1 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1,2,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_4]]] = %[[VAL_2]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_7]]] = %[[VAL_5]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_10]]] = %[[VAL_8]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_11]]{{\[}}%[[VAL_13]]] = %[[VAL_1]] : <2,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_11]]{{\[}}%[[VAL_15]]] = %[[VAL_1]] : <2,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_17]]] = %[[VAL_11]] : <1,2,3 x !felt.type<"bn128">>, <2,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_18]]{{\[}}%[[VAL_21]]] = %[[VAL_19]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_18]]{{\[}}%[[VAL_24]]] = %[[VAL_22]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_18]]{{\[}}%[[VAL_27]]] = %[[VAL_25]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_28]]{{\[}}%[[VAL_31]]] = %[[VAL_29]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_28]]{{\[}}%[[VAL_34]]] = %[[VAL_32]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_28]]{{\[}}%[[VAL_37]]] = %[[VAL_35]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_38]]{{\[}}%[[VAL_40]]] = %[[VAL_18]] : <2,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_38]]{{\[}}%[[VAL_42]]] = %[[VAL_28]] : <2,3 x !felt.type<"bn128">>, <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_44]]] = %[[VAL_38]] : <1,2,3 x !felt.type<"bn128">>, <2,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1,1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_48]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_46]]{{\[}}%[[VAL_49]]] = %[[VAL_47]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_50]]{{\[}}%[[VAL_52]]] = %[[VAL_46]] : <1,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_45]]{{\[}}%[[VAL_54]]] = %[[VAL_50]] : <1,1,1 x !felt.type<"bn128">>, <1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_55]], %[[VAL_56]] : index +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_57]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_55]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_56]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_59]], %[[VAL_60]] : index +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_61]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_59]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_60]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = arith.constant 3 : index +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_63]], %[[VAL_64]] : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_65]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_63]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_64]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_69:[0-9a-zA-Z_\.]+]] = %[[VAL_67]] to %[[VAL_58]] step %[[VAL_68]] { +// CHECK-NEXT: scf.for %[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_67]] to %[[VAL_62]] step %[[VAL_68]] { +// CHECK-NEXT: scf.for %[[VAL_71:[0-9a-zA-Z_\.]+]] = %[[VAL_67]] to %[[VAL_66]] step %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_69]], %[[VAL_70]], %[[VAL_71]]] : <1,2,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_45]]{{\[}}%[[VAL_69]], %[[VAL_70]], %[[VAL_71]]] = %[[VAL_72]] : <1,1,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_45]] : !array.type<1,1,1 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @ArrayShenanigans_0 { +// CHECK-NEXT: struct.def @ArrayShenanigans_0 { +// CHECK-NEXT: struct.member @outp : !array.type<2,2,2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayShenanigans_0::@ArrayShenanigans_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayShenanigans_0::@ArrayShenanigans_0<[]>> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_77]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_75]]{{\[}}%[[VAL_78]]] = %[[VAL_76]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_75]]{{\[}}%[[VAL_81]]] = %[[VAL_79]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_82]]{{\[}}%[[VAL_84]]] = %[[VAL_75]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_85]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_82]]{{\[}}%[[VAL_86]]] = %[[VAL_75]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_87]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_74]]{{\[}}%[[VAL_88]]] = %[[VAL_82]] : <2,2,2 x !felt.type<"bn128">>, <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_89]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_74]]{{\[}}%[[VAL_90]]] = %[[VAL_82]] : <2,2,2 x !felt.type<"bn128">>, <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = function.call @arr_0() : () -> !array.type<1,1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_92]], %[[VAL_93]] : index +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_94]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_92]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_93]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_96]], %[[VAL_97]] : index +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_98]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_96]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_97]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_100]], %[[VAL_101]] : index +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_102]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_100]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_101]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_106:[0-9a-zA-Z_\.]+]] = %[[VAL_104]] to %[[VAL_95]] step %[[VAL_105]] { +// CHECK-NEXT: scf.for %[[VAL_107:[0-9a-zA-Z_\.]+]] = %[[VAL_104]] to %[[VAL_99]] step %[[VAL_105]] { +// CHECK-NEXT: scf.for %[[VAL_108:[0-9a-zA-Z_\.]+]] = %[[VAL_104]] to %[[VAL_103]] step %[[VAL_105]] { +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_91]]{{\[}}%[[VAL_106]], %[[VAL_107]], %[[VAL_108]]] : <1,1,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_74]]{{\[}}%[[VAL_106]], %[[VAL_107]], %[[VAL_108]]] = %[[VAL_109]] : <2,2,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_73]][@outp] = %[[VAL_74]] : <@ArrayShenanigans_0::@ArrayShenanigans_0<[]>>, !array.type<2,2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_73]] : !struct.type<@ArrayShenanigans_0::@ArrayShenanigans_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_110:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayShenanigans_0::@ArrayShenanigans_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_110]][@outp] : <@ArrayShenanigans_0::@ArrayShenanigans_0<[]>>, !array.type<2,2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_115]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_113]]{{\[}}%[[VAL_116]]] = %[[VAL_114]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_118]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_113]]{{\[}}%[[VAL_119]]] = %[[VAL_117]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_121]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_120]]{{\[}}%[[VAL_122]]] = %[[VAL_113]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_123]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_120]]{{\[}}%[[VAL_124]]] = %[[VAL_113]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_125]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_112]]{{\[}}%[[VAL_126]]] = %[[VAL_120]] : <2,2,2 x !felt.type<"bn128">>, <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_127]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_112]]{{\[}}%[[VAL_128]]] = %[[VAL_120]] : <2,2,2 x !felt.type<"bn128">>, <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = function.call @arr_0() : () -> !array.type<1,1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_130]], %[[VAL_131]] : index +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_132]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_130]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_131]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_134]], %[[VAL_135]] : index +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_136]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_134]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_135]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_138]], %[[VAL_139]] : index +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_140]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_138]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_139]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_144:[0-9a-zA-Z_\.]+]] = %[[VAL_142]] to %[[VAL_133]] step %[[VAL_143]] { +// CHECK-NEXT: scf.for %[[VAL_145:[0-9a-zA-Z_\.]+]] = %[[VAL_142]] to %[[VAL_137]] step %[[VAL_143]] { +// CHECK-NEXT: scf.for %[[VAL_146:[0-9a-zA-Z_\.]+]] = %[[VAL_142]] to %[[VAL_141]] step %[[VAL_143]] { +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_129]]{{\[}}%[[VAL_144]], %[[VAL_145]], %[[VAL_146]]] : <1,1,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_112]]{{\[}}%[[VAL_144]], %[[VAL_145]], %[[VAL_146]]] = %[[VAL_147]] : <2,2,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_111]], %[[VAL_112]] : !array.type<2,2,2 x !felt.type<"bn128">>, !array.type<2,2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_size_mismatch_store_3.circom b/circom/tests/arrays/array_size_mismatch_store_3.circom new file mode 100644 index 000000000..f04592ede --- /dev/null +++ b/circom/tests/arrays/array_size_mismatch_store_3.circom @@ -0,0 +1,232 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// The circom compiler only gives a warning for this: +// warning[T3001]: Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, +// the remaining positions are not modified. Initially all variables are initialized to 0. + +template SmallToLarge() { + signal output out[10]; + var temp[10] = [99, 98, 97, 96, 95, 94, 93, 92 /*, 0, 0 */]; + temp = [89, 88, 87, 86, 85]; + assert(temp[0] == 89); + assert(temp[4] == 85); + assert(temp[5] == 94); + assert(temp[7] == 92); + assert(temp[8] == 0); + assert(temp[9] == 0); + for (var i = 0; i < 10; i++) { + out[i] <-- temp[i]; + } +} + +component main = SmallToLarge(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SmallToLarge::@SmallToLarge<[]>>} { +// CHECK-NEXT: poly.template @SmallToLarge { +// CHECK-NEXT: struct.def @SmallToLarge { +// CHECK-NEXT: struct.member @out : !array.type<10 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@SmallToLarge::@SmallToLarge<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@SmallToLarge::@SmallToLarge<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 97 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 96 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 95 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 94 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 93 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 92 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]] : <8 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 8 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 10 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_13]], %[[VAL_14]] : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_15]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_13]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_14]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_17]] to %[[VAL_16]] step %[[VAL_18]] { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_19]]] : <8 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_19]]] = %[[VAL_20]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 89 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 88 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 87 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 86 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 85 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_21]], %[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_25]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = arith.constant 10 : index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_27]], %[[VAL_28]] : index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_29]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_27]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_28]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_33:[0-9a-zA-Z_\.]+]] = %[[VAL_31]] to %[[VAL_30]] step %[[VAL_32]] { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_33]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_33]]] = %[[VAL_34]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_36]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 89 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_37]], %[[VAL_38]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_39]], "assertion failed" +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_41]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 85 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_42]], %[[VAL_43]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_44]], "assertion failed" +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_46]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 94 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_47]], %[[VAL_48]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_49]], "assertion failed" +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_51]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 92 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_52]], %[[VAL_53]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_54]], "assertion failed" +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_56]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_57]], %[[VAL_58]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_59]], "assertion failed" +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_60]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_61]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_62]], %[[VAL_63]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_64]], "assertion failed" +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_67:[0-9a-zA-Z_\.]+]] = %[[VAL_65]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_67]], %[[VAL_68]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_69]]) %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_70:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_71]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_73]]] = %[[VAL_72]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_70]], %[[VAL_74]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_75]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_1]] : <@SmallToLarge::@SmallToLarge<[]>>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@SmallToLarge::@SmallToLarge<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_76:[0-9a-zA-Z_\.]+]]: !struct.type<@SmallToLarge::@SmallToLarge<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_76]][@out] : <@SmallToLarge::@SmallToLarge<[]>>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_78]], %[[VAL_78]], %[[VAL_78]], %[[VAL_78]], %[[VAL_78]], %[[VAL_78]], %[[VAL_78]], %[[VAL_78]], %[[VAL_78]], %[[VAL_78]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 97 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 96 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 95 +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 94 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 93 +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 92 +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_80]], %[[VAL_81]], %[[VAL_82]], %[[VAL_83]], %[[VAL_84]], %[[VAL_85]], %[[VAL_86]], %[[VAL_87]] : <8 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = arith.constant 8 : index +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = arith.constant 10 : index +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_89]], %[[VAL_90]] : index +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_91]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_89]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_90]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_95:[0-9a-zA-Z_\.]+]] = %[[VAL_93]] to %[[VAL_92]] step %[[VAL_94]] { +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_88]]{{\[}}%[[VAL_95]]] : <8 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_79]]{{\[}}%[[VAL_95]]] = %[[VAL_96]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.const 89 +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 88 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.const 87 +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.const 86 +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.const 85 +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_97]], %[[VAL_98]], %[[VAL_99]], %[[VAL_100]], %[[VAL_101]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = arith.constant 10 : index +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_103]], %[[VAL_104]] : index +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_105]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_103]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_104]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_109:[0-9a-zA-Z_\.]+]] = %[[VAL_107]] to %[[VAL_106]] step %[[VAL_108]] { +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_102]]{{\[}}%[[VAL_109]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_79]]{{\[}}%[[VAL_109]]] = %[[VAL_110]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_111]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_79]]{{\[}}%[[VAL_112]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.const 89 +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_113]], %[[VAL_114]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_115]], "assertion failed" +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_116]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_79]]{{\[}}%[[VAL_117]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = felt.const 85 +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_118]], %[[VAL_119]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_120]], "assertion failed" +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_121]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_79]]{{\[}}%[[VAL_122]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.const 94 +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_123]], %[[VAL_124]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_125]], "assertion failed" +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_126]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_79]]{{\[}}%[[VAL_127]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = felt.const 92 +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_128]], %[[VAL_129]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_130]], "assertion failed" +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_131]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_79]]{{\[}}%[[VAL_132]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_133]], %[[VAL_134]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_135]], "assertion failed" +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_136]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_79]]{{\[}}%[[VAL_137]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_138]], %[[VAL_139]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_140]], "assertion failed" +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_143:[0-9a-zA-Z_\.]+]] = %[[VAL_141]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_143]], %[[VAL_144]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_145]]) %[[VAL_143]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_146:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_146]], %[[VAL_147]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_148]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_size_mismatch_store_4.circom b/circom/tests/arrays/array_size_mismatch_store_4.circom new file mode 100644 index 000000000..1cd9ac139 --- /dev/null +++ b/circom/tests/arrays/array_size_mismatch_store_4.circom @@ -0,0 +1,105 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// The circom compiler only gives a warning for this: +// warning[T3001]: Typing warning: Mismatched dimensions, assigning to an array an expression of greater length, +// the remaining positions of the expression are not assigned to the array. + +template LargeToSmall() { + signal output out[2]; + var temp[2] = [99, 98, 97, 96, 95]; + assert(temp[0] == 99); + assert(temp[1] == 98); + out <== temp; +} + +component main = LargeToSmall(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@LargeToSmall::@LargeToSmall<[]>>} { +// CHECK-NEXT: poly.template @LargeToSmall { +// CHECK-NEXT: struct.def @LargeToSmall { +// CHECK-NEXT: struct.member @out : !array.type<2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@LargeToSmall::@LargeToSmall<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@LargeToSmall::@LargeToSmall<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 97 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 96 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 95 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_4]], %[[VAL_5]], %[[VAL_6]], %[[VAL_7]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_9]], %[[VAL_10]] : index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_11]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_9]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_10]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_13]] to %[[VAL_12]] step %[[VAL_14]] { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_8]]{{\[}}%[[VAL_15]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_15]]] = %[[VAL_16]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_18]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_19]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_21]], "assertion failed" +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_23]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_24]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_26]], "assertion failed" +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_2]] : <@LargeToSmall::@LargeToSmall<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@LargeToSmall::@LargeToSmall<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !struct.type<@LargeToSmall::@LargeToSmall<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_27]][@out] : <@LargeToSmall::@LargeToSmall<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_29]], %[[VAL_29]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 97 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 96 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 95 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_31]], %[[VAL_32]], %[[VAL_33]], %[[VAL_34]], %[[VAL_35]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_37]], %[[VAL_38]] : index +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_39]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_37]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_38]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_41]] to %[[VAL_40]] step %[[VAL_42]] { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_36]]{{\[}}%[[VAL_43]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_30]]{{\[}}%[[VAL_43]]] = %[[VAL_44]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_30]]{{\[}}%[[VAL_46]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_47]], %[[VAL_48]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_49]], "assertion failed" +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_30]]{{\[}}%[[VAL_51]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_52]], %[[VAL_53]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_54]], "assertion failed" +// CHECK-NEXT: constrain.eq %[[VAL_28]], %[[VAL_30]] : !array.type<2 x !felt.type<"bn128">>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_write1_loop.circom b/circom/tests/arrays/array_write1_loop.circom new file mode 100644 index 000000000..7307e6895 --- /dev/null +++ b/circom/tests/arrays/array_write1_loop.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Array1() { + signal output out[5][1]; + + for (var i = 0; i < 5; i++) { + out[i][0] <== i; + } +} + +component main = Array1(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array1::@Array1<[]>>} { +// CHECK-NEXT: poly.template @Array1 { +// CHECK-NEXT: struct.def @Array1 { +// CHECK-NEXT: struct.member @out : !array.type<5,1 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Array1::@Array1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Array1::@Array1<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<5,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_4:[0-9a-zA-Z_\.]+]] = %[[VAL_2]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_4]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_6]]) %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_8]], %[[VAL_10]]] = %[[VAL_7]] : <5,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_1]] : <@Array1::@Array1<[]>>, !array.type<5,1 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Array1::@Array1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@Array1::@Array1<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@Array1::@Array1<[]>>, !array.type<5,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_15]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_17]], %[[VAL_18]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_19]]) %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_21]], %[[VAL_23]]] : <5,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_20]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_write2_loop.circom b/circom/tests/arrays/array_write2_loop.circom new file mode 100644 index 000000000..7da479298 --- /dev/null +++ b/circom/tests/arrays/array_write2_loop.circom @@ -0,0 +1,99 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Array1() { + signal output out[5][2]; + + for (var i = 0; i < 5; i++) { + out[i][0] <== i; + } + + for (var i = 0; i < 5; i++) { + out[i][1] <== i; + } +} + +component main = Array1(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array1::@Array1<[]>>} { +// CHECK-NEXT: poly.template @Array1 { +// CHECK-NEXT: struct.def @Array1 { +// CHECK-NEXT: struct.member @out : !array.type<5,2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Array1::@Array1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Array1::@Array1<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<5,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_4:[0-9a-zA-Z_\.]+]] = %[[VAL_2]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_4]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_6]]) %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_8]], %[[VAL_10]]] = %[[VAL_7]] : <5,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_13]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_15]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_17]]) %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_19]], %[[VAL_21]]] = %[[VAL_18]] : <5,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_18]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_1]] : <@Array1::@Array1<[]>>, !array.type<5,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Array1::@Array1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@Array1::@Array1<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@out] : <@Array1::@Array1<[]>>, !array.type<5,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_28:[0-9a-zA-Z_\.]+]] = %[[VAL_26]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_28]], %[[VAL_29]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_30]]) %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_25]]{{\[}}%[[VAL_32]], %[[VAL_34]]] : <5,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_35]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_31]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_40:[0-9a-zA-Z_\.]+]] = %[[VAL_38]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_40]], %[[VAL_41]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_42]]) %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_25]]{{\[}}%[[VAL_44]], %[[VAL_46]]] : <5,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_43]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_write3_loop.circom b/circom/tests/arrays/array_write3_loop.circom new file mode 100644 index 000000000..41269e412 --- /dev/null +++ b/circom/tests/arrays/array_write3_loop.circom @@ -0,0 +1,151 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Foo() { + signal input a; + signal output b; + + b <== a; +} + +template Array1() { + signal output out[5]; + component foo[5]; + + for (var i = 0; i < 5; i++) { + foo[i] = Foo(); + foo[i].a <== i; + out[i] <== foo[i].b; + } +} + +component main = Array1(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array1::@Array1<[]>>} { +// CHECK-NEXT: poly.template @Array1 { +// CHECK-NEXT: struct.def @Array1 { +// CHECK-NEXT: struct.member @out : !array.type<5 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @foo : !array.type<5 x !struct.type<@Foo::@Foo<[]>>> +// CHECK-NEXT: struct.member @foo$inputs : !array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@Array1::@Array1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Array1::@Array1<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <5 x !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <5 x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128">) -> (!array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_6]], %[[VAL_7]] : !array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_13]], @params = %[[VAL_12]] } : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_15]]] = %[[VAL_14]] : <5 x !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_16]]] : <5 x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_17]][@a] = %[[VAL_11]] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <5 x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_19]]] : <5 x !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@count] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_21]], %[[VAL_22]] : index +// CHECK-NEXT: pod.write %[[VAL_20]][@count] = %[[VAL_23]] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_23]], %[[VAL_24]] : index +// CHECK-NEXT: scf.if %[[VAL_25]] { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@params] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @Foo::@Foo::@compute(%[[VAL_27]]) : (!felt.type<"bn128">) -> !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: pod.write %[[VAL_20]][@comp] = %[[VAL_28]] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_29]]] = %[[VAL_20]] : <5 x !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_30]]] : <5 x !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@comp] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_32]][@b] : <@Foo::@Foo<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_34]]] = %[[VAL_33]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_35]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_36]] : !array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@foo$inputs] = %[[VAL_5]]#0 : <@Array1::@Array1<[]>>, !array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = array.new : <5 x !struct.type<@Foo::@Foo<[]>>> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_39]] to %[[VAL_38]] step %[[VAL_40]] { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_41]]] : <5 x !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@comp] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: array.write %[[VAL_37]]{{\[}}%[[VAL_41]]] = %[[VAL_43]] : <5 x !struct.type<@Foo::@Foo<[]>>>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@foo] = %[[VAL_37]] : <@Array1::@Array1<[]>>, !array.type<5 x !struct.type<@Foo::@Foo<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_1]] : <@Array1::@Array1<[]>>, !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Array1::@Array1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !struct.type<@Array1::@Array1<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@out] : <@Array1::@Array1<[]>>, !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@foo] : <@Array1::@Array1<[]>>, !array.type<5 x !struct.type<@Foo::@Foo<[]>>> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@foo$inputs] : <@Array1::@Array1<[]>>, !array.type<5 x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_50:[0-9a-zA-Z_\.]+]] = %[[VAL_48]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_50]], %[[VAL_51]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_52]]) %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_53:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_56]]] : <5 x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_57]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_58]], %[[VAL_53]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_46]]{{\[}}%[[VAL_59]]] : <5 x !struct.type<@Foo::@Foo<[]>>>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_60]][@b] : <@Foo::@Foo<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_45]]{{\[}}%[[VAL_62]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_63]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_53]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.constant 5 : index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_69:[0-9a-zA-Z_\.]+]] = %[[VAL_67]] to %[[VAL_66]] step %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_46]]{{\[}}%[[VAL_69]]] : <5 x !struct.type<@Foo::@Foo<[]>>>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_69]]] : <5 x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Foo::@Foo::@constrain(%[[VAL_70]], %[[VAL_72]]) : (!struct.type<@Foo::@Foo<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: struct.member @b : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_73:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Foo::@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[]>> +// CHECK-NEXT: struct.writem %[[VAL_74]][@b] = %[[VAL_73]] : <@Foo::@Foo<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_74]] : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_75:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[]>>, %[[VAL_76:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_75]][@b] : <@Foo::@Foo<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_77]], %[[VAL_76]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/array_write_in_function.circom b/circom/tests/arrays/array_write_in_function.circom new file mode 100644 index 000000000..69d80e66e --- /dev/null +++ b/circom/tests/arrays/array_write_in_function.circom @@ -0,0 +1,71 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function compute(start) { + var arr[2][1]; + arr[0][0] = start; + arr[1][0] = arr[0][0] + 1; + return arr[1][0]; +} + +template Caller() { + signal input in; + signal output out <== compute(in); +} + +component main = Caller(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller::@Caller<[]>>} { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]] : <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_4]]] = %[[VAL_2]] : <2,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_5]]] = %[[VAL_2]] : <2,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_7]], %[[VAL_9]]] = %[[VAL_0]] : <2,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_11]], %[[VAL_13]]] : <2,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]], %[[VAL_20]]] = %[[VAL_16]] : <2,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_22]], %[[VAL_24]]] : <2,1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Caller { +// CHECK-NEXT: struct.def @Caller { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Caller::@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller::@Caller<[]>> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @compute(%[[VAL_26]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_27]][@out] = %[[VAL_28]] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_27]] : !struct.type<@Caller::@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller::@Caller<[]>>, %[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_29]][@out] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = function.call @compute(%[[VAL_30]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_31]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/basic_array_00.circom b/circom/tests/arrays/basic_array_00.circom new file mode 100644 index 000000000..8df8e906b --- /dev/null +++ b/circom/tests/arrays/basic_array_00.circom @@ -0,0 +1,45 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Array00() { + signal input a[1]; + signal output b[1]; + + b[0] <== a[0]; +} + +component main = Array00(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array00::@Array00<[]>>} { +// CHECK-NEXT: poly.template @Array00 { +// CHECK-NEXT: struct.def @Array00 { +// CHECK-NEXT: struct.member @b : !array.type<1 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<1 x !felt.type<"bn128">>) -> !struct.type<@Array00::@Array00<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array00::@Array00<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_4]]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_7]]] = %[[VAL_5]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_2]] : <@Array00::@Array00<[]>>, !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array00::@Array00<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@Array00::@Array00<[]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !array.type<1 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_8]][@b] : <@Array00::@Array00<[]>>, !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_12]]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_15]]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_16]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/basic_array_01.circom b/circom/tests/arrays/basic_array_01.circom new file mode 100644 index 000000000..13101d58c --- /dev/null +++ b/circom/tests/arrays/basic_array_01.circom @@ -0,0 +1,193 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n) { + signal input a[n]; + signal output b[n]; + + for (var i = 0; i < n; i++) { + b[i] <== a[i]; + } +} + +template Array01(n) { + signal input a[n]; + signal output b[n]; + + component a_cmp = A(n); + + for (var i = 0; i < n; i++) { + a_cmp.a[i] <== a[i]; + } + + for (var i = 0; i < n; i++) { + a_cmp.b[i] ==> b[i]; + } +} + +component main = Array01(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Array01::@Array01<[5]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_9]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@A::@A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@b] : <@A::@A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_18]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_15]]{{\[}}%[[VAL_23]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_25]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Array01 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Array01 { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @a_cmp : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.member @a_cmp$inputs : !pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Array01::@Array01<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.new : <@Array01::@Array01<[@n]>> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_34]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_36]], @params = %[[VAL_35]] } : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_40:[0-9a-zA-Z_\.]+]] = %[[VAL_33]], %[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_38]]) : (!pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128">) -> (!pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_41]], %[[VAL_31]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_42]]) %[[VAL_40]], %[[VAL_41]] : !pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]>, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_45]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_43]][@a] : <[@a: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_47]]{{\[}}%[[VAL_48]]] = %[[VAL_46]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_43]][@a] = %[[VAL_47]] : <[@a: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_49]], %[[VAL_50]] : index +// CHECK-NEXT: pod.write %[[VAL_37]][@count] = %[[VAL_51]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_51]], %[[VAL_52]] : index +// CHECK-NEXT: scf.if %[[VAL_53]] { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_43]][@a] : <[@a: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_55]]) : (!array.type<@n x !felt.type<"bn128">>) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_37]][@comp] = %[[VAL_56]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_44]], %[[VAL_57]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_43]], %[[VAL_58]] : !pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_61:[0-9a-zA-Z_\.]+]] = %[[VAL_59]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_61]], %[[VAL_31]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_62]]) %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_63:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@b] : <@A::@A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_63]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_65]]{{\[}}%[[VAL_66]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_63]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_68]]] = %[[VAL_67]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_63]], %[[VAL_69]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_30]][@a_cmp$inputs] = %[[VAL_39]]#0 : <@Array01::@Array01<[@n]>>, !pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_30]][@a_cmp] = %[[VAL_71]] : <@Array01::@Array01<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_30]][@b] = %[[VAL_32]] : <@Array01::@Array01<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_30]] : !struct.type<@Array01::@Array01<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_72:[0-9a-zA-Z_\.]+]]: !struct.type<@Array01::@Array01<[@n]>>, %[[VAL_73:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_72]][@b] : <@Array01::@Array01<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_72]][@a_cmp] : <@Array01::@Array01<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_72]][@a_cmp$inputs] : <@Array01::@Array01<[@n]>>, !pod.type<[@a: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_78]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_83:[0-9a-zA-Z_\.]+]] = %[[VAL_81]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_83]], %[[VAL_74]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_84]]) %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_85:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_85]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_73]]{{\[}}%[[VAL_86]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_77]][@a] : <[@a: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_85]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_88]]{{\[}}%[[VAL_89]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_90]], %[[VAL_87]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_85]], %[[VAL_91]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_92]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_95:[0-9a-zA-Z_\.]+]] = %[[VAL_93]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_95]], %[[VAL_74]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_96]]) %[[VAL_95]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_97:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_76]][@b] : <@A::@A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_97]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_98]]{{\[}}%[[VAL_99]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_97]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_75]]{{\[}}%[[VAL_101]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_102]], %[[VAL_100]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_97]], %[[VAL_103]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_104]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_77]][@a] : <[@a: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_76]], %[[VAL_105]]) : (!struct.type<@A::@A<[@n]>>, !array.type<@n x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/basic_array_02.circom b/circom/tests/arrays/basic_array_02.circom new file mode 100644 index 000000000..0a203b2dc --- /dev/null +++ b/circom/tests/arrays/basic_array_02.circom @@ -0,0 +1,303 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input x; + signal output y; + + y <== x * x; +} + +template C() { + signal input x; + signal output y; + + y <== x + x; +} + +template A() { + signal input x; + signal output y; + component c = C(); + component bs[3]; + + bs[0] = B(); + bs[0].x <== x; + + bs[1] = B(); + bs[1].x <== x; + + bs[2] = B(); + bs[2].x <== x; + + c.x <== x; + + y <== bs[0].y + bs[1].y + bs[2].y + c.y; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @bs : !array.type<3 x !struct.type<@B::@B<[]>>> +// CHECK-NEXT: struct.member @bs$inputs : !array.type<3 x !pod.type<[@x: !felt.type<"bn128">]>> +// CHECK-NEXT: struct.member @c : !struct.type<@C::@C<[]>> +// CHECK-NEXT: struct.member @c$inputs : !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <3 x !pod.type<[@x: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_6]], @params = %[[VAL_5]] } : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_9]], @params = %[[VAL_8]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_12]]] = %[[VAL_10]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_14]]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_15]][@x] = %[[VAL_0]] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_17]]] = %[[VAL_15]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_19]]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_21]], %[[VAL_22]] : index +// CHECK-NEXT: pod.write %[[VAL_20]][@count] = %[[VAL_23]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_23]], %[[VAL_24]] : index +// CHECK-NEXT: scf.if %[[VAL_25]] { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_27]]) : (!felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_20]][@comp] = %[[VAL_28]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_30]]] = %[[VAL_20]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_32]], @params = %[[VAL_31]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_35]]] = %[[VAL_33]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_37]]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_38]][@x] = %[[VAL_0]] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_40]]] = %[[VAL_38]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_42]]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_43]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_44]], %[[VAL_45]] : index +// CHECK-NEXT: pod.write %[[VAL_43]][@count] = %[[VAL_46]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_46]], %[[VAL_47]] : index +// CHECK-NEXT: scf.if %[[VAL_48]] { +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_43]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_38]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_50]]) : (!felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_43]][@comp] = %[[VAL_51]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_52]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_53]]] = %[[VAL_43]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_55]], @params = %[[VAL_54]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_57]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_58]]] = %[[VAL_56]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_60]]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_61]][@x] = %[[VAL_0]] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_63]]] = %[[VAL_61]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_64]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_65]]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_66]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_67]], %[[VAL_68]] : index +// CHECK-NEXT: pod.write %[[VAL_66]][@count] = %[[VAL_69]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_69]], %[[VAL_70]] : index +// CHECK-NEXT: scf.if %[[VAL_71]] { +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_66]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_61]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_73]]) : (!felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_66]][@comp] = %[[VAL_74]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_75]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_76]]] = %[[VAL_66]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: pod.write %[[VAL_4]][@x] = %[[VAL_0]] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@count] : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_77]], %[[VAL_78]] : index +// CHECK-NEXT: pod.write %[[VAL_7]][@count] = %[[VAL_79]] : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_79]], %[[VAL_80]] : index +// CHECK-NEXT: scf.if %[[VAL_81]] { +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@params] : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = function.call @C::@C::@compute(%[[VAL_83]]) : (!felt.type<"bn128">) -> !struct.type<@C::@C<[]>> +// CHECK-NEXT: pod.write %[[VAL_7]][@comp] = %[[VAL_84]] : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_85]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_86]]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_87]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_88]][@y] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_90]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_91]]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_92]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_93]][@y] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_89]], %[[VAL_94]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_96]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_97]]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_98]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_99]][@y] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_95]], %[[VAL_100]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_102]][@y] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_101]], %[[VAL_103]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@y] = %[[VAL_104]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@bs$inputs] = %[[VAL_3]] : <@A::@A<[]>>, !array.type<3 x !pod.type<[@x: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = array.new : <3 x !struct.type<@B::@B<[]>>> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = arith.constant 3 : index +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_109:[0-9a-zA-Z_\.]+]] = %[[VAL_107]] to %[[VAL_106]] step %[[VAL_108]] { +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_109]]] : <3 x !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_110]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: array.write %[[VAL_105]]{{\[}}%[[VAL_109]]] = %[[VAL_111]] : <3 x !struct.type<@B::@B<[]>>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@bs] = %[[VAL_105]] : <@A::@A<[]>>, !array.type<3 x !struct.type<@B::@B<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@c$inputs] = %[[VAL_4]] : <@A::@A<[]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@c] = %[[VAL_112]] : <@A::@A<[]>>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_113:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_114:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_113]][@y] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_113]][@bs] : <@A::@A<[]>>, !array.type<3 x !struct.type<@B::@B<[]>>> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_113]][@bs$inputs] : <@A::@A<[]>>, !array.type<3 x !pod.type<[@x: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_113]][@c] : <@A::@A<[]>>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_113]][@c$inputs] : <@A::@A<[]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_124]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_117]]{{\[}}%[[VAL_125]]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_126]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_127]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_130]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_117]]{{\[}}%[[VAL_131]]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_132]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_133]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_136]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_117]]{{\[}}%[[VAL_137]]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_138]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_139]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_119]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_140]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_141]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_116]]{{\[}}%[[VAL_142]]] : <3 x !struct.type<@B::@B<[]>>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_143]][@y] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_145]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_116]]{{\[}}%[[VAL_146]]] : <3 x !struct.type<@B::@B<[]>>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_147]][@y] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_144]], %[[VAL_148]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_150]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_116]]{{\[}}%[[VAL_151]]] : <3 x !struct.type<@B::@B<[]>>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_152]][@y] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_149]], %[[VAL_153]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_155:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_118]][@y] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_154]], %[[VAL_155]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_115]], %[[VAL_156]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = arith.constant 3 : index +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_160:[0-9a-zA-Z_\.]+]] = %[[VAL_158]] to %[[VAL_157]] step %[[VAL_159]] { +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_116]]{{\[}}%[[VAL_160]]] : <3 x !struct.type<@B::@B<[]>>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_117]]{{\[}}%[[VAL_160]]] : <3 x !pod.type<[@x: !felt.type<"bn128">]>>, !pod.type<[@x: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_162]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_161]], %[[VAL_163]]) : (!struct.type<@B::@B<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_119]][@x] : <[@x: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @C::@C::@constrain(%[[VAL_118]], %[[VAL_164]]) : (!struct.type<@C::@C<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_165:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_165]], %[[VAL_165]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_166]][@y] = %[[VAL_167]] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_166]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_168:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_169:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_168]][@y] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_169]], %[[VAL_169]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_170]], %[[VAL_171]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_172:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_172]], %[[VAL_172]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_173]][@y] = %[[VAL_174]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_173]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_175:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_176:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_177:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_175]][@y] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_176]], %[[VAL_176]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_177]], %[[VAL_178]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/basic_array_03.circom b/circom/tests/arrays/basic_array_03.circom new file mode 100644 index 000000000..327c1a979 --- /dev/null +++ b/circom/tests/arrays/basic_array_03.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function return_array_A(l, m, n) { + var ret[5]; + for (var j = 0; j < n; j++) { + ret[j] = 999; + } + return ret; +} + +function return_array_B(n, m) { + var r[5] = return_array_A(12, m, n); + return r; +} + + +template ArrayReturnTemplate(n) { + var r[5] = return_array_B(n, 13); +} + +component main = ArrayReturnTemplate(4); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/basic_array_04.circom b/circom/tests/arrays/basic_array_04.circom new file mode 100644 index 000000000..d0db29444 --- /dev/null +++ b/circom/tests/arrays/basic_array_04.circom @@ -0,0 +1,20 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function copy(inp) { + var ret[3] = inp; + return ret; +} + +template ArrayCopyTemplate() { + var inp[3]; + var outp[3] = copy(inp); +} + +component main = ArrayCopyTemplate(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/dimension_handling.circom b/circom/tests/arrays/dimension_handling.circom new file mode 100644 index 000000000..b847f3d1f --- /dev/null +++ b/circom/tests/arrays/dimension_handling.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function myAdd(x1,y1,x2,y2) { + var res[2] = [0x1 + y1, x2 + y2]; + return res; +} + +function myFun() { + var out[1][1]; + var dbl[2] = [18446744073709551557,18446744073709551557]; + for (var i=0; i < 4; i++) { + dbl = myAdd(dbl[0], dbl[1], dbl[0], dbl[1]); + } + return out; +} + +template A() { + var table[1][1]; + table = myFun(); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { diff --git a/circom/tests/arrays/multi_dim_arrays.circom b/circom/tests/arrays/multi_dim_arrays.circom new file mode 100644 index 000000000..b27d9981d --- /dev/null +++ b/circom/tests/arrays/multi_dim_arrays.circom @@ -0,0 +1,90 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Arrays() { + var default_init[3][2][1]; + var inline_init[2][2] = [[1, 2], [3, 4]]; +} + +component main = Arrays(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Arrays::@Arrays<[]>>} { +// CHECK-NEXT: poly.template @Arrays { +// CHECK-NEXT: struct.def @Arrays { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Arrays::@Arrays<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Arrays::@Arrays<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]] : <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_4]]] = %[[VAL_2]] : <2,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_5]]] = %[[VAL_2]] : <2,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.new : <3,2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_6]]{{\[}}%[[VAL_7]]] = %[[VAL_3]] : <3,2,1 x !felt.type<"bn128">>, <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_6]]{{\[}}%[[VAL_8]]] = %[[VAL_3]] : <3,2,1 x !felt.type<"bn128">>, <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: array.insert %[[VAL_6]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <3,2,1 x !felt.type<"bn128">>, <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_10]], %[[VAL_10]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_12]]{{\[}}%[[VAL_13]]] = %[[VAL_11]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_12]]{{\[}}%[[VAL_14]]] = %[[VAL_11]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_15]], %[[VAL_16]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_18]], %[[VAL_19]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_21]]{{\[}}%[[VAL_22]]] = %[[VAL_17]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_21]]{{\[}}%[[VAL_23]]] = %[[VAL_20]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Arrays::@Arrays<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@Arrays::@Arrays<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_25]] : <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_27]]{{\[}}%[[VAL_28]]] = %[[VAL_26]] : <2,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_27]]{{\[}}%[[VAL_29]]] = %[[VAL_26]] : <2,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.new : <3,2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_30]]{{\[}}%[[VAL_31]]] = %[[VAL_27]] : <3,2,1 x !felt.type<"bn128">>, <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_30]]{{\[}}%[[VAL_32]]] = %[[VAL_27]] : <3,2,1 x !felt.type<"bn128">>, <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: array.insert %[[VAL_30]]{{\[}}%[[VAL_33]]] = %[[VAL_27]] : <3,2,1 x !felt.type<"bn128">>, <2,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_34]], %[[VAL_34]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_36]]{{\[}}%[[VAL_37]]] = %[[VAL_35]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_36]]{{\[}}%[[VAL_38]]] = %[[VAL_35]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_39]], %[[VAL_40]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_42]], %[[VAL_43]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_45]]{{\[}}%[[VAL_46]]] = %[[VAL_41]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_45]]{{\[}}%[[VAL_47]]] = %[[VAL_44]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/pass_and_constrain_arrays.circom b/circom/tests/arrays/pass_and_constrain_arrays.circom new file mode 100644 index 000000000..e45b10ab7 --- /dev/null +++ b/circom/tests/arrays/pass_and_constrain_arrays.circom @@ -0,0 +1,55 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function add(x1,y1,x2,y2) { + var a = 168700; + var d = 168696; + + var res[2]; + res[0] = (x1*y2 + y1*x2) / (1 + d*x1*x2*y1*y2); + res[1] = (y1*y2 - a*x1*x2) / (1 - d*x1*x2*y1*y2); + return res; +} + +function mul(base, k) { + var out[16][2]; + + var i; + var p[2]; + + var dbl[2] = base; + + for (i=0; i>} { diff --git a/circom/tests/arrays/unknown_index_constrained.circom b/circom/tests/arrays/unknown_index_constrained.circom new file mode 100644 index 000000000..5102857f5 --- /dev/null +++ b/circom/tests/arrays/unknown_index_constrained.circom @@ -0,0 +1,527 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +template LessThan(n) { + assert(n <= 252); + signal input in[2]; + signal output out; + + component n2b = Num2Bits(n+1); + + n2b.in <== in[0]+ (1< out; +} + +template ForUnknownIndex(n) { + signal input in; + signal output out; + + var arr2[10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + component get = GreaterEqThan(n); + get.in[0] <== in; + get.in[1] <== 0; + get.out === 1; + + component lt = LessThan(n); + lt.in[0] <== in; + lt.in[1] <== 10; + lt.out === 1; + + // non-quadractic constraint + // out <== arr[acc]; + out <-- arr2[in]; +} + +component main = ForUnknownIndex(252); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ForUnknownIndex::@ForUnknownIndex<[252]>>} { +// CHECK-NEXT: poly.template @ForUnknownIndex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @ForUnknownIndex { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @get : !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: struct.member @get$inputs : !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: struct.member @lt : !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: struct.member @lt$inputs : !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ForUnknownIndex::@ForUnknownIndex<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ForUnknownIndex::@ForUnknownIndex<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_5]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 7 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 8 : <"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 9 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]], %[[VAL_13]], %[[VAL_14]], %[[VAL_15]], %[[VAL_16]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_18]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_20]], @params = %[[VAL_19]] } : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_22]]{{\[}}%[[VAL_24]]] = %[[VAL_0]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_3]][@in] = %[[VAL_22]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@count] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_25]], %[[VAL_26]] : index +// CHECK-NEXT: pod.write %[[VAL_21]][@count] = %[[VAL_27]] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_27]], %[[VAL_28]] : index +// CHECK-NEXT: scf.if %[[VAL_29]] { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@params] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = function.call @GreaterEqThan::@GreaterEqThan::@compute(%[[VAL_31]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_21]][@comp] = %[[VAL_32]] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_34]]{{\[}}%[[VAL_36]]] = %[[VAL_33]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_3]][@in] = %[[VAL_34]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@count] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_37]], %[[VAL_38]] : index +// CHECK-NEXT: pod.write %[[VAL_21]][@count] = %[[VAL_39]] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_39]], %[[VAL_40]] : index +// CHECK-NEXT: scf.if %[[VAL_41]] { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@params] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = function.call @GreaterEqThan::@GreaterEqThan::@compute(%[[VAL_43]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_21]][@comp] = %[[VAL_44]] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_45]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_47]], @params = %[[VAL_46]] } : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_49]]{{\[}}%[[VAL_51]]] = %[[VAL_0]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_4]][@in] = %[[VAL_49]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@count] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_52]], %[[VAL_53]] : index +// CHECK-NEXT: pod.write %[[VAL_48]][@count] = %[[VAL_54]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_54]], %[[VAL_55]] : index +// CHECK-NEXT: scf.if %[[VAL_56]] { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@params] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = function.call @LessThan::@LessThan::@compute(%[[VAL_58]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_48]][@comp] = %[[VAL_59]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_61]]{{\[}}%[[VAL_63]]] = %[[VAL_60]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_4]][@in] = %[[VAL_61]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@count] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_64]], %[[VAL_65]] : index +// CHECK-NEXT: pod.write %[[VAL_48]][@count] = %[[VAL_66]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_66]], %[[VAL_67]] : index +// CHECK-NEXT: scf.if %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@params] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = function.call @LessThan::@LessThan::@compute(%[[VAL_70]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_48]][@comp] = %[[VAL_71]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_72]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_73]] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@get$inputs] = %[[VAL_3]] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@comp] : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@get] = %[[VAL_74]] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@lt$inputs] = %[[VAL_4]] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@comp] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@lt] = %[[VAL_75]] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ForUnknownIndex::@ForUnknownIndex<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_76:[0-9a-zA-Z_\.]+]]: !struct.type<@ForUnknownIndex::@ForUnknownIndex<[@n]>>, %[[VAL_77:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_76]][@out] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_76]][@get] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_76]][@get$inputs] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_76]][@lt] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_76]][@lt$inputs] : <@ForUnknownIndex::@ForUnknownIndex<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_84]], %[[VAL_84]], %[[VAL_84]], %[[VAL_84]], %[[VAL_84]], %[[VAL_84]], %[[VAL_84]], %[[VAL_84]], %[[VAL_84]], %[[VAL_84]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 7 : <"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.const 8 : <"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 9 : <"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_86]], %[[VAL_87]], %[[VAL_88]], %[[VAL_89]], %[[VAL_90]], %[[VAL_91]], %[[VAL_92]], %[[VAL_93]], %[[VAL_94]], %[[VAL_95]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_97]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_81]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_101]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_100]]{{\[}}%[[VAL_102]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_103]], %[[VAL_77]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_81]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_106]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_107]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_108]], %[[VAL_104]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_80]][@out] : <@GreaterEqThan::@GreaterEqThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_109]], %[[VAL_110]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_111]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_83]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_115]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_114]]{{\[}}%[[VAL_116]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_117]], %[[VAL_77]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_83]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_120]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_119]]{{\[}}%[[VAL_121]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_122]], %[[VAL_118]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_82]][@out] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_123]], %[[VAL_124]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_81]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @GreaterEqThan::@GreaterEqThan::@constrain(%[[VAL_80]], %[[VAL_125]]) : (!struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_83]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @LessThan::@LessThan::@constrain(%[[VAL_82]], %[[VAL_126]]) : (!struct.type<@LessThan::@LessThan<[@n]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @GreaterEqThan { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @GreaterEqThan { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @lt : !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: struct.member @lt$inputs : !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_127:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = struct.new : <@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_131]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_133]], @params = %[[VAL_132]] } : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_135]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_127]]{{\[}}%[[VAL_136]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_130]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_139]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_138]]{{\[}}%[[VAL_140]]] = %[[VAL_137]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_130]][@in] = %[[VAL_138]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_134]][@count] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_141]], %[[VAL_142]] : index +// CHECK-NEXT: pod.write %[[VAL_134]][@count] = %[[VAL_143]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_143]], %[[VAL_144]] : index +// CHECK-NEXT: scf.if %[[VAL_145]] { +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_134]][@params] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_130]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = function.call @LessThan::@LessThan::@compute(%[[VAL_147]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_134]][@comp] = %[[VAL_148]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_149]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_127]]{{\[}}%[[VAL_150]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_151]], %[[VAL_152]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_130]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_155:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_155]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_154]]{{\[}}%[[VAL_156]]] = %[[VAL_153]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_130]][@in] = %[[VAL_154]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_134]][@count] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_157]], %[[VAL_158]] : index +// CHECK-NEXT: pod.write %[[VAL_134]][@count] = %[[VAL_159]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_159]], %[[VAL_160]] : index +// CHECK-NEXT: scf.if %[[VAL_161]] { +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_134]][@params] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_130]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = function.call @LessThan::@LessThan::@compute(%[[VAL_163]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_134]][@comp] = %[[VAL_164]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_134]][@comp] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_165]][@out] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_128]][@out] = %[[VAL_166]] : <@GreaterEqThan::@GreaterEqThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_128]][@lt$inputs] = %[[VAL_130]] : <@GreaterEqThan::@GreaterEqThan<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_134]][@comp] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_128]][@lt] = %[[VAL_167]] : <@GreaterEqThan::@GreaterEqThan<[@n]>>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: function.return %[[VAL_128]] : !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_168:[0-9a-zA-Z_\.]+]]: !struct.type<@GreaterEqThan::@GreaterEqThan<[@n]>>, %[[VAL_169:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_168]][@out] : <@GreaterEqThan::@GreaterEqThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_172:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_168]][@lt] : <@GreaterEqThan::@GreaterEqThan<[@n]>>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_168]][@lt$inputs] : <@GreaterEqThan::@GreaterEqThan<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_174]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_176:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_177:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_177]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_179:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_169]]{{\[}}%[[VAL_178]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_180:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_173]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_181:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_182:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_181]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_183:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_180]]{{\[}}%[[VAL_182]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_183]], %[[VAL_179]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_184:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_185:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_184]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_186:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_169]]{{\[}}%[[VAL_185]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_187:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_188:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_186]], %[[VAL_187]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_189:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_173]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_190:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_191:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_190]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_192:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_189]]{{\[}}%[[VAL_191]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_192]], %[[VAL_188]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_193:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_172]][@out] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_171]], %[[VAL_193]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_194:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_173]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @LessThan::@LessThan::@constrain(%[[VAL_172]], %[[VAL_194]]) : (!struct.type<@LessThan::@LessThan<[@n]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @LessThan { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.expr @"n_Add_1@633" { +// CHECK-NEXT: %[[VAL_195:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_196:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_197:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_198:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_197]], %[[VAL_195]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_198]], "assertion failed" +// CHECK-NEXT: %[[VAL_199:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_197]], %[[VAL_196]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_199]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @LessThan { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n2b : !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: struct.member @n2b$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_200:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_201:[0-9a-zA-Z_\.]+]] = struct.new : <@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: %[[VAL_202:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_203:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_204:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_205:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_206:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_202]], %[[VAL_205]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_206]], "assertion failed" +// CHECK-NEXT: %[[VAL_207:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_208:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_207]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_209:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_210:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_209]], @params = %[[VAL_208]] } : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_211:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_210]] : (!pod.type<[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_212:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_213:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_212]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_214:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_200]]{{\[}}%[[VAL_213]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_215:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_216:[0-9a-zA-Z_\.]+]] = felt.shl %[[VAL_215]], %[[VAL_202]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_217:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_214]], %[[VAL_216]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_218:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_219:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_218]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_220:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_200]]{{\[}}%[[VAL_219]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_221:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_217]], %[[VAL_220]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_204]][@in] = %[[VAL_221]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_222:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_211]][@count] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_223:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_224:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_222]], %[[VAL_223]] : index +// CHECK-NEXT: pod.write %[[VAL_211]][@count] = %[[VAL_224]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_225:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_226:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_224]], %[[VAL_225]] : index +// CHECK-NEXT: scf.if %[[VAL_226]] { +// CHECK-NEXT: %[[VAL_227:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_211]][@params] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_228:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_204]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_229:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits::@Num2Bits::@compute(%[[VAL_228]]) : (!felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: pod.write %[[VAL_211]][@comp] = %[[VAL_229]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_230:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_231:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_211]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: %[[VAL_232:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_231]][@out] : <@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_233:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_202]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_234:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_232]]{{\[}}%[[VAL_233]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_235:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_230]], %[[VAL_234]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_201]][@out] = %[[VAL_235]] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_201]][@n2b$inputs] = %[[VAL_204]] : <@LessThan::@LessThan<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_236:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_211]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: struct.writem %[[VAL_201]][@n2b] = %[[VAL_236]] : <@LessThan::@LessThan<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: function.return %[[VAL_201]] : !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_237:[0-9a-zA-Z_\.]+]]: !struct.type<@LessThan::@LessThan<[@n]>>, %[[VAL_238:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_239:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_240:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_241:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_237]][@out] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_242:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_237]][@n2b] : <@LessThan::@LessThan<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: %[[VAL_243:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_237]][@n2b$inputs] : <@LessThan::@LessThan<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_244:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_245:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_239]], %[[VAL_244]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_245]], "assertion failed" +// CHECK-NEXT: %[[VAL_246:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_247:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_246]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_248:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_249:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_250:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_249]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_251:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_238]]{{\[}}%[[VAL_250]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_252:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_253:[0-9a-zA-Z_\.]+]] = felt.shl %[[VAL_252]], %[[VAL_239]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_254:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_251]], %[[VAL_253]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_255:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_256:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_255]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_257:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_238]]{{\[}}%[[VAL_256]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_258:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_254]], %[[VAL_257]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_259:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_243]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_259]], %[[VAL_258]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_260:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_261:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_242]][@out] : <@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_262:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_239]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_263:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_261]]{{\[}}%[[VAL_262]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_264:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_260]], %[[VAL_263]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_241]], %[[VAL_264]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_265:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_243]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Num2Bits::@Num2Bits::@constrain(%[[VAL_242]], %[[VAL_265]]) : (!struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"n_Add_1@633_0" { +// CHECK-NEXT: %[[VAL_266:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_267:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_268:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_269:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_268]], %[[VAL_266]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_269]], "assertion failed" +// CHECK-NEXT: %[[VAL_270:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_268]], %[[VAL_267]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_270]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_271:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_272:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_273:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_274:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_275:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_276:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_277:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_278:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_279:[0-9a-zA-Z_\.]+]] = %[[VAL_276]], %[[VAL_280:[0-9a-zA-Z_\.]+]] = %[[VAL_277]], %[[VAL_281:[0-9a-zA-Z_\.]+]] = %[[VAL_275]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_282:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_280]], %[[VAL_273]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_282]]) %[[VAL_279]], %[[VAL_280]], %[[VAL_281]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_283:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_284:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_285:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_286:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_271]], %[[VAL_284]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_287:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_288:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_286]], %[[VAL_287]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_289:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_284]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_274]]{{\[}}%[[VAL_289]]] = %[[VAL_288]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_290:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_284]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_291:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_274]]{{\[}}%[[VAL_290]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_292:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_291]], %[[VAL_276]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_293:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_285]], %[[VAL_292]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_294:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_283]], %[[VAL_283]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_295:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_296:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_284]], %[[VAL_295]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_294]], %[[VAL_296]], %[[VAL_293]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_272]][@out] = %[[VAL_274]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_272]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_297:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_298:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_299:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_300:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_297]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_301:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_302:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_303:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_304:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_305:[0-9a-zA-Z_\.]+]] = %[[VAL_302]], %[[VAL_306:[0-9a-zA-Z_\.]+]] = %[[VAL_303]], %[[VAL_307:[0-9a-zA-Z_\.]+]] = %[[VAL_301]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_308:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_306]], %[[VAL_299]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_308]]) %[[VAL_305]], %[[VAL_306]], %[[VAL_307]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_309:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_310:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_311:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_312:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_310]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_313:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_300]]{{\[}}%[[VAL_312]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_314:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_310]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_315:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_300]]{{\[}}%[[VAL_314]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_316:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_317:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_315]], %[[VAL_316]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_318:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_313]], %[[VAL_317]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_319:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_318]], %[[VAL_319]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_320:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_310]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_321:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_300]]{{\[}}%[[VAL_320]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_322:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_321]], %[[VAL_302]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_323:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_311]], %[[VAL_322]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_324:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_309]], %[[VAL_309]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_325:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_326:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_310]], %[[VAL_325]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_324]], %[[VAL_326]], %[[VAL_323]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_304]]#2, %[[VAL_298]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/unknown_index_load_store.circom b/circom/tests/arrays/unknown_index_load_store.circom new file mode 100644 index 000000000..5b2e6e61b --- /dev/null +++ b/circom/tests/arrays/unknown_index_load_store.circom @@ -0,0 +1,118 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template UnknownIndexLoadStore() { + signal input in; + signal output out[8]; + + var unused1[9] = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + var arr2[10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + var unused2[11] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + out[in] <-- arr2[in]; +} + +component main = UnknownIndexLoadStore(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownIndexLoadStore::@UnknownIndexLoadStore<[]>>} { +// CHECK-NEXT: poly.template @UnknownIndexLoadStore { +// CHECK-NEXT: struct.def @UnknownIndexLoadStore { +// CHECK-NEXT: struct.member @out : !array.type<8 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@UnknownIndexLoadStore::@UnknownIndexLoadStore<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownIndexLoadStore::@UnknownIndexLoadStore<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<8 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]], %[[VAL_13]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_17]], %[[VAL_18]], %[[VAL_19]], %[[VAL_20]], %[[VAL_21]], %[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_26]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]] : <11 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_30]], %[[VAL_31]], %[[VAL_32]], %[[VAL_33]], %[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]], %[[VAL_39]], %[[VAL_40]] : <11 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_27]]{{\[}}%[[VAL_42]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_44]]] = %[[VAL_43]] : <8 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_2]] : <@UnknownIndexLoadStore::@UnknownIndexLoadStore<[]>>, !array.type<8 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexLoadStore::@UnknownIndexLoadStore<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexLoadStore::@UnknownIndexLoadStore<[]>>, %[[VAL_46:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_45]][@out] : <@UnknownIndexLoadStore::@UnknownIndexLoadStore<[]>>, !array.type<8 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_48]], %[[VAL_48]], %[[VAL_48]], %[[VAL_48]], %[[VAL_48]], %[[VAL_48]], %[[VAL_48]], %[[VAL_48]], %[[VAL_48]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_50]], %[[VAL_51]], %[[VAL_52]], %[[VAL_53]], %[[VAL_54]], %[[VAL_55]], %[[VAL_56]], %[[VAL_57]], %[[VAL_58]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_60]], %[[VAL_60]], %[[VAL_60]], %[[VAL_60]], %[[VAL_60]], %[[VAL_60]], %[[VAL_60]], %[[VAL_60]], %[[VAL_60]], %[[VAL_60]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_62]], %[[VAL_63]], %[[VAL_64]], %[[VAL_65]], %[[VAL_66]], %[[VAL_67]], %[[VAL_68]], %[[VAL_69]], %[[VAL_70]], %[[VAL_71]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]], %[[VAL_73]] : <11 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_75]], %[[VAL_76]], %[[VAL_77]], %[[VAL_78]], %[[VAL_79]], %[[VAL_80]], %[[VAL_81]], %[[VAL_82]], %[[VAL_83]], %[[VAL_84]], %[[VAL_85]] : <11 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/unknown_index_overwrites_known_A.circom b/circom/tests/arrays/unknown_index_overwrites_known_A.circom new file mode 100644 index 000000000..4aabaa350 --- /dev/null +++ b/circom/tests/arrays/unknown_index_overwrites_known_A.circom @@ -0,0 +1,99 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template UnknownIndexOverwriteKnown() { + signal input in; + signal output out; + + var arr[10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + arr[in] = 99; + + // NOTE: The compiler will not allow constraints to be generated using 'arr' since an + // unknown index was used to assign a value within 'arr'. The following are not allowed: + // arr[in] === 99; + // arr[9] === 9; + // out <== arr[9]; + + // We can however use assert statements... + // Neither of the below can be known at compile time since index 'in' is overwritten. + // In fact, either assert could fail in certain runs, depends on the value of 'in'. + assert(arr[9] == 9); + assert(arr[9] == 99); +} + +component main = UnknownIndexOverwriteKnown(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>>} { +// CHECK-NEXT: poly.template @UnknownIndexOverwriteKnown { +// CHECK-NEXT: struct.def @UnknownIndexOverwriteKnown { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]], %[[VAL_13]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_14]]{{\[}}%[[VAL_16]]] = %[[VAL_15]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_18]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_19]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_21]], "assertion failed" +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_23]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_24]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_26]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>>, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_27]][@out] : <@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_30]], %[[VAL_30]], %[[VAL_30]], %[[VAL_30]], %[[VAL_30]], %[[VAL_30]], %[[VAL_30]], %[[VAL_30]], %[[VAL_30]], %[[VAL_30]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_32]], %[[VAL_33]], %[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]], %[[VAL_39]], %[[VAL_40]], %[[VAL_41]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_42]]{{\[}}%[[VAL_44]]] = %[[VAL_43]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_42]]{{\[}}%[[VAL_46]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_47]], %[[VAL_48]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_49]], "assertion failed" +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_42]]{{\[}}%[[VAL_51]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_52]], %[[VAL_53]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_54]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/unknown_index_overwrites_known_B.circom b/circom/tests/arrays/unknown_index_overwrites_known_B.circom new file mode 100644 index 000000000..d476aa540 --- /dev/null +++ b/circom/tests/arrays/unknown_index_overwrites_known_B.circom @@ -0,0 +1,212 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template UnknownIndexOverwriteKnown() { + signal input in; + signal output out; + + var scalar1 = 45; + var arr1[10] = [00, 01, 02, 03, 04, 05, 06, 07, 08, 09]; + var arr2[10] = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]; + var arr3[10] = [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]; + + arr2[in] = 99; + scalar1 = 46; + + assert(arr2[9] == 19); + assert(arr2[9] == 99); + + assert(arr1[9] == 09); + assert(arr1[4] == 04); + assert(arr3[0] == 20); + assert(arr3[7] == 27); + + assert(scalar1 == 46); +} + +component main = UnknownIndexOverwriteKnown(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>>} { +// CHECK-NEXT: poly.template @UnknownIndexOverwriteKnown { +// CHECK-NEXT: struct.def @UnknownIndexOverwriteKnown { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 45 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]], %[[VAL_13]], %[[VAL_14]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_16]], %[[VAL_16]], %[[VAL_16]], %[[VAL_16]], %[[VAL_16]], %[[VAL_16]], %[[VAL_16]], %[[VAL_16]], %[[VAL_16]], %[[VAL_16]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 17 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 18 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 19 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_18]], %[[VAL_19]], %[[VAL_20]], %[[VAL_21]], %[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_26]], %[[VAL_27]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 21 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 22 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 23 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 24 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 25 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 26 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 27 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 28 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 29 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_31]], %[[VAL_32]], %[[VAL_33]], %[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]], %[[VAL_39]], %[[VAL_40]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_28]]{{\[}}%[[VAL_43]]] = %[[VAL_42]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 46 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_46]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 19 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_47]], %[[VAL_48]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_49]], "assertion failed" +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_51]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_52]], %[[VAL_53]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_54]], "assertion failed" +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_15]]{{\[}}%[[VAL_56]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_57]], %[[VAL_58]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_59]], "assertion failed" +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_60]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_15]]{{\[}}%[[VAL_61]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_62]], %[[VAL_63]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_64]], "assertion failed" +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_41]]{{\[}}%[[VAL_66]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_67]], %[[VAL_68]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_69]], "assertion failed" +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_41]]{{\[}}%[[VAL_71]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 27 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_72]], %[[VAL_73]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_74]], "assertion failed" +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 46 +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_44]], %[[VAL_75]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_76]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_77:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>>, %[[VAL_78:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_77]][@out] : <@UnknownIndexOverwriteKnown::@UnknownIndexOverwriteKnown<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 45 +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_81]], %[[VAL_81]], %[[VAL_81]], %[[VAL_81]], %[[VAL_81]], %[[VAL_81]], %[[VAL_81]], %[[VAL_81]], %[[VAL_81]], %[[VAL_81]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_83]], %[[VAL_84]], %[[VAL_85]], %[[VAL_86]], %[[VAL_87]], %[[VAL_88]], %[[VAL_89]], %[[VAL_90]], %[[VAL_91]], %[[VAL_92]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_94]], %[[VAL_94]], %[[VAL_94]], %[[VAL_94]], %[[VAL_94]], %[[VAL_94]], %[[VAL_94]], %[[VAL_94]], %[[VAL_94]], %[[VAL_94]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.const 17 +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 18 +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.const 19 +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_96]], %[[VAL_97]], %[[VAL_98]], %[[VAL_99]], %[[VAL_100]], %[[VAL_101]], %[[VAL_102]], %[[VAL_103]], %[[VAL_104]], %[[VAL_105]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_107]], %[[VAL_107]], %[[VAL_107]], %[[VAL_107]], %[[VAL_107]], %[[VAL_107]], %[[VAL_107]], %[[VAL_107]], %[[VAL_107]], %[[VAL_107]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 21 +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.const 22 +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.const 23 +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = felt.const 24 +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.const 25 +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.const 26 +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 27 +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = felt.const 28 +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = felt.const 29 +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_109]], %[[VAL_110]], %[[VAL_111]], %[[VAL_112]], %[[VAL_113]], %[[VAL_114]], %[[VAL_115]], %[[VAL_116]], %[[VAL_117]], %[[VAL_118]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_78]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_106]]{{\[}}%[[VAL_121]]] = %[[VAL_120]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.const 46 +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_123]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_106]]{{\[}}%[[VAL_124]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = felt.const 19 +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_125]], %[[VAL_126]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_127]], "assertion failed" +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_128]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_106]]{{\[}}%[[VAL_129]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_130]], %[[VAL_131]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_132]], "assertion failed" +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_133]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_93]]{{\[}}%[[VAL_134]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_135]], %[[VAL_136]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_137]], "assertion failed" +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_93]]{{\[}}%[[VAL_139]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_140]], %[[VAL_141]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_142]], "assertion failed" +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_143]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_119]]{{\[}}%[[VAL_144]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_145]], %[[VAL_146]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_147]], "assertion failed" +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_148]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_119]]{{\[}}%[[VAL_149]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = felt.const 27 +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_150]], %[[VAL_151]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_152]], "assertion failed" +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = felt.const 46 +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_122]], %[[VAL_153]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_154]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/unknown_index_store.circom b/circom/tests/arrays/unknown_index_store.circom new file mode 100644 index 000000000..6f3b6d6ca --- /dev/null +++ b/circom/tests/arrays/unknown_index_store.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template UnknownIndexStore() { + signal input in; + signal output out[8]; + + out[in] <-- 999; +} + +component main = UnknownIndexStore(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownIndexStore::@UnknownIndexStore<[]>>} { +// CHECK-NEXT: poly.template @UnknownIndexStore { +// CHECK-NEXT: struct.def @UnknownIndexStore { +// CHECK-NEXT: struct.member @out : !array.type<8 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@UnknownIndexStore::@UnknownIndexStore<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownIndexStore::@UnknownIndexStore<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<8 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_4]]] = %[[VAL_3]] : <8 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_2]] : <@UnknownIndexStore::@UnknownIndexStore<[]>>, !array.type<8 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexStore::@UnknownIndexStore<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexStore::@UnknownIndexStore<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@out] : <@UnknownIndexStore::@UnknownIndexStore<[]>>, !array.type<8 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/arrays/unknown_index_unconstrained.circom b/circom/tests/arrays/unknown_index_unconstrained.circom new file mode 100644 index 000000000..a9df84e8b --- /dev/null +++ b/circom/tests/arrays/unknown_index_unconstrained.circom @@ -0,0 +1,63 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template UnknownIndex() { + signal input in; + signal output out; + + var arr2[10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + // non-quadractic constraint + // out <== arr[acc]; + out <-- arr2[in]; +} + +component main = UnknownIndex(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownIndex::@UnknownIndex<[]>>} { +// CHECK-NEXT: poly.template @UnknownIndex { +// CHECK-NEXT: struct.def @UnknownIndex { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@UnknownIndex::@UnknownIndex<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownIndex::@UnknownIndex<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]], %[[VAL_13]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_15]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_16]] : <@UnknownIndex::@UnknownIndex<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndex::@UnknownIndex<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndex::@UnknownIndex<[]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@out] : <@UnknownIndex::@UnknownIndex<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_20]], %[[VAL_20]], %[[VAL_20]], %[[VAL_20]], %[[VAL_20]], %[[VAL_20]], %[[VAL_20]], %[[VAL_20]], %[[VAL_20]], %[[VAL_20]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_26]], %[[VAL_27]], %[[VAL_28]], %[[VAL_29]], %[[VAL_30]], %[[VAL_31]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/basic_call_01.circom b/circom/tests/calls/basic_call_01.circom new file mode 100644 index 000000000..1c24e2d7b --- /dev/null +++ b/circom/tests/calls/basic_call_01.circom @@ -0,0 +1,115 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input a; + signal input b; + signal output x; + + x <== a * b; +} + +template Call1() { + signal input m; + signal input n; + signal output y; + + component a = B(); + a.a <== m; + a.b <== n; + // Call to B::compute should happen here + y <== a.x; +} + +component main = Call1(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Call1::@Call1<[]>>} { +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@x] = %[[VAL_3]] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@x] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Call1 { +// CHECK-NEXT: struct.def @Call1 { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Call1::@Call1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.new : <@Call1::@Call1<[]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_14]], @params = %[[VAL_13]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_12]][@a] = %[[VAL_9]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_16]], %[[VAL_17]] : index +// CHECK-NEXT: pod.write %[[VAL_15]][@count] = %[[VAL_18]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_18]], %[[VAL_19]] : index +// CHECK-NEXT: scf.if %[[VAL_20]] { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_12]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_12]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_22]], %[[VAL_23]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_15]][@comp] = %[[VAL_24]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: pod.write %[[VAL_12]][@b] = %[[VAL_10]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_25]], %[[VAL_26]] : index +// CHECK-NEXT: pod.write %[[VAL_15]][@count] = %[[VAL_27]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_27]], %[[VAL_28]] : index +// CHECK-NEXT: scf.if %[[VAL_29]] { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_12]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_12]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_31]], %[[VAL_32]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_15]][@comp] = %[[VAL_33]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_34]][@x] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_11]][@y] = %[[VAL_35]] : <@Call1::@Call1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_11]][@a$inputs] = %[[VAL_12]] : <@Call1::@Call1<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.writem %[[VAL_11]][@a] = %[[VAL_36]] : <@Call1::@Call1<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: function.return %[[VAL_11]] : !struct.type<@Call1::@Call1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_37:[0-9a-zA-Z_\.]+]]: !struct.type<@Call1::@Call1<[]>>, %[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_37]][@y] : <@Call1::@Call1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_37]][@a] : <@Call1::@Call1<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_37]][@a$inputs] : <@Call1::@Call1<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_45]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_46]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@x] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_40]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_41]], %[[VAL_48]], %[[VAL_49]]) : (!struct.type<@B::@B<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/basic_call_02.circom b/circom/tests/calls/basic_call_02.circom new file mode 100644 index 000000000..23cfd5477 --- /dev/null +++ b/circom/tests/calls/basic_call_02.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function nbits(a) { + var n = 1; + var r = 0; + while (n-1>} { +// CHECK-NEXT: function.def @nbits(%[[V_A:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N0:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_N1:[0-9a-zA-Z_\.]+]] = %[[V_N0]], %[[V_R1:[0-9a-zA-Z_\.]+]] = %[[V_R0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_N1]], %[[V_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_7]], %[[V_A]]) +// CHECK-NEXT: scf.condition(%[[V_8]]) %[[V_N1]], %[[V_R1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_N2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_R2]], %[[V_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_N3:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_N2]], %[[V_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_N3]], %[[V_R3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_3]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Call2 { +// CHECK-NEXT: struct.def @Call2 { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Call2::@Call2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.new : <@Call2::@Call2<[]>> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = function.call @nbits(%[[VAL_15]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_16]][@y] = %[[VAL_17]] : <@Call2::@Call2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_16]] : !struct.type<@Call2::@Call2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@Call2::@Call2<[]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@y] : <@Call2::@Call2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/call_arg_array.circom b/circom/tests/calls/call_arg_array.circom new file mode 100644 index 000000000..d8c777329 --- /dev/null +++ b/circom/tests/calls/call_arg_array.circom @@ -0,0 +1,21 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a) { + return a[0] + a[1] + a[2] + a[3]; +} + +template CallArgTest() { + signal input x[4]; + signal output y; + + y <-- sum(x); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_array_array.circom b/circom/tests/calls/call_arg_array_array.circom new file mode 100644 index 000000000..7e394f283 --- /dev/null +++ b/circom/tests/calls/call_arg_array_array.circom @@ -0,0 +1,22 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a, b) { + return a[0] + a[1] + a[2] + a[3] + b[0] + b[1]; +} + +template CallArgTest() { + signal input x[4]; + signal input y[2]; + signal output z; + + z <-- sum(x, y); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_array_array_scalar.circom b/circom/tests/calls/call_arg_array_array_scalar.circom new file mode 100644 index 000000000..2e77de3da --- /dev/null +++ b/circom/tests/calls/call_arg_array_array_scalar.circom @@ -0,0 +1,23 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a, b, c) { + return a[0][0][0] + a[1][0][0] + a[2][0][0] + a[3][0][0] + b[0][0] + b[1][2] + c; +} + +template CallArgTest() { + signal input x[4][2][3]; + signal input y[2][3]; + signal input z; + signal output q; + + q <-- sum(x, y, z); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_arraymulti.circom b/circom/tests/calls/call_arg_arraymulti.circom new file mode 100644 index 000000000..4824c515f --- /dev/null +++ b/circom/tests/calls/call_arg_arraymulti.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a) { + var agg = 0; + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 2; j++) { + agg += a[i][j]; + } + } + return agg; +} + +template CallArgTest() { + signal input x[3][2]; + signal output y; + + y <-- sum(x); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_arraymulti_arraymulti.circom b/circom/tests/calls/call_arg_arraymulti_arraymulti.circom new file mode 100644 index 000000000..191783071 --- /dev/null +++ b/circom/tests/calls/call_arg_arraymulti_arraymulti.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a, b) { + var agg = 0; + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 2; j++) { + agg += a[i][j] - b[i][j]; + } + } + return agg; +} + +template CallArgTest() { + signal input x[3][2]; + signal input y[3][2]; + signal output a; + + a <-- sum(x, y); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_arrays_complex_A.circom b/circom/tests/calls/call_arg_arrays_complex_A.circom new file mode 100644 index 000000000..7088860b8 --- /dev/null +++ b/circom/tests/calls/call_arg_arrays_complex_A.circom @@ -0,0 +1,23 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a, b) { + a[0] = b[1]; + return b; +} + +template CallArgTest() { + signal input x[2][3]; + signal input y[2][3]; + signal output z[2][3]; + + z <-- sum(x, y); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_arrays_complex_B.circom b/circom/tests/calls/call_arg_arrays_complex_B.circom new file mode 100644 index 000000000..a9dceeecb --- /dev/null +++ b/circom/tests/calls/call_arg_arrays_complex_B.circom @@ -0,0 +1,24 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a, b) { + b[0] = a[1]; + b[1] = a[0]; + return b; +} + +template CallArgTest() { + signal input x[2][3]; + signal output z[2][3]; + + var y[2][3]; + z <-- sum(x, y); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_arrays_complex_C.circom b/circom/tests/calls/call_arg_arrays_complex_C.circom new file mode 100644 index 000000000..96958c56b --- /dev/null +++ b/circom/tests/calls/call_arg_arrays_complex_C.circom @@ -0,0 +1,25 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a, b) { + b[0] = a[1]; + b[1] = a[0]; + return a[0][0]; +} + +template CallArgTest() { + signal input x[2][3]; + signal input y[2][3]; + signal output z[2][3]; + signal output q; + + q <-- sum(x, y); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_arrays_complex_D.circom b/circom/tests/calls/call_arg_arrays_complex_D.circom new file mode 100644 index 000000000..c46b8839b --- /dev/null +++ b/circom/tests/calls/call_arg_arrays_complex_D.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a, b, c, d) { + if (a < 7) { + return b; + } else if (a > 12) { + return c; + } else { + return d; + } +} + +template CallArgTest() { + signal input a; + signal input b[2][3]; + signal input c[2][3]; + signal input d[2][3]; + signal output z[2][3]; + + z <-- sum(a, b, c, d); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_scalar_array_scalar.circom b/circom/tests/calls/call_arg_scalar_array_scalar.circom new file mode 100644 index 000000000..cf8a2ea87 --- /dev/null +++ b/circom/tests/calls/call_arg_scalar_array_scalar.circom @@ -0,0 +1,21 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(x, a, y) { + return x + a[0] + a[1] + a[2] + a[3] + y; +} + +template CallArgTest() { + signal input x[4]; + signal output y; + + y <-- sum(77, x, 99); +} + +component main = CallArgTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_arg_scalar_multiple.circom b/circom/tests/calls/call_arg_scalar_multiple.circom new file mode 100644 index 000000000..9ee66a163 --- /dev/null +++ b/circom/tests/calls/call_arg_scalar_multiple.circom @@ -0,0 +1,43 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(a, b, c) { + var x = a; + var y = b; + var z = c; + return y; +} + +template A() { + _ = f(5, 10, 15); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_4]], %[[VAL_5]], %[[VAL_6]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_9]], %[[VAL_10]], %[[VAL_11]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/call_diff_array_per_call.circom b/circom/tests/calls/call_diff_array_per_call.circom new file mode 100644 index 000000000..6da2ec8ea --- /dev/null +++ b/circom/tests/calls/call_diff_array_per_call.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function f(a) { + return a[0][0]; +} + +// The two calls to `f()` have different argument types which requires +// two versions of `f()` to be generated. +template CallDiffTypeTest() { + signal input inA[10][5][5]; + signal input inB[10][5]; + signal output outA[5]; + signal output outB; + + outA <== f(inA); // f: (felt[10][5][5]) -> felt[5] + outB <== f(inB); // f: (felt[10][5]) -> felt +} + +component main = CallDiffTypeTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_diff_array_per_template.circom b/circom/tests/calls/call_diff_array_per_template.circom new file mode 100644 index 000000000..a21b3cefc --- /dev/null +++ b/circom/tests/calls/call_diff_array_per_template.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function f(a) { + return a[0][0]; +} + +// The two calls to `f()` have different argument types which requires +// two versions of `f()` to be generated. These functions must also be +// parametric in the size of the last array dimension. The flattened +// circuit thus has 4 versions of `f()`. +template CallDiffTypeTest(N) { + signal input inA[8][5][N]; + signal input inB[8][N]; + signal output outA[N]; + signal output outB; + + outA <== f(inA); // f: (felt[8][5][N]) -> felt[N] + outB <== f(inB); // f: (felt[8][N]) -> felt +} + +template Main() { + signal input inA[8][5][3]; + signal input inB[8][3]; + signal input inC[8][5][2]; + signal input inD[8][2]; + + signal output outA[3]; + signal output outB; + signal output outC[2]; + signal output outD; + + component crt1 = CallDiffTypeTest(3); + crt1.inA <== inA; + crt1.inB <== inB; + outA <== crt1.outA; + outB <== crt1.outB; + component crt2 = CallDiffTypeTest(2); + crt2.inA <== inC; + crt2.inB <== inD; + outC <== crt2.outA; + outD <== crt2.outB; +} + +component main = Main(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_diff_type_per_call.circom b/circom/tests/calls/call_diff_type_per_call.circom new file mode 100644 index 000000000..563294b67 --- /dev/null +++ b/circom/tests/calls/call_diff_type_per_call.circom @@ -0,0 +1,59 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +function f(a, b) { + return a + b; +} + +// In circom, `signal` and `var` are both field elements, so there's +// actually no difference in the function type between the two calls. +template CallDiffTypeTest() { + signal input in1; + signal input in2; + signal output out1; + signal output out2; + + out1 <== f(in1, in2); // f: (felt, felt) -> felt + + var a = 1; + var x = f(a, in2); // f: (felt, felt) -> felt + out2 <== x; +} + +component main = CallDiffTypeTest(); + +// +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CallDiffTypeTest::@CallDiffTypeTest<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @CallDiffTypeTest { +// CHECK-NEXT: struct.def @CallDiffTypeTest { +// CHECK-NEXT: struct.member @out1 : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @out2 : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@CallDiffTypeTest::@CallDiffTypeTest<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.new : <@CallDiffTypeTest::@CallDiffTypeTest<[]>> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_3]], %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_5]][@out1] = %[[VAL_6]] : <@CallDiffTypeTest::@CallDiffTypeTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_7]], %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_5]][@out2] = %[[VAL_8]] : <@CallDiffTypeTest::@CallDiffTypeTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_5]] : !struct.type<@CallDiffTypeTest::@CallDiffTypeTest<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@CallDiffTypeTest::@CallDiffTypeTest<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out1] : <@CallDiffTypeTest::@CallDiffTypeTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out2] : <@CallDiffTypeTest::@CallDiffTypeTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_10]], %[[VAL_11]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_15]], %[[VAL_11]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/call_in_function.circom b/circom/tests/calls/call_in_function.circom new file mode 100644 index 000000000..009823036 --- /dev/null +++ b/circom/tests/calls/call_in_function.circom @@ -0,0 +1,49 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +function passthrough(x) { + return x; +} + +function sum(a, b) { + return passthrough(a) + passthrough(b); +} + +template CallInFnTest() { + signal input x, y; + signal output z; + + z <-- sum(x,y); +} + +component main = CallInFnTest(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CallInFnTest::@CallInFnTest<[]>>} { +// CHECK-NEXT: function.def @passthrough(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @sum(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @passthrough(%[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = function.call @passthrough(%[[VAL_2]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @CallInFnTest { +// CHECK-NEXT: struct.def @CallInFnTest { +// CHECK-NEXT: struct.member @z : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@CallInFnTest::@CallInFnTest<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@CallInFnTest::@CallInFnTest<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @sum(%[[VAL_6]], %[[VAL_7]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_8]][@z] = %[[VAL_9]] : <@CallInFnTest::@CallInFnTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@CallInFnTest::@CallInFnTest<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@CallInFnTest::@CallInFnTest<[]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_10]][@z] : <@CallInFnTest::@CallInFnTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/call_ret_array.circom b/circom/tests/calls/call_ret_array.circom new file mode 100644 index 000000000..d4dcc451b --- /dev/null +++ b/circom/tests/calls/call_ret_array.circom @@ -0,0 +1,21 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a) { + return a; +} + +template CallRetTest() { + signal input x[4]; + signal output y[4]; + + y <-- sum(x); +} + +component main = CallRetTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_ret_arraymulti.circom b/circom/tests/calls/call_ret_arraymulti.circom new file mode 100644 index 000000000..5fc0c032d --- /dev/null +++ b/circom/tests/calls/call_ret_arraymulti.circom @@ -0,0 +1,22 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function sum(a) { + var b[2][4][3] = a; + return b; +} + +template CallRetTest() { + signal input x[2][4][3]; + signal output y[2][4][3]; + + y <-- sum(x); +} + +component main = CallRetTest(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_ret_arraymulti_nonzero.circom b/circom/tests/calls/call_ret_arraymulti_nonzero.circom new file mode 100644 index 000000000..8eca4e8ad --- /dev/null +++ b/circom/tests/calls/call_ret_arraymulti_nonzero.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.3; + +function long_gt(a, b) { + for (var i = 1; i >= 0; i--) { + if (a[i] > b[i]) { + return 1; + } + if (a[i] < b[i]) { + return 2; + } + } + return 0; +} + +function long_scalar_mult() { + return [[99, 88, 77], [66, 55, 44]]; +} + +template Test() { + var norm[2][3] = long_scalar_mult(); + var out[1] = [long_gt(norm[0], norm[1])]; +} + +component main = Test(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/call_ret_scalar.circom b/circom/tests/calls/call_ret_scalar.circom new file mode 100644 index 000000000..6ed05131b --- /dev/null +++ b/circom/tests/calls/call_ret_scalar.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +function sum(a) { + var b = a; + return b; +} + +template CallRetTest() { + signal input x; + signal output y; + + y <-- sum(x); +} + +component main = CallRetTest(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CallRetTest::@CallRetTest<[]>>} { +// CHECK-NEXT: function.def @sum(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @CallRetTest { +// CHECK-NEXT: struct.def @CallRetTest { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@CallRetTest::@CallRetTest<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@CallRetTest::@CallRetTest<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @sum(%[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@y] = %[[VAL_3]] : <@CallRetTest::@CallRetTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@CallRetTest::@CallRetTest<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@CallRetTest::@CallRetTest<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@y] : <@CallRetTest::@CallRetTest<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/call_without_arena_gotcha.circom b/circom/tests/calls/call_without_arena_gotcha.circom new file mode 100644 index 000000000..889b0a4b8 --- /dev/null +++ b/circom/tests/calls/call_without_arena_gotcha.circom @@ -0,0 +1,25 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +function overwrite(a, b) { + a[0] = b[1]; + a[1] = b[0]; + return a[0] + a[1]; +} + +template Gotcha() { + signal input x[2]; + signal input y[2]; + signal output p; + log(x[0]); + p <== overwrite(x, y); + log(x[0]); +} + +component main = Gotcha(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/circompairing_bigint.circom b/circom/tests/calls/circompairing_bigint.circom new file mode 100644 index 000000000..17808020a --- /dev/null +++ b/circom/tests/calls/circompairing_bigint.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// COM: Adapted from `bigint.circom` in https://github.com/yi-sun/circom-pairing +// XFAIL:.* + +pragma circom 2.0.0; + +function long_sub(k, a, b) { + var d[9]; + for (var i = 1; i < k; i++) { // 3 iterations b/c k=4 + if (a[i] >= b[i]) { + d[i] = a[i] - b[i]; + } + } + return d; +} + +function long_div(k, in) { + var out[9]; + for (var i = k; i >= 0; i--) { // 5 iterations b/c k=4 + var sub[9] = in; + var mul[9] = out; + for (var j = 0; j <= k; j++) { // 5 iterations b/c k=4 + sub[i + j] = mul[j]; + } + out = long_sub(k, out, sub); + } + return out; +} + +template BigMod() { + signal input in[9]; + signal output out[9]; + out <-- long_div(4, in); +} + +component main = BigMod(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/circompairing_bigint_func_A.circom b/circom/tests/calls/circompairing_bigint_func_A.circom new file mode 100644 index 000000000..7b243f6b0 --- /dev/null +++ b/circom/tests/calls/circompairing_bigint_func_A.circom @@ -0,0 +1,324 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// COM: Adapted from `bigint_func.circom` in https://github.com/yi-sun/circom-pairing +// XFAIL:.* + +pragma circom 2.0.0; + +function SplitFn(in, n, m) { + return [in % (1 << n), (in \ (1 << n)) % (1 << m)]; +} + +function SplitThreeFn(in, n, m, k) { + return [in % (1 << n), (in \ (1 << n)) % (1 << m), (in \ (1 << n + m)) % (1 << k)]; +} + +// 1 if true, 0 if false +function long_gt(n, k, a, b) { + for (var i = k - 1; i >= 0; i--) { + if (a[i] > b[i]) { + return 1; + } + if (a[i] < b[i]) { + return 0; + } + } + return 0; +} + +// n bits per register +// a has k registers +// b has k registers +// a >= b +function long_sub(n, k, a, b) { + var diff[100]; + var borrow[100]; + for (var i = 0; i < k; i++) { + if (i == 0) { + if (a[i] >= b[i]) { + diff[i] = a[i] - b[i]; + borrow[i] = 0; + } else { + diff[i] = a[i] - b[i] + (1 << n); + borrow[i] = 1; + } + } else { + if (a[i] >= b[i] + borrow[i - 1]) { + diff[i] = a[i] - b[i] - borrow[i - 1]; + borrow[i] = 0; + } else { + diff[i] = (1 << n) + a[i] - b[i] - borrow[i - 1]; + borrow[i] = 1; + } + } + } + return diff; +} + +// a is a n-bit scalar +// b has k registers +function long_scalar_mult(n, k, a, b) { + var out[100]; + for (var i = 0; i < 100; i++) { + out[i] = 0; + } + for (var i = 0; i < k; i++) { + var temp = out[i] + (a * b[i]); + out[i] = temp % (1 << n); + out[i + 1] = out[i + 1] + temp \ (1 << n); + } + return out; +} + + +// n bits per register +// a has k + m registers +// b has k registers +// out[0] has length m + 1 -- quotient +// out[1] has length k -- remainder +// implements algorithm of https://people.eecs.berkeley.edu/~fateman/282/F%20Wright%20notes/week4.pdf +// b[k-1] must be nonzero! +function long_div(n, k, a, b) { + var out[2][100]; + + var remainder[200]; + for (var i = 0; i < 2 * k; i++) { + remainder[i] = a[i]; + } + + var mult[200]; + var dividend[200]; + for (var i = k; i >= 0; i--) { + if (i == k) { + dividend[k] = 0; + for (var j = k - 1; j >= 0; j--) { + dividend[j] = remainder[j + k]; + } + } else { + for (var j = k; j >= 0; j--) { + dividend[j] = remainder[j + i]; + } + } + + out[0][i] = short_div(n, k, dividend, b); + + var mult_shift[100] = long_scalar_mult(n, k, out[0][i], b); + var subtrahend[200]; + for (var j = 0; j < 2 * k; j++) { + subtrahend[j] = 0; + } + for (var j = 0; j <= k; j++) { + if (i + j < 2 * k) { + subtrahend[i + j] = mult_shift[j]; + } + } + remainder = long_sub(n, 2 * k, remainder, subtrahend); + } + for (var i = 0; i < k; i++) { + out[1][i] = remainder[i]; + } + out[1][k] = 0; + + return out; +} + +// n bits per register +// a has k + 1 registers +// b has k registers +// assumes leading digit of b is at least 2 ** (n - 1) +// 0 <= a < (2**n) * b +function short_div_norm(n, k, a, b) { + var qhat = (a[k] * (1 << n) + a[k - 1]) \ b[k - 1]; + if (qhat > (1 << n) - 1) { + qhat = (1 << n) - 1; + } + + var mult[100] = long_scalar_mult(n, k, qhat, b); + if (long_gt(n, k + 1, mult, a) == 1) { + mult = long_sub(n, k + 1, mult, b); + if (long_gt(n, k + 1, mult, a) == 1) { + return qhat - 2; + } else { + return qhat - 1; + } + } else { + return qhat; + } +} + +// n bits per register +// a has k + 1 registers +// b has k registers +// assumes leading digit of b is non-zero +// 0 <= a < (2**n) * b +function short_div(n, k, a, b) { + var scale = (1 << n) \ (1 + b[k - 1]); + + // k + 2 registers now + var norm_a[200] = long_scalar_mult(n, k + 1, scale, a); + // k + 1 registers now + var norm_b[200] = long_scalar_mult(n, k, scale, b); + + var ret; + if (norm_b[k] != 0) { + ret = short_div_norm(n, k + 1, norm_a, norm_b); + } else { + ret = short_div_norm(n, k, norm_a, norm_b); + } + return ret; +} + +// n bits per register +// a and b both have k registers +// out[0] has length 2 * k +// adapted from BigMulShortLong and LongToShortNoEndCarry2 witness computation +function prod(n, k, a, b) { + // first compute the intermediate values. taken from BigMulShortLong + var prod_val[100]; // length is 2 * k - 1 + for (var i = 0; i < 2 * k - 1; i++) { + prod_val[i] = 0; + if (i < k) { + for (var a_idx = 0; a_idx <= i; a_idx++) { + prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; + } + } else { + for (var a_idx = i - k + 1; a_idx < k; a_idx++) { + prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; + } + } + } + + // now do a bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry2 + var out[100]; // length is 2 * k + + var split[100][3]; // first dimension has length 2 * k - 1 + for (var i = 0; i < 2 * k - 1; i++) { + split[i] = SplitThreeFn(prod_val[i], n, n, n); + } + + var carry[100]; // length is 2 * k - 1 + carry[0] = 0; + out[0] = split[0][0]; + if (2 * k - 1 > 1) { + var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n); + out[1] = sumAndCarry[0]; + carry[1] = sumAndCarry[1]; + } + if (2 * k - 1 > 2) { + for (var i = 2; i < 2 * k - 1; i++) { + var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n); + out[i] = sumAndCarry[0]; + carry[i] = sumAndCarry[1]; + } + out[2 * k - 1] = split[2*k-2][1] + split[2*k-3][2] + carry[2*k-2]; + } + return out; +} + +// n bits per register +// a has k registers +// p has k registers +// e has k registers +// k * n <= 500 +// p is a prime +// computes a^e mod p +function mod_exp(n, k, a, p, e) { + var eBits[500]; // length is k * n + for (var i = 0; i < k; i++) { + for (var j = 0; j < n; j++) { + eBits[j + n * i] = (e[i] >> j) & 1; + } + } + + var out[100]; // length is k + for (var i = 0; i < 100; i++) { + out[i] = 0; + } + out[0] = 1; + + // repeated squaring + for (var i = k * n - 1; i >= 0; i--) { + // multiply by a if bit is 0 + if (eBits[i] == 1) { + var temp[200]; // length 2 * k + temp = prod(n, k, out, a); // NOTE: assigns [100] to [200], remaining positions are not modified + var temp2[2][100]; + temp2 = long_div(n, k, temp, p); + out = temp2[1]; + } + + // square, unless we're at the end + if (i > 0) { + var temp[200]; // length 2 * k + temp = prod(n, k, out, out); // NOTE: assigns [100] to [200], remaining positions are not modified + var temp2[2][100]; + temp2 = long_div(n, k, temp, p); + out = temp2[1]; + } + + } + return out; +} + +// n bits per register +// a has k registers +// p has k registers +// k * n <= 500 +// p is a prime +// if a == 0 mod p, returns 0 +// else computes inv = a^(p-2) mod p +function mod_inv(n, k, a, p) { + var isZero = 1; + for (var i = 0; i < k; i++) { + if (a[i] != 0) { + isZero = 0; + } + } + if (isZero == 1) { + var ret[100]; + for (var i = 0; i < k; i++) { + ret[i] = 0; + } + return ret; + } + + var pCopy[100]; + for (var i = 0; i < 100; i++) { + if (i < k) { + pCopy[i] = p[i]; + } else { + pCopy[i] = 0; + } + } + + var two[100]; + for (var i = 0; i < 100; i++) { + two[i] = 0; + } + two[0] = 2; + + var pMinusTwo[100]; + pMinusTwo = long_sub(n, k, pCopy, two); // length k + var out[100]; + out = mod_exp(n, k, a, pCopy, pMinusTwo); + return out; +} + + +template BigModInv51() { + signal input in[3]; + signal output out[3]; + + var p[3] = [38685626227668133590597613, 38685626227668133590597631, 38685626227668133590597631]; + + // length k + var inv[100] = mod_inv(85, 3, in, p); + for (var i = 0; i < 3; i++) { + out[i] <-- inv[i]; + } +} + +component main = BigModInv51(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/circompairing_bigint_func_B.circom b/circom/tests/calls/circompairing_bigint_func_B.circom new file mode 100644 index 000000000..acda22475 --- /dev/null +++ b/circom/tests/calls/circompairing_bigint_func_B.circom @@ -0,0 +1,495 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// COM: Adapted from `bigint_func.circom` in https://github.com/yi-sun/circom-pairing +// XFAIL:.* + +pragma circom 2.0.3; + +function min(a, b) { + if(a < b) + return a; + return b; +} + +function max(a, b) { + if(a > b) + return a; + return b; +} + +function log_ceil(n) { + var n_temp = n; + for (var i = 0; i < 254; i++) { + if (n_temp == 0) { + return i; + } + n_temp = n_temp \ 2; + } + return 254; +} + +function SplitFn(in, n, m) { + return [in % (1 << n), (in \ (1 << n)) % (1 << m)]; +} + +function SplitThreeFn(in, n, m, k) { + return [in % (1 << n), (in \ (1 << n)) % (1 << m), (in \ (1 << n + m)) % (1 << k)]; +} + +// 1 if true, 0 if false +function long_gt(n, k, a, b) { + for (var i = k - 1; i >= 0; i--) { + if (a[i] > b[i]) { + return 1; + } + if (a[i] < b[i]) { + return 0; + } + } + return 0; +} + +function long_is_zero(k, a){ + for(var idx=0; idx k2 +// output has k1+1 registers +function long_add_unequal(n, k1, k2, a, b){ + var carry = 0; + var sum[50]; + for(var i=0; i= b +function long_sub(n, k, a, b) { + var diff[50]; + var borrow[50]; + for (var i = 0; i < k; i++) { + if (i == 0) { + if (a[i] >= b[i]) { + diff[i] = a[i] - b[i]; + borrow[i] = 0; + } else { + diff[i] = a[i] - b[i] + (1 << n); + borrow[i] = 1; + } + } else { + if (a[i] >= b[i] + borrow[i - 1]) { + diff[i] = a[i] - b[i] - borrow[i - 1]; + borrow[i] = 0; + } else { + diff[i] = (1 << n) + a[i] - b[i] - borrow[i - 1]; + borrow[i] = 1; + } + } + } + return diff; +} + +// a is a n-bit scalar +// b has k registers +function long_scalar_mult(n, k, a, b) { + var out[50]; + for (var i = 0; i < 50; i++) { + out[i] = 0; + } + for (var i = 0; i < k; i++) { + var temp = out[i] + (a * b[i]); + out[i] = temp % (1 << n); + out[i + 1] = out[i + 1] + temp \ (1 << n); + } + return out; +} + + +// n bits per register +// a has k + m registers +// b has k registers +// out[0] has length m + 1 -- quotient +// out[1] has length k -- remainder +// implements algorithm of https://people.eecs.berkeley.edu/~fateman/282/F%20Wright%20notes/week4.pdf +// b[k-1] must be nonzero! +function long_div2(n, k, m, a, b){ + var out[2][50]; + // assume k+m < 50 + var remainder[50]; + for (var i = 0; i < m + k; i++) { + remainder[i] = a[i]; + } + + var dividend[50]; + for (var i = m; i >= 0; i--) { + if (i == m) { + dividend[k] = 0; + for (var j = k - 1; j >= 0; j--) { + dividend[j] = remainder[j + m]; + } + } else { + for (var j = k; j >= 0; j--) { + dividend[j] = remainder[j + i]; + } + } + out[0][i] = short_div(n, k, dividend, b); + var mult_shift[50] = long_scalar_mult(n, k, out[0][i], b); + var subtrahend[50]; + for (var j = 0; j < m + k; j++) { + subtrahend[j] = 0; + } + for (var j = 0; j <= k; j++) { + if (i + j < m + k) { + subtrahend[i + j] = mult_shift[j]; + } + } + remainder = long_sub(n, m + k, remainder, subtrahend); + } + for (var i = 0; i < k; i++) { + out[1][i] = remainder[i]; + } + out[1][k] = 0; + return out; +} + +function long_div(n, k, a, b) { + return long_div2(n, k, k, a, b); +} + +// n bits per register +// a has k + 1 registers +// b has k registers +// assumes leading digit of b is at least 2^(n - 1) +// 0 <= a < (2**n) * b +function short_div_norm(n, k, a, b) { + var qhat = (a[k] * (1 << n) + a[k - 1]) \ b[k - 1]; + if (qhat > (1 << n) - 1) { + qhat = (1 << n) - 1; + } + + var mult[50] = long_scalar_mult(n, k, qhat, b); + if (long_gt(n, k + 1, mult, a) == 1) { + mult = long_sub(n, k + 1, mult, b); + if (long_gt(n, k + 1, mult, a) == 1) { + return qhat - 2; + } else { + return qhat - 1; + } + } else { + return qhat; + } +} + +// n bits per register +// a has k + 1 registers +// b has k registers +// assumes leading digit of b is non-zero +// 0 <= a < b * 2^n +function short_div(n, k, a, b) { + var scale = (1 << n) \ (1 + b[k - 1]); + // k + 2 registers now + var norm_a[50] = long_scalar_mult(n, k + 1, scale, a); + // k + 1 registers now + var norm_b[50] = long_scalar_mult(n, k, scale, b); + + var ret; + if (norm_b[k] != 0) { + ret = short_div_norm(n, k + 1, norm_a, norm_b); + } else { + ret = short_div_norm(n, k, norm_a, norm_b); + } + return ret; +} + +// a = a0 + a1 * X + ... + a[k-1] * X^{k-1} with X = 2^n +// a_i can be "negative" assume a_i in (-2^251, 2^251) +// output is the value of a with a_i all of the same sign +// out[50] = 0 if positive, 1 if negative +function signed_long_to_short(n, k, a){ + var out[51]; + var MAXL = 50; + var temp[51]; + + // is a positive? + for(var i=0; i= 0){ // circom automatically takes care of signs in comparator + out[i] = temp[i] % X; + temp[i+1] += temp[i] \ X; + }else{ + var borrow = (-temp[i] + X - 1 ) \ X; + out[i] = temp[i] + borrow * X; + temp[i+1] -= borrow; + } + } + if(temp[MAXL] >= 0){ + assert(temp[MAXL]==0); // otherwise not enough registers! + out[MAXL] = 0; + return out; + } + + // must be negative then, reset + for(var i=0; i 1) { + var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n); + out[1] = sumAndCarry[0]; + carry[1] = sumAndCarry[1]; + } + if (2 * k - 1 > 2) { + for (var i = 2; i < 2 * k - 1; i++) { + var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n); + out[i] = sumAndCarry[0]; + carry[i] = sumAndCarry[1]; + } + out[2 * k - 1] = split[2*k-2][1] + split[2*k-3][2] + carry[2*k-2]; + } + return out; +} + + +// n bits per register +// a and b both have l x k registers +// out has length 2l - 1 x 2k +// adapted from BigMultShortLong2D and LongToShortNoEndCarry2 witness computation +function prod2D(n, k, l, a, b) { + // first compute the intermediate values. taken from BigMulShortLong + var prod_val[20][50]; // length is 2l - 1 by 2k - 1 + for (var i = 0; i < 2 * k - 1; i++) { + for (var j = 0; j < 2 * l - 1; j ++) { + prod_val[j][i] = 0; + } + } + for (var i1 = 0; i1 < k; i1 ++) { + for (var i2 = 0; i2 < k; i2 ++) { + for (var j1 = 0; j1 < l; j1 ++) { + for (var j2 = 0; j2 < l; j2 ++) { + prod_val[j1+j2][i1+i2] = prod_val[j1+j2][i1+i2] + a[j1][i1] * b[j2][i2]; + } + } + } + } + + // now do a bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry2 + var out[20][50]; // length is 2 * l by 2 * k + + var split[20][50][3]; // second dimension has length 2 * k - 1 + for (var j = 0; j < 2 * l - 1; j ++) { + for (var i = 0; i < 2 * k - 1; i++) { + split[j][i] = SplitThreeFn(prod_val[j][i], n, n, n); + } + } + + var carry[20][50]; // length is 2l-1 x 2k + var sumAndCarry[20][2]; + for ( var j = 0; j < 2 * l - 1; j ++) { + carry[j][0] = 0; + out[j][0] = split[j][0][0]; + if (2 * k - 1 > 1) { + sumAndCarry[j] = SplitFn(split[j][0][1] + split[j][1][0], n, n); + out[j][1] = sumAndCarry[j][0]; + carry[j][1] = sumAndCarry[j][1]; + } + if (2 * k - 1 > 2) { + for (var i = 2; i < 2 * k - 1; i++) { + sumAndCarry[j] = SplitFn(split[j][i][0] + split[j][i-1][1] + split[j][i-2][2] + carry[j][i-1], n, n); + out[j][i] = sumAndCarry[j][0]; + carry[j][i] = sumAndCarry[j][1]; + } + out[j][2 * k - 1] = split[j][2*k-2][1] + split[j][2*k-3][2] + carry[j][2*k-2]; + } + } + + return out; +} + +// Put all modular arithmetic, aka F_p field stuff, at the end + +function long_add_mod(n, k, a, b, p) { + var sum[50] = long_add(n,k,a,b); + var temp[2][50] = long_div2(n,k,1,sum,p); + return temp[1]; +} + +function long_sub_mod(n, k, a, b, p) { + if(long_gt(n, k, b, a) == 1){ + return long_add(n, k, a, long_sub(n,k,p,b)); + }else{ + return long_sub(n, k, a, b); + } +} + +function prod_mod(n, k, a, b, p) { + var prod[50] = prod(n,k,a,b); + var temp[2][50] = long_div(n,k,prod,p); + return temp[1]; +} + +// n bits per register +// a has k registers +// p has k registers +// e has k registers +// k * n <= 500 +// p is a prime +// computes a^e mod p +function mod_exp(n, k, a, p, e) { + var eBits[500]; // length is k * n + var bitlength; + for (var i = 0; i < k; i++) { + for (var j = 0; j < n; j++) { + eBits[j + n * i] = (e[i] >> j) & 1; + if(eBits[j + n * i] == 1) + bitlength = j + n * i + 1; + } + } + + var out[50]; // length is k + for (var i = 0; i < 50; i++) { + out[i] = 0; + } + out[0] = 1; + + // repeated squaring + for (var i = bitlength-1; i >= 0; i--) { + // multiply by a if bit is 0 + if (eBits[i] == 1) { + var temp[50]; // length 2 * k + temp = prod(n, k, out, a); + var temp2[2][50]; + temp2 = long_div(n, k, temp, p); + out = temp2[1]; + } + + // square, unless we're at the end + if (i > 0) { + var temp[50]; // length 2 * k + temp = prod(n, k, out, out); + var temp2[2][50]; + temp2 = long_div(n, k, temp, p); + out = temp2[1]; + } + + } + return out; +} + +template Test() { + var out[50]; + var n = 55; + var k = 1; + var a[k] = [3]; + var p[k] = [35747322042231467]; + var e[k] = [7]; + + out = mod_exp(n, k, a, p, e); +} + +component main = Test(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/calls/recurse_known.circom b/circom/tests/calls/recurse_known.circom new file mode 100644 index 000000000..214551bdd --- /dev/null +++ b/circom/tests/calls/recurse_known.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function Recurse(i, n) { + if (n == 0) { + return i; + } + return Recurse(i, n-1); +} + +template FnAssign() { + signal input inp; + signal output outp; + + outp <== Recurse(inp, 20); +} + +component main = FnAssign(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@FnAssign::@FnAssign<[]>>} { +// CHECK-NEXT: function.def @Recurse(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_4]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_0]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_2]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_1]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = function.call @Recurse(%[[VAL_0]], %[[VAL_10]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @FnAssign { +// CHECK-NEXT: struct.def @FnAssign { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@FnAssign::@FnAssign<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.new : <@FnAssign::@FnAssign<[]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = function.call @Recurse(%[[VAL_12]], %[[VAL_14]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_13]][@outp] = %[[VAL_15]] : <@FnAssign::@FnAssign<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_13]] : !struct.type<@FnAssign::@FnAssign<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@FnAssign::@FnAssign<[]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@outp] : <@FnAssign::@FnAssign<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = function.call @Recurse(%[[VAL_17]], %[[VAL_19]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_18]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/recurse_unknown.circom b/circom/tests/calls/recurse_unknown.circom new file mode 100644 index 000000000..23fef158b --- /dev/null +++ b/circom/tests/calls/recurse_unknown.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function factorial(x) { + if (x == 0 || x == 1) return 1; + return x * factorial(x - 1); +} + +template Caller() { + signal input inp; + signal output outp; + // Cannot use `<==` here because it creates a non quadratic constraint + // due to `||` in the function. + outp <-- factorial(inp); +} + +component main = Caller(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller::@Caller<[]>>} { +// CHECK-NEXT: function.def @factorial(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_3]], %[[VAL_5]] : i1, i1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_6]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_9]], %[[VAL_8]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_1]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_7]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_7]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_0]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @factorial(%[[VAL_13]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Caller { +// CHECK-NEXT: struct.def @Caller { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Caller::@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller::@Caller<[]>> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @factorial(%[[VAL_16]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_17]][@outp] = %[[VAL_18]] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_17]] : !struct.type<@Caller::@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller::@Caller<[]>>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@outp] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/calls/return_intermediate.circom b/circom/tests/calls/return_intermediate.circom new file mode 100644 index 000000000..7e3fc8d9f --- /dev/null +++ b/circom/tests/calls/return_intermediate.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function Fn(a, b) { + return a * b; +} + +template Foo() { + signal input inp[2]; + signal output outp[1]; + + outp[0] <-- Fn(inp[0], inp[1]); + outp[0] === Fn(inp[0], inp[1]); +} + +component main = Foo(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Foo::@Foo<[]>>} { +// CHECK-NEXT: function.def @Fn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: struct.member @outp : !array.type<1 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@Foo::@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_7]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_10]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @Fn(%[[VAL_8]], %[[VAL_11]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_5]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_4]][@outp] = %[[VAL_5]] : <@Foo::@Foo<[]>>, !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_4]] : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@outp] : <@Foo::@Foo<[]>>, !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_19]]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_22]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_25]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = function.call @Fn(%[[VAL_23]], %[[VAL_26]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/01.circom b/circom/tests/circom_doc_examples/01.circom new file mode 100644 index 000000000..1c0642a5d --- /dev/null +++ b/circom/tests/circom_doc_examples/01.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Multiplier2() { + //Declaration of signals + signal input in1; + signal input in2; + signal output out; + out <== in1 * in2; +} + +component main {public [in1,in2]} = Multiplier2(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Multiplier2::@Multiplier2<[]>>} { +// CHECK-NEXT: poly.template @Multiplier2 { +// CHECK-NEXT: struct.def @Multiplier2 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) -> !struct.type<@Multiplier2::@Multiplier2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier2::@Multiplier2<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/02.circom b/circom/tests/circom_doc_examples/02.circom new file mode 100644 index 000000000..254338774 --- /dev/null +++ b/circom/tests/circom_doc_examples/02.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Multiplier2(){ + //Declaration of signals + signal input in1; + signal input in2; + signal output out <== in1 * in2; +} + +component main {public [in1,in2]} = Multiplier2(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Multiplier2::@Multiplier2<[]>>} { +// CHECK-NEXT: poly.template @Multiplier2 { +// CHECK-NEXT: struct.def @Multiplier2 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) -> !struct.type<@Multiplier2::@Multiplier2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier2::@Multiplier2<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/03-arr.circom b/circom/tests/circom_doc_examples/03-arr.circom new file mode 100644 index 000000000..54c18a032 --- /dev/null +++ b/circom/tests/circom_doc_examples/03-arr.circom @@ -0,0 +1,178 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(N, M){ + signal input in; + signal output out; + out <== in; +} +template C(N){ + signal output out; + out <== N; +} +template B(N){ + signal output out; + component a[1]; + if(N > 0){ + a[0] = A(N, 1); + } + else{ + a[0] = A(0, 1); + } + a[0].in <== 1; + a[0].out ==> out; +} + +component main = B(1); + +// CHECK: #[[$ATTR_0:[0-9a-zA-Z_\.]+]] = affine_map<(d0) -> (d0)> +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[1]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.param @M +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@N, @M]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@N, @M]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@A::@A<[@N, @M]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@N, @M]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@N, @M]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@A::@A<[@N, @M]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !array.type<1 x !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>> +// CHECK-NEXT: struct.member @a$inputs : !array.type<1 x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@B::@B<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@N]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.new : <1 x !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new : <1 x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_10]], %[[VAL_13]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_14]] { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_15]], @M = %[[VAL_16]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_18]], @params = %[[VAL_17]] } : <[@count: index, @comp: !struct.type<@A::@A<[@N, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_19]] : (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[@N, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_21]]] = %[[VAL_22]] : <1 x !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_23]], @M = %[[VAL_24]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_26]], @params = %[[VAL_25]] } : <[@count: index, @comp: !struct.type<@A::@A<[0, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_27]] : (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[0, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_29]]] = %[[VAL_30]] : <1 x !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_33]]] : <1 x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_34]][@in] = %[[VAL_31]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_12]]{{\[}}%[[VAL_36]]] = %[[VAL_34]] : <1 x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_38]]] : <1 x !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_39]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_40]], %[[VAL_41]] : index +// CHECK-NEXT: pod.write %[[VAL_39]][@count] = %[[VAL_42]] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_42]], %[[VAL_43]] : index +// CHECK-NEXT: scf.if %[[VAL_44]] { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_39]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_34]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@N] : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_46]]) {(%[[VAL_48]])} : (!felt.type<"bn128">) -> !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: pod.write %[[VAL_39]][@comp] = %[[VAL_49]] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_51]]] = %[[VAL_39]] : <1 x !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_52]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_53]]] : <1 x !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_54]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@out] : <@A::@A<[#[[$ATTR_0]], 1]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_9]][@out] = %[[VAL_56]] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_9]][@a$inputs] = %[[VAL_12]] : <@B::@B<[@N]>>, !array.type<1 x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.new : <1 x !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_61:[0-9a-zA-Z_\.]+]] = %[[VAL_59]] to %[[VAL_58]] step %[[VAL_60]] { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_61]]] : <1 x !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_62]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: array.write %[[VAL_57]]{{\[}}%[[VAL_61]]] = %[[VAL_63]] : <1 x !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_9]][@a] = %[[VAL_57]] : <@B::@B<[@N]>>, !array.type<1 x !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@B::@B<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_64:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@out] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@a] : <@B::@B<[@N]>>, !array.type<1 x !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@a$inputs] : <@B::@B<[@N]>>, !array.type<1 x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_65]], %[[VAL_69]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_70]] { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_71]], @M = %[[VAL_72]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@N, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_75]], @M = %[[VAL_76]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[0, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_68]]{{\[}}%[[VAL_81]]] : <1 x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_82]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_83]], %[[VAL_79]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_84]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_67]]{{\[}}%[[VAL_85]]] : <1 x !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_86]][@out] : <@A::@A<[#[[$ATTR_0]], 1]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_66]], %[[VAL_87]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_91:[0-9a-zA-Z_\.]+]] = %[[VAL_89]] to %[[VAL_88]] step %[[VAL_90]] { +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_67]]{{\[}}%[[VAL_91]]] : <1 x !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_68]]{{\[}}%[[VAL_91]]] : <1 x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_93]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_92]], %[[VAL_94]]) : (!struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/03-bis.circom b/circom/tests/circom_doc_examples/03-bis.circom new file mode 100644 index 000000000..81170bd92 --- /dev/null +++ b/circom/tests/circom_doc_examples/03-bis.circom @@ -0,0 +1,308 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(N){ + signal input in; + signal output out; + out <== in; +} +template C(N){ + signal output out; + out <== N; +} +template B(N){ + signal output out; + signal output branch; + component a; + + if(N > 0){ + a = A(N+2); + branch <== 1; + } + else{ + a = A(N+1); + branch <== 0; + } + a.in <== 1; + a.out ==> out; +} +template D() { + component b0 = B(0); + component b1 = B(1); + + signal output outs[2]; + signal output branches[2]; + outs[0] <== b0.out; + outs[1] <== b1.out; + branches[0] <== b0.branch; + branches[1] <== b1.branch; +} + +component main = D(); + +// CHECK: #[[$ATTR_0:[0-9a-zA-Z_\.]+]] = affine_map<(d0) -> (d0)> +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@D::@D<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@A::@A<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@N]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@out] : <@A::@A<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.expr @"N_Add_1@509" { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_9]], %[[VAL_7]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_10]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.yield %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"N_Add_2@461" { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_16]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_17]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.yield %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @branch : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@A::@A<[#[[$ATTR_0]]]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@B::@B<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@N]>> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_1@509" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_2@461" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_22]], %[[VAL_26]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_27]] -> (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_2@461_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_29]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_31]], @params = %[[VAL_30]] } : <[@count: index, @comp: !struct.type<@A::@A<[@"N_Add_2@461_0"]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_32]] : (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[@"N_Add_2@461_0"]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_21]][@branch] = %[[VAL_34]] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_33]], %[[VAL_34]] : !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_1@509_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_35]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_37]], @params = %[[VAL_36]] } : <[@count: index, @comp: !struct.type<@A::@A<[@"N_Add_1@509_0"]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_38]] : (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[@"N_Add_1@509_0"]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_21]][@branch] = %[[VAL_40]] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_39]], %[[VAL_40]] : !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: pod.write %[[VAL_25]][@in] = %[[VAL_41]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]]#0[@count] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_42]], %[[VAL_43]] : index +// CHECK-NEXT: pod.write %[[VAL_28]]#0[@count] = %[[VAL_44]] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_44]], %[[VAL_45]] : index +// CHECK-NEXT: scf.if %[[VAL_46]] { +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]]#0[@params] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !pod.type<[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_47]][@N] : <[@N: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_48]]) {(%[[VAL_50]])} : (!felt.type<"bn128">) -> !struct.type<@A::@A<[#[[$ATTR_0]]]>> +// CHECK-NEXT: pod.write %[[VAL_28]]#0[@comp] = %[[VAL_51]] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]]]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]]#0[@comp] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_52]][@out] : <@A::@A<[#[[$ATTR_0]]]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_21]][@out] = %[[VAL_53]] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_21]][@a$inputs] = %[[VAL_25]] : <@B::@B<[@N]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]]#0[@comp] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]]]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]]]>> +// CHECK-NEXT: struct.writem %[[VAL_21]][@a] = %[[VAL_54]] : <@B::@B<[@N]>>, !struct.type<@A::@A<[#[[$ATTR_0]]]>> +// CHECK-NEXT: function.return %[[VAL_21]] : !struct.type<@B::@B<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_55:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_1@509" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_2@461" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@out] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@branch] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@a] : <@B::@B<[@N]>>, !struct.type<@A::@A<[#[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@a$inputs] : <@B::@B<[@N]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_56]], %[[VAL_63]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_64]] { +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_2@461_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_65]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@"N_Add_2@461_0"]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_60]], %[[VAL_68]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Add_1@509_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_69]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@"N_Add_1@509_0"]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_60]], %[[VAL_72]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_62]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_74]], %[[VAL_73]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_61]][@out] : <@A::@A<[#[[$ATTR_0]]]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_59]], %[[VAL_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_62]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_61]], %[[VAL_76]]) : (!struct.type<@A::@A<[#[[$ATTR_0]]]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"N_Add_2@461_0" { +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_79]], %[[VAL_77]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_80]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_79]], %[[VAL_78]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_82]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.yield %[[VAL_81]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"N_Add_1@509_0" { +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_86]], %[[VAL_84]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_87]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_89]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_86]], %[[VAL_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_90]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.yield %[[VAL_88]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @D { +// CHECK-NEXT: struct.def @D { +// CHECK-NEXT: struct.member @outs : !array.type<2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @branches : !array.type<2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @b0 : !struct.type<@B::@B<[0]>> +// CHECK-NEXT: struct.member @b0$inputs : !pod.type<[]> +// CHECK-NEXT: struct.member @b1 : !struct.type<@B::@B<[1]>> +// CHECK-NEXT: struct.member @b1$inputs : !pod.type<[]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@D::@D<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = struct.new : <@D::@D<[]>> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_96]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute() : () -> !struct.type<@B::@B<[0]>> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_99]] } : <[@count: index, @comp: !struct.type<@B::@B<[0]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_101]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute() : () -> !struct.type<@B::@B<[1]>> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_104]] } : <[@count: index, @comp: !struct.type<@B::@B<[1]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_100]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[0]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[0]>> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_106]][@out] : <@B::@B<[0]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_108]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_93]]{{\[}}%[[VAL_109]]] = %[[VAL_107]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_105]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[1]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[1]>> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_110]][@out] : <@B::@B<[1]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_112]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_93]]{{\[}}%[[VAL_113]]] = %[[VAL_111]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_100]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[0]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[0]>> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_114]][@branch] : <@B::@B<[0]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_116]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_92]]{{\[}}%[[VAL_117]]] = %[[VAL_115]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_105]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[1]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[1]>> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_118]][@branch] : <@B::@B<[1]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_120]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_92]]{{\[}}%[[VAL_121]]] = %[[VAL_119]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_91]][@b0$inputs] = %[[VAL_94]] : <@D::@D<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_100]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[0]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[0]>> +// CHECK-NEXT: struct.writem %[[VAL_91]][@b0] = %[[VAL_122]] : <@D::@D<[]>>, !struct.type<@B::@B<[0]>> +// CHECK-NEXT: struct.writem %[[VAL_91]][@b1$inputs] = %[[VAL_95]] : <@D::@D<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_105]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[1]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[1]>> +// CHECK-NEXT: struct.writem %[[VAL_91]][@b1] = %[[VAL_123]] : <@D::@D<[]>>, !struct.type<@B::@B<[1]>> +// CHECK-NEXT: struct.writem %[[VAL_91]][@outs] = %[[VAL_93]] : <@D::@D<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_91]][@branches] = %[[VAL_92]] : <@D::@D<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_91]] : !struct.type<@D::@D<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_124:[0-9a-zA-Z_\.]+]]: !struct.type<@D::@D<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_124]][@outs] : <@D::@D<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_124]][@branches] : <@D::@D<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_124]][@b0] : <@D::@D<[]>>, !struct.type<@B::@B<[0]>> +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_124]][@b0$inputs] : <@D::@D<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_124]][@b1] : <@D::@D<[]>>, !struct.type<@B::@B<[1]>> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_124]][@b1$inputs] : <@D::@D<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_131]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[0]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_134]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[1]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_127]][@out] : <@B::@B<[0]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_125]]{{\[}}%[[VAL_139]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_140]], %[[VAL_137]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_129]][@out] : <@B::@B<[1]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_142]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_125]]{{\[}}%[[VAL_143]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_144]], %[[VAL_141]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_127]][@branch] : <@B::@B<[0]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_146]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_126]]{{\[}}%[[VAL_147]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_148]], %[[VAL_145]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_129]][@branch] : <@B::@B<[1]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_150]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_126]]{{\[}}%[[VAL_151]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_152]], %[[VAL_149]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_127]]) : (!struct.type<@B::@B<[0]>>) -> () +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_129]]) : (!struct.type<@B::@B<[1]>>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/03.circom b/circom/tests/circom_doc_examples/03.circom new file mode 100644 index 000000000..5d72b96ba --- /dev/null +++ b/circom/tests/circom_doc_examples/03.circom @@ -0,0 +1,137 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(N, M){ + signal input in; + signal output out; + out <== in; +} +template C(N){ + signal output out; + out <== N; +} +template B(N){ + signal output out; + component a; + if(N > 0){ + a = A(N, 1); + } + else{ + a = A(0, 1); + } + a.in <== 1; + a.out ==> out; +} + +component main = B(1); + +// CHECK: #[[$ATTR_0:[0-9a-zA-Z_\.]+]] = affine_map<(d0) -> (d0)> +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[1]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.param @M +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@N, @M]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@N, @M]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@A::@A<[@N, @M]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@N, @M]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@N, @M]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@A::@A<[@N, @M]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@B::@B<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@N]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_10]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_15]], @M = %[[VAL_16]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_18]], @params = %[[VAL_17]] } : <[@count: index, @comp: !struct.type<@A::@A<[@N, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_19]] : (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[@N, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: scf.yield %[[VAL_20]] : !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_21]], @M = %[[VAL_22]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_24]], @params = %[[VAL_23]] } : <[@count: index, @comp: !struct.type<@A::@A<[0, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_25]] : (!pod.type<[@count: index, @comp: !struct.type<@A::@A<[0, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: scf.yield %[[VAL_26]] : !pod.type<[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: pod.write %[[VAL_11]][@in] = %[[VAL_27]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_28]], %[[VAL_29]] : index +// CHECK-NEXT: pod.write %[[VAL_14]][@count] = %[[VAL_30]] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_30]], %[[VAL_31]] : index +// CHECK-NEXT: scf.if %[[VAL_32]] { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_33]][@N] : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_34]]) {(%[[VAL_36]])} : (!felt.type<"bn128">) -> !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: pod.write %[[VAL_14]][@comp] = %[[VAL_37]] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_38]][@out] : <@A::@A<[#[[$ATTR_0]], 1]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_9]][@out] = %[[VAL_39]] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_9]][@a$inputs] = %[[VAL_11]] : <@B::@B<[@N]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@a] = %[[VAL_40]] : <@B::@B<[@N]>>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@B::@B<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@out] : <@B::@B<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@a] : <@B::@B<[@N]>>, !struct.type<@A::@A<[#[[$ATTR_0]], 1]>> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@a$inputs] : <@B::@B<[@N]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_42]], %[[VAL_46]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_47]] { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_48]], @M = %[[VAL_49]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@N, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_52]], @M = %[[VAL_53]] } : <[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[0, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @M: !felt.type<"bn128">]>]> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_57]], %[[VAL_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@out] : <@A::@A<[#[[$ATTR_0]], 1]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_43]], %[[VAL_58]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_44]], %[[VAL_59]]) : (!struct.type<@A::@A<[#[[$ATTR_0]], 1]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/04.circom b/circom/tests/circom_doc_examples/04.circom new file mode 100644 index 000000000..962d5b4e2 --- /dev/null +++ b/circom/tests/circom_doc_examples/04.circom @@ -0,0 +1,45 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template AND() { + signal input a; + signal input b; + signal output out; + + out <== a*b; +} + +template MultiAND(n) { + signal input in[n]; + signal output out; + component and; + component ands[2]; + var i; + if (n==1) { + out <== in[0]; + } else if (n==2) { + and = AND(); + and.a <== in[0]; + and.b <== in[1]; + out <== and.out; + } else { + and = AND(); + var n1 = n\2; + var n2 = n-n\2; + ands[0] = MultiAND(n1); + ands[1] = MultiAND(n2); + for (i=0; i>} { +// CHECK-NEXT: poly.template @Example { +// CHECK-NEXT: struct.def @Example { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Example::@Example<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Example::@Example<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Example::@Example<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@Example::@Example<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @UsingExample { +// CHECK-NEXT: struct.def @UsingExample { +// CHECK-NEXT: struct.member @example : !struct.type<@Example::@Example<[]>> +// CHECK-NEXT: struct.member @example$inputs : !pod.type<[]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@UsingExample::@UsingExample<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@UsingExample::@UsingExample<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = function.call @Example::@Example::@compute() : () -> !struct.type<@Example::@Example<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_6]] } : <[@count: index, @comp: !struct.type<@Example::@Example<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: struct.writem %[[VAL_2]][@example$inputs] = %[[VAL_3]] : <@UsingExample::@UsingExample<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Example::@Example<[]>>, @params: !pod.type<[]>]>, !struct.type<@Example::@Example<[]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@example] = %[[VAL_8]] : <@UsingExample::@UsingExample<[]>>, !struct.type<@Example::@Example<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@UsingExample::@UsingExample<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@UsingExample::@UsingExample<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@example] : <@UsingExample::@UsingExample<[]>>, !struct.type<@Example::@Example<[]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@example$inputs] : <@UsingExample::@UsingExample<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Example::@Example<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: function.call @Example::@Example::@constrain(%[[VAL_10]]) : (!struct.type<@Example::@Example<[]>>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/06.circom b/circom/tests/circom_doc_examples/06.circom new file mode 100644 index 000000000..77cf643bd --- /dev/null +++ b/circom/tests/circom_doc_examples/06.circom @@ -0,0 +1,83 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// This function calculates the number of extra bits +// in the output to do the full sum. +function nbits(a) { + var n = 1; + var r = 0; + while (n-1= 0) { return 1; } + else { return 0; } +} + +template Caller() { + signal input in; + signal output out; + + out <== example(nbits(in)); +} + +component main = Caller(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller::@Caller<[]>>} { +// CHECK-NEXT: function.def @example(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_2:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[V_0]], %[[V_1]]) +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = scf.if %[[V_2]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[V_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[V_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @nbits(%[[V_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N0:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_N1:[0-9a-zA-Z_\.]+]] = %[[V_N0]], %[[V_R1:[0-9a-zA-Z_\.]+]] = %[[V_R0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_N1]], %[[V_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_13]], %[[V_6]]) +// CHECK-NEXT: scf.condition(%[[V_14]]) %[[V_N1]], %[[V_R1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_N2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_R2]], %[[V_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_N3:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_N2]], %[[V_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_N3]], %[[V_R3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_9]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Caller { +// CHECK-NEXT: struct.def @Caller { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Caller::@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller::@Caller<[]>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = function.call @nbits(%[[VAL_21]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = function.call @example(%[[VAL_23]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_22]][@out] = %[[VAL_24]] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_22]] : !struct.type<@Caller::@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller::@Caller<[]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@out] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @nbits(%[[VAL_26]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = function.call @example(%[[VAL_28]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/07.circom b/circom/tests/circom_doc_examples/07.circom new file mode 100644 index 000000000..8791d590b --- /dev/null +++ b/circom/tests/circom_doc_examples/07.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(){ + signal input in1; + signal input in2; + signal output out; + out <== in1 * in2; +} + +component main {public [in1]}= A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/08.circom b/circom/tests/circom_doc_examples/08.circom new file mode 100644 index 000000000..8506c5533 --- /dev/null +++ b/circom/tests/circom_doc_examples/08.circom @@ -0,0 +1,58 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template IsZero() { + signal input in; + signal output out; + signal inv; + inv <-- in!=0 ? 1/in : 0; + out <== -in*inv +1; + in*out === 0; +} + +component main {public [in]}= IsZero(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@IsZero::@IsZero<[]>>} { +// CHECK-NEXT: poly.template @IsZero { +// CHECK-NEXT: struct.def @IsZero { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @inv : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) -> !struct.type<@IsZero::@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_5]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@inv] = %[[VAL_4]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_11]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero::@IsZero<[]>>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_12]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_12]][@inv] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_16]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_13]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/09.circom b/circom/tests/circom_doc_examples/09.circom new file mode 100644 index 000000000..57aa9e4b7 --- /dev/null +++ b/circom/tests/circom_doc_examples/09.circom @@ -0,0 +1,91 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + lc1 === in; +} + +component main {public [in]}= Num2Bits(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Num2Bits::@Num2Bits<[3]>>} { +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_6]], %[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_15]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_31]], %[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_30]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_34]], %[[VAL_35]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_49]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_50]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_53]], %[[VAL_55]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_33]]#2, %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/10.circom b/circom/tests/circom_doc_examples/10.circom new file mode 100644 index 000000000..fb3bfa65c --- /dev/null +++ b/circom/tests/circom_doc_examples/10.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template T14() { + signal output out; + var x = 0; + var y = 1; + if (x >= 0) { + x = y + 1; + y += 1; + } else { + y = x; + } + out <== x + y; +} + +component main = T14(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T14::@T14<[]>>} { +// CHECK-NEXT: poly.template @T14 { +// CHECK-NEXT: struct.def @T14 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@T14::@T14<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@T14::@T14<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_1]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_4]] -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_2]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_2]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]]#0, %[[VAL_5]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_10]] : <@T14::@T14<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@T14::@T14<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@T14::@T14<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_11]][@out] : <@T14::@T14<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_13]], %[[VAL_15]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_16]] -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_19]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]]#0, %[[VAL_17]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/11.circom b/circom/tests/circom_doc_examples/11.circom new file mode 100644 index 000000000..a812f12d8 --- /dev/null +++ b/circom/tests/circom_doc_examples/11.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template T15() { + signal output out; + var y = 0; + for(var i = 0; i < 100; i++){ + y++; + } + out <== y; +} + +component main = T15(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T15::@T15<[]>>} { +// CHECK-NEXT: poly.template @T15 { +// CHECK-NEXT: struct.def @T15 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@T15::@T15<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@T15::@T15<[]>> +// CHECK-NEXT: %[[VAL_y_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_i_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_i_2:[0-9a-zA-Z_\.]+]] = %[[VAL_i_1]], %[[VAL_y_2:[0-9a-zA-Z_\.]+]] = %[[VAL_y_1]]) +// CHECK-SAME: : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_i_2]], %[[VAL_100]]) +// CHECK-NEXT: scf.condition(%[[VAL_4]]) %[[VAL_i_2]], %[[VAL_y_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_i_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_y_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_y_3]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_i_3]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_5]]#1 : <@T15::@T15<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@T15::@T15<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@T15::@T15<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@out] : <@T15::@T15<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_y_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_i_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_i_2:[0-9a-zA-Z_\.]+]] = %[[VAL_i_1]], %[[VAL_y_2:[0-9a-zA-Z_\.]+]] = %[[VAL_y_1]]) +// CHECK-SAME: : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_i_2]], %[[VAL_100]]) +// CHECK-NEXT: scf.condition(%[[VAL_19]]) %[[VAL_i_2]], %[[VAL_y_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_i_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_y_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_y_3]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_i_3]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_28]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_29]], %[[VAL_20]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/12.circom b/circom/tests/circom_doc_examples/12.circom new file mode 100644 index 000000000..5c4158880 --- /dev/null +++ b/circom/tests/circom_doc_examples/12.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template T16() { + signal output out; + var y = 0; + var i = 0; + while(i < 100){ + i++; + y += y; + } + out <== y; +} + +component main = T16(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T16::@T16<[]>>} { +// CHECK-NEXT: poly.template @T16 { +// CHECK-NEXT: struct.def @T16 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@T16::@T16<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@T16::@T16<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_4:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_1]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_4]], %[[VAL_6]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_4]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_3]]#1 : <@T16::@T16<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@T16::@T16<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@T16::@T16<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@T16::@T16<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_16]], %[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_15]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_18]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_18]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_25]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_17]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/13.circom b/circom/tests/circom_doc_examples/13.circom new file mode 100644 index 000000000..d3ff7e069 --- /dev/null +++ b/circom/tests/circom_doc_examples/13.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template right(N){ + signal input in; + var x = 2; + var t = 5; + if(in > N){ + t = 2; + } +} + +component main = right(10); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@right::@right<[10]>>} { +// CHECK-NEXT: poly.template @right { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @right { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@right::@right<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@right::@right<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@right::@right<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@right::@right<[@N]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_9]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/14.circom b/circom/tests/circom_doc_examples/14.circom new file mode 100644 index 000000000..8d59e0ddf --- /dev/null +++ b/circom/tests/circom_doc_examples/14.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template right(N1,N2){ + signal input in; + var x = 2; + var t = 5; + if(N1 > N2){ + t = 2; + } + x === t; +} + +component main = right(10, 5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@right::@right<[10, 5]>>} { +// CHECK-NEXT: poly.template @right { +// CHECK-NEXT: poly.param @N1 +// CHECK-NEXT: poly.param @N2 +// CHECK-NEXT: struct.def @right { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@right::@right<[@N1, @N2]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@right::@right<[@N1, @N2]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N1 : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @N2 : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_2]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_6]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@right::@right<[@N1, @N2]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@right::@right<[@N1, @N2]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @N1 : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @N2 : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_11]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_15]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/15.circom b/circom/tests/circom_doc_examples/15.circom new file mode 100644 index 000000000..712f0c45d --- /dev/null +++ b/circom/tests/circom_doc_examples/15.circom @@ -0,0 +1,212 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template mult(){ + signal input in[2]; + signal output out; + out <== in[0] * in[1]; +} + +template mult4(){ + signal input in[4]; + component comp1 = mult(); + component comp2 = mult(); + comp1.in[0] <== in[0]; + comp2.in[0] <== in[1]; + comp2.in[1] <== in[2]; + comp1.in[1] <== in[3]; +} + +component main = mult4(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@mult4::@mult4<[]>>} { +// CHECK-NEXT: poly.template @mult { +// CHECK-NEXT: struct.def @mult { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@mult::@mult<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@mult::@mult<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_3]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_6]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_4]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_8]] : <@mult::@mult<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@mult::@mult<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out] : <@mult::@mult<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_13]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_16]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_14]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @mult4 { +// CHECK-NEXT: struct.def @mult4 { +// CHECK-NEXT: struct.member @comp1 : !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: struct.member @comp1$inputs : !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: struct.member @comp2 : !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: struct.member @comp2$inputs : !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !array.type<4 x !felt.type<"bn128">>) -> !struct.type<@mult4::@mult4<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.new : <@mult4::@mult4<[]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_24]], @params = %[[VAL_23]] } : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_27]], @params = %[[VAL_26]] } : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_30]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_34]]] = %[[VAL_31]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_21]][@in] = %[[VAL_32]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@count] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_35]], %[[VAL_36]] : index +// CHECK-NEXT: pod.write %[[VAL_25]][@count] = %[[VAL_37]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_37]], %[[VAL_38]] : index +// CHECK-NEXT: scf.if %[[VAL_39]] { +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@params] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = function.call @mult::@mult::@compute(%[[VAL_41]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: pod.write %[[VAL_25]][@comp] = %[[VAL_42]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_44]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_22]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_46]]{{\[}}%[[VAL_48]]] = %[[VAL_45]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_22]][@in] = %[[VAL_46]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@count] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_49]], %[[VAL_50]] : index +// CHECK-NEXT: pod.write %[[VAL_28]][@count] = %[[VAL_51]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_51]], %[[VAL_52]] : index +// CHECK-NEXT: scf.if %[[VAL_53]] { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@params] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_22]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = function.call @mult::@mult::@compute(%[[VAL_55]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: pod.write %[[VAL_28]][@comp] = %[[VAL_56]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_57]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_58]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_22]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_60]]{{\[}}%[[VAL_62]]] = %[[VAL_59]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_22]][@in] = %[[VAL_60]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@count] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_63]], %[[VAL_64]] : index +// CHECK-NEXT: pod.write %[[VAL_28]][@count] = %[[VAL_65]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_65]], %[[VAL_66]] : index +// CHECK-NEXT: scf.if %[[VAL_67]] { +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@params] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_22]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = function.call @mult::@mult::@compute(%[[VAL_69]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: pod.write %[[VAL_28]][@comp] = %[[VAL_70]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_71]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_72]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_75]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_74]]{{\[}}%[[VAL_76]]] = %[[VAL_73]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_21]][@in] = %[[VAL_74]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@count] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_77]], %[[VAL_78]] : index +// CHECK-NEXT: pod.write %[[VAL_25]][@count] = %[[VAL_79]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_79]], %[[VAL_80]] : index +// CHECK-NEXT: scf.if %[[VAL_81]] { +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@params] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = function.call @mult::@mult::@compute(%[[VAL_83]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: pod.write %[[VAL_25]][@comp] = %[[VAL_84]] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_20]][@comp1$inputs] = %[[VAL_21]] : <@mult4::@mult4<[]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@comp] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: struct.writem %[[VAL_20]][@comp1] = %[[VAL_85]] : <@mult4::@mult4<[]>>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: struct.writem %[[VAL_20]][@comp2$inputs] = %[[VAL_22]] : <@mult4::@mult4<[]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@comp] : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: struct.writem %[[VAL_20]][@comp2] = %[[VAL_86]] : <@mult4::@mult4<[]>>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: function.return %[[VAL_20]] : !struct.type<@mult4::@mult4<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_87:[0-9a-zA-Z_\.]+]]: !struct.type<@mult4::@mult4<[]>>, %[[VAL_88:[0-9a-zA-Z_\.]+]]: !array.type<4 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_87]][@comp1] : <@mult4::@mult4<[]>>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_87]][@comp1$inputs] : <@mult4::@mult4<[]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_87]][@comp2] : <@mult4::@mult4<[]>>, !struct.type<@mult::@mult<[]>> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_87]][@comp2$inputs] : <@mult4::@mult4<[]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@mult::@mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_97]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_88]]{{\[}}%[[VAL_98]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_90]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_101]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_100]]{{\[}}%[[VAL_102]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_103]], %[[VAL_99]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_104]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_88]]{{\[}}%[[VAL_105]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_92]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_108]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_107]]{{\[}}%[[VAL_109]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_110]], %[[VAL_106]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_111]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_88]]{{\[}}%[[VAL_112]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_92]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_115]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_114]]{{\[}}%[[VAL_116]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_117]], %[[VAL_113]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_118]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_88]]{{\[}}%[[VAL_119]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_90]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_122]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_121]]{{\[}}%[[VAL_123]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_124]], %[[VAL_120]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_90]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @mult::@mult::@constrain(%[[VAL_89]], %[[VAL_125]]) : (!struct.type<@mult::@mult<[]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_92]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @mult::@mult::@constrain(%[[VAL_91]], %[[VAL_126]]) : (!struct.type<@mult::@mult<[]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/16.circom b/circom/tests/circom_doc_examples/16.circom new file mode 100644 index 000000000..f87cfe5f0 --- /dev/null +++ b/circom/tests/circom_doc_examples/16.circom @@ -0,0 +1,22 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template fun(N){ + signal output out; + out <== N; +} + +template all(N){ + component c[N]; + for(var i = 0; i < N; i++){ + c[i] = fun(i); + } +} + +component main = all(5); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/17.circom b/circom/tests/circom_doc_examples/17.circom new file mode 100644 index 000000000..a6499a2e5 --- /dev/null +++ b/circom/tests/circom_doc_examples/17.circom @@ -0,0 +1,22 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* +// COM: failure related to the "TODO" on `DeclarationInfo::visit` + +pragma circom 2.1.5; + +template A(n){ + signal input in; + signal output outA; + var i = 0; + if(i < n){ + signal out <== 2; + i = out; + } + outA <== i; +} + +component main = A(5); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/18.circom b/circom/tests/circom_doc_examples/18.circom new file mode 100644 index 000000000..94366c9e8 --- /dev/null +++ b/circom/tests/circom_doc_examples/18.circom @@ -0,0 +1,128 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n){ + signal input a, b; + signal output c; + c <== a*b; +} +template B(n){ + signal input in[n]; + signal out; + component temp_a = A(n); + temp_a.a <== in[0]; + temp_a.b <== in[1]; + out <== temp_a.c; +} +component main = B(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[2]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_4]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @temp_a : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.member @temp_a$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@B::@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@n]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_15]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_17]], @params = %[[VAL_16]] } : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_20]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_14]][@a] = %[[VAL_21]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_22]], %[[VAL_23]] : index +// CHECK-NEXT: pod.write %[[VAL_18]][@count] = %[[VAL_24]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_24]], %[[VAL_25]] : index +// CHECK-NEXT: scf.if %[[VAL_26]] { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_28]], %[[VAL_29]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_18]][@comp] = %[[VAL_30]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_32]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_14]][@b] = %[[VAL_33]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_34]], %[[VAL_35]] : index +// CHECK-NEXT: pod.write %[[VAL_18]][@count] = %[[VAL_36]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_36]], %[[VAL_37]] : index +// CHECK-NEXT: scf.if %[[VAL_38]] { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_40]], %[[VAL_41]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_18]][@comp] = %[[VAL_42]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@out] = %[[VAL_44]] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@temp_a$inputs] = %[[VAL_14]] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_12]][@temp_a] = %[[VAL_45]] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@B::@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@n]>>, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@out] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@temp_a] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@temp_a$inputs] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_52]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_56]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_58]], %[[VAL_57]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_60]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_62]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_49]], %[[VAL_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_50]], %[[VAL_64]], %[[VAL_65]]) : (!struct.type<@A::@A<[@n]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/19.circom b/circom/tests/circom_doc_examples/19.circom new file mode 100644 index 000000000..0165399de --- /dev/null +++ b/circom/tests/circom_doc_examples/19.circom @@ -0,0 +1,124 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template A(n){ + signal input a, b; + signal output c; + c <== a*b; +} +template B(n){ + signal input in[n]; + signal out <== A(n)(in[0],in[1]); +} +component main = B(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[2]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_4]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @A_14_363 : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.member @A_14_363$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@B::@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@n]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_15]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_17]], @params = %[[VAL_16]] } : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_20]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_14]][@a] = %[[VAL_21]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_22]], %[[VAL_23]] : index +// CHECK-NEXT: pod.write %[[VAL_18]][@count] = %[[VAL_24]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_24]], %[[VAL_25]] : index +// CHECK-NEXT: scf.if %[[VAL_26]] { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_28]], %[[VAL_29]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_18]][@comp] = %[[VAL_30]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_32]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_14]][@b] = %[[VAL_33]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_34]], %[[VAL_35]] : index +// CHECK-NEXT: pod.write %[[VAL_18]][@count] = %[[VAL_36]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_36]], %[[VAL_37]] : index +// CHECK-NEXT: scf.if %[[VAL_38]] { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_40]], %[[VAL_41]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_18]][@comp] = %[[VAL_42]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@out] = %[[VAL_44]] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@A_14_363$inputs] = %[[VAL_14]] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_12]][@A_14_363] = %[[VAL_45]] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@B::@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@n]>>, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@out] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@A_14_363] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@A_14_363$inputs] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_52]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_56]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_58]], %[[VAL_57]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_60]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_62]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_49]], %[[VAL_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_50]], %[[VAL_64]], %[[VAL_65]]) : (!struct.type<@A::@A<[@n]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/20.circom b/circom/tests/circom_doc_examples/20.circom new file mode 100644 index 000000000..7f2bc7e85 --- /dev/null +++ b/circom/tests/circom_doc_examples/20.circom @@ -0,0 +1,124 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template A(n){ + signal input a, b; + signal output c; + c <== a*b; +} +template B(n){ + signal input in[n]; + signal out <== A(n)(b <== in[1], a <== in[0]); +} +component main = B(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[2]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_4]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @A_14_363 : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.member @A_14_363$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@B::@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@n]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_15]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_17]], @params = %[[VAL_16]] } : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_20]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_14]][@a] = %[[VAL_21]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_22]], %[[VAL_23]] : index +// CHECK-NEXT: pod.write %[[VAL_18]][@count] = %[[VAL_24]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_24]], %[[VAL_25]] : index +// CHECK-NEXT: scf.if %[[VAL_26]] { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_28]], %[[VAL_29]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_18]][@comp] = %[[VAL_30]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_32]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_14]][@b] = %[[VAL_33]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_34]], %[[VAL_35]] : index +// CHECK-NEXT: pod.write %[[VAL_18]][@count] = %[[VAL_36]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_36]], %[[VAL_37]] : index +// CHECK-NEXT: scf.if %[[VAL_38]] { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_40]], %[[VAL_41]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_18]][@comp] = %[[VAL_42]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@out] = %[[VAL_44]] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@A_14_363$inputs] = %[[VAL_14]] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_12]][@A_14_363] = %[[VAL_45]] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@B::@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@n]>>, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@out] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@A_14_363] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_46]][@A_14_363$inputs] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_52]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_56]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_58]], %[[VAL_57]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_60]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_62]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_49]], %[[VAL_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_50]], %[[VAL_64]], %[[VAL_65]]) : (!struct.type<@A::@A<[@n]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/21.circom b/circom/tests/circom_doc_examples/21.circom new file mode 100644 index 000000000..58ed284b7 --- /dev/null +++ b/circom/tests/circom_doc_examples/21.circom @@ -0,0 +1,38 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template Ex(n,m){ + signal input in[n]; + signal output out[m]; + out <== in; +} + +component main = Ex(3,3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Ex::@Ex<[3, 3]>>} { +// CHECK-NEXT: poly.template @Ex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.param @m +// CHECK-NEXT: struct.def @Ex { +// CHECK-NEXT: struct.member @out : !array.type<@m x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Ex::@Ex<[@n, @m]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Ex::@Ex<[@n, @m]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_0]] : (!array.type<@n x !felt.type<"bn128">>) -> !array.type<@m x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_4]] : <@Ex::@Ex<[@n, @m]>>, !array.type<@m x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Ex::@Ex<[@n, @m]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@Ex::@Ex<[@n, @m]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@out] : <@Ex::@Ex<[@n, @m]>>, !array.type<@m x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_6]] : !array.type<@m x !felt.type<"bn128">>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/22.circom b/circom/tests/circom_doc_examples/22.circom new file mode 100644 index 000000000..d94afac25 --- /dev/null +++ b/circom/tests/circom_doc_examples/22.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template Ex(n, m){ + signal input in[n]; + signal output out[m]; + var i = 0; + while(i < n) { + out[i] <== in[i]; + i += 1; + } +} + +component main = Ex(3, 3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Ex::@Ex<[3, 3]>>} { +// CHECK-NEXT: poly.template @Ex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.param @m +// CHECK-NEXT: struct.def @Ex { +// CHECK-NEXT: struct.member @out : !array.type<@m x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Ex::@Ex<[@n, @m]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Ex::@Ex<[@n, @m]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@m x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_5]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_10]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_12]]] = %[[VAL_11]] : <@m x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_4]] : <@Ex::@Ex<[@n, @m]>>, !array.type<@m x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Ex::@Ex<[@n, @m]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@Ex::@Ex<[@n, @m]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@out] : <@Ex::@Ex<[@n, @m]>>, !array.type<@m x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_20]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_22]], %[[VAL_17]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_23]]) %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_25]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_27]]] : <@m x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_28]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_24]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/23.circom b/circom/tests/circom_doc_examples/23.circom new file mode 100644 index 000000000..adaa7568c --- /dev/null +++ b/circom/tests/circom_doc_examples/23.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +template Ex(n, m){ + signal input in[n]; + signal output out[m]; + var i = 0; + while(i < n) { + out[i] <== in[i]; + i += 1; + } +} + +template A { + signal input i[4]; + signal output o[4]; + o <== Ex(4,4)(i); +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/24.circom b/circom/tests/circom_doc_examples/24.circom new file mode 100644 index 000000000..1b5a9616a --- /dev/null +++ b/circom/tests/circom_doc_examples/24.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +template Ex(n, m){ + signal input in[n]; + signal output out[m]; + var i = 0; + while(i < n) { + out[i] <== in[i]; + i += 1; + } +} + +template A{ + signal input inp[4]; + signal output out[4]; + component anon = Ex(4,4); + var i = 0; + while(i < 4){ + anon.in[i] <== inp[i]; + i += 1; + } + i = 0; + while(i < 4){ + out[i] <== anon.out[i]; + i += 1; + } +} +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/25.circom b/circom/tests/circom_doc_examples/25.circom new file mode 100644 index 000000000..20034c8b4 --- /dev/null +++ b/circom/tests/circom_doc_examples/25.circom @@ -0,0 +1,152 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template A(n){ + signal input a, b, c; + signal output d; + d <== a*b+c; + a * b === c; +} +template B(n){ + signal input in[n]; + _ <== A(n)(in[0],in[1],in[2]); +} +component main = B(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[3]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @d : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@d] = %[[VAL_6]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_7]][@d] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_15]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @A_15_375 : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.member @A_15_375$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@B::@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@n]>> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_20]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 3 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_22]], @params = %[[VAL_21]] } : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_25]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_19]][@a] = %[[VAL_26]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_27]], %[[VAL_28]] : index +// CHECK-NEXT: pod.write %[[VAL_23]][@count] = %[[VAL_29]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_29]], %[[VAL_30]] : index +// CHECK-NEXT: scf.if %[[VAL_31]] { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@c] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_33]], %[[VAL_34]], %[[VAL_35]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_23]][@comp] = %[[VAL_36]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_38]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_19]][@b] = %[[VAL_39]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_40]], %[[VAL_41]] : index +// CHECK-NEXT: pod.write %[[VAL_23]][@count] = %[[VAL_42]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_42]], %[[VAL_43]] : index +// CHECK-NEXT: scf.if %[[VAL_44]] { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@c] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_46]], %[[VAL_47]], %[[VAL_48]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_23]][@comp] = %[[VAL_49]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_51]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_19]][@c] = %[[VAL_52]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_53]], %[[VAL_54]] : index +// CHECK-NEXT: pod.write %[[VAL_23]][@count] = %[[VAL_55]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_55]], %[[VAL_56]] : index +// CHECK-NEXT: scf.if %[[VAL_57]] { +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@c] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_59]], %[[VAL_60]], %[[VAL_61]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_23]][@comp] = %[[VAL_62]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_63]][@d] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_17]][@A_15_375$inputs] = %[[VAL_19]] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_23]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_17]][@A_15_375] = %[[VAL_65]] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_17]] : !struct.type<@B::@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_66:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@n]>>, %[[VAL_67:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_66]][@A_15_375] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_66]][@A_15_375$inputs] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_71]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_74]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_67]]{{\[}}%[[VAL_75]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_77]], %[[VAL_76]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_78]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_67]]{{\[}}%[[VAL_79]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_81]], %[[VAL_80]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_82]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_67]]{{\[}}%[[VAL_83]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@c] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_85]], %[[VAL_84]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_69]][@d] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@c] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">, @c: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_69]], %[[VAL_87]], %[[VAL_88]], %[[VAL_89]]) : (!struct.type<@A::@A<[@n]>>, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/26.circom b/circom/tests/circom_doc_examples/26.circom new file mode 100644 index 000000000..fd4cc818e --- /dev/null +++ b/circom/tests/circom_doc_examples/26.circom @@ -0,0 +1,120 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template A(n){ + signal input a; + signal output b, c, d; + b <== a * a; + c <== a + 2; + d <== a * a + 2; +} +template B(n){ + signal input in; + signal output out1; + (_,out1,_) <== A(n)(in); +} +component main = B(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[3]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @b : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @d : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@c] = %[[VAL_5]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@d] = %[[VAL_8]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@b] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@d] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_10]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_10]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_18]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out1 : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @A_17_424 : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.member @A_17_424$inputs : !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@B::@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@n]>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_25]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_27]], @params = %[[VAL_26]] } : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_24]][@a] = %[[VAL_21]] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_29]], %[[VAL_30]] : index +// CHECK-NEXT: pod.write %[[VAL_28]][@count] = %[[VAL_31]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_31]], %[[VAL_32]] : index +// CHECK-NEXT: scf.if %[[VAL_33]] { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_35]]) : (!felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_28]][@comp] = %[[VAL_36]] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_37]][@b] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_39]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_22]][@out1] = %[[VAL_40]] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@d] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_22]][@A_17_424$inputs] = %[[VAL_24]] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_22]][@A_17_424] = %[[VAL_43]] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_22]] : !struct.type<@B::@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@n]>>, %[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@out1] : <@B::@B<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@A_17_424] : <@B::@B<[@n]>>, !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@A_17_424$inputs] : <@B::@B<[@n]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_50]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_49]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_53]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_48]][@b] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_48]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_55]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_48]][@d] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_49]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_48]], %[[VAL_57]]) : (!struct.type<@A::@A<[@n]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/27.circom b/circom/tests/circom_doc_examples/27.circom new file mode 100644 index 000000000..ee7417aac --- /dev/null +++ b/circom/tests/circom_doc_examples/27.circom @@ -0,0 +1,135 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template Bits2Num(n) { + signal input {binary} in[n]; + signal output out; + var lc1=0; + + var e2 = 1; + for (var i = 0; i out; +} + +template A(){ + signal input a[10]; + signal output out; + component b = Bits2Num(10); + b.in <== a; + out <== b.out; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @b : !struct.type<@Bits2Num::@Bits2Num<[10]>> +// CHECK-NEXT: struct.member @b$inputs : !pod.type<[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_3]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 10 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_5]], @params = %[[VAL_4]] } : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_2]][@in] = %[[VAL_0]] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@count] : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_7]], %[[VAL_8]] : index +// CHECK-NEXT: pod.write %[[VAL_6]][@count] = %[[VAL_9]] : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_9]], %[[VAL_10]] : index +// CHECK-NEXT: scf.if %[[VAL_11]] { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@params] : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@in] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @Bits2Num::@Bits2Num::@compute(%[[VAL_13]]) : (!array.type<10 x !felt.type<"bn128">>) -> !struct.type<@Bits2Num::@Bits2Num<[10]>> +// CHECK-NEXT: pod.write %[[VAL_6]][@comp] = %[[VAL_14]] : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Bits2Num::@Bits2Num<[10]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@comp] : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Bits2Num::@Bits2Num<[10]>> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@out] : <@Bits2Num::@Bits2Num<[10]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_16]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b$inputs] = %[[VAL_2]] : <@A::@A<[]>>, !pod.type<[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@comp] : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Bits2Num::@Bits2Num<[10]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_17]] : <@A::@A<[]>>, !struct.type<@Bits2Num::@Bits2Num<[10]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@b] : <@A::@A<[]>>, !struct.type<@Bits2Num::@Bits2Num<[10]>> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@b$inputs] : <@A::@A<[]>>, !pod.type<[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_23]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Bits2Num::@Bits2Num<[10]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_22]][@in] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_19]] : !array.type<10 x !felt.type<"bn128">>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_21]][@out] : <@Bits2Num::@Bits2Num<[10]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_22]][@in] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Bits2Num::@Bits2Num::@constrain(%[[VAL_21]], %[[VAL_28]]) : (!struct.type<@Bits2Num::@Bits2Num<[10]>>, !array.type<10 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Bits2Num { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Bits2Num { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Bits2Num::@Bits2Num<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.new : <@Bits2Num::@Bits2Num<[@n]>> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_33]], %[[VAL_37:[0-9a-zA-Z_\.]+]] = %[[VAL_34]], %[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_32]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_37]], %[[VAL_31]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_39]]) %[[VAL_36]], %[[VAL_37]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_41:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_44]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_42]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_47]], %[[VAL_49]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_30]][@out] = %[[VAL_35]]#2 : <@Bits2Num::@Bits2Num<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_30]] : !struct.type<@Bits2Num::@Bits2Num<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_50:[0-9a-zA-Z_\.]+]]: !struct.type<@Bits2Num::@Bits2Num<[@n]>>, %[[VAL_51:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@out] : <@Bits2Num::@Bits2Num<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_58:[0-9a-zA-Z_\.]+]] = %[[VAL_55]], %[[VAL_59:[0-9a-zA-Z_\.]+]] = %[[VAL_56]], %[[VAL_60:[0-9a-zA-Z_\.]+]] = %[[VAL_54]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_59]], %[[VAL_52]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_61]]) %[[VAL_58]], %[[VAL_59]], %[[VAL_60]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_62:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_63:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_64:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_63]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_51]]{{\[}}%[[VAL_65]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_66]], %[[VAL_55]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_64]], %[[VAL_67]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_62]], %[[VAL_62]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_63]], %[[VAL_70]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_69]], %[[VAL_71]], %[[VAL_68]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_53]], %[[VAL_57]]#2 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/28.circom b/circom/tests/circom_doc_examples/28.circom new file mode 100644 index 000000000..2c4c6a70c --- /dev/null +++ b/circom/tests/circom_doc_examples/28.circom @@ -0,0 +1,94 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template A() { + signal input {binary} in; + signal intermediate; + signal output {binary} out; + intermediate <== in; + out <== intermediate; +} + +template Caller(){ + signal input inp; + signal output out; + component a = A(); + a.in <== inp; + out <== a.out; +} + +component main = Caller(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller::@Caller<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @intermediate : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@intermediate] = %[[VAL_0]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@intermediate] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Caller { +// CHECK-NEXT: struct.def @Caller { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@A::@A<[]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Caller::@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller::@Caller<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_10]], @params = %[[VAL_9]] } : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_8]][@in] = %[[VAL_6]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@count] : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_12]], %[[VAL_13]] : index +// CHECK-NEXT: pod.write %[[VAL_11]][@count] = %[[VAL_14]] : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_14]], %[[VAL_15]] : index +// CHECK-NEXT: scf.if %[[VAL_16]] { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@params] : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_8]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = function.call @A::@A::@compute(%[[VAL_18]]) : (!felt.type<"bn128">) -> !struct.type<@A::@A<[]>> +// CHECK-NEXT: pod.write %[[VAL_11]][@comp] = %[[VAL_19]] : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]>, !struct.type<@A::@A<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]>, !struct.type<@A::@A<[]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@out] = %[[VAL_21]] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@a$inputs] = %[[VAL_8]] : <@Caller::@Caller<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@comp] : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]>, !struct.type<@A::@A<[]>> +// CHECK-NEXT: struct.writem %[[VAL_7]][@a] = %[[VAL_22]] : <@Caller::@Caller<[]>>, !struct.type<@A::@A<[]>> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@Caller::@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller::@Caller<[]>>, %[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_23]][@out] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_23]][@a] : <@Caller::@Caller<[]>>, !struct.type<@A::@A<[]>> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_23]][@a$inputs] : <@Caller::@Caller<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@A::@A<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_27]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_25]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_27]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @A::@A::@constrain(%[[VAL_26]], %[[VAL_32]]) : (!struct.type<@A::@A<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/29.circom b/circom/tests/circom_doc_examples/29.circom new file mode 100644 index 000000000..80150de9b --- /dev/null +++ b/circom/tests/circom_doc_examples/29.circom @@ -0,0 +1,58 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template IsZero() { + signal input in; + signal output {binary} out; + signal inv; + inv <-- in!=0 ? 1/in : 0; + out <== -in*inv +1; + in*out === 0; +} + +component main = IsZero(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@IsZero::@IsZero<[]>>} { +// CHECK-NEXT: poly.template @IsZero { +// CHECK-NEXT: struct.def @IsZero { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @inv : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_5]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@inv] = %[[VAL_4]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_11]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero::@IsZero<[]>>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_12]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_12]][@inv] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_16]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_13]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/30.circom b/circom/tests/circom_doc_examples/30.circom new file mode 100644 index 000000000..30fb702b3 --- /dev/null +++ b/circom/tests/circom_doc_examples/30.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +template Bits2Num(n) { + signal input {binary} in[n]; + signal output {maxbit} out; + var lc1=0; + + var e2 = 1; + for (var i = 0; i out; +} + +template Caller(n) { + signal input in[n]; + signal output out <== Bits2Num(n)(in); +} + +component main = Caller(64); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/31.circom b/circom/tests/circom_doc_examples/31.circom new file mode 100644 index 000000000..33832964b --- /dev/null +++ b/circom/tests/circom_doc_examples/31.circom @@ -0,0 +1,16 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.0; + +template A(){ + signal output {max} out[100]; + out[0] <== 1; + out.max = 10; +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/32.circom b/circom/tests/circom_doc_examples/32.circom new file mode 100644 index 000000000..9816a788c --- /dev/null +++ b/circom/tests/circom_doc_examples/32.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.2.0; + +bus Point(){ + signal x; + signal y; +} + +template Edwards2Montgomery() { + input Point() { edwards_point } in ; + output Point() { montgomery_point } out ; + + out.x <-- (1 + in.y ) / (1 - in.y ) ; + out.y <-- out.x / in.x ; + + out.x * (1 - in.y ) === (1 + in.y ) ; + out.y * in.x === out.x ; +} + +template Caller() { + input signal a, b; + output Point() conv; + + Point() p; + p.x <== a; + p.y <== b; + + conv <== Edwards2Montgomery()(p); +} + +component main = Caller(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/33.circom b/circom/tests/circom_doc_examples/33.circom new file mode 100644 index 000000000..2afd98336 --- /dev/null +++ b/circom/tests/circom_doc_examples/33.circom @@ -0,0 +1,94 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.2.0; + +template IsZero() { + signal input in; + signal output out; + + signal inv; + + inv <-- in!=0 ? 1/in : 0; + + out <== -in*inv +1; + in*out === 0; +} + +template IsEqual() { + signal input in[2]; + signal output out; + + component isz = IsZero(); + + in[1] - in[0] ==> isz.in; + + isz.out ==> out; +} + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +template LessThan(n) { + assert(n <= 252); + signal input in[2]; + signal output out; + + component n2b = Num2Bits(n+1); + + n2b.in <== in[0]+ (1< isz.in; + + isz.out ==> out; +} + +bus PointN(dim){ + signal x[dim]; +} + +bus Line(dim){ + PointN(dim) start; + PointN(dim) end; +} + +bus Figure(num_sides, dim){ + Line(dim) side[num_sides]; +} + +bus Triangle2D(){ + Figure(3,2) {well_defined} triangle; +} + +bus Square3D(){ + Figure(4,3) {well_defined} square; +} + +template well_defined_figure(num_sides, dimension){ + input Figure(num_sides,dimension) t; + output Figure(num_sides,dimension) {well_defined} correct_t; + var all_equals = 0; + var isequal = 0; + for(var i = 0; i < num_sides; i=i+1){ + for(var j = 0; j < dimension; j=j+1){ + isequal = IsEqual()([t.side[i].end.x[j],t.side[(i+1)%num_sides].start.x[j]]); + all_equals += isequal; + } + } + all_equals === num_sides; + correct_t <== t; +} + +component main = well_defined_figure(3,2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/35A.circom b/circom/tests/circom_doc_examples/35A.circom new file mode 100644 index 000000000..ff06578e4 --- /dev/null +++ b/circom/tests/circom_doc_examples/35A.circom @@ -0,0 +1,38 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n) { + signal input in; + assert(n>0); + in * in === n; +} + +component main = A(0); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[0]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_2]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_4]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/35B.circom b/circom/tests/circom_doc_examples/35B.circom new file mode 100644 index 000000000..b19ff2d17 --- /dev/null +++ b/circom/tests/circom_doc_examples/35B.circom @@ -0,0 +1,38 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n) { + signal input in; + assert(n>0); + in * in === n; +} + +component main = A(1); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[1]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_2]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_4]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/36.circom b/circom/tests/circom_doc_examples/36.circom new file mode 100644 index 000000000..b4880692d --- /dev/null +++ b/circom/tests/circom_doc_examples/36.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Translate(n) { + signal input in; + assert(in<=254); +} + +component main = Translate(1); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Translate::@Translate<[1]>>} { +// CHECK-NEXT: poly.template @Translate { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Translate { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Translate::@Translate<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Translate::@Translate<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 254 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_4]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Translate::@Translate<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@Translate::@Translate<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 254 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/37.circom b/circom/tests/circom_doc_examples/37.circom new file mode 100644 index 000000000..feb223842 --- /dev/null +++ b/circom/tests/circom_doc_examples/37.circom @@ -0,0 +1,172 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Multiplier2() { + signal input in1; + signal input in2; + signal output out <== in1 * in2; +} + +//This circuit multiplies in1, in2, and in3. +template Multiplier3() { + //Declaration of signals and components. + signal input in1; + signal input in2; + signal input in3; + signal output out; + component mult1 = Multiplier2(); + component mult2 = Multiplier2(); + + //Statements. + mult1.in1 <== in1; + mult1.in2 <== in2; + mult2.in1 <== mult1.out; + mult2.in2 <== in3; + out <== mult2.out; +} + +component main = Multiplier3(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Multiplier3::@Multiplier3<[]>>} { +// CHECK-NEXT: poly.template @Multiplier2 { +// CHECK-NEXT: struct.def @Multiplier2 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier2::@Multiplier2<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Multiplier3 { +// CHECK-NEXT: struct.def @Multiplier3 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @mult1 : !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: struct.member @mult1$inputs : !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: struct.member @mult2 : !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: struct.member @mult2$inputs : !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Multiplier3::@Multiplier3<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier3::@Multiplier3<[]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_16]], @params = %[[VAL_15]] } : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_19]], @params = %[[VAL_18]] } : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_13]][@in1] = %[[VAL_9]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_21]], %[[VAL_22]] : index +// CHECK-NEXT: pod.write %[[VAL_17]][@count] = %[[VAL_23]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_23]], %[[VAL_24]] : index +// CHECK-NEXT: scf.if %[[VAL_25]] { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_27]], %[[VAL_28]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_17]][@comp] = %[[VAL_29]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: pod.write %[[VAL_13]][@in2] = %[[VAL_10]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_30]], %[[VAL_31]] : index +// CHECK-NEXT: pod.write %[[VAL_17]][@count] = %[[VAL_32]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_32]], %[[VAL_33]] : index +// CHECK-NEXT: scf.if %[[VAL_34]] { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_36]], %[[VAL_37]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_17]][@comp] = %[[VAL_38]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@comp] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_39]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_14]][@in1] = %[[VAL_40]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_41]], %[[VAL_42]] : index +// CHECK-NEXT: pod.write %[[VAL_20]][@count] = %[[VAL_43]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_43]], %[[VAL_44]] : index +// CHECK-NEXT: scf.if %[[VAL_45]] { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_47]], %[[VAL_48]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_20]][@comp] = %[[VAL_49]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: pod.write %[[VAL_14]][@in2] = %[[VAL_11]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_50]], %[[VAL_51]] : index +// CHECK-NEXT: pod.write %[[VAL_20]][@count] = %[[VAL_52]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_52]], %[[VAL_53]] : index +// CHECK-NEXT: scf.if %[[VAL_54]] { +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_14]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_56]], %[[VAL_57]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_20]][@comp] = %[[VAL_58]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@comp] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_59]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@out] = %[[VAL_60]] : <@Multiplier3::@Multiplier3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@mult1$inputs] = %[[VAL_13]] : <@Multiplier3::@Multiplier3<[]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@comp] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: struct.writem %[[VAL_12]][@mult1] = %[[VAL_61]] : <@Multiplier3::@Multiplier3<[]>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: struct.writem %[[VAL_12]][@mult2$inputs] = %[[VAL_14]] : <@Multiplier3::@Multiplier3<[]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@comp] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: struct.writem %[[VAL_12]][@mult2] = %[[VAL_62]] : <@Multiplier3::@Multiplier3<[]>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@Multiplier3::@Multiplier3<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_63:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier3::@Multiplier3<[]>>, %[[VAL_64:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_65:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_66:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_63]][@out] : <@Multiplier3::@Multiplier3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_63]][@mult1] : <@Multiplier3::@Multiplier3<[]>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_63]][@mult1$inputs] : <@Multiplier3::@Multiplier3<[]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_63]][@mult2] : <@Multiplier3::@Multiplier3<[]>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_63]][@mult2$inputs] : <@Multiplier3::@Multiplier3<[]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_69]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_76]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_69]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_77]], %[[VAL_65]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_68]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_79]], %[[VAL_78]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_80]], %[[VAL_66]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_70]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_67]], %[[VAL_81]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_69]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_69]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Multiplier2::@Multiplier2::@constrain(%[[VAL_68]], %[[VAL_82]], %[[VAL_83]]) : (!struct.type<@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Multiplier2::@Multiplier2::@constrain(%[[VAL_70]], %[[VAL_84]], %[[VAL_85]]) : (!struct.type<@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/38.circom b/circom/tests/circom_doc_examples/38.circom new file mode 100644 index 000000000..72899c2d8 --- /dev/null +++ b/circom/tests/circom_doc_examples/38.circom @@ -0,0 +1,360 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Multiplier2(){ + //Declaration of signals. + signal input in1; + signal input in2; + signal output out; + + //Statements. + out <== in1 * in2; +} + +template Multiplier3() { + //Declaration of signals. + signal input in1; + signal input in2; + signal input in3; + signal output out; + component mult1 = Multiplier2(); + component mult2 = Multiplier2(); + + //Statements. + mult1.in1 <== in1; + mult1.in2 <== in2; + mult2.in1 <== mult1.out; + mult2.in2 <== in3; + out <== mult2.out; +} + +template MultiplierN(N){ + //Declaration of signals. + signal input in[N]; + signal output out; + component comp[N-1]; + + //Statements. + for(var i = 0; i < N-1; i++){ + comp[i] = Multiplier2(); + } + comp[0].in1 <== in[0]; + comp[0].in2 <== in[1]; + for(var i = 0; i < N-2; i++){ + comp[i+1].in1 <== comp[i].out; + comp[i+1].in2 <== in[i+2]; + + } + out <== comp[N-2].out; +} + +component main {public [in]} = MultiplierN(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@MultiplierN::@MultiplierN<[3]>>} { +// CHECK-NEXT: poly.template @Multiplier2 { +// CHECK-NEXT: struct.def @Multiplier2 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier2::@Multiplier2<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @MultiplierN { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.expr @"N_Sub_1@859" { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_10]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @MultiplierN { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @comp : !array.type<@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>> +// CHECK-NEXT: struct.member @comp$inputs : !array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">> {llzk.pub}) -> !struct.type<@MultiplierN::@MultiplierN<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.new : <@MultiplierN::@MultiplierN<[@N]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Sub_1@859" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.new : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.new : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_18]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_14]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_22]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_23]]) %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_26]], @params = %[[VAL_25]] } : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_28]]] = %[[VAL_27]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_24]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_32]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_35]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_36]][@in1] = %[[VAL_33]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_17]]{{\[}}%[[VAL_38]]] = %[[VAL_36]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_40]]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_41]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_42]], %[[VAL_43]] : index +// CHECK-NEXT: pod.write %[[VAL_41]][@count] = %[[VAL_44]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_44]], %[[VAL_45]] : index +// CHECK-NEXT: scf.if %[[VAL_46]] { +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_41]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_36]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_36]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_48]], %[[VAL_49]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_41]][@comp] = %[[VAL_50]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_52]]] = %[[VAL_41]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_54]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_56]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_57]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_58]][@in2] = %[[VAL_55]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_17]]{{\[}}%[[VAL_60]]] = %[[VAL_58]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_62]]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_64]], %[[VAL_65]] : index +// CHECK-NEXT: pod.write %[[VAL_63]][@count] = %[[VAL_66]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_66]], %[[VAL_67]] : index +// CHECK-NEXT: scf.if %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_58]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_58]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_70]], %[[VAL_71]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_63]][@comp] = %[[VAL_72]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_73]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_74]]] = %[[VAL_63]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_77:[0-9a-zA-Z_\.]+]] = %[[VAL_17]], %[[VAL_78:[0-9a-zA-Z_\.]+]] = %[[VAL_75]]) : (!array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128">) -> (!array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_14]], %[[VAL_79]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_78]], %[[VAL_80]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_81]]) %[[VAL_77]], %[[VAL_78]] : !array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_82:[0-9a-zA-Z_\.]+]]: !array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, %[[VAL_83:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_84]]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_85]][@comp] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_86]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_89]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_82]]{{\[}}%[[VAL_90]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_91]][@in1] = %[[VAL_87]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_92]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_93]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_82]]{{\[}}%[[VAL_94]]] = %[[VAL_91]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_95]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_96]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_97]]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_98]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_99]], %[[VAL_100]] : index +// CHECK-NEXT: pod.write %[[VAL_98]][@count] = %[[VAL_101]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_101]], %[[VAL_102]] : index +// CHECK-NEXT: scf.if %[[VAL_103]] { +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_98]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_91]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_91]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_105]], %[[VAL_106]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_98]][@comp] = %[[VAL_107]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_108]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_109]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_110]]] = %[[VAL_98]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_111]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_112]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_113]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_115]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_116]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_82]]{{\[}}%[[VAL_117]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_118]][@in2] = %[[VAL_114]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_119]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_120]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_82]]{{\[}}%[[VAL_121]]] = %[[VAL_118]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_122]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_123]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_124]]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_125]][@count] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_126]], %[[VAL_127]] : index +// CHECK-NEXT: pod.write %[[VAL_125]][@count] = %[[VAL_128]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_128]], %[[VAL_129]] : index +// CHECK-NEXT: scf.if %[[VAL_130]] { +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_125]][@params] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_118]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_118]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@Multiplier2::@compute(%[[VAL_132]], %[[VAL_133]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: pod.write %[[VAL_125]][@comp] = %[[VAL_134]] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_135]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_136]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_137]]] = %[[VAL_125]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_138]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_82]], %[[VAL_139]] : !array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_14]], %[[VAL_140]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_141]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_142]]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_143]][@comp] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_144]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_13]][@out] = %[[VAL_145]] : <@MultiplierN::@MultiplierN<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_13]][@comp$inputs] = %[[VAL_76]]#0 : <@MultiplierN::@MultiplierN<[@N]>>, !array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = array.new : <@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Sub_1@859" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_147]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_151:[0-9a-zA-Z_\.]+]] = %[[VAL_149]] to %[[VAL_148]] step %[[VAL_150]] { +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_151]]] : <@"N_Sub_1@859" x !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_152]][@comp] : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: array.write %[[VAL_146]]{{\[}}%[[VAL_151]]] = %[[VAL_153]] : <@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_13]][@comp] = %[[VAL_146]] : <@MultiplierN::@MultiplierN<[@N]>>, !array.type<@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>> +// CHECK-NEXT: function.return %[[VAL_13]] : !struct.type<@MultiplierN::@MultiplierN<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_154:[0-9a-zA-Z_\.]+]]: !struct.type<@MultiplierN::@MultiplierN<[@N]>>, %[[VAL_155:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Sub_1@859" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_154]][@out] : <@MultiplierN::@MultiplierN<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_154]][@comp] : <@MultiplierN::@MultiplierN<[@N]>>, !array.type<@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>> +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_154]][@comp$inputs] : <@MultiplierN::@MultiplierN<[@N]>>, !array.type<@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_163:[0-9a-zA-Z_\.]+]] = %[[VAL_161]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_156]], %[[VAL_164]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_163]], %[[VAL_165]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_166]]) %[[VAL_163]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_167:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_168:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_169:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Multiplier2::@Multiplier2<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_167]], %[[VAL_170]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_171]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_172:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_172]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_155]]{{\[}}%[[VAL_173]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_176:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_175]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_177:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_160]]{{\[}}%[[VAL_176]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_177]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_178]], %[[VAL_174]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_179:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_180:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_179]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_181:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_155]]{{\[}}%[[VAL_180]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_182:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_183:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_182]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_184:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_160]]{{\[}}%[[VAL_183]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_185:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_184]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_185]], %[[VAL_181]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_186:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_187:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_188:[0-9a-zA-Z_\.]+]] = %[[VAL_186]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_189:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_190:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_156]], %[[VAL_189]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_191:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_188]], %[[VAL_190]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_191]]) %[[VAL_188]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_192:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_193:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_192]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_194:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_159]]{{\[}}%[[VAL_193]]] : <@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_195:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_194]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_196:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_197:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_192]], %[[VAL_196]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_198:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_197]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_199:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_160]]{{\[}}%[[VAL_198]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_200:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_199]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_200]], %[[VAL_195]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_201:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_202:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_192]], %[[VAL_201]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_203:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_202]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_204:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_155]]{{\[}}%[[VAL_203]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_205:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_206:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_192]], %[[VAL_205]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_207:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_206]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_208:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_160]]{{\[}}%[[VAL_207]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_209:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_208]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_209]], %[[VAL_204]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_210:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_211:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_192]], %[[VAL_210]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_211]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_212:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_213:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_156]], %[[VAL_212]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_214:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_213]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_215:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_159]]{{\[}}%[[VAL_214]]] : <@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_216:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_215]][@out] : <@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_158]], %[[VAL_216]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_217:[0-9a-zA-Z_\.]+]] = poly.read_const @"N_Sub_1@859" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_218:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_217]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_219:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_220:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_221:[0-9a-zA-Z_\.]+]] = %[[VAL_219]] to %[[VAL_218]] step %[[VAL_220]] { +// CHECK-NEXT: %[[VAL_222:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_159]]{{\[}}%[[VAL_221]]] : <@"N_Sub_1@859" x !struct.type<@Multiplier2::@Multiplier2<[]>>>, !struct.type<@Multiplier2::@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_223:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_160]]{{\[}}%[[VAL_221]]] : <@"N_Sub_1@859" x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_224:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_223]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_225:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_223]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Multiplier2::@Multiplier2::@constrain(%[[VAL_222]], %[[VAL_224]], %[[VAL_225]]) : (!struct.type<@Multiplier2::@Multiplier2<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/39A.circom b/circom/tests/circom_doc_examples/39A.circom new file mode 100644 index 000000000..e643a532e --- /dev/null +++ b/circom/tests/circom_doc_examples/39A.circom @@ -0,0 +1,122 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input in; + signal output out; + out <== in + 1; +} + +template A(n) { + signal aux; + signal out; + if(n == 2) { + aux <== 2; + out <== B()(aux); + } else { + out <== 5; + } +} + +component main = A(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[3]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @aux : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @B_18_396 : !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.member @B_18_396$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:4 = scf.if %[[VAL_6]] -> (!pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[@in: !felt.type<"bn128">]>, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@aux] = %[[VAL_8]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_10]], @params = %[[VAL_9]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_4]][@in] = %[[VAL_8]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_12]], %[[VAL_13]] : index +// CHECK-NEXT: pod.write %[[VAL_11]][@count] = %[[VAL_14]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_14]], %[[VAL_15]] : index +// CHECK-NEXT: scf.if %[[VAL_16]] { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_18]]) : (!felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_11]][@comp] = %[[VAL_19]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@out] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_21]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_4]], %[[VAL_8]], %[[VAL_21]] : !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[@in: !felt.type<"bn128">]>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_22]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_3]], %[[VAL_4]], %[[VAL_2]], %[[VAL_22]] : !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[@in: !felt.type<"bn128">]>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@B_18_396$inputs] = %[[VAL_7]]#1 : <@A::@A<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]]#0[@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.writem %[[VAL_0]][@B_18_396] = %[[VAL_23]] : <@A::@A<[@n]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@aux] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@out] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@B_18_396] : <@A::@A<[@n]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@B_18_396$inputs] : <@A::@A<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_25]], %[[VAL_30]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_31]] { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_35]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_28]][@out] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_28]], %[[VAL_38]]) : (!struct.type<@B::@B<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_40]][@out] = %[[VAL_42]] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_40]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@out] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_44]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_45]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/39B.circom b/circom/tests/circom_doc_examples/39B.circom new file mode 100644 index 000000000..2a3e0021a --- /dev/null +++ b/circom/tests/circom_doc_examples/39B.circom @@ -0,0 +1,127 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input in; + signal output out; + out <== in + 1; +} + +template A(n) { + signal aux; + signal out; + if(n == 2) { + aux <== 2; + out <== B()(aux); + } else { + aux <== 0; + _ <== aux; + out <== 5; + } +} + +component main = A(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[3]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @aux : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @B_18_396 : !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.member @B_18_396$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]]:4 = scf.if %[[VAL_5]] -> (!pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[@in: !felt.type<"bn128">]>, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@aux] = %[[VAL_7]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_9]], @params = %[[VAL_8]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_3]][@in] = %[[VAL_7]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_11]], %[[VAL_12]] : index +// CHECK-NEXT: pod.write %[[VAL_10]][@count] = %[[VAL_13]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_13]], %[[VAL_14]] : index +// CHECK-NEXT: scf.if %[[VAL_15]] { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_17]]) : (!felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_10]][@comp] = %[[VAL_18]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@out] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_20]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_3]], %[[VAL_7]], %[[VAL_20]] : !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[@in: !felt.type<"bn128">]>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@aux] = %[[VAL_21]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_22]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_2]], %[[VAL_3]], %[[VAL_21]], %[[VAL_22]] : !pod.type<[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[@in: !felt.type<"bn128">]>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@B_18_396$inputs] = %[[VAL_6]]#1 : <@A::@A<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]]#0[@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.writem %[[VAL_0]][@B_18_396] = %[[VAL_23]] : <@A::@A<[@n]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@aux] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@out] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@B_18_396] : <@A::@A<[@n]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@B_18_396$inputs] : <@A::@A<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_25]], %[[VAL_30]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_31]] { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_35]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_28]][@out] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_28]], %[[VAL_39]]) : (!struct.type<@B::@B<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_41]][@out] = %[[VAL_43]] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_41]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@out] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_46]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/39C.circom b/circom/tests/circom_doc_examples/39C.circom new file mode 100644 index 000000000..e5de3ffeb --- /dev/null +++ b/circom/tests/circom_doc_examples/39C.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.1.5; + +template B() { + signal input in; + signal output out; + out <== in + 1; +} + +template A(n) { + signal out; + if(n == 2) { + // this demonstrates declaration of signal within a scope other than the initial one + signal aux <== 2; + out <== B()(aux); + } else { + out <== 5; + } +} + +component main = A(3); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/40A.circom b/circom/tests/circom_doc_examples/40A.circom new file mode 100644 index 000000000..cd97921c0 --- /dev/null +++ b/circom/tests/circom_doc_examples/40A.circom @@ -0,0 +1,146 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +template check_bits(n) { + signal input in; + component check = Num2Bits(n); + check.in <== in; +} + +component main = check_bits(10); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@check_bits::@check_bits<[10]>>} { +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_6]], %[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_15]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_31]], %[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_30]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_34]], %[[VAL_35]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_49]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_50]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_53]], %[[VAL_55]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_33]]#2, %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @check_bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @check_bits { +// CHECK-NEXT: struct.member @check : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.member @check$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@check_bits::@check_bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = struct.new : <@check_bits::@check_bits<[@n]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_60]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_62]], @params = %[[VAL_61]] } : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_59]][@in] = %[[VAL_56]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@count] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_64]], %[[VAL_65]] : index +// CHECK-NEXT: pod.write %[[VAL_63]][@count] = %[[VAL_66]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_66]], %[[VAL_67]] : index +// CHECK-NEXT: scf.if %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@params] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_59]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits::@Num2Bits::@compute(%[[VAL_70]]) : (!felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_63]][@comp] = %[[VAL_71]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_57]][@check$inputs] = %[[VAL_59]] : <@check_bits::@check_bits<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_57]][@check] = %[[VAL_72]] : <@check_bits::@check_bits<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: function.return %[[VAL_57]] : !struct.type<@check_bits::@check_bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_73:[0-9a-zA-Z_\.]+]]: !struct.type<@check_bits::@check_bits<[@n]>>, %[[VAL_74:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_73]][@check] : <@check_bits::@check_bits<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_73]][@check$inputs] : <@check_bits::@check_bits<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_78]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_77]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_81]], %[[VAL_74]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_77]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Num2Bits::@Num2Bits::@constrain(%[[VAL_76]], %[[VAL_82]]) : (!struct.type<@Num2Bits::@Num2Bits<[@n]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/40B.circom b/circom/tests/circom_doc_examples/40B.circom new file mode 100644 index 000000000..68eaa7b88 --- /dev/null +++ b/circom/tests/circom_doc_examples/40B.circom @@ -0,0 +1,150 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +template check_bits(n) { + signal input in; + component check = Num2Bits(n); + check.in <== in; + _ <== check.out; +} + +component main = check_bits(20); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@check_bits::@check_bits<[20]>>} { +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_6]], %[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_15]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_31]], %[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_30]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_34]], %[[VAL_35]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_49]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_50]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_53]], %[[VAL_55]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_33]]#2, %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @check_bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @check_bits { +// CHECK-NEXT: struct.member @check : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.member @check$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@check_bits::@check_bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = struct.new : <@check_bits::@check_bits<[@n]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_60]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_62]], @params = %[[VAL_61]] } : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_59]][@in] = %[[VAL_56]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@count] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_64]], %[[VAL_65]] : index +// CHECK-NEXT: pod.write %[[VAL_63]][@count] = %[[VAL_66]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_66]], %[[VAL_67]] : index +// CHECK-NEXT: scf.if %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@params] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_59]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits::@Num2Bits::@compute(%[[VAL_70]]) : (!felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_63]][@comp] = %[[VAL_71]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_72]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_57]][@check$inputs] = %[[VAL_59]] : <@check_bits::@check_bits<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_57]][@check] = %[[VAL_74]] : <@check_bits::@check_bits<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: function.return %[[VAL_57]] : !struct.type<@check_bits::@check_bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_75:[0-9a-zA-Z_\.]+]]: !struct.type<@check_bits::@check_bits<[@n]>>, %[[VAL_76:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_75]][@check] : <@check_bits::@check_bits<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_75]][@check$inputs] : <@check_bits::@check_bits<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_80]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_79]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_83]], %[[VAL_76]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_78]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_79]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Num2Bits::@Num2Bits::@constrain(%[[VAL_78]], %[[VAL_85]]) : (!struct.type<@Num2Bits::@Num2Bits<[@n]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/40C.circom b/circom/tests/circom_doc_examples/40C.circom new file mode 100644 index 000000000..4d04a4956 --- /dev/null +++ b/circom/tests/circom_doc_examples/40C.circom @@ -0,0 +1,148 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +template check_bits(n){ + signal input in; + _ <== Num2Bits(n)(in); +} + +component main = check_bits(16); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@check_bits::@check_bits<[16]>>} { +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_6]], %[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_15]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_31]], %[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_30]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_34]], %[[VAL_35]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_49]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_50]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_53]], %[[VAL_55]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_33]]#2, %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @check_bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @check_bits { +// CHECK-NEXT: struct.member @Num2Bits_25_562 : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.member @Num2Bits_25_562$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@check_bits::@check_bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = struct.new : <@check_bits::@check_bits<[@n]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_60]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_62]], @params = %[[VAL_61]] } : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_59]][@in] = %[[VAL_56]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@count] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_64]], %[[VAL_65]] : index +// CHECK-NEXT: pod.write %[[VAL_63]][@count] = %[[VAL_66]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_66]], %[[VAL_67]] : index +// CHECK-NEXT: scf.if %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@params] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_59]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits::@Num2Bits::@compute(%[[VAL_70]]) : (!felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_63]][@comp] = %[[VAL_71]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_72]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_57]][@Num2Bits_25_562$inputs] = %[[VAL_59]] : <@check_bits::@check_bits<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_57]][@Num2Bits_25_562] = %[[VAL_74]] : <@check_bits::@check_bits<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: function.return %[[VAL_57]] : !struct.type<@check_bits::@check_bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_75:[0-9a-zA-Z_\.]+]]: !struct.type<@check_bits::@check_bits<[@n]>>, %[[VAL_76:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_75]][@Num2Bits_25_562] : <@check_bits::@check_bits<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_75]][@Num2Bits_25_562$inputs] : <@check_bits::@check_bits<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_80]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_79]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_83]], %[[VAL_76]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_78]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_79]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Num2Bits::@Num2Bits::@constrain(%[[VAL_78]], %[[VAL_85]]) : (!struct.type<@Num2Bits::@Num2Bits<[@n]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circom_doc_examples/41.circom b/circom/tests/circom_doc_examples/41.circom new file mode 100644 index 000000000..82f00cbbf --- /dev/null +++ b/circom/tests/circom_doc_examples/41.circom @@ -0,0 +1,165 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +template parity(n) { + signal input in; + signal output out; + component check = Num2Bits(n); + check.in <== in; + out <== check.out[0]; + _ <== check.out; +} + +component main = parity(10); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@parity::@parity<[10]>>} { +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_6]], %[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_15]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_31]], %[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_30]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_34]], %[[VAL_35]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_49]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_50]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_53]], %[[VAL_55]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_33]]#2, %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @parity { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @parity { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @check : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.member @check$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@parity::@parity<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = struct.new : <@parity::@parity<[@n]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_60]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_62]], @params = %[[VAL_61]] } : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_59]][@in] = %[[VAL_56]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@count] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_64]], %[[VAL_65]] : index +// CHECK-NEXT: pod.write %[[VAL_63]][@count] = %[[VAL_66]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_66]], %[[VAL_67]] : index +// CHECK-NEXT: scf.if %[[VAL_68]] { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@params] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_59]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits::@Num2Bits::@compute(%[[VAL_70]]) : (!felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_63]][@comp] = %[[VAL_71]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_72]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_74]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_73]]{{\[}}%[[VAL_75]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_57]][@out] = %[[VAL_76]] : <@parity::@parity<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_77]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_57]][@check$inputs] = %[[VAL_59]] : <@parity::@parity<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_63]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_57]][@check] = %[[VAL_79]] : <@parity::@parity<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: function.return %[[VAL_57]] : !struct.type<@parity::@parity<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_80:[0-9a-zA-Z_\.]+]]: !struct.type<@parity::@parity<[@n]>>, %[[VAL_81:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_80]][@out] : <@parity::@parity<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_80]][@check] : <@parity::@parity<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_80]][@check$inputs] : <@parity::@parity<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_86]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_85]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_89]], %[[VAL_81]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_84]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_91]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_90]]{{\[}}%[[VAL_92]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_83]], %[[VAL_93]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_84]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_85]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Num2Bits::@Num2Bits::@constrain(%[[VAL_84]], %[[VAL_95]]) : (!struct.type<@Num2Bits::@Num2Bits<[@n]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/circomlib/gates.circom b/circom/tests/circomlib/gates.circom new file mode 100644 index 000000000..081a08f26 --- /dev/null +++ b/circom/tests/circomlib/gates.circom @@ -0,0 +1,356 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +/* + Copyright 2018 0KIMS association. + + This file is part of circom (Zero Knowledge Circuit Compiler). + + circom is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + circom is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with circom. If not, see . +*/ + +pragma circom 2.0.0; + +template XOR() { + signal input a; + signal input b; + signal output out; + + out <== a + b - 2*a*b; +} + +template AND() { + signal input a; + signal input b; + signal output out; + + out <== a*b; +} + +template OR() { + signal input a; + signal input b; + signal output out; + + out <== a + b - a*b; +} + +template NOT() { + signal input in; + signal output out; + + out <== 1 + in - 2*in; +} + +template NAND() { + signal input a; + signal input b; + signal output out; + + out <== 1 - a*b; +} + +template NOR() { + signal input a; + signal input b; + signal output out; + + out <== a*b + 1 - a - b; +} + +template MultiAND(n) { + signal input in[n]; + signal output out; + component and1; + component and2; + component ands[2]; + if (n==1) { + out <== in[0]; + } else if (n==2) { + and1 = AND(); + and1.a <== in[0]; + and1.b <== in[1]; + out <== and1.out; + } else { + and2 = AND(); + var n1 = n\2; + var n2 = n-n\2; + ands[0] = MultiAND(n1); + ands[1] = MultiAND(n2); + var i; + for (i=0; i>} { +// CHECK-NEXT: poly.template @AND { +// CHECK-NEXT: struct.def @AND { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@AND::@AND<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@AND::@AND<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@AND::@AND<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@AND::@AND<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@AND::@AND<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@AND::@AND<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Main { +// CHECK-NEXT: struct.def @Main { +// CHECK-NEXT: struct.member @and1 : !struct.type<@AND::@AND<[]>> +// CHECK-NEXT: struct.member @and1$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: struct.member @nand1 : !struct.type<@NAND::@NAND<[]>> +// CHECK-NEXT: struct.member @nand1$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: struct.member @nor1 : !struct.type<@NOR::@NOR<[]>> +// CHECK-NEXT: struct.member @nor1$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: struct.member @not1 : !struct.type<@NOT::@NOT<[]>> +// CHECK-NEXT: struct.member @not1$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: struct.member @or1 : !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: struct.member @or1$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: struct.member @xor1 : !struct.type<@XOR::@XOR<[]>> +// CHECK-NEXT: struct.member @xor1$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@Main::@Main<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@Main::@Main<[]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_17]], @params = %[[VAL_16]] } : <[@count: index, @comp: !struct.type<@XOR::@XOR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_20]], @params = %[[VAL_19]] } : <[@count: index, @comp: !struct.type<@AND::@AND<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_23]], @params = %[[VAL_22]] } : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_26]], @params = %[[VAL_25]] } : <[@count: index, @comp: !struct.type<@NOT::@NOT<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_29]], @params = %[[VAL_28]] } : <[@count: index, @comp: !struct.type<@NAND::@NAND<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_32]], @params = %[[VAL_31]] } : <[@count: index, @comp: !struct.type<@NOR::@NOR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: struct.writem %[[VAL_9]][@and1$inputs] = %[[VAL_10]] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@comp] : <[@count: index, @comp: !struct.type<@AND::@AND<[]>>, @params: !pod.type<[]>]>, !struct.type<@AND::@AND<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@and1] = %[[VAL_34]] : <@Main::@Main<[]>>, !struct.type<@AND::@AND<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@nand1$inputs] = %[[VAL_11]] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_30]][@comp] : <[@count: index, @comp: !struct.type<@NAND::@NAND<[]>>, @params: !pod.type<[]>]>, !struct.type<@NAND::@NAND<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@nand1] = %[[VAL_35]] : <@Main::@Main<[]>>, !struct.type<@NAND::@NAND<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@nor1$inputs] = %[[VAL_12]] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_33]][@comp] : <[@count: index, @comp: !struct.type<@NOR::@NOR<[]>>, @params: !pod.type<[]>]>, !struct.type<@NOR::@NOR<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@nor1] = %[[VAL_36]] : <@Main::@Main<[]>>, !struct.type<@NOR::@NOR<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@not1$inputs] = %[[VAL_13]] : <@Main::@Main<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_27]][@comp] : <[@count: index, @comp: !struct.type<@NOT::@NOT<[]>>, @params: !pod.type<[]>]>, !struct.type<@NOT::@NOT<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@not1] = %[[VAL_37]] : <@Main::@Main<[]>>, !struct.type<@NOT::@NOT<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@or1$inputs] = %[[VAL_14]] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@comp] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@or1] = %[[VAL_38]] : <@Main::@Main<[]>>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@xor1$inputs] = %[[VAL_15]] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@comp] : <[@count: index, @comp: !struct.type<@XOR::@XOR<[]>>, @params: !pod.type<[]>]>, !struct.type<@XOR::@XOR<[]>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@xor1] = %[[VAL_39]] : <@Main::@Main<[]>>, !struct.type<@XOR::@XOR<[]>> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@Main::@Main<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_40:[0-9a-zA-Z_\.]+]]: !struct.type<@Main::@Main<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@and1] : <@Main::@Main<[]>>, !struct.type<@AND::@AND<[]>> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@and1$inputs] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@nand1] : <@Main::@Main<[]>>, !struct.type<@NAND::@NAND<[]>> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@nand1$inputs] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@nor1] : <@Main::@Main<[]>>, !struct.type<@NOR::@NOR<[]>> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@nor1$inputs] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@not1] : <@Main::@Main<[]>>, !struct.type<@NOT::@NOT<[]>> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@not1$inputs] : <@Main::@Main<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@or1] : <@Main::@Main<[]>>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@or1$inputs] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@xor1] : <@Main::@Main<[]>>, !struct.type<@XOR::@XOR<[]>> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@xor1$inputs] : <@Main::@Main<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@XOR::@XOR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@AND::@AND<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@NOT::@NOT<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@NAND::@NAND<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@NOR::@NOR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @AND::@AND::@constrain(%[[VAL_41]], %[[VAL_65]], %[[VAL_66]]) : (!struct.type<@AND::@AND<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_44]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_44]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @NAND::@NAND::@constrain(%[[VAL_43]], %[[VAL_67]], %[[VAL_68]]) : (!struct.type<@NAND::@NAND<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_46]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_46]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @NOR::@NOR::@constrain(%[[VAL_45]], %[[VAL_69]], %[[VAL_70]]) : (!struct.type<@NOR::@NOR<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @NOT::@NOT::@constrain(%[[VAL_47]], %[[VAL_71]]) : (!struct.type<@NOT::@NOT<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_50]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_50]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @OR::@OR::@constrain(%[[VAL_49]], %[[VAL_72]], %[[VAL_73]]) : (!struct.type<@OR::@OR<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_52]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_52]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @XOR::@XOR::@constrain(%[[VAL_51]], %[[VAL_74]], %[[VAL_75]]) : (!struct.type<@XOR::@XOR<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @NAND { +// CHECK-NEXT: struct.def @NAND { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_76:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_77:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@NAND::@NAND<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.new : <@NAND::@NAND<[]>> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_76]], %[[VAL_77]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_79]], %[[VAL_80]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_78]][@out] = %[[VAL_81]] : <@NAND::@NAND<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_78]] : !struct.type<@NAND::@NAND<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_82:[0-9a-zA-Z_\.]+]]: !struct.type<@NAND::@NAND<[]>>, %[[VAL_83:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_84:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_82]][@out] : <@NAND::@NAND<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_83]], %[[VAL_84]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_86]], %[[VAL_87]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_85]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @NOR { +// CHECK-NEXT: struct.def @NOR { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_89:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_90:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@NOR::@NOR<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = struct.new : <@NOR::@NOR<[]>> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_89]], %[[VAL_90]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_92]], %[[VAL_93]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_94]], %[[VAL_89]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_95]], %[[VAL_90]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_91]][@out] = %[[VAL_96]] : <@NOR::@NOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_91]] : !struct.type<@NOR::@NOR<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_97:[0-9a-zA-Z_\.]+]]: !struct.type<@NOR::@NOR<[]>>, %[[VAL_98:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_99:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_97]][@out] : <@NOR::@NOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_98]], %[[VAL_99]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_101]], %[[VAL_102]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_103]], %[[VAL_98]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_104]], %[[VAL_99]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_100]], %[[VAL_105]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @NOT { +// CHECK-NEXT: struct.def @NOT { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_106:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@NOT::@NOT<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = struct.new : <@NOT::@NOT<[]>> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_108]], %[[VAL_106]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_110]], %[[VAL_106]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_109]], %[[VAL_111]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_107]][@out] = %[[VAL_112]] : <@NOT::@NOT<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_107]] : !struct.type<@NOT::@NOT<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_113:[0-9a-zA-Z_\.]+]]: !struct.type<@NOT::@NOT<[]>>, %[[VAL_114:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_113]][@out] : <@NOT::@NOT<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_116]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_118]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_117]], %[[VAL_119]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_115]], %[[VAL_120]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @OR { +// CHECK-NEXT: struct.def @OR { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_121:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_122:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@OR::@OR<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = struct.new : <@OR::@OR<[]>> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_121]], %[[VAL_122]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_121]], %[[VAL_122]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_124]], %[[VAL_125]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_123]][@out] = %[[VAL_126]] : <@OR::@OR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_123]] : !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_127:[0-9a-zA-Z_\.]+]]: !struct.type<@OR::@OR<[]>>, %[[VAL_128:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_129:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_127]][@out] : <@OR::@OR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_128]], %[[VAL_129]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_128]], %[[VAL_129]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_131]], %[[VAL_132]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_130]], %[[VAL_133]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @XOR { +// CHECK-NEXT: struct.def @XOR { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_134:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_135:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@XOR::@XOR<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = struct.new : <@XOR::@XOR<[]>> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_134]], %[[VAL_135]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_138]], %[[VAL_134]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_139]], %[[VAL_135]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_137]], %[[VAL_140]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_136]][@out] = %[[VAL_141]] : <@XOR::@XOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_136]] : !struct.type<@XOR::@XOR<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_142:[0-9a-zA-Z_\.]+]]: !struct.type<@XOR::@XOR<[]>>, %[[VAL_143:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_144:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_142]][@out] : <@XOR::@XOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_143]], %[[VAL_144]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_147]], %[[VAL_143]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_148]], %[[VAL_144]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_146]], %[[VAL_149]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_145]], %[[VAL_150]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/arr_cpy_store.circom b/circom/tests/constraints/arr_cpy_store.circom new file mode 100644 index 000000000..94a642b04 --- /dev/null +++ b/circom/tests/constraints/arr_cpy_store.circom @@ -0,0 +1,103 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sum(N) { + signal input inp[N]; +} + +template Foo(N) { + signal input inp[N]; + signal input out[N]; + + component c = Sum(N); + for (var i = N; i <= N; i++) { + c.inp <== inp; + } +} + +component main = Foo(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Foo::@Foo<[2]>>} { +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: struct.member @c : !struct.type<@Sum::@Sum<[@N]>> +// CHECK-NEXT: struct.member @c$inputs : !pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) -> !struct.type<@Foo::@Foo<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[@N]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<@N x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_5]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_7]], @params = %[[VAL_6]] } : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@N]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]>, !felt.type<"bn128">) -> (!pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_11]], %[[VAL_11]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_12]]) %[[VAL_10]], %[[VAL_11]] : !pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: pod.write %[[VAL_13]][@inp] = %[[VAL_0]] : <[@inp: !array.type<@N x !felt.type<"bn128">>]>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_8]][@count] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@N]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_15]], %[[VAL_16]] : index +// CHECK-NEXT: pod.write %[[VAL_8]][@count] = %[[VAL_17]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@N]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_17]], %[[VAL_18]] : index +// CHECK-NEXT: scf.if %[[VAL_19]] { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_8]][@params] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@N]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !pod.type<[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@inp] : <[@inp: !array.type<@N x !felt.type<"bn128">>]>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = function.call @Sum::@Sum::@compute(%[[VAL_21]]) : (!array.type<@N x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@N]>> +// CHECK-NEXT: pod.write %[[VAL_8]][@comp] = %[[VAL_22]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@N]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@N]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_24]] : !pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_2]][@c$inputs] = %[[VAL_9]]#0 : <@Foo::@Foo<[@N]>>, !pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_8]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@N]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@N]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_25]] : <@Foo::@Foo<[@N]>>, !struct.type<@Sum::@Sum<[@N]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Foo::@Foo<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[@N]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@c] : <@Foo::@Foo<[@N]>>, !struct.type<@Sum::@Sum<[@N]>> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@c$inputs] : <@Foo::@Foo<[@N]>>, !pod.type<[@inp: !array.type<@N x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_32]] } : <[@N: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@N]>>, @params: !pod.type<[@N: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_29]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_36]], %[[VAL_36]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@inp] : <[@inp: !array.type<@N x !felt.type<"bn128">>]>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_39]], %[[VAL_27]] : !array.type<@N x !felt.type<"bn128">>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@inp] : <[@inp: !array.type<@N x !felt.type<"bn128">>]>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum::@Sum::@constrain(%[[VAL_30]], %[[VAL_42]]) : (!struct.type<@Sum::@Sum<[@N]>>, !array.type<@N x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sum { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @Sum { +// CHECK-NEXT: function.def @compute(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum::@Sum<[@N]>> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_44]] : !struct.type<@Sum::@Sum<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum::@Sum<[@N]>>, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/known0.circom b/circom/tests/constraints/known0.circom new file mode 100644 index 000000000..9107cef34 --- /dev/null +++ b/circom/tests/constraints/known0.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + 0 === 1; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: constrain.eq %[[VAL_2]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/known1.circom b/circom/tests/constraints/known1.circom new file mode 100644 index 000000000..34d93178e --- /dev/null +++ b/circom/tests/constraints/known1.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal output val; + + val <-- 0; + val * (val - 1) === 0; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @val : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writem %[[VAL_0]][@val] = %[[VAL_1]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@val] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_3]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/known2.circom b/circom/tests/constraints/known2.circom new file mode 100644 index 000000000..03685f233 --- /dev/null +++ b/circom/tests/constraints/known2.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal output val; + + val <-- 0; + 0 === val * (val - 1); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @val : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writem %[[VAL_0]][@val] = %[[VAL_1]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@val] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_3]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_3]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/known3.circom b/circom/tests/constraints/known3.circom new file mode 100644 index 000000000..e1a14e4a6 --- /dev/null +++ b/circom/tests/constraints/known3.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal output val; + + val <-- 0; + val === 0; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @val : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writem %[[VAL_0]][@val] = %[[VAL_1]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@val] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/known4.circom b/circom/tests/constraints/known4.circom new file mode 100644 index 000000000..3c2e6065c --- /dev/null +++ b/circom/tests/constraints/known4.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal output val; + + val <-- 0; + 0 === val; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @val : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writem %[[VAL_0]][@val] = %[[VAL_1]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@val] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/known5.circom b/circom/tests/constraints/known5.circom new file mode 100644 index 000000000..4d25106d9 --- /dev/null +++ b/circom/tests/constraints/known5.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal output valA; + signal output valB; + + valA <-- 0; + valB <== valA * 1; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @valA : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @valB : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writem %[[VAL_0]][@valA] = %[[VAL_1]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_1]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@valB] = %[[VAL_3]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@valA] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@valB] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/missed_index_simple.circom b/circom/tests/constraints/missed_index_simple.circom new file mode 100644 index 000000000..708fc3e73 --- /dev/null +++ b/circom/tests/constraints/missed_index_simple.circom @@ -0,0 +1,175 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Mult() { + signal input in[2]; + signal output out; +} + +template Good(N) { + signal input inp[N][2]; + component c[N]; + + for (var i = 0; i < N; i++) { + c[i] = Mult(); + for (var j = 0; j < 2; j++) { + c[i].in[j] <== inp[i][j]; + } + + c[i].out === 77; + } +} + +component main = Good(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Good::@Good<[2]>>} { +// CHECK-NEXT: poly.template @Good { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @Good { +// CHECK-NEXT: struct.member @c : !array.type<@N x !struct.type<@Mult::@Mult<[]>>> +// CHECK-NEXT: struct.member @c$inputs : !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N,2 x !felt.type<"bn128">>) -> !struct.type<@Good::@Good<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Good::@Good<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <@N x !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_5]]) : (!array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128">) -> (!array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_8]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_7]], %[[VAL_8]] : !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_13]], @params = %[[VAL_12]] } : <[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_14]] : <@N x !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_10]], %[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_16]]) : (!array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128">) -> (!array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_19]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_18]], %[[VAL_19]] : !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_24]], %[[VAL_25]]] : <@N,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_22]]{{\[}}%[[VAL_27]]] : <@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_29]]{{\[}}%[[VAL_30]]] = %[[VAL_26]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_22]]{{\[}}%[[VAL_31]]] : <@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: pod.write %[[VAL_32]][@in] = %[[VAL_29]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_22]]{{\[}}%[[VAL_33]]] = %[[VAL_32]] : <@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_34]]] : <@N x !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_35]][@count] : <[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_36]], %[[VAL_37]] : index +// CHECK-NEXT: pod.write %[[VAL_35]][@count] = %[[VAL_38]] : <[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_38]], %[[VAL_39]] : index +// CHECK-NEXT: scf.if %[[VAL_40]] { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_35]][@params] : <[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_32]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = function.call @Mult::@Mult::@compute(%[[VAL_42]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@Mult::@Mult<[]>> +// CHECK-NEXT: pod.write %[[VAL_35]][@comp] = %[[VAL_43]] : <[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@Mult::@Mult<[]>> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_44]]] = %[[VAL_35]] : <@N x !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_22]], %[[VAL_46]] : !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_17]]#0, %[[VAL_48]] : !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@c$inputs] = %[[VAL_6]]#0 : <@Good::@Good<[@N]>>, !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = array.new : <@N x !struct.type<@Mult::@Mult<[]>>> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = poly.read_const @N : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_53:[0-9a-zA-Z_\.]+]] = %[[VAL_51]] to %[[VAL_50]] step %[[VAL_52]] { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_53]]] : <@N x !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_54]][@comp] : <[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]>, !struct.type<@Mult::@Mult<[]>> +// CHECK-NEXT: array.write %[[VAL_49]]{{\[}}%[[VAL_53]]] = %[[VAL_55]] : <@N x !struct.type<@Mult::@Mult<[]>>>, !struct.type<@Mult::@Mult<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@c] = %[[VAL_49]] : <@Good::@Good<[@N]>>, !array.type<@N x !struct.type<@Mult::@Mult<[]>>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Good::@Good<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@Good::@Good<[@N]>>, %[[VAL_57:[0-9a-zA-Z_\.]+]]: !array.type<@N,2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_56]][@c] : <@Good::@Good<[@N]>>, !array.type<@N x !struct.type<@Mult::@Mult<[]>>> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_56]][@c$inputs] : <@Good::@Good<[@N]>>, !array.type<@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_63:[0-9a-zA-Z_\.]+]] = %[[VAL_61]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_63]], %[[VAL_58]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_64]]) %[[VAL_63]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_65:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Mult::@Mult<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_68]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_70]], %[[VAL_71]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_72]]) %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_73:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_73]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_57]]{{\[}}%[[VAL_74]], %[[VAL_75]]] : <@N,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_60]]{{\[}}%[[VAL_77]]] : <@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_78]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_73]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_79]]{{\[}}%[[VAL_80]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_81]], %[[VAL_76]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_73]], %[[VAL_82]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_59]]{{\[}}%[[VAL_84]]] : <@N x !struct.type<@Mult::@Mult<[]>>>, !struct.type<@Mult::@Mult<[]>> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_85]][@out] : <@Mult::@Mult<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 77 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_86]], %[[VAL_87]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_65]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_89]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = poly.read_const @N : index +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_93:[0-9a-zA-Z_\.]+]] = %[[VAL_91]] to %[[VAL_90]] step %[[VAL_92]] { +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_59]]{{\[}}%[[VAL_93]]] : <@N x !struct.type<@Mult::@Mult<[]>>>, !struct.type<@Mult::@Mult<[]>> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_60]]{{\[}}%[[VAL_93]]] : <@N x !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_95]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Mult::@Mult::@constrain(%[[VAL_94]], %[[VAL_96]]) : (!struct.type<@Mult::@Mult<[]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Mult { +// CHECK-NEXT: struct.def @Mult { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_97:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@Mult::@Mult<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = struct.new : <@Mult::@Mult<[]>> +// CHECK-NEXT: function.return %[[VAL_98]] : !struct.type<@Mult::@Mult<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_99:[0-9a-zA-Z_\.]+]]: !struct.type<@Mult::@Mult<[]>>, %[[VAL_100:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_99]][@out] : <@Mult::@Mult<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/with_call_scalars.circom b/circom/tests/constraints/with_call_scalars.circom new file mode 100644 index 000000000..173f41be2 --- /dev/null +++ b/circom/tests/constraints/with_call_scalars.circom @@ -0,0 +1,77 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function feeShiftTable(i) { + var out[2] = [3,9]; + return out[i]; +} + +template ComputeFee() { + signal output feeOut[2]; + + for (var i = 0; i < 2; i++) { + feeOut[i] <== feeShiftTable(i); + } +} + +component main = ComputeFee(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ComputeFee::@ComputeFee<[]>>} { +// CHECK-NEXT: function.def @feeShiftTable(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_4]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_5]]{{\[}}%[[VAL_6]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @ComputeFee { +// CHECK-NEXT: struct.def @ComputeFee { +// CHECK-NEXT: struct.member @feeOut : !array.type<2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ComputeFee::@ComputeFee<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@ComputeFee::@ComputeFee<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_10]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_12]], %[[VAL_13]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_14]]) %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = function.call @feeShiftTable(%[[VAL_15]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_9]]{{\[}}%[[VAL_17]]] = %[[VAL_16]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_15]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_8]][@feeOut] = %[[VAL_9]] : <@ComputeFee::@ComputeFee<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@ComputeFee::@ComputeFee<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !struct.type<@ComputeFee::@ComputeFee<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@feeOut] : <@ComputeFee::@ComputeFee<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_24:[0-9a-zA-Z_\.]+]] = %[[VAL_22]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_24]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_26]]) %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @feeShiftTable(%[[VAL_27]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_21]]{{\[}}%[[VAL_29]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/constraints/with_call_vec_arg.circom b/circom/tests/constraints/with_call_vec_arg.circom new file mode 100644 index 000000000..1e8eb14ae --- /dev/null +++ b/circom/tests/constraints/with_call_vec_arg.circom @@ -0,0 +1,23 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function feeShiftTable(a, i) { + return a[i]; +} + +template ComputeFee() { + signal output feeOut[2]; + var temp[2][2] = [[3,9],[6,7]]; + + for (var i = 0; i < 2; i++) { + feeOut[i] <== feeShiftTable(temp[i], i); + } +} + +component main = ComputeFee(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/constraints/with_call_vec_ret.circom b/circom/tests/constraints/with_call_vec_ret.circom new file mode 100644 index 000000000..0ad5275e6 --- /dev/null +++ b/circom/tests/constraints/with_call_vec_ret.circom @@ -0,0 +1,23 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function feeShiftTable() { + var out[2] = [3,9]; + return out; +} + +template ComputeFee() { + signal output feeOut[3][2]; + + for (var i = 0; i < 3; i++) { + feeOut[i] <== feeShiftTable(); + } +} + +component main = ComputeFee(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/bigmod_01.circom b/circom/tests/controlflow/bigmod_01.circom new file mode 100644 index 000000000..e89b9d2d1 --- /dev/null +++ b/circom/tests/controlflow/bigmod_01.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function short_div(k) { + if (k == 0) { + return k; + } else { + return -k; + } +} + +function long_div(){ + var out[1]; + out[0] = short_div(2); + return out; +} + +template BigModOld() { + var out[1] = long_div(); +} + +component main = BigModOld(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/bigmod_02.circom b/circom/tests/controlflow/bigmod_02.circom new file mode 100644 index 000000000..2f2099135 --- /dev/null +++ b/circom/tests/controlflow/bigmod_02.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function short_div(n) { + if (n > 1) { + if (n > 5) { + return 2; + } else { + return 1; + } + } else { + return 0; + } +} + +function long_div(){ + var out[1]; + out[0] = short_div(8); + return out; +} + +template BigModOld() { + var longdiv[1] = long_div(); +} + +component main = BigModOld(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/bigmod_03.circom b/circom/tests/controlflow/bigmod_03.circom new file mode 100644 index 000000000..223bcc0df --- /dev/null +++ b/circom/tests/controlflow/bigmod_03.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function long_div2(n, k, m, a) { + var dividend[5]; + for (var i = m; i >= 0; i--) { + if (i == m) { + dividend[k] = 0; + for (var j = 0; j < k; j++) { + dividend[j] = a[j + m]; + } + } + } + return dividend; +} + +template BigModOld(n, k) { + signal input a[2 * k]; + var r[5] = long_div2(n, k, k, a); +} + +component main = BigModOld(8, 2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/bigmod_04.circom b/circom/tests/controlflow/bigmod_04.circom new file mode 100644 index 000000000..e2abda6c5 --- /dev/null +++ b/circom/tests/controlflow/bigmod_04.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function long_div2(n, k, m, a) { + var dividend[5]; + for (var i = m; i >= 0; i--) { + if (i == m) { + dividend[k] = 0; + for (var j = 0; j < k; j++) { + dividend[j] = a[j + m]; + } + } else { + for (var j = k; j >= 0; j--) { + dividend[j] = a[j + i]; + } + } + } + return dividend; +} + +template BigModOld(n, k) { + signal input a[2 * k]; + var r[5] = long_div2(n, k, k, a); +} + +component main = BigModOld(8, 2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/bigmod_05.circom b/circom/tests/controlflow/bigmod_05.circom new file mode 100644 index 000000000..80655e821 --- /dev/null +++ b/circom/tests/controlflow/bigmod_05.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function long_div2(n, k, m, a) { + var dividend[5]; + for (var i = m; i >= 0; i--) { + if (i == m) { + dividend[k] = 0; + for (var j = k - 1; j >= 0; j-=2) { + dividend[j] = a[j + m]; + } + for (var j = k - 2; j >= 0; j-=2) { + dividend[j] = a[j + m]; + } + } + } + return dividend; +} + +template BigModOld(n, k) { + signal input a[2 * k]; + var r[5] = long_div2(n, k, k, a); +} + +component main = BigModOld(8, 2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/bigmod_06.circom b/circom/tests/controlflow/bigmod_06.circom new file mode 100644 index 000000000..6df9b5296 --- /dev/null +++ b/circom/tests/controlflow/bigmod_06.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function identity(n) { + return n; +} + +function short_div(n) { + var ret; + if (n != 0) { + ret = identity(n); + } + return ret; +} + +function long_div(n) { + var out[1]; + out[0] = short_div(n); + return out; +} + +template BigModOld(n) { + var r[1] = long_div(n); +} + +component main = BigModOld(2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/branch_in_func_00.circom b/circom/tests/controlflow/branch_in_func_00.circom new file mode 100644 index 000000000..40c8717e2 --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_00.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(n){ + var x; // default initialization to 0 + if (n < 0) { + var y = 12; + var x = 23; + } else { + var y = 67; + var x = 74; + var z = 73; + } + return x; +} + +template C() { + signal input in; + signal output out; + out <-- f(in); +} + +component main = C(); + +// +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_3]] { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 23 +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 67 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 74 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 73 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: function.return %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_10]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_11]][@out] = %[[VAL_12]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_11]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_func_01.circom b/circom/tests/controlflow/branch_in_func_01.circom new file mode 100644 index 000000000..72120ecfc --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_01.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function negative(n){ + if (n < 0) { + return 1; + } else { + return 0; + } +} + +template C() { + signal input in; + signal output out; + out <-- negative(in); +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @negative(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@out] = %[[VAL_8]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_func_02.circom b/circom/tests/controlflow/branch_in_func_02.circom new file mode 100644 index 000000000..7ccdbc72a --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_02.circom @@ -0,0 +1,58 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function negative(n){ + if (n < 0) { + return 1; + } + return 0; +} + +template C() { + signal input in; + signal output out; + out <-- negative(in); +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @negative(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_3]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_5]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_1]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_10]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_11]][@out] = %[[VAL_12]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_11]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_func_03.circom b/circom/tests/controlflow/branch_in_func_03.circom new file mode 100644 index 000000000..97e36d7fb --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_03.circom @@ -0,0 +1,54 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function negative(n){ + var x; // default initialization to 0 + if (n < 0) { + x = 1; + } else { + x = 0; + } + return x; +} + +template C() { + signal input in; + signal output out; + out <-- negative(in); +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @negative(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_7]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_8]][@out] = %[[VAL_9]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_10]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_func_04.circom b/circom/tests/controlflow/branch_in_func_04.circom new file mode 100644 index 000000000..9c0e4cda3 --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_04.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function negative(n){ + var x; // default initialization to 0 + if (n < 0) { + x = 1; + } + return x; +} + +template C() { + signal input in; + signal output out; + out <-- negative(in); +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @negative(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@out] = %[[VAL_8]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_func_05.circom b/circom/tests/controlflow/branch_in_func_05.circom new file mode 100644 index 000000000..f21bfd11c --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_05.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function complicated(n){ + var x = 99; + var y = 88; + var z = 77; + if (n < 0) { + x = 1; + y = 2; + z = 4; + } + return x + y + z; +} + +template C() { + signal input in; + signal output out; + out <-- complicated(in); +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @complicated(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 88 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 77 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_5]] -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]], %[[VAL_2]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_6]]#0, %[[VAL_6]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_6]]#2 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @complicated(%[[VAL_12]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_13]][@out] = %[[VAL_14]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_13]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_func_06.circom b/circom/tests/controlflow/branch_in_func_06.circom new file mode 100644 index 000000000..9712b5b1f --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_06.circom @@ -0,0 +1,71 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function complicated(n){ + var x = 0; + var y = 0; + var z = 0; + if (n < 0) { + x = 1; + y = 2; + z = 4; + } else { + return n; + } + return x + y + z; +} + +template C() { + signal input in; + signal output out; + out <-- complicated(in); +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @complicated(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:5 = scf.if %[[VAL_6]] -> (i1, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_1]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : i1, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_12]], %[[VAL_0]], %[[VAL_2]], %[[VAL_3]], %[[VAL_4]] : i1, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_7]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_7]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]]#2, %[[VAL_7]]#3 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_7]]#4 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @complicated(%[[VAL_16]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_17]][@out] = %[[VAL_18]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_17]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_template_01.circom b/circom/tests/controlflow/branch_in_template_01.circom new file mode 100644 index 000000000..d824a27d7 --- /dev/null +++ b/circom/tests/controlflow/branch_in_template_01.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Conditional() { + signal input inp; + var q; + if (inp) { + q = 0; + } else { + q = 1; + } +} + +component main = Conditional(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Conditional::@Conditional<[]>>} { +// CHECK-NEXT: poly.template @Conditional { +// CHECK-NEXT: struct.def @Conditional { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Conditional::@Conditional<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Conditional::@Conditional<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Conditional::@Conditional<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@Conditional::@Conditional<[]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_9]], %[[VAL_11]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_12]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/branch_in_template_02.circom b/circom/tests/controlflow/branch_in_template_02.circom new file mode 100644 index 000000000..e97a58025 --- /dev/null +++ b/circom/tests/controlflow/branch_in_template_02.circom @@ -0,0 +1,54 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input in1; + signal input in2; + signal output out; + + var x; + if (in1 > 0) { + x = in1; + } else { + x = in2; + } + out <-- x; +} + +component main = B(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[]>>} { +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_6]] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_7]][@out] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_8]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_if_1.circom b/circom/tests/controlflow/early_return_if_1.circom new file mode 100644 index 000000000..cf679031b --- /dev/null +++ b/circom/tests/controlflow/early_return_if_1.circom @@ -0,0 +1,63 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(i, n) { + if (n == 0) { + return i; + assert(0 == 1); // Unreachable because of the early return above + } + return 0; +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp, 0); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_4]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_0]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_2]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_10]], %[[VAL_12]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_11]][@outp] = %[[VAL_13]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_11]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_15]], %[[VAL_17]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_16]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_if_2.circom b/circom/tests/controlflow/early_return_if_2.circom new file mode 100644 index 000000000..5a5018037 --- /dev/null +++ b/circom/tests/controlflow/early_return_if_2.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function earlyReturnFn(inp, n, m, k, a) { + if (n == 0) { + return inp; + // Everything below is unreachable because of the return above + var dividend[5]; + for (var i = m; i >= 0; i--) { + if (i == m) { + dividend[k] = 0; + for (var j = 0; j < k; j++) { + dividend[j] = a[j + m]; + } + } else { + for (var j = k; j >= 0; j--) { + dividend[j] = a[j + i]; + } + } + } + } + return 0; +} + +template EarlyReturn() { + signal input inp; + signal input a[10]; + signal output outp; + + outp <== earlyReturnFn(inp, 0, inp, inp, a); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/early_return_if_3.circom b/circom/tests/controlflow/early_return_if_3.circom new file mode 100644 index 000000000..b14e30bb1 --- /dev/null +++ b/circom/tests/controlflow/early_return_if_3.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + if (in < 10) { + if (in == 0) { + return in + 1; + } else { + return in + 2; + } + } else { + return in + 3; + } + return -1; // Syntactically unreachable because all branches above return +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_13]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_14]][@outp] = %[[VAL_15]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_14]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-DAG: %[[VAL_19:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_17]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_18]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_if_4.circom b/circom/tests/controlflow/early_return_if_4.circom new file mode 100644 index 000000000..a4c6fd96d --- /dev/null +++ b/circom/tests/controlflow/early_return_if_4.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + var x = 0; + if (in < 10) { + x = 2; + } else { + return in + 3; + } + return x; +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_4]] -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_1]], %[[VAL_6]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_9]], %[[VAL_2]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_5]]#2 : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_12]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_13]][@outp] = %[[VAL_14]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_13]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_16]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_17]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_if_5.circom b/circom/tests/controlflow/early_return_if_5.circom new file mode 100644 index 000000000..1ded2b1dc --- /dev/null +++ b/circom/tests/controlflow/early_return_if_5.circom @@ -0,0 +1,88 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + var x = 0; + if (in < 10) { + if (in == 0) { + return in + 1; + } else { + x = 2; + } + } else { + return in + 3; + } + return x; +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_4]] -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_6]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_7]] -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_10]], %[[VAL_2]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_1]], %[[VAL_12]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_8]]#0 -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]], %[[VAL_8]]#1, %[[VAL_16]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_17]], %[[VAL_1]], %[[VAL_8]]#2 : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_14]]#0, %[[VAL_14]]#1, %[[VAL_14]]#2 : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_20]], %[[VAL_19]], %[[VAL_2]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_5]]#2 : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_22]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_23]][@outp] = %[[VAL_24]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_23]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-DAG: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_26]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_if_6.circom b/circom/tests/controlflow/early_return_if_6.circom new file mode 100644 index 000000000..ef0b4101b --- /dev/null +++ b/circom/tests/controlflow/early_return_if_6.circom @@ -0,0 +1,81 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(i, n) { + if (n == 0) { + return i; + } + if (n == 1) { + return i + 2; + } + return 0; +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp, 0); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_4]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_0]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_2]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_9]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_10]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_14]], %[[VAL_13]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_15]], %[[VAL_5]]#1 : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_11]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_11]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_18]], %[[VAL_20]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_19]][@outp] = %[[VAL_21]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_19]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_23]], %[[VAL_25]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_loop_1.circom b/circom/tests/controlflow/early_return_loop_1.circom new file mode 100644 index 000000000..af7001686 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_1.circom @@ -0,0 +1,161 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + for (var i = 0; i < 6; i++) { + if (i == 0) { + return in; + } + assert(0 == 1); // Unreachable because of the early return above + } + return -1; +} + +function noEarlyReturnFn(in) { + for (var i = 0; i < 6; i++) { + if (i == 99) { + return in; + } + assert(in == 0); + } + return -1; +} + + +template EarlyReturn() { + signal input inp; + signal output outp[2]; + + outp[0] <== noEarlyReturnFn(inp); + outp[1] <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : i1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_1]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (i1, !felt.type<"bn128">, !felt.type<"bn128">) -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.not %[[VAL_5]] : i1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_10]], %[[VAL_9]] : i1, i1 +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_5]], %[[VAL_6]], %[[VAL_7]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: i1, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_14]], %[[VAL_15]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_16]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_0]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_19]], %[[VAL_13]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_17]]#0 -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_21]], %[[VAL_17]]#1, %[[VAL_22]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_23]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_25]], "assertion failed" +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_17]]#0, %[[VAL_17]]#1, %[[VAL_27]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_20]]#0, %[[VAL_20]]#1, %[[VAL_20]]#2 : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @noEarlyReturnFn(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = llzk.nondet : i1 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_33]], %[[VAL_37:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_34]]) : (i1, !felt.type<"bn128">, !felt.type<"bn128">) -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_38]], %[[VAL_39]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = bool.not %[[VAL_36]] : i1 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_41]], %[[VAL_40]] : i1, i1 +// CHECK-NEXT: scf.condition(%[[VAL_42]]) %[[VAL_36]], %[[VAL_37]], %[[VAL_38]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_43:[0-9a-zA-Z_\.]+]]: i1, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_45]], %[[VAL_46]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_47]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_49]], %[[VAL_31]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_50]], %[[VAL_44]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_48]]#0 -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_52]], %[[VAL_48]]#1, %[[VAL_53]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_31]], %[[VAL_54]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_55]], "assertion failed" +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_48]]#0, %[[VAL_48]]#1, %[[VAL_57]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_51]]#0, %[[VAL_51]]#1, %[[VAL_51]]#2 : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_35]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_35]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_60]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_58]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !array.type<2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_61:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = function.call @noEarlyReturnFn(%[[VAL_61]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_63]]{{\[}}%[[VAL_66]]] = %[[VAL_64]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_61]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_68]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_63]]{{\[}}%[[VAL_69]]] = %[[VAL_67]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_62]][@outp] = %[[VAL_63]] : <@EarlyReturn::@EarlyReturn<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_62]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_70:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_71:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_72:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_70]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-DAG: %[[VAL_73:[0-9a-zA-Z_\.]+]] = function.call @noEarlyReturnFn(%[[VAL_71]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_74]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_72]]{{\[}}%[[VAL_75]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_76]], %[[VAL_73]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_71]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_78]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_72]]{{\[}}%[[VAL_79]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_80]], %[[VAL_77]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_loop_2A.circom b/circom/tests/controlflow/early_return_loop_2A.circom new file mode 100644 index 000000000..6d00d75aa --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_2A.circom @@ -0,0 +1,68 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + for (var i = 0; i < 6; i++) { + return in; + assert(0 == 1); // Unreachable because of the early return above + } + return -1; +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_R1:[0-9a-zA-Z_\.]+]] = %[[V_R0]], %[[V_6:[0-9a-zA-Z_\.]+]] = %[[V_1]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (i1, !felt.type<"bn128">, !felt.type<"bn128">) -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_8]]) +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = bool.not %[[V_R1]] : i1 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = bool.and %[[V_10]], %[[V_9]] : i1, i1 +// CHECK-NEXT: scf.condition(%[[V_12]]) %[[V_R1]], %[[V_6]], %[[V_I1]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_R2:[0-9a-zA-Z_\.]+]]: i1, %[[V_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_R3]], %[[V_0]], %[[V_I3]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = felt.neg %[[V_17]] : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_18]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_21]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_22]][@outp] = %[[VAL_23]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_22]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_25]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_loop_2B.circom b/circom/tests/controlflow/early_return_loop_2B.circom new file mode 100644 index 000000000..8e82ff106 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_2B.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// This test is identical to early_return_loop_2A.circom but uses a while loop instead of a for loop. +// Note that, because of the return inside the loop, the `i++` is unreachable and `i` is not added as a loop-carried variable. +function earlyReturnFn(in) { + var i = 0; + while (i < 6) { + return in; + assert(0 == 1); // Unreachable because of the early return above + i++; + } + return -1; +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_E0:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_E1:[0-9a-zA-Z_\.]+]] = %[[V_E0]], %[[V_R1:[0-9a-zA-Z_\.]+]] = %[[V_R0]]) : (i1, !felt.type<"bn128">) -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I0]], %[[V_7]]) +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = bool.not %[[V_E1]] : i1 +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = bool.and %[[V_9]], %[[V_8]] : i1, i1 +// CHECK-NEXT: scf.condition(%[[V_10]]) %[[V_E1]], %[[V_R1]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_E2:[0-9a-zA-Z_\.]+]]: i1, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_E3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_E3]], %[[V_0]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = felt.neg %[[V_13]] : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_17]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_18]][@outp] = %[[VAL_19]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_18]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_21]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_22]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_loop_3.circom b/circom/tests/controlflow/early_return_loop_3.circom new file mode 100644 index 000000000..94db07e65 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_3.circom @@ -0,0 +1,83 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + for (var i = 0; i < 6; i++) { + if (i == 0) { + return in + 1; + } else { + return in + 2; // Semantically unreachable because `i == 0` will always hit first + } + return in + 5; // Syntactically unreachable because both branches above return + } + return -1; // Semantically unreachable because `i == 0` will always hit first +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_F0:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_F1:[0-9a-zA-Z_\.]+]] = %[[V_F0]], %[[V_R1:[0-9a-zA-Z_\.]+]] = %[[V_R0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (i1, !felt.type<"bn128">, !felt.type<"bn128">) -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_8]]) +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = bool.not %[[V_F1]] : i1 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = bool.and %[[V_10]], %[[V_9]] : i1, i1 +// CHECK-NEXT: scf.condition(%[[V_12]]) %[[V_F1]], %[[V_R1]], %[[V_I1]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_F2:[0-9a-zA-Z_\.]+]]: i1, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_I2]], %[[V_13]]) +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = scf.if %[[V_14]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_R4:[0-9a-zA-Z_\.]+]] = felt.add %[[V_IN]], %[[V_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_R4]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_R5:[0-9a-zA-Z_\.]+]] = felt.add %[[V_IN]], %[[V_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_R5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_F3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_F3]], %[[V_R3]], %[[V_I3]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.neg %[[V_24]] : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_25]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_28]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_29]][@outp] = %[[VAL_30]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_29]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_32:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_32]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_loop_4.circom b/circom/tests/controlflow/early_return_loop_4.circom new file mode 100644 index 000000000..6a10a21ba --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_4.circom @@ -0,0 +1,82 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + for (var i = 0; i < 6; i++) { + if (i == 0) { + return in + 1; + } else { + return in + 2; + } + } + return -1; // Semantically unreachable because `i == 0` will always hit first +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_R1:[0-9a-zA-Z_\.]+]] = %[[V_R0]], %[[V_6:[0-9a-zA-Z_\.]+]] = %[[V_1]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (i1, !felt.type<"bn128">, !felt.type<"bn128">) -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_8]]) +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = bool.not %[[V_R1]] : i1 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = bool.and %[[V_10]], %[[V_9]] : i1, i1 +// CHECK-NEXT: scf.condition(%[[V_12]]) %[[V_R1]], %[[V_6]], %[[V_I1]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_R2:[0-9a-zA-Z_\.]+]]: i1, %[[V_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_I2]], %[[V_13]]) +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = scf.if %[[V_14]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.add %[[V_0]], %[[V_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.add %[[V_0]], %[[V_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_19]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_R3]], %[[V_15]], %[[V_I3]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.neg %[[V_24]] : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_25]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_28]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_29]][@outp] = %[[VAL_30]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_29]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_32:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_32]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_loop_5.circom b/circom/tests/controlflow/early_return_loop_5.circom new file mode 100644 index 000000000..c01cf097a --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_5.circom @@ -0,0 +1,90 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function earlyReturnFn(in) { + for (var i = 0; i < 6; i++) { + if (i == 0) { + return in; + } + assert(0 == 1); // Semantically unreachable because `i == 0` will always hit first + } + return -1; // Semantically unreachable because `i == 0` will always hit first +} + +template EarlyReturn() { + signal input inp; + signal output outp; + + outp <== earlyReturnFn(inp); +} + +component main = EarlyReturn(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EarlyReturn::@EarlyReturn<[]>>} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_F0:[0-9a-zA-Z_\.]+]] = llzk.nondet : i1 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_F1:[0-9a-zA-Z_\.]+]] = %[[V_F0]], %[[V_R1:[0-9a-zA-Z_\.]+]] = %[[V_R0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (i1, !felt.type<"bn128">, !felt.type<"bn128">) -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_8]]) +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = bool.not %[[V_F1]] : i1 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = bool.and %[[V_10]], %[[V_9]] : i1, i1 +// CHECK-NEXT: scf.condition(%[[V_12]]) %[[V_F1]], %[[V_R1]], %[[V_I1]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_F2:[0-9a-zA-Z_\.]+]]: i1, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_I2]], %[[V_13]]) +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[V_14]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_F3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_F3]], %[[V_IN]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_F4:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[V_F4]], %[[V_R2]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[V_15]]#0 -> (i1, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_F5:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_F5]], %[[V_15]]#1, %[[V_I3]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_21]], %[[V_22]]) +// CHECK-NEXT: bool.assert %[[V_23]], "assertion failed" +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I4:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_15]]#0, %[[V_15]]#1, %[[V_I4]] : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// COM: flag return val loop index,i +// CHECK-NEXT: scf.yield %[[V_18]]#0, %[[V_18]]#1, %[[V_18]]#2 : i1, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = felt.neg %[[V_27]] : !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_26]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @EarlyReturn { +// CHECK-NEXT: struct.def @EarlyReturn { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@EarlyReturn::@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_31]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_32]][@outp] = %[[VAL_33]] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_32]] : !struct.type<@EarlyReturn::@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_34:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn::@EarlyReturn<[]>>, %[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_34]][@outp] : <@EarlyReturn::@EarlyReturn<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_35]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_36]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/early_return_loop_with_unknown_if.circom b/circom/tests/controlflow/early_return_loop_with_unknown_if.circom new file mode 100644 index 000000000..0b1b12463 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_with_unknown_if.circom @@ -0,0 +1,38 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.3; + +function long_gt(a, b) { + for (var i = 1; i >= 0; i--) { + if (a[i] > b[i]) { + return 1; + } + if (a[i] <= b[i]) { + return 0; + } + } + return 0; +} + +function long_scalar_mult(in) { + var out[2] = in; + return out; +} + +function long_div2(in){ + var norm[2] = long_scalar_mult(in); + var out[1] = [long_gt(norm, norm)]; + return out; +} + +template Test() { + signal input in[2]; + var out[1] = long_div2(in); +} + +component main = Test(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/early_return_simple.circom b/circom/tests/controlflow/early_return_simple.circom new file mode 100644 index 000000000..a59255533 --- /dev/null +++ b/circom/tests/controlflow/early_return_simple.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(a) { + return a; + // Circom allows code after a return and includes it in the parsed AST but it doesn't + // process it further or this statement would cause "error[T3001]: False assert reached." + // Thus, any code after the first return in a block should be ignored when generating LLZK. + assert(1 == 0); + return a; +} + +template Foo() { + signal input inp; + + _ <-- f(inp); +} + +component main = Foo(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Foo::@Foo<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Foo::@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/indexing_within_unknown_if.circom b/circom/tests/controlflow/indexing_within_unknown_if.circom new file mode 100644 index 000000000..57a602cf8 --- /dev/null +++ b/circom/tests/controlflow/indexing_within_unknown_if.circom @@ -0,0 +1,86 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template TestSetAllUnknownWithinUnknownCondition(k) { + signal input in; + var ret[10]; + + if (in == 1) { + for (var i = 0; i < k; i++) { + ret[i] = 8; + } + } + var x = ret[0]; +} + +component main = TestSetAllUnknownWithinUnknownCondition(1); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@TestSetAllUnknownWithinUnknownCondition::@TestSetAllUnknownWithinUnknownCondition<[1]>>} { +// CHECK-NEXT: poly.template @TestSetAllUnknownWithinUnknownCondition { +// CHECK-NEXT: poly.param @k +// CHECK-NEXT: struct.def @TestSetAllUnknownWithinUnknownCondition { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@TestSetAllUnknownWithinUnknownCondition::@TestSetAllUnknownWithinUnknownCondition<[@k]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@TestSetAllUnknownWithinUnknownCondition::@TestSetAllUnknownWithinUnknownCondition<[@k]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_6]] -> (!array.type<10 x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_8]], %[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">>) -> (!felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_12]]) %[[VAL_10]], %[[VAL_11]] : !felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_14]]{{\[}}%[[VAL_16]]] = %[[VAL_15]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_14]] : !felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_9]]#1 : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_4]] : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_7]]{{\[}}%[[VAL_20]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@TestSetAllUnknownWithinUnknownCondition::@TestSetAllUnknownWithinUnknownCondition<[@k]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@TestSetAllUnknownWithinUnknownCondition::@TestSetAllUnknownWithinUnknownCondition<[@k]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_25]], %[[VAL_25]], %[[VAL_25]], %[[VAL_25]], %[[VAL_25]], %[[VAL_25]], %[[VAL_25]], %[[VAL_25]], %[[VAL_25]], %[[VAL_25]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_23]], %[[VAL_27]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_28]] -> (!array.type<10 x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_32:[0-9a-zA-Z_\.]+]] = %[[VAL_30]], %[[VAL_33:[0-9a-zA-Z_\.]+]] = %[[VAL_26]]) : (!felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">>) -> (!felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_32]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_32]], %[[VAL_33]] : !felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_36]]{{\[}}%[[VAL_38]]] = %[[VAL_37]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_35]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_40]], %[[VAL_36]] : !felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_31]]#1 : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_26]] : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_42]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/loop_multi_iv.circom b/circom/tests/controlflow/loop_multi_iv.circom new file mode 100644 index 000000000..278e2e4d7 --- /dev/null +++ b/circom/tests/controlflow/loop_multi_iv.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function complicatedLoopFn(in) { + var res = 0; + for (var i = 1, j = 2; i + j < 9; i++) { + j++; + res += in; + } + return res; +} + +template LoopMultiIV() { + signal input inp; + signal output outp <== complicatedLoopFn(inp); +} + +component main = LoopMultiIV(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@LoopMultiIV::@LoopMultiIV<[]>>} { +// CHECK-NEXT: function.def @complicatedLoopFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_1]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_8]], %[[VAL_9]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_10]]) %[[VAL_5]], %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_15]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_4]]#2 : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @LoopMultiIV { +// CHECK-NEXT: struct.def @LoopMultiIV { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@LoopMultiIV::@LoopMultiIV<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.new : <@LoopMultiIV::@LoopMultiIV<[]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = function.call @complicatedLoopFn(%[[VAL_19]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_20]][@outp] = %[[VAL_21]] : <@LoopMultiIV::@LoopMultiIV<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_20]] : !struct.type<@LoopMultiIV::@LoopMultiIV<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@LoopMultiIV::@LoopMultiIV<[]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@outp] : <@LoopMultiIV::@LoopMultiIV<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = function.call @complicatedLoopFn(%[[VAL_23]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/controlflow/multiuse_func_with_loop_diff.circom b/circom/tests/controlflow/multiuse_func_with_loop_diff.circom new file mode 100644 index 000000000..ecfc1ed52 --- /dev/null +++ b/circom/tests/controlflow/multiuse_func_with_loop_diff.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function f(s, n) { + var sum = 0; + for (var i = 0; i < n; i++) { + sum += s[i]; + } + return sum; +} + +template MultiUse() { + signal input inp[10]; + signal output outp[3]; + + outp[0] <-- f(inp, 2); + outp[1] <-- f(inp, 5); + outp[2] <-- f(inp, 9); +} + +component main = MultiUse(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/multiuse_func_with_loop_diff_more.circom b/circom/tests/controlflow/multiuse_func_with_loop_diff_more.circom new file mode 100644 index 000000000..38a209f0a --- /dev/null +++ b/circom/tests/controlflow/multiuse_func_with_loop_diff_more.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function f(s, count, offset) { + var sum = 0; + for (var i = 0; i < count; i++) { + sum += s[i + offset]; + } + return sum; +} + +template MultiUse() { + signal input inp[10]; + signal output outp[3]; + + outp[0] <-- f(inp, 2, 1); + outp[1] <-- f(inp, 2, 0); + outp[2] <-- f(inp, 2, 3); +} + +component main = MultiUse(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/multiuse_func_with_loop_same.circom b/circom/tests/controlflow/multiuse_func_with_loop_same.circom new file mode 100644 index 000000000..c0629f9f6 --- /dev/null +++ b/circom/tests/controlflow/multiuse_func_with_loop_same.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function f(s, n) { + var sum = 0; + for (var i = 0; i < n; i++) { + sum += s[i]; + } + return sum; +} + +template MultiUse() { + signal input inp[10]; + signal output outp[3]; + + outp[0] <-- f(inp, 2); + outp[1] <-- f(inp, 2); + outp[2] <-- f(inp, 2); +} + +component main = MultiUse(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/controlflow/return_in_branch.circom b/circom/tests/controlflow/return_in_branch.circom new file mode 100644 index 000000000..87e01796e --- /dev/null +++ b/circom/tests/controlflow/return_in_branch.circom @@ -0,0 +1,54 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(a) { + if (a < 0) { + return -a; + } + return a; +} + +template Foo() { + signal input inp; + + _ <-- f(inp); +} + +component main = Foo(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Foo::@Foo<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_3]] -> (i1, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_5]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_1]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]]#0 -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_4]]#1 : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: function.def @compute(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Foo::@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_9]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_10]] : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[]>>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_function/array_dim_expr_call.circom b/circom/tests/declaration_in_function/array_dim_expr_call.circom new file mode 100644 index 000000000..045720e09 --- /dev/null +++ b/circom/tests/declaration_in_function/array_dim_expr_call.circom @@ -0,0 +1,24 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function id(x) { + return x; +} + +function f() { + var s = 3; + var x[id(s)]; + return x; +} + +template A() { + _ = f(); +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_function/array_dim_expr_infix.circom b/circom/tests/declaration_in_function/array_dim_expr_infix.circom new file mode 100644 index 000000000..54379bf41 --- /dev/null +++ b/circom/tests/declaration_in_function/array_dim_expr_infix.circom @@ -0,0 +1,20 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function f() { + var s = 3; + var x[2*s]; + return x; +} + +template A() { + _ = f(); +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_function/array_dim_expr_inlineswitch.circom b/circom/tests/declaration_in_function/array_dim_expr_inlineswitch.circom new file mode 100644 index 000000000..88c06b815 --- /dev/null +++ b/circom/tests/declaration_in_function/array_dim_expr_inlineswitch.circom @@ -0,0 +1,20 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function f() { + var s = -23; + var x[s > 0 ? s : -s]; + return x; +} + +template A() { + _ = f(); +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_function/array_dim_expr_number.circom b/circom/tests/declaration_in_function/array_dim_expr_number.circom new file mode 100644 index 000000000..8038bd64c --- /dev/null +++ b/circom/tests/declaration_in_function/array_dim_expr_number.circom @@ -0,0 +1,19 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function f() { + var x[6]; + return x; +} + +template A() { + _ = f(); +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_function/array_dim_expr_prefix.circom b/circom/tests/declaration_in_function/array_dim_expr_prefix.circom new file mode 100644 index 000000000..d8267eb15 --- /dev/null +++ b/circom/tests/declaration_in_function/array_dim_expr_prefix.circom @@ -0,0 +1,20 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function f() { + var s = -6; + var x[-s]; + return x; +} + +template A() { + _ = f(); +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_function/array_dim_expr_var.circom b/circom/tests/declaration_in_function/array_dim_expr_var.circom new file mode 100644 index 000000000..2be2fbc15 --- /dev/null +++ b/circom/tests/declaration_in_function/array_dim_expr_var.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f() { + var s = 6; + var x[s]; + return x[1]; +} + +template A() { + _ = f(); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: function.def @f() -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]] : <6 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_4]]] : <6 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_6]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_function/scalar_number.circom b/circom/tests/declaration_in_function/scalar_number.circom new file mode 100644 index 000000000..a7d4f90ad --- /dev/null +++ b/circom/tests/declaration_in_function/scalar_number.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_passes='any(remove-dead-values)' --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f() { + var x = 0; + return x; +} + +template A() { + _ = f(); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: function.def @f() -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: function.return %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_function/scalar_var.circom b/circom/tests/declaration_in_function/scalar_var.circom new file mode 100644 index 000000000..525bb4a69 --- /dev/null +++ b/circom/tests/declaration_in_function/scalar_var.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(in) { + var x = in; + return x; +} + +template A() { + _ = f(5); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_2]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_5]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_template/array_dim_expr_call.circom b/circom/tests/declaration_in_template/array_dim_expr_call.circom new file mode 100644 index 000000000..cffac9102 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_call.circom @@ -0,0 +1,20 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function id(x) { + return x; +} + +template A() { + var s = 6; + signal input in[id(s)]; + var x[id(s)] = in; +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_template/array_dim_expr_infix.circom b/circom/tests/declaration_in_template/array_dim_expr_infix.circom new file mode 100644 index 000000000..273d55636 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_infix.circom @@ -0,0 +1,16 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A() { + var s = 6; + signal input in[2*s]; + var x[2*s] = in; +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_template/array_dim_expr_inlineswitch.circom b/circom/tests/declaration_in_template/array_dim_expr_inlineswitch.circom new file mode 100644 index 000000000..812986a43 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_inlineswitch.circom @@ -0,0 +1,16 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A() { + var s = -23; + signal input in[s > 0 ? s : -s]; + var x[s > 0 ? s : -s] = in; +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_template/array_dim_expr_number.circom b/circom/tests/declaration_in_template/array_dim_expr_number.circom new file mode 100644 index 000000000..0f4650796 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_number.circom @@ -0,0 +1,49 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal input in[5]; + // NOTE: This assignment is not generated in LLZK since LLZK does not have direct assignments. + // Internally, references to 'x' will just point to 'in'. + var x[5] = in; + var y[5] = in; + // NOTE: This assignment will forward the reference of 'y' to 'in' internally. + signal output out <== y[3]; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type<"bn128">>) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]], %[[VAL_4]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_7]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_8]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_12]], %[[VAL_12]], %[[VAL_12]], %[[VAL_12]], %[[VAL_12]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_14]], %[[VAL_14]], %[[VAL_14]], %[[VAL_14]], %[[VAL_14]] : <5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_17]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_template/array_dim_expr_param.circom b/circom/tests/declaration_in_template/array_dim_expr_param.circom new file mode 100644 index 000000000..78fa49947 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_param.circom @@ -0,0 +1,47 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(s) { + signal input in[s]; + var x[s] = in; +} + +component main = A(12); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[12]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @s +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type<"bn128">>) -> !struct.type<@A::@A<[@s]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@s]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_4]], %[[VAL_5]] : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] { +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <@s x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@s]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@s]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_14]], %[[VAL_15]] : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_17]] to %[[VAL_16]] step %[[VAL_18]] { +// CHECK-NEXT: array.write %[[VAL_14]]{{\[}}%[[VAL_19]]] = %[[VAL_13]] : <@s x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_template/array_dim_expr_param_B.circom b/circom/tests/declaration_in_template/array_dim_expr_param_B.circom new file mode 100644 index 000000000..2ac3e0782 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_param_B.circom @@ -0,0 +1,52 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(s) { + signal input in[s]; + var x[s] = in; + signal output out[s] <== x; +} + +component main = A(12); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[12]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @s +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !array.type<@s x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type<"bn128">>) -> !struct.type<@A::@A<[@s]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@s]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_4]], %[[VAL_5]] : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] { +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <@s x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@A::@A<[@s]>>, !array.type<@s x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@s]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@s]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_10]][@out] : <@A::@A<[@s]>>, !array.type<@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.new : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_15]], %[[VAL_16]] : <@s x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_18]] to %[[VAL_17]] step %[[VAL_19]] { +// CHECK-NEXT: array.write %[[VAL_15]]{{\[}}%[[VAL_20]]] = %[[VAL_14]] : <@s x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_11]] : !array.type<@s x !felt.type<"bn128">>, !array.type<@s x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_template/array_dim_expr_prefix.circom b/circom/tests/declaration_in_template/array_dim_expr_prefix.circom new file mode 100644 index 000000000..fef02492f --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_prefix.circom @@ -0,0 +1,16 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A() { + var s = -6; + signal input in[-s]; + var x[-s] = in; +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_template/array_dim_expr_prefix_B.circom b/circom/tests/declaration_in_template/array_dim_expr_prefix_B.circom new file mode 100644 index 000000000..0a0792540 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_prefix_B.circom @@ -0,0 +1,17 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A() { + var s = -6; + signal input in[-s]; + s = -12; + var x[-s] = in; +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_template/array_dim_expr_prefix_C.circom b/circom/tests/declaration_in_template/array_dim_expr_prefix_C.circom new file mode 100644 index 000000000..238c08f88 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_prefix_C.circom @@ -0,0 +1,14 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A() { + var x[-12]; +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/declaration_in_template/array_dim_expr_var.circom b/circom/tests/declaration_in_template/array_dim_expr_var.circom new file mode 100644 index 000000000..a2efe0426 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_var.circom @@ -0,0 +1,91 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + var s = 6; + signal input in[s]; + var x[s] = in; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.expr @"s@280" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"s@292" { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !array.type<@"s@280" x !felt.type<"bn128">>) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@280" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@292" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.new : <@"s@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_8]], %[[VAL_9]] : <@"s@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_13:[0-9a-zA-Z_\.]+]] = %[[VAL_11]] to %[[VAL_10]] step %[[VAL_12]] { +// CHECK-NEXT: array.write %[[VAL_8]]{{\[}}%[[VAL_13]]] = %[[VAL_7]] : <@"s@292" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@280" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@292" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_15]], %[[VAL_17]] : index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_18]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_15]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_17]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_20]] to %[[VAL_19]] step %[[VAL_21]] { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_22]]] : <@"s@280" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_8]]{{\[}}%[[VAL_22]]] = %[[VAL_23]] : <@"s@292" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !array.type<@"s@280" x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@280" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@292" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 6 : <"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.new : <@"s@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_30]], %[[VAL_31]] : <@"s@292" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_33]] to %[[VAL_32]] step %[[VAL_34]] { +// CHECK-NEXT: array.write %[[VAL_30]]{{\[}}%[[VAL_35]]] = %[[VAL_29]] : <@"s@292" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@280" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = poly.read_const @"s@292" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = arith.cmpi ult, %[[VAL_37]], %[[VAL_39]] : index +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_40]] -> (index) { +// CHECK-NEXT: scf.yield %[[VAL_37]] : index +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_39]] : index +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_44:[0-9a-zA-Z_\.]+]] = %[[VAL_42]] to %[[VAL_41]] step %[[VAL_43]] { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_25]]{{\[}}%[[VAL_44]]] : <@"s@280" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_30]]{{\[}}%[[VAL_44]]] = %[[VAL_45]] : <@"s@292" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_template/scalar_number.circom b/circom/tests/declaration_in_template/scalar_number.circom new file mode 100644 index 000000000..ea0ab5259 --- /dev/null +++ b/circom/tests/declaration_in_template/scalar_number.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + var x = 7; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/declaration_in_template/scalar_var.circom b/circom/tests/declaration_in_template/scalar_var.circom new file mode 100644 index 000000000..57a93cb65 --- /dev/null +++ b/circom/tests/declaration_in_template/scalar_var.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal input in; + var x = in; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// COM: // There's nothing generated for the assignment to 'x' since LLZK does not have a +// COM: // simple assignment op. To account for this, codegen does an automatic value +// COM: // propagation to use site(s) of 'x' (and there are none in this trivial example). +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/arr_cpy_store.circom b/circom/tests/from_concrete/arr_cpy_store.circom new file mode 100644 index 000000000..c51c4c871 --- /dev/null +++ b/circom/tests/from_concrete/arr_cpy_store.circom @@ -0,0 +1,97 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sum(N) { + signal input inp[N]; +} + +template Foo(N) { + signal input inp[N]; + signal input out[N]; + + component c = Sum(N); + for (var i = N; i <= N; i++) { + c.inp <== inp; + } +} + +component main = Foo(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Foo_1::@Foo_1<[]>>} { +// CHECK-NEXT: poly.template @Foo_1 { +// CHECK-NEXT: struct.def @Foo_1 { +// CHECK-NEXT: struct.member @c : !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: struct.member @c$inputs : !pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@Foo_1::@Foo_1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo_1::@Foo_1<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !pod.type<[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_6]]) : (!pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]>, !felt.type<"bn128">) -> (!pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_9]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]] : !pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: pod.write %[[VAL_12]][@inp] = %[[VAL_0]] : <[@inp: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@count] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_14]], %[[VAL_15]] : index +// CHECK-NEXT: pod.write %[[VAL_3]][@count] = %[[VAL_16]] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_16]], %[[VAL_17]] : index +// CHECK-NEXT: scf.if %[[VAL_18]] { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@params] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_12]][@inp] : <[@inp: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = function.call @Sum_0::@Sum_0::@compute(%[[VAL_20]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: pod.write %[[VAL_3]][@comp] = %[[VAL_21]] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]], %[[VAL_22]] : !pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_2]][@c$inputs] = %[[VAL_7]]#0 : <@Foo_1::@Foo_1<[]>>, !pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@comp] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_23]] : <@Foo_1::@Foo_1<[]>>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Foo_1::@Foo_1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo_1::@Foo_1<[]>>, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@c] : <@Foo_1::@Foo_1<[]>>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@c$inputs] : <@Foo_1::@Foo_1<[]>>, !pod.type<[@inp: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_32:[0-9a-zA-Z_\.]+]] = %[[VAL_30]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_32]], %[[VAL_33]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@inp] : <[@inp: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_36]], %[[VAL_25]] : !array.type<2 x !felt.type<"bn128">>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@inp] : <[@inp: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum_0::@Sum_0::@constrain(%[[VAL_27]], %[[VAL_38]]) : (!struct.type<@Sum_0::@Sum_0<[]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sum_0 { +// CHECK-NEXT: struct.def @Sum_0 { +// CHECK-NEXT: function.def @compute(%[[VAL_39:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@Sum_0::@Sum_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: function.return %[[VAL_40]] : !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_42:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum_0::@Sum_0<[]>>, %[[VAL_43:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/array_copy_constraints.circom b/circom/tests/from_concrete/array_copy_constraints.circom new file mode 100644 index 000000000..f18633758 --- /dev/null +++ b/circom/tests/from_concrete/array_copy_constraints.circom @@ -0,0 +1,126 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sum(n) { + signal input inp[n]; + signal output outp; + + var acc = 0; + for (var i = 0; i < n; i++) { + acc += inp[i]; + } + + outp <== acc; +} + +template Caller(n) { + signal input inp[n]; + signal outp; + + component op = Sum(n); + op.inp <== inp; + + outp <== op.outp; +} + +component main = Caller(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller_1::@Caller_1<[]>>} { +// CHECK-NEXT: poly.template @Caller_1 { +// CHECK-NEXT: struct.def @Caller_1 { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @op : !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: struct.member @op$inputs : !pod.type<[@inp: !array.type<5 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type<"bn128">>) -> !struct.type<@Caller_1::@Caller_1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller_1::@Caller_1<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !pod.type<[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<5 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: pod.write %[[VAL_3]][@inp] = %[[VAL_0]] : <[@inp: !array.type<5 x !felt.type<"bn128">>]>, !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@count] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_5]], %[[VAL_6]] : index +// CHECK-NEXT: pod.write %[[VAL_2]][@count] = %[[VAL_7]] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_7]], %[[VAL_8]] : index +// CHECK-NEXT: scf.if %[[VAL_9]] { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@params] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@inp] : <[@inp: !array.type<5 x !felt.type<"bn128">>]>, !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @Sum_0::@Sum_0::@compute(%[[VAL_11]]) : (!array.type<5 x !felt.type<"bn128">>) -> !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: pod.write %[[VAL_2]][@comp] = %[[VAL_12]] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@comp] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@outp] : <@Sum_0::@Sum_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@outp] = %[[VAL_14]] : <@Caller_1::@Caller_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@op$inputs] = %[[VAL_3]] : <@Caller_1::@Caller_1<[]>>, !pod.type<[@inp: !array.type<5 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@comp] : <[@count: index, @comp: !struct.type<@Sum_0::@Sum_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@op] = %[[VAL_15]] : <@Caller_1::@Caller_1<[]>>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Caller_1::@Caller_1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller_1::@Caller_1<[]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@outp] : <@Caller_1::@Caller_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@op] : <@Caller_1::@Caller_1<[]>>, !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@op$inputs] : <@Caller_1::@Caller_1<[]>>, !pod.type<[@inp: !array.type<5 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@inp] : <[@inp: !array.type<5 x !felt.type<"bn128">>]>, !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_22]], %[[VAL_17]] : !array.type<5 x !felt.type<"bn128">>, !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@outp] : <@Sum_0::@Sum_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_18]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@inp] : <[@inp: !array.type<5 x !felt.type<"bn128">>]>, !array.type<5 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum_0::@Sum_0::@constrain(%[[VAL_19]], %[[VAL_24]]) : (!struct.type<@Sum_0::@Sum_0<[]>>, !array.type<5 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sum_0 { +// CHECK-NEXT: struct.def @Sum_0 { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type<"bn128">>) -> !struct.type<@Sum_0::@Sum_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_31:[0-9a-zA-Z_\.]+]] = %[[VAL_28]], %[[VAL_32:[0-9a-zA-Z_\.]+]] = %[[VAL_29]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_32]], %[[VAL_33]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_31]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_25]]{{\[}}%[[VAL_37]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_35]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_36]], %[[VAL_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_39]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_26]][@outp] = %[[VAL_30]]#0 : <@Sum_0::@Sum_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_26]] : !struct.type<@Sum_0::@Sum_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_42:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum_0::@Sum_0<[]>>, %[[VAL_43:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_42]][@outp] : <@Sum_0::@Sum_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_49:[0-9a-zA-Z_\.]+]] = %[[VAL_46]], %[[VAL_50:[0-9a-zA-Z_\.]+]] = %[[VAL_47]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_50]], %[[VAL_51]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_52]]) %[[VAL_49]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_53:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_54:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_54]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_43]]{{\[}}%[[VAL_55]]] : <5 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_53]], %[[VAL_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_54]], %[[VAL_58]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_57]], %[[VAL_59]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_44]], %[[VAL_48]]#0 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/array_insert_in_template.circom b/circom/tests/from_concrete/array_insert_in_template.circom new file mode 100644 index 000000000..c27bafba2 --- /dev/null +++ b/circom/tests/from_concrete/array_insert_in_template.circom @@ -0,0 +1,92 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function default_init() { + var out[3][2]; + return out; +} + +template Main() { + var a[3][2] = default_init(); +} + +component main = Main(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Main_0::@Main_0<[]>>} { +// CHECK-NEXT: function.def @default_init_0() -> !array.type<3,2 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_4]]] = %[[VAL_2]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_7]]] = %[[VAL_5]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_9]]] = %[[VAL_1]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_11]]] = %[[VAL_1]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_13]]] = %[[VAL_1]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !array.type<3,2 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Main_0 { +// CHECK-NEXT: struct.def @Main_0 { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Main_0::@Main_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.new : <@Main_0::@Main_0<[]>> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_22]]] = %[[VAL_20]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_24]]] = %[[VAL_16]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_26]]] = %[[VAL_16]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_28]]] = %[[VAL_16]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = function.call @default_init_0() : () -> !array.type<3,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_14]] : !struct.type<@Main_0::@Main_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@Main_0::@Main_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_35]]] = %[[VAL_33]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_38]]] = %[[VAL_36]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_31]]{{\[}}%[[VAL_40]]] = %[[VAL_32]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_31]]{{\[}}%[[VAL_42]]] = %[[VAL_32]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_31]]{{\[}}%[[VAL_44]]] = %[[VAL_32]] : <3,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = function.call @default_init_0() : () -> !array.type<3,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/assert.circom b/circom/tests/from_concrete/assert.circom new file mode 100644 index 000000000..fc0ec9b80 --- /dev/null +++ b/circom/tests/from_concrete/assert.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(nBits) { + // When using `concrete` option, this becomes `assert(1)` + assert(nBits <= 512); +} + +component main = A(32); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A_0::@A_0<[]>>} { +// CHECK-NEXT: poly.template @A_0 { +// CHECK-NEXT: struct.def @A_0 { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A_0::@A_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A_0::@A_0<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_2]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_4]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A_0::@A_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A_0::@A_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/assign_array_from_param.circom b/circom/tests/from_concrete/assign_array_from_param.circom new file mode 100644 index 000000000..bd87f6dfb --- /dev/null +++ b/circom/tests/from_concrete/assign_array_from_param.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Template(m) { + signal output ret[2][2] <== m; +} + +component main = Template([[0, 1], [2, 3]]); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Template_0::@Template_0<[]>>} { +// CHECK-NEXT: poly.template @Template_0 { +// CHECK-NEXT: struct.def @Template_0 { +// CHECK-NEXT: struct.member @ret : !array.type<2,2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template_0::@Template_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Template_0::@Template_0<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_1]], %[[VAL_2]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_5]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_7]]{{\[}}%[[VAL_8]]] = %[[VAL_3]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_7]]{{\[}}%[[VAL_9]]] = %[[VAL_6]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_0]][@ret] = %[[VAL_7]] : <@Template_0::@Template_0<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Template_0::@Template_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@Template_0::@Template_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_10]][@ret] : <@Template_0::@Template_0<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_12]], %[[VAL_13]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_15]], %[[VAL_16]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_18]]{{\[}}%[[VAL_19]]] = %[[VAL_14]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_18]]{{\[}}%[[VAL_20]]] = %[[VAL_17]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_18]] : !array.type<2,2 x !felt.type<"bn128">>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/assign_array_from_var.circom b/circom/tests/from_concrete/assign_array_from_var.circom new file mode 100644 index 000000000..e528b88d6 --- /dev/null +++ b/circom/tests/from_concrete/assign_array_from_var.circom @@ -0,0 +1,110 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Template() { + var m[2][2] = [[0, 1], [2, 3]]; + signal output ret[2][2] <== m; +} + +component main = Template(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Template_0::@Template_0<[]>>} { +// CHECK-NEXT: poly.template @Template_0 { +// CHECK-NEXT: struct.def @Template_0 { +// CHECK-NEXT: struct.member @ret : !array.type<2,2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template_0::@Template_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Template_0::@Template_0<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_5]]] = %[[VAL_3]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_8]]] = %[[VAL_6]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_10]]] = %[[VAL_2]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_12]]] = %[[VAL_2]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_16]]] = %[[VAL_14]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_20]]{{\[}}%[[VAL_23]]] = %[[VAL_21]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_20]]{{\[}}%[[VAL_26]]] = %[[VAL_24]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_28]]] = %[[VAL_13]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_30]]] = %[[VAL_20]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_0]][@ret] = %[[VAL_1]] : <@Template_0::@Template_0<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Template_0::@Template_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !struct.type<@Template_0::@Template_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@ret] : <@Template_0::@Template_0<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_34]]{{\[}}%[[VAL_37]]] = %[[VAL_35]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_34]]{{\[}}%[[VAL_40]]] = %[[VAL_38]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_33]]{{\[}}%[[VAL_42]]] = %[[VAL_34]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_33]]{{\[}}%[[VAL_44]]] = %[[VAL_34]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_45]]{{\[}}%[[VAL_48]]] = %[[VAL_46]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_45]]{{\[}}%[[VAL_51]]] = %[[VAL_49]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_54]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_52]]{{\[}}%[[VAL_55]]] = %[[VAL_53]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_57]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_52]]{{\[}}%[[VAL_58]]] = %[[VAL_56]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_33]]{{\[}}%[[VAL_60]]] = %[[VAL_45]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_33]]{{\[}}%[[VAL_62]]] = %[[VAL_52]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_32]], %[[VAL_33]] : !array.type<2,2 x !felt.type<"bn128">>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/bench_alias_check.circom b/circom/tests/from_concrete/bench_alias_check.circom new file mode 100644 index 000000000..5c5576042 --- /dev/null +++ b/circom/tests/from_concrete/bench_alias_check.circom @@ -0,0 +1,509 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +// Returns 1 if in (in binary) > ct +template CompConstant(ct) { + signal input in[254]; + signal output out; + + signal parts[127]; + signal sout; + + var clsb; + var cmsb; + var slsb; + var smsb; + + var sum=0; + + var b = (1 << 128) -1; + var a = 1; + var e = 1; + var i; + + for (i=0;i<127; i++) { + clsb = (ct >> (i*2)) & 1; + cmsb = (ct >> (i*2+1)) & 1; + slsb = in[i*2]; + smsb = in[i*2+1]; + + if ((cmsb==0)&&(clsb==0)) { + parts[i] <== -b*smsb*slsb + b*smsb + b*slsb; + } else if ((cmsb==0)&&(clsb==1)) { + parts[i] <== a*smsb*slsb - a*slsb + b*smsb - a*smsb + a; + } else if ((cmsb==1)&&(clsb==0)) { + parts[i] <== b*smsb*slsb - a*smsb + a; + } else { + parts[i] <== -a*smsb*slsb + a; + } + + sum = sum + parts[i]; + + b = b -e; + a = a +e; + e = e*2; + } + + sout <== sum; + + component num2bits = Num2Bits(135); + num2bits.in <== sum; + + out <== num2bits.out[127]; +} + +template AliasCheck() { + signal input in[254]; + + component compConstant = CompConstant(-1); + for (var i=0; i<254; i++) in[i] ==> compConstant.in[i]; + + compConstant.out === 0; +} + +component main = AliasCheck(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@AliasCheck_2::@AliasCheck_2<[]>>} { +// CHECK-NEXT: poly.template @AliasCheck_2 { +// CHECK-NEXT: struct.def @AliasCheck_2 { +// CHECK-NEXT: struct.member @compConstant : !struct.type<@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: struct.member @compConstant$inputs : !pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<254 x !felt.type<"bn128">>) -> !struct.type<@AliasCheck_2::@AliasCheck_2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@AliasCheck_2::@AliasCheck_2<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !pod.type<[@count: index, @comp: !struct.type<@CompConstant_1::@CompConstant_1<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<254 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]>, !felt.type<"bn128">) -> (!pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 254 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_6]], %[[VAL_7]] : !pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_12]]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@in] : <[@in: !array.type<254 x !felt.type<"bn128">>]>, !array.type<254 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_14]]{{\[}}%[[VAL_15]]] = %[[VAL_13]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_10]][@in] = %[[VAL_14]] : <[@in: !array.type<254 x !felt.type<"bn128">>]>, !array.type<254 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@count] : <[@count: index, @comp: !struct.type<@CompConstant_1::@CompConstant_1<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_16]], %[[VAL_17]] : index +// CHECK-NEXT: pod.write %[[VAL_2]][@count] = %[[VAL_18]] : <[@count: index, @comp: !struct.type<@CompConstant_1::@CompConstant_1<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_18]], %[[VAL_19]] : index +// CHECK-NEXT: scf.if %[[VAL_20]] { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@params] : <[@count: index, @comp: !struct.type<@CompConstant_1::@CompConstant_1<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@in] : <[@in: !array.type<254 x !felt.type<"bn128">>]>, !array.type<254 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = function.call @CompConstant_1::@CompConstant_1::@compute(%[[VAL_22]]) : (!array.type<254 x !felt.type<"bn128">>) -> !struct.type<@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: pod.write %[[VAL_2]][@comp] = %[[VAL_23]] : <[@count: index, @comp: !struct.type<@CompConstant_1::@CompConstant_1<[]>>, @params: !pod.type<[]>]>, !struct.type<@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_25]] : !pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@compConstant$inputs] = %[[VAL_5]]#0 : <@AliasCheck_2::@AliasCheck_2<[]>>, !pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@comp] : <[@count: index, @comp: !struct.type<@CompConstant_1::@CompConstant_1<[]>>, @params: !pod.type<[]>]>, !struct.type<@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@compConstant] = %[[VAL_26]] : <@AliasCheck_2::@AliasCheck_2<[]>>, !struct.type<@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@AliasCheck_2::@AliasCheck_2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !struct.type<@AliasCheck_2::@AliasCheck_2<[]>>, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !array.type<254 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_27]][@compConstant] : <@AliasCheck_2::@AliasCheck_2<[]>>, !struct.type<@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_27]][@compConstant$inputs] : <@AliasCheck_2::@AliasCheck_2<[]>>, !pod.type<[@in: !array.type<254 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_33:[0-9a-zA-Z_\.]+]] = %[[VAL_31]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 254 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_33]], %[[VAL_34]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_35]]) %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_37]]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_30]][@in] : <[@in: !array.type<254 x !felt.type<"bn128">>]>, !array.type<254 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_39]]{{\[}}%[[VAL_40]]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_41]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_36]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_29]][@out] : <@CompConstant_1::@CompConstant_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_30]][@in] : <[@in: !array.type<254 x !felt.type<"bn128">>]>, !array.type<254 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @CompConstant_1::@CompConstant_1::@constrain(%[[VAL_29]], %[[VAL_46]]) : (!struct.type<@CompConstant_1::@CompConstant_1<[]>>, !array.type<254 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @CompConstant_1 { +// CHECK-NEXT: struct.def @CompConstant_1 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @parts : !array.type<127 x !felt.type<"bn128">> +// CHECK-NEXT: struct.member @sout : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @num2bits : !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: struct.member @num2bits$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !array.type<254 x !felt.type<"bn128">>) -> !struct.type<@CompConstant_1::@CompConstant_1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = struct.new : <@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<127 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = llzk.nondet : !pod.type<[@count: index, @comp: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 21888242871839275222246405745257275088548364400416034343698204186575808495616 : <"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 340282366920938463463374607431768211455 : <"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]]:9 = scf.while (%[[VAL_64:[0-9a-zA-Z_\.]+]] = %[[VAL_59]], %[[VAL_65:[0-9a-zA-Z_\.]+]] = %[[VAL_58]], %[[VAL_66:[0-9a-zA-Z_\.]+]] = %[[VAL_53]], %[[VAL_67:[0-9a-zA-Z_\.]+]] = %[[VAL_54]], %[[VAL_68:[0-9a-zA-Z_\.]+]] = %[[VAL_60]], %[[VAL_69:[0-9a-zA-Z_\.]+]] = %[[VAL_62]], %[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_55]], %[[VAL_71:[0-9a-zA-Z_\.]+]] = %[[VAL_56]], %[[VAL_72:[0-9a-zA-Z_\.]+]] = %[[VAL_57]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 127 : <"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_69]], %[[VAL_73]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_74]]) %[[VAL_64]], %[[VAL_65]], %[[VAL_66]], %[[VAL_67]], %[[VAL_68]], %[[VAL_69]], %[[VAL_70]], %[[VAL_71]], %[[VAL_72]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_75:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_76:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_77:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_78:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_79:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_80:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_81:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_82:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_83:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 21888242871839275222246405745257275088548364400416034343698204186575808495616 : <"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_80]], %[[VAL_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_84]], %[[VAL_86]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_87]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 21888242871839275222246405745257275088548364400416034343698204186575808495616 : <"bn128"> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_80]], %[[VAL_91]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_92]], %[[VAL_93]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_90]], %[[VAL_94]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_95]], %[[VAL_96]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_80]], %[[VAL_98]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_99]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_100]]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_80]], %[[VAL_102]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_103]], %[[VAL_104]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_105]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_106]]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_97]], %[[VAL_108]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_89]], %[[VAL_110]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_109]], %[[VAL_111]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_112]] { +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_76]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_113]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_114]], %[[VAL_101]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_76]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_115]], %[[VAL_116]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_76]], %[[VAL_101]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_117]], %[[VAL_118]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_49]]{{\[}}%[[VAL_120]]] = %[[VAL_119]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_97]], %[[VAL_121]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_89]], %[[VAL_123]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_122]], %[[VAL_124]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_125]] { +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_75]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_126]], %[[VAL_101]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_75]], %[[VAL_101]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_127]], %[[VAL_128]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_76]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_129]], %[[VAL_130]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_75]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_131]], %[[VAL_132]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_133]], %[[VAL_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_49]]{{\[}}%[[VAL_135]]] = %[[VAL_134]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_89]], %[[VAL_137]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_136]], %[[VAL_139]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_140]], %[[VAL_138]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_141]] { +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_76]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_142]], %[[VAL_101]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_75]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_143]], %[[VAL_144]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_145]], %[[VAL_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_49]]{{\[}}%[[VAL_147]]] = %[[VAL_146]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_75]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_148]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_149]], %[[VAL_101]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_150]], %[[VAL_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_49]]{{\[}}%[[VAL_152]]] = %[[VAL_151]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_80]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_49]]{{\[}}%[[VAL_153]]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_155:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_154]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_76]], %[[VAL_79]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_75]], %[[VAL_79]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_79]], %[[VAL_158]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_80]], %[[VAL_160]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_157]], %[[VAL_156]], %[[VAL_89]], %[[VAL_97]], %[[VAL_159]], %[[VAL_161]], %[[VAL_101]], %[[VAL_107]], %[[VAL_155]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_48]][@sout] = %[[VAL_63]]#8 : <@CompConstant_1::@CompConstant_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_51]][@in] = %[[VAL_63]]#8 : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_50]][@count] : <[@count: index, @comp: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_162]], %[[VAL_163]] : index +// CHECK-NEXT: pod.write %[[VAL_50]][@count] = %[[VAL_164]] : <[@count: index, @comp: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_164]], %[[VAL_165]] : index +// CHECK-NEXT: scf.if %[[VAL_166]] { +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_50]][@params] : <[@count: index, @comp: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_168:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_169:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits_0::@Num2Bits_0::@compute(%[[VAL_168]]) : (!felt.type<"bn128">) -> !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: pod.write %[[VAL_50]][@comp] = %[[VAL_169]] : <[@count: index, @comp: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_50]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_170]][@out] : <@Num2Bits_0::@Num2Bits_0<[]>>, !array.type<135 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_172:[0-9a-zA-Z_\.]+]] = felt.const 127 : <"bn128"> +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_172]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_171]]{{\[}}%[[VAL_173]]] : <135 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_48]][@out] = %[[VAL_174]] : <@CompConstant_1::@CompConstant_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_48]][@num2bits$inputs] = %[[VAL_51]] : <@CompConstant_1::@CompConstant_1<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_50]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: struct.writem %[[VAL_48]][@num2bits] = %[[VAL_175]] : <@CompConstant_1::@CompConstant_1<[]>>, !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: struct.writem %[[VAL_48]][@parts] = %[[VAL_49]] : <@CompConstant_1::@CompConstant_1<[]>>, !array.type<127 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_48]] : !struct.type<@CompConstant_1::@CompConstant_1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_176:[0-9a-zA-Z_\.]+]]: !struct.type<@CompConstant_1::@CompConstant_1<[]>>, %[[VAL_177:[0-9a-zA-Z_\.]+]]: !array.type<254 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_176]][@out] : <@CompConstant_1::@CompConstant_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_179:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_176]][@parts] : <@CompConstant_1::@CompConstant_1<[]>>, !array.type<127 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_180:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_176]][@sout] : <@CompConstant_1::@CompConstant_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_181:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_176]][@num2bits] : <@CompConstant_1::@CompConstant_1<[]>>, !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: %[[VAL_182:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_176]][@num2bits$inputs] : <@CompConstant_1::@CompConstant_1<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_183:[0-9a-zA-Z_\.]+]] = felt.const 21888242871839275222246405745257275088548364400416034343698204186575808495616 : <"bn128"> +// CHECK-NEXT: %[[VAL_184:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_185:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_186:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_187:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_188:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_189:[0-9a-zA-Z_\.]+]] = felt.const 340282366920938463463374607431768211455 : <"bn128"> +// CHECK-NEXT: %[[VAL_190:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_191:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_192:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_193:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_194:[0-9a-zA-Z_\.]+]]:9 = scf.while (%[[VAL_195:[0-9a-zA-Z_\.]+]] = %[[VAL_190]], %[[VAL_196:[0-9a-zA-Z_\.]+]] = %[[VAL_189]], %[[VAL_197:[0-9a-zA-Z_\.]+]] = %[[VAL_184]], %[[VAL_198:[0-9a-zA-Z_\.]+]] = %[[VAL_185]], %[[VAL_199:[0-9a-zA-Z_\.]+]] = %[[VAL_191]], %[[VAL_200:[0-9a-zA-Z_\.]+]] = %[[VAL_193]], %[[VAL_201:[0-9a-zA-Z_\.]+]] = %[[VAL_186]], %[[VAL_202:[0-9a-zA-Z_\.]+]] = %[[VAL_187]], %[[VAL_203:[0-9a-zA-Z_\.]+]] = %[[VAL_188]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_204:[0-9a-zA-Z_\.]+]] = felt.const 127 : <"bn128"> +// CHECK-NEXT: %[[VAL_205:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_200]], %[[VAL_204]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_205]]) %[[VAL_195]], %[[VAL_196]], %[[VAL_197]], %[[VAL_198]], %[[VAL_199]], %[[VAL_200]], %[[VAL_201]], %[[VAL_202]], %[[VAL_203]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_206:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_207:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_208:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_209:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_210:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_211:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_212:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_213:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_214:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_215:[0-9a-zA-Z_\.]+]] = felt.const 21888242871839275222246405745257275088548364400416034343698204186575808495616 : <"bn128"> +// CHECK-NEXT: %[[VAL_216:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_217:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_211]], %[[VAL_216]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_218:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_215]], %[[VAL_217]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_219:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_220:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_218]], %[[VAL_219]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_221:[0-9a-zA-Z_\.]+]] = felt.const 21888242871839275222246405745257275088548364400416034343698204186575808495616 : <"bn128"> +// CHECK-NEXT: %[[VAL_222:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_223:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_211]], %[[VAL_222]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_224:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_225:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_223]], %[[VAL_224]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_226:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_221]], %[[VAL_225]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_227:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_228:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_226]], %[[VAL_227]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_229:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_230:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_211]], %[[VAL_229]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_231:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_230]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_232:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_177]]{{\[}}%[[VAL_231]]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_233:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_234:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_211]], %[[VAL_233]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_235:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_236:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_234]], %[[VAL_235]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_237:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_236]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_238:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_177]]{{\[}}%[[VAL_237]]] : <254 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_239:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_240:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_228]], %[[VAL_239]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_241:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_242:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_220]], %[[VAL_241]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_243:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_240]], %[[VAL_242]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_243]] { +// CHECK-NEXT: %[[VAL_244:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_207]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_245:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_244]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_246:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_245]], %[[VAL_232]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_247:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_207]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_248:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_246]], %[[VAL_247]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_249:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_207]], %[[VAL_232]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_250:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_248]], %[[VAL_249]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_251:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_211]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_252:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_179]]{{\[}}%[[VAL_251]]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_252]], %[[VAL_250]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_253:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_254:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_228]], %[[VAL_253]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_255:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_256:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_220]], %[[VAL_255]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_257:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_254]], %[[VAL_256]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_257]] { +// CHECK-NEXT: %[[VAL_258:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_206]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_259:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_258]], %[[VAL_232]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_260:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_206]], %[[VAL_232]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_261:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_259]], %[[VAL_260]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_262:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_207]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_263:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_261]], %[[VAL_262]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_264:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_206]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_265:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_263]], %[[VAL_264]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_266:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_265]], %[[VAL_206]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_267:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_211]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_268:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_179]]{{\[}}%[[VAL_267]]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_268]], %[[VAL_266]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_269:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_270:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_271:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_220]], %[[VAL_270]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_272:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_273:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_269]], %[[VAL_272]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_274:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_273]], %[[VAL_271]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_274]] { +// CHECK-NEXT: %[[VAL_275:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_207]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_276:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_275]], %[[VAL_232]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_277:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_206]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_278:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_276]], %[[VAL_277]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_279:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_278]], %[[VAL_206]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_280:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_211]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_281:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_179]]{{\[}}%[[VAL_280]]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_281]], %[[VAL_279]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_282:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_206]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_283:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_282]], %[[VAL_238]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_284:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_283]], %[[VAL_232]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_285:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_284]], %[[VAL_206]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_286:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_211]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_287:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_179]]{{\[}}%[[VAL_286]]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_287]], %[[VAL_285]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_288:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_211]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_289:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_179]]{{\[}}%[[VAL_288]]] : <127 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_290:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_214]], %[[VAL_289]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_291:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_207]], %[[VAL_210]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_292:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_206]], %[[VAL_210]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_293:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_294:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_210]], %[[VAL_293]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_295:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_296:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_211]], %[[VAL_295]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_292]], %[[VAL_291]], %[[VAL_220]], %[[VAL_228]], %[[VAL_294]], %[[VAL_296]], %[[VAL_232]], %[[VAL_238]], %[[VAL_290]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_180]], %[[VAL_194]]#8 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_297:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_182]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_297]], %[[VAL_194]]#8 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_298:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_181]][@out] : <@Num2Bits_0::@Num2Bits_0<[]>>, !array.type<135 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_299:[0-9a-zA-Z_\.]+]] = felt.const 127 : <"bn128"> +// CHECK-NEXT: %[[VAL_300:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_299]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_301:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_298]]{{\[}}%[[VAL_300]]] : <135 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_178]], %[[VAL_301]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_302:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_182]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Num2Bits_0::@Num2Bits_0::@constrain(%[[VAL_181]], %[[VAL_302]]) : (!struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Num2Bits_0 { +// CHECK-NEXT: struct.def @Num2Bits_0 { +// CHECK-NEXT: struct.member @out : !array.type<135 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_303:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_304:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: %[[VAL_305:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<135 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_306:[0-9a-zA-Z_\.]+]] = felt.const 135 : <"bn128"> +// CHECK-NEXT: %[[VAL_307:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_308:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_309:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_310:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_311:[0-9a-zA-Z_\.]+]] = %[[VAL_308]], %[[VAL_312:[0-9a-zA-Z_\.]+]] = %[[VAL_309]], %[[VAL_313:[0-9a-zA-Z_\.]+]] = %[[VAL_307]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_314:[0-9a-zA-Z_\.]+]] = felt.const 135 : <"bn128"> +// CHECK-NEXT: %[[VAL_315:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_312]], %[[VAL_314]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_315]]) %[[VAL_311]], %[[VAL_312]], %[[VAL_313]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_316:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_317:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_318:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_319:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_303]], %[[VAL_317]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_320:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_321:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_319]], %[[VAL_320]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_322:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_317]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_305]]{{\[}}%[[VAL_322]]] = %[[VAL_321]] : <135 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_323:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_317]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_324:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_305]]{{\[}}%[[VAL_323]]] : <135 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_325:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_324]], %[[VAL_308]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_326:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_318]], %[[VAL_325]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_327:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_316]], %[[VAL_316]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_328:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_329:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_317]], %[[VAL_328]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_327]], %[[VAL_329]], %[[VAL_326]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_304]][@out] = %[[VAL_305]] : <@Num2Bits_0::@Num2Bits_0<[]>>, !array.type<135 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_304]] : !struct.type<@Num2Bits_0::@Num2Bits_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_330:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits_0::@Num2Bits_0<[]>>, %[[VAL_331:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_332:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_330]][@out] : <@Num2Bits_0::@Num2Bits_0<[]>>, !array.type<135 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_333:[0-9a-zA-Z_\.]+]] = felt.const 135 : <"bn128"> +// CHECK-NEXT: %[[VAL_334:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_335:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_336:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_337:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_338:[0-9a-zA-Z_\.]+]] = %[[VAL_335]], %[[VAL_339:[0-9a-zA-Z_\.]+]] = %[[VAL_336]], %[[VAL_340:[0-9a-zA-Z_\.]+]] = %[[VAL_334]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_341:[0-9a-zA-Z_\.]+]] = felt.const 135 : <"bn128"> +// CHECK-NEXT: %[[VAL_342:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_339]], %[[VAL_341]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_342]]) %[[VAL_338]], %[[VAL_339]], %[[VAL_340]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_343:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_344:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_345:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_346:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_344]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_347:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_332]]{{\[}}%[[VAL_346]]] : <135 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_348:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_344]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_349:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_332]]{{\[}}%[[VAL_348]]] : <135 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_350:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_351:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_349]], %[[VAL_350]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_352:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_347]], %[[VAL_351]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_353:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_352]], %[[VAL_353]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_354:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_344]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_355:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_332]]{{\[}}%[[VAL_354]]] : <135 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_356:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_355]], %[[VAL_335]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_357:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_345]], %[[VAL_356]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_358:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_343]], %[[VAL_343]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_359:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_360:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_344]], %[[VAL_359]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_358]], %[[VAL_360]], %[[VAL_357]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_337]]#2, %[[VAL_331]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/bench_mat_mul.circom b/circom/tests/from_concrete/bench_mat_mul.circom new file mode 100644 index 000000000..7d77630d1 --- /dev/null +++ b/circom/tests/from_concrete/bench_mat_mul.circom @@ -0,0 +1,634 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.3; + +// matrix multiplication by element +template matElemMul (m,n) { + signal input a[m][n]; + signal input b[m][n]; + signal output out[m][n]; + + for (var i=0; i < m; i++) { + for (var j=0; j < n; j++) { + out[i][j] <== a[i][j] * b[i][j]; + } + } +} + +// sum of all elements in a matrix +template matElemSum (m,n) { + signal input a[m][n]; + signal output out; + + signal sum[m*n]; + sum[0] <== a[0][0]; + var idx = 0; + + for (var i=0; i < m; i++) { + for (var j=0; j < n; j++) { + if (idx > 0) { + sum[idx] <== sum[idx-1] + a[i][j]; + } + idx++; + } + } + + out <== sum[m*n-1]; +} + +// matrix multiplication +template matMul (m,n,p) { + signal input a[m][n]; + signal input b[n][p]; + signal output out[m][p]; + + component matElemMulComp[m][p]; + component matElemSumComp[m][p]; + + for (var i=0; i < m; i++) { + for (var j=0; j < p; j++) { + matElemMulComp[i][j] = matElemMul(1,n); + matElemSumComp[i][j] = matElemSum(1,n); + for (var k=0; k < n; k++) { + matElemMulComp[i][j].a[0][k] <== a[i][k]; + matElemMulComp[i][j].b[0][k] <== b[k][j]; + } + for (var k=0; k < n; k++) { + matElemSumComp[i][j].a[0][k] <== matElemMulComp[i][j].out[0][k]; + } + out[i][j] <== matElemSumComp[i][j].out; + } + } +} + +component main = matMul(2,3,2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@matMul_2::@matMul_2<[]>>} { +// CHECK-NEXT: poly.template @matElemMul_0 { +// CHECK-NEXT: struct.def @matElemMul_0 { +// CHECK-NEXT: struct.member @out : !array.type<1,3 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<1,3 x !felt.type<"bn128">>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<1,3 x !felt.type<"bn128">>) -> !struct.type<@matElemMul_0::@matElemMul_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_8]], %[[VAL_9]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_10]]) %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_14:[0-9a-zA-Z_\.]+]] = %[[VAL_12]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_14]], %[[VAL_15]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_16]]) %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_19]], %[[VAL_20]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_23]], %[[VAL_24]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_21]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_28]], %[[VAL_29]]] = %[[VAL_26]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@matElemMul_0::@matElemMul_0<[]>>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_33:[0-9a-zA-Z_\.]+]]: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, %[[VAL_34:[0-9a-zA-Z_\.]+]]: !array.type<1,3 x !felt.type<"bn128">>, %[[VAL_35:[0-9a-zA-Z_\.]+]]: !array.type<1,3 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_33]][@out] : <@matElemMul_0::@matElemMul_0<[]>>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_39]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_41]], %[[VAL_42]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_43]]) %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_47:[0-9a-zA-Z_\.]+]] = %[[VAL_45]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_47]], %[[VAL_48]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_49]]) %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_50:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_34]]{{\[}}%[[VAL_52]], %[[VAL_53]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_35]]{{\[}}%[[VAL_56]], %[[VAL_57]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_54]], %[[VAL_58]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_60]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_36]]{{\[}}%[[VAL_61]], %[[VAL_62]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_63]], %[[VAL_59]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_50]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_66]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @matElemSum_1 { +// CHECK-NEXT: struct.def @matElemSum_1 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @sum : !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: function.def @compute(%[[VAL_67:[0-9a-zA-Z_\.]+]]: !array.type<1,3 x !felt.type<"bn128">>) -> !struct.type<@matElemSum_1::@matElemSum_1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = struct.new : <@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_74]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_67]]{{\[}}%[[VAL_73]], %[[VAL_75]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_77]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_69]]{{\[}}%[[VAL_78]]] = %[[VAL_76]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_82:[0-9a-zA-Z_\.]+]] = %[[VAL_80]], %[[VAL_83:[0-9a-zA-Z_\.]+]] = %[[VAL_79]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_82]], %[[VAL_84]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_85]]) %[[VAL_82]], %[[VAL_83]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_86:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_87:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_90:[0-9a-zA-Z_\.]+]] = %[[VAL_87]], %[[VAL_91:[0-9a-zA-Z_\.]+]] = %[[VAL_88]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_91]], %[[VAL_92]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_93]]) %[[VAL_90]], %[[VAL_91]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_94:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_95:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_94]], %[[VAL_96]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_97]] { +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_94]], %[[VAL_98]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_99]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_69]]{{\[}}%[[VAL_100]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_102]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_95]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_67]]{{\[}}%[[VAL_103]], %[[VAL_104]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_101]], %[[VAL_105]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_94]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_69]]{{\[}}%[[VAL_107]]] = %[[VAL_106]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_94]], %[[VAL_108]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_95]], %[[VAL_110]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_109]], %[[VAL_111]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_112]], %[[VAL_89]]#0 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_113]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_69]]{{\[}}%[[VAL_114]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_68]][@out] = %[[VAL_115]] : <@matElemSum_1::@matElemSum_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_68]][@sum] = %[[VAL_69]] : <@matElemSum_1::@matElemSum_1<[]>>, !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_68]] : !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_116:[0-9a-zA-Z_\.]+]]: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, %[[VAL_117:[0-9a-zA-Z_\.]+]]: !array.type<1,3 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_116]][@out] : <@matElemSum_1::@matElemSum_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_116]][@sum] : <@matElemSum_1::@matElemSum_1<[]>>, !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_122]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_124]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_117]]{{\[}}%[[VAL_123]], %[[VAL_125]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_127]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_119]]{{\[}}%[[VAL_128]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_129]], %[[VAL_126]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_133:[0-9a-zA-Z_\.]+]] = %[[VAL_131]], %[[VAL_134:[0-9a-zA-Z_\.]+]] = %[[VAL_130]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_133]], %[[VAL_135]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_136]]) %[[VAL_133]], %[[VAL_134]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_137:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_138:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_141:[0-9a-zA-Z_\.]+]] = %[[VAL_138]], %[[VAL_142:[0-9a-zA-Z_\.]+]] = %[[VAL_139]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_142]], %[[VAL_143]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_144]]) %[[VAL_141]], %[[VAL_142]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_145:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_146:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_145]], %[[VAL_147]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_148]] { +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_145]], %[[VAL_149]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_150]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_119]]{{\[}}%[[VAL_151]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_153]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_155:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_146]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_117]]{{\[}}%[[VAL_154]], %[[VAL_155]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_152]], %[[VAL_156]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_145]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_119]]{{\[}}%[[VAL_158]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_159]], %[[VAL_157]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_145]], %[[VAL_160]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_146]], %[[VAL_162]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_161]], %[[VAL_163]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_164]], %[[VAL_140]]#0 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_165]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_119]]{{\[}}%[[VAL_166]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_118]], %[[VAL_167]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @matMul_2 { +// CHECK-NEXT: struct.def @matMul_2 { +// CHECK-NEXT: struct.member @out : !array.type<2,2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @matElemMulComp : !array.type<2,2 x !struct.type<@matElemMul_0::@matElemMul_0<[]>>> +// CHECK-NEXT: struct.member @matElemMulComp$inputs : !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: struct.member @matElemSumComp : !array.type<2,2 x !struct.type<@matElemSum_1::@matElemSum_1<[]>>> +// CHECK-NEXT: struct.member @matElemSumComp$inputs : !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: function.def @compute(%[[VAL_168:[0-9a-zA-Z_\.]+]]: !array.type<2,3 x !felt.type<"bn128">>, %[[VAL_169:[0-9a-zA-Z_\.]+]]: !array.type<3,2 x !felt.type<"bn128">>) -> !struct.type<@matMul_2::@matMul_2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = struct.new : <@matMul_2::@matMul_2<[]>> +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_172:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_176:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_177:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_179:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_180:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_181:[0-9a-zA-Z_\.]+]] = %[[VAL_179]], %[[VAL_182:[0-9a-zA-Z_\.]+]] = %[[VAL_173]], %[[VAL_183:[0-9a-zA-Z_\.]+]] = %[[VAL_175]]) : (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>) -> (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>) { +// CHECK-NEXT: %[[VAL_184:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_185:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_181]], %[[VAL_184]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_185]]) %[[VAL_181]], %[[VAL_182]], %[[VAL_183]] : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_186:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_187:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, %[[VAL_188:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>): +// CHECK-NEXT: %[[VAL_189:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_190:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_191:[0-9a-zA-Z_\.]+]] = %[[VAL_189]], %[[VAL_192:[0-9a-zA-Z_\.]+]] = %[[VAL_187]], %[[VAL_193:[0-9a-zA-Z_\.]+]] = %[[VAL_188]]) : (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>) -> (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>) { +// CHECK-NEXT: %[[VAL_194:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_195:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_191]], %[[VAL_194]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_195]]) %[[VAL_191]], %[[VAL_192]], %[[VAL_193]] : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_196:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_197:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, %[[VAL_198:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>): +// CHECK-NEXT: %[[VAL_199:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_200:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_201:[0-9a-zA-Z_\.]+]] = %[[VAL_199]], %[[VAL_202:[0-9a-zA-Z_\.]+]] = %[[VAL_197]]) : (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>) -> (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>) { +// CHECK-NEXT: %[[VAL_203:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_204:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_201]], %[[VAL_203]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_204]]) %[[VAL_201]], %[[VAL_202]] : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_205:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_206:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>): +// CHECK-NEXT: %[[VAL_207:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_208:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_205]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_209:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_168]]{{\[}}%[[VAL_207]], %[[VAL_208]]] : <2,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_210:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_211:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_212:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_206]]{{\[}}%[[VAL_210]], %[[VAL_211]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_213:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_212]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_214:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_215:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_214]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_216:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_205]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_213]]{{\[}}%[[VAL_215]], %[[VAL_216]]] = %[[VAL_209]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_217:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_218:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_219:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_206]]{{\[}}%[[VAL_217]], %[[VAL_218]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: pod.write %[[VAL_219]][@a] = %[[VAL_213]] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_220:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_221:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_206]]{{\[}}%[[VAL_220]], %[[VAL_221]]] = %[[VAL_219]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_222:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_223:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_224:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_172]]{{\[}}%[[VAL_222]], %[[VAL_223]]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_225:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_224]][@count] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_226:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_227:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_225]], %[[VAL_226]] : index +// CHECK-NEXT: pod.write %[[VAL_224]][@count] = %[[VAL_227]] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_228:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_229:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_227]], %[[VAL_228]] : index +// CHECK-NEXT: scf.if %[[VAL_229]] { +// CHECK-NEXT: %[[VAL_230:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_224]][@params] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_231:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_219]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_232:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_219]][@b] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_233:[0-9a-zA-Z_\.]+]] = function.call @matElemMul_0::@matElemMul_0::@compute(%[[VAL_231]], %[[VAL_232]]) : (!array.type<1,3 x !felt.type<"bn128">>, !array.type<1,3 x !felt.type<"bn128">>) -> !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: pod.write %[[VAL_224]][@comp] = %[[VAL_233]] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: %[[VAL_234:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_235:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_172]]{{\[}}%[[VAL_234]], %[[VAL_235]]] = %[[VAL_224]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_236:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_205]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_237:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_238:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_169]]{{\[}}%[[VAL_236]], %[[VAL_237]]] : <3,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_239:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_240:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_241:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_206]]{{\[}}%[[VAL_239]], %[[VAL_240]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_242:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_241]][@b] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_243:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_244:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_243]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_245:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_205]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_242]]{{\[}}%[[VAL_244]], %[[VAL_245]]] = %[[VAL_238]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_246:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_247:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_248:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_206]]{{\[}}%[[VAL_246]], %[[VAL_247]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: pod.write %[[VAL_248]][@b] = %[[VAL_242]] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_249:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_250:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_206]]{{\[}}%[[VAL_249]], %[[VAL_250]]] = %[[VAL_248]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_251:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_252:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_253:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_172]]{{\[}}%[[VAL_251]], %[[VAL_252]]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_254:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_253]][@count] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_255:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_256:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_254]], %[[VAL_255]] : index +// CHECK-NEXT: pod.write %[[VAL_253]][@count] = %[[VAL_256]] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_257:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_258:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_256]], %[[VAL_257]] : index +// CHECK-NEXT: scf.if %[[VAL_258]] { +// CHECK-NEXT: %[[VAL_259:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_253]][@params] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_260:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_248]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_261:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_248]][@b] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_262:[0-9a-zA-Z_\.]+]] = function.call @matElemMul_0::@matElemMul_0::@compute(%[[VAL_260]], %[[VAL_261]]) : (!array.type<1,3 x !felt.type<"bn128">>, !array.type<1,3 x !felt.type<"bn128">>) -> !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: pod.write %[[VAL_253]][@comp] = %[[VAL_262]] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: %[[VAL_263:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_264:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_172]]{{\[}}%[[VAL_263]], %[[VAL_264]]] = %[[VAL_253]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_265:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_266:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_205]], %[[VAL_265]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_266]], %[[VAL_206]] : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_267:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_268:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_269:[0-9a-zA-Z_\.]+]] = %[[VAL_267]], %[[VAL_270:[0-9a-zA-Z_\.]+]] = %[[VAL_198]]) : (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>) -> (!felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>) { +// CHECK-NEXT: %[[VAL_271:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_272:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_269]], %[[VAL_271]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_272]]) %[[VAL_269]], %[[VAL_270]] : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_273:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_274:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>): +// CHECK-NEXT: %[[VAL_275:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_276:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_277:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_172]]{{\[}}%[[VAL_275]], %[[VAL_276]]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_278:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_277]][@comp] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: %[[VAL_279:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_278]][@out] : <@matElemMul_0::@matElemMul_0<[]>>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_280:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_281:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_280]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_282:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_273]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_283:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_279]]{{\[}}%[[VAL_281]], %[[VAL_282]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_284:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_285:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_286:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_274]]{{\[}}%[[VAL_284]], %[[VAL_285]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_287:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_286]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_288:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_289:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_288]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_290:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_273]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_287]]{{\[}}%[[VAL_289]], %[[VAL_290]]] = %[[VAL_283]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_291:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_292:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_293:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_274]]{{\[}}%[[VAL_291]], %[[VAL_292]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: pod.write %[[VAL_293]][@a] = %[[VAL_287]] : <[@a: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_294:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_295:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_274]]{{\[}}%[[VAL_294]], %[[VAL_295]]] = %[[VAL_293]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_296:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_297:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_298:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_174]]{{\[}}%[[VAL_296]], %[[VAL_297]]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_299:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_298]][@count] : <[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_300:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_301:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_299]], %[[VAL_300]] : index +// CHECK-NEXT: pod.write %[[VAL_298]][@count] = %[[VAL_301]] : <[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_302:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_303:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_301]], %[[VAL_302]] : index +// CHECK-NEXT: scf.if %[[VAL_303]] { +// CHECK-NEXT: %[[VAL_304:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_298]][@params] : <[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_305:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_293]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_306:[0-9a-zA-Z_\.]+]] = function.call @matElemSum_1::@matElemSum_1::@compute(%[[VAL_305]]) : (!array.type<1,3 x !felt.type<"bn128">>) -> !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: pod.write %[[VAL_298]][@comp] = %[[VAL_306]] : <[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>, !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: %[[VAL_307:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_308:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_174]]{{\[}}%[[VAL_307]], %[[VAL_308]]] = %[[VAL_298]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_309:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_310:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_273]], %[[VAL_309]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_310]], %[[VAL_274]] : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_311:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_312:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_313:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_174]]{{\[}}%[[VAL_311]], %[[VAL_312]]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_314:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_313]][@comp] : <[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>, !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: %[[VAL_315:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_314]][@out] : <@matElemSum_1::@matElemSum_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_316:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_317:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_171]]{{\[}}%[[VAL_316]], %[[VAL_317]]] = %[[VAL_315]] : <2,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_318:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_319:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_196]], %[[VAL_318]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_319]], %[[VAL_200]]#1, %[[VAL_268]]#1 : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_320:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_321:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_186]], %[[VAL_320]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_321]], %[[VAL_190]]#1, %[[VAL_190]]#2 : !felt.type<"bn128">, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_170]][@matElemMulComp$inputs] = %[[VAL_180]]#1 : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_322:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !struct.type<@matElemMul_0::@matElemMul_0<[]>>> +// CHECK-NEXT: %[[VAL_323:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_324:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_325:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_326:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_327:[0-9a-zA-Z_\.]+]] = %[[VAL_325]] to %[[VAL_323]] step %[[VAL_326]] { +// CHECK-NEXT: scf.for %[[VAL_328:[0-9a-zA-Z_\.]+]] = %[[VAL_325]] to %[[VAL_324]] step %[[VAL_326]] { +// CHECK-NEXT: %[[VAL_329:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_172]]{{\[}}%[[VAL_327]], %[[VAL_328]]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_330:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_329]][@comp] : <[@count: index, @comp: !struct.type<@matElemMul_0::@matElemMul_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: array.write %[[VAL_322]]{{\[}}%[[VAL_327]], %[[VAL_328]]] = %[[VAL_330]] : <2,2 x !struct.type<@matElemMul_0::@matElemMul_0<[]>>>, !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_170]][@matElemMulComp] = %[[VAL_322]] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !struct.type<@matElemMul_0::@matElemMul_0<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_170]][@matElemSumComp$inputs] = %[[VAL_180]]#2 : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_331:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !struct.type<@matElemSum_1::@matElemSum_1<[]>>> +// CHECK-NEXT: %[[VAL_332:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_333:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_334:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_335:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_336:[0-9a-zA-Z_\.]+]] = %[[VAL_334]] to %[[VAL_332]] step %[[VAL_335]] { +// CHECK-NEXT: scf.for %[[VAL_337:[0-9a-zA-Z_\.]+]] = %[[VAL_334]] to %[[VAL_333]] step %[[VAL_335]] { +// CHECK-NEXT: %[[VAL_338:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_174]]{{\[}}%[[VAL_336]], %[[VAL_337]]] : <2,2 x !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_339:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_338]][@comp] : <[@count: index, @comp: !struct.type<@matElemSum_1::@matElemSum_1<[]>>, @params: !pod.type<[]>]>, !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: array.write %[[VAL_331]]{{\[}}%[[VAL_336]], %[[VAL_337]]] = %[[VAL_339]] : <2,2 x !struct.type<@matElemSum_1::@matElemSum_1<[]>>>, !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_170]][@matElemSumComp] = %[[VAL_331]] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !struct.type<@matElemSum_1::@matElemSum_1<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_170]][@out] = %[[VAL_171]] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_170]] : !struct.type<@matMul_2::@matMul_2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_340:[0-9a-zA-Z_\.]+]]: !struct.type<@matMul_2::@matMul_2<[]>>, %[[VAL_341:[0-9a-zA-Z_\.]+]]: !array.type<2,3 x !felt.type<"bn128">>, %[[VAL_342:[0-9a-zA-Z_\.]+]]: !array.type<3,2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_343:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_340]][@out] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_344:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_340]][@matElemMulComp] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !struct.type<@matElemMul_0::@matElemMul_0<[]>>> +// CHECK-NEXT: %[[VAL_345:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_340]][@matElemMulComp$inputs] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_346:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_340]][@matElemSumComp] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !struct.type<@matElemSum_1::@matElemSum_1<[]>>> +// CHECK-NEXT: %[[VAL_347:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_340]][@matElemSumComp$inputs] : <@matMul_2::@matMul_2<[]>>, !array.type<2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>> +// CHECK-NEXT: %[[VAL_348:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_349:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_350:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_351:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_352:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_353:[0-9a-zA-Z_\.]+]] = %[[VAL_351]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_354:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_355:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_353]], %[[VAL_354]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_355]]) %[[VAL_353]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_356:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_357:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_358:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_359:[0-9a-zA-Z_\.]+]] = %[[VAL_357]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_360:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_361:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_359]], %[[VAL_360]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_361]]) %[[VAL_359]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_362:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_363:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_364:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_365:[0-9a-zA-Z_\.]+]] = %[[VAL_363]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_366:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_367:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_365]], %[[VAL_366]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_367]]) %[[VAL_365]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_368:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_369:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_356]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_370:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_368]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_371:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_341]]{{\[}}%[[VAL_369]], %[[VAL_370]]] : <2,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_372:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_356]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_373:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_362]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_374:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_345]]{{\[}}%[[VAL_372]], %[[VAL_373]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_375:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_374]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_376:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_377:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_376]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_378:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_368]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_379:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_375]]{{\[}}%[[VAL_377]], %[[VAL_378]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_379]], %[[VAL_371]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_380:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_368]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_381:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_362]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_382:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_342]]{{\[}}%[[VAL_380]], %[[VAL_381]]] : <3,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_383:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_356]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_384:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_362]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_385:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_345]]{{\[}}%[[VAL_383]], %[[VAL_384]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_386:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_385]][@b] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_387:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_388:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_387]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_389:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_368]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_390:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_386]]{{\[}}%[[VAL_388]], %[[VAL_389]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_390]], %[[VAL_382]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_391:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_392:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_368]], %[[VAL_391]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_392]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_393:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_394:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_395:[0-9a-zA-Z_\.]+]] = %[[VAL_393]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_396:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_397:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_395]], %[[VAL_396]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_397]]) %[[VAL_395]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_398:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_399:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_356]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_400:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_362]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_401:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_344]]{{\[}}%[[VAL_399]], %[[VAL_400]]] : <2,2 x !struct.type<@matElemMul_0::@matElemMul_0<[]>>>, !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: %[[VAL_402:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_401]][@out] : <@matElemMul_0::@matElemMul_0<[]>>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_403:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_404:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_403]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_405:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_398]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_406:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_402]]{{\[}}%[[VAL_404]], %[[VAL_405]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_407:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_356]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_408:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_362]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_409:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_347]]{{\[}}%[[VAL_407]], %[[VAL_408]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_410:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_409]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_411:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_412:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_411]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_413:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_398]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_414:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_410]]{{\[}}%[[VAL_412]], %[[VAL_413]]] : <1,3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_414]], %[[VAL_406]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_415:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_416:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_398]], %[[VAL_415]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_416]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_417:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_356]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_418:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_362]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_419:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_346]]{{\[}}%[[VAL_417]], %[[VAL_418]]] : <2,2 x !struct.type<@matElemSum_1::@matElemSum_1<[]>>>, !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: %[[VAL_420:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_419]][@out] : <@matElemSum_1::@matElemSum_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_421:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_356]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_422:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_362]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_423:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_343]]{{\[}}%[[VAL_421]], %[[VAL_422]]] : <2,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_423]], %[[VAL_420]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_424:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_425:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_362]], %[[VAL_424]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_425]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_426:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_427:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_356]], %[[VAL_426]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_427]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_428:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_429:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_430:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_431:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_432:[0-9a-zA-Z_\.]+]] = %[[VAL_430]] to %[[VAL_428]] step %[[VAL_431]] { +// CHECK-NEXT: scf.for %[[VAL_433:[0-9a-zA-Z_\.]+]] = %[[VAL_430]] to %[[VAL_429]] step %[[VAL_431]] { +// CHECK-NEXT: %[[VAL_434:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_344]]{{\[}}%[[VAL_432]], %[[VAL_433]]] : <2,2 x !struct.type<@matElemMul_0::@matElemMul_0<[]>>>, !struct.type<@matElemMul_0::@matElemMul_0<[]>> +// CHECK-NEXT: %[[VAL_435:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_345]]{{\[}}%[[VAL_432]], %[[VAL_433]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_436:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_435]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_437:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_435]][@b] : <[@a: !array.type<1,3 x !felt.type<"bn128">>, @b: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @matElemMul_0::@matElemMul_0::@constrain(%[[VAL_434]], %[[VAL_436]], %[[VAL_437]]) : (!struct.type<@matElemMul_0::@matElemMul_0<[]>>, !array.type<1,3 x !felt.type<"bn128">>, !array.type<1,3 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_438:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_439:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_440:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_441:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_442:[0-9a-zA-Z_\.]+]] = %[[VAL_440]] to %[[VAL_438]] step %[[VAL_441]] { +// CHECK-NEXT: scf.for %[[VAL_443:[0-9a-zA-Z_\.]+]] = %[[VAL_440]] to %[[VAL_439]] step %[[VAL_441]] { +// CHECK-NEXT: %[[VAL_444:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_346]]{{\[}}%[[VAL_442]], %[[VAL_443]]] : <2,2 x !struct.type<@matElemSum_1::@matElemSum_1<[]>>>, !struct.type<@matElemSum_1::@matElemSum_1<[]>> +// CHECK-NEXT: %[[VAL_445:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_347]]{{\[}}%[[VAL_442]], %[[VAL_443]]] : <2,2 x !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]>>, !pod.type<[@a: !array.type<1,3 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_446:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_445]][@a] : <[@a: !array.type<1,3 x !felt.type<"bn128">>]>, !array.type<1,3 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @matElemSum_1::@matElemSum_1::@constrain(%[[VAL_444]], %[[VAL_446]]) : (!struct.type<@matElemSum_1::@matElemSum_1<[]>>, !array.type<1,3 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/bool_4.circom b/circom/tests/from_concrete/bool_4.circom new file mode 100644 index 000000000..4d1381d6f --- /dev/null +++ b/circom/tests/from_concrete/bool_4.circom @@ -0,0 +1,108 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function binop_bool_array(a, b) { + var arr[10]; + for (var i = 0; i < 10; i++) { + arr[i] = a[i] || b[i]; + } + return arr; +} + +template A() { + signal input in1[10]; + signal input in2[10]; + signal output out[10]; + + out <-- binop_bool_array(in1, in2); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A_0::@A_0<[]>>} { +// CHECK-NEXT: function.def @binop_bool_array_0(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) -> !array.type<10 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_5]]] = %[[VAL_3]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_8]]] = %[[VAL_6]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_11]]] = %[[VAL_9]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_17]]] = %[[VAL_15]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_20]]] = %[[VAL_18]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_23]]] = %[[VAL_21]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_26]]] = %[[VAL_24]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_29]]] = %[[VAL_27]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_32]]] = %[[VAL_30]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_33]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_36]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_39]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_41]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_40]], %[[VAL_43]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_42]], %[[VAL_45]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_44]], %[[VAL_46]] : i1, i1 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_47]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_48]]] = %[[VAL_49]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_2]] : !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A_0 { +// CHECK-NEXT: struct.def @A_0 { +// CHECK-NEXT: struct.member @out : !array.type<10 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_52:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>, %[[VAL_53:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) -> !struct.type<@A_0::@A_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = struct.new : <@A_0::@A_0<[]>> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = function.call @binop_bool_array_0(%[[VAL_52]], %[[VAL_53]]) : (!array.type<10 x !felt.type<"bn128">>, !array.type<10 x !felt.type<"bn128">>) -> !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_54]][@out] = %[[VAL_55]] : <@A_0::@A_0<[]>>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_54]] : !struct.type<@A_0::@A_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@A_0::@A_0<[]>>, %[[VAL_57:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>, %[[VAL_58:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_56]][@out] : <@A_0::@A_0<[]>>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/call_and_return.circom b/circom/tests/from_concrete/call_and_return.circom new file mode 100644 index 000000000..66eb2fc70 --- /dev/null +++ b/circom/tests/from_concrete/call_and_return.circom @@ -0,0 +1,54 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template C() { + signal input in; + signal output out; + var x = 12; + out <-- negative(in); +} + +function negative(n){ + if (n < 0) { + return 1; + } else { + return 0; + } +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C_0::@C_0<[]>>} { +// CHECK-NEXT: function.def @negative_0(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C_0 { +// CHECK-NEXT: struct.def @C_0 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C_0::@C_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@C_0::@C_0<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @negative_0(%[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@out] = %[[VAL_9]] : <@C_0::@C_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@C_0::@C_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@C_0::@C_0<[]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_10]][@out] : <@C_0::@C_0<[]>>, !felt.type<"bn128"> +// CHECK-DAG: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/dimension_handling.circom b/circom/tests/from_concrete/dimension_handling.circom new file mode 100644 index 000000000..8f38c9ea3 --- /dev/null +++ b/circom/tests/from_concrete/dimension_handling.circom @@ -0,0 +1,134 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function myAdd(x1,y1,x2,y2) { + var res[2] = [0x1 + y1, x2 + y2]; + return res; +} + +function myFun() { + var out[1][1]; + var dbl[2] = [18446744073709551557,18446744073709551557]; + for (var i=0; i < 4; i++) { + dbl = myAdd(dbl[0], dbl[1], dbl[0], dbl[1]); + } + return out; +} + +template A() { + var table[1][1]; + table = myFun(); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A_0::@A_0<[]>>} { +// CHECK-NEXT: function.def @myAdd_1(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !array.type<2 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_7]]] = %[[VAL_5]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_10]]] = %[[VAL_8]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_2]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_17]]] = %[[VAL_15]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_4]] : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @myFun_0() -> !array.type<1,1 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_19]]{{\[}}%[[VAL_22]]] = %[[VAL_20]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_18]]{{\[}}%[[VAL_24]]] = %[[VAL_19]] : <1,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_25]]{{\[}}%[[VAL_28]]] = %[[VAL_26]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_25]]{{\[}}%[[VAL_31]]] = %[[VAL_29]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 18446744073709551557 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_25]]{{\[}}%[[VAL_34]]] = %[[VAL_32]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 18446744073709551557 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_25]]{{\[}}%[[VAL_37]]] = %[[VAL_35]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_40:[0-9a-zA-Z_\.]+]] = %[[VAL_25]], %[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_38]]) : (!array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_41]], %[[VAL_42]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_43]]) %[[VAL_40]], %[[VAL_41]] : !array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>, %[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_46]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_44]]{{\[}}%[[VAL_47]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_44]]{{\[}}%[[VAL_50]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_52]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_44]]{{\[}}%[[VAL_53]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_44]]{{\[}}%[[VAL_56]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = function.call @myAdd_1(%[[VAL_48]], %[[VAL_51]], %[[VAL_54]], %[[VAL_57]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_59]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_58]], %[[VAL_60]] : !array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_18]] : !array.type<1,1 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A_0 { +// CHECK-NEXT: struct.def @A_0 { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A_0::@A_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = struct.new : <@A_0::@A_0<[]>> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_63]]{{\[}}%[[VAL_66]]] = %[[VAL_64]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_62]]{{\[}}%[[VAL_68]]] = %[[VAL_63]] : <1,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = function.call @myFun_0() : () -> !array.type<1,1 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_61]] : !struct.type<@A_0::@A_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_70:[0-9a-zA-Z_\.]+]]: !struct.type<@A_0::@A_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1,1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_74]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_72]]{{\[}}%[[VAL_75]]] = %[[VAL_73]] : <1 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_76]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_71]]{{\[}}%[[VAL_77]]] = %[[VAL_72]] : <1,1 x !felt.type<"bn128">>, <1 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = function.call @myFun_0() : () -> !array.type<1,1 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/doc_example_27.circom b/circom/tests/from_concrete/doc_example_27.circom new file mode 100644 index 000000000..e507c0508 --- /dev/null +++ b/circom/tests/from_concrete/doc_example_27.circom @@ -0,0 +1,130 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.1.0; + +template Bits2Num(n) { + signal input in[n]; + signal output out; + var lc1=0; + + var e2 = 1; + for (var i = 0; i out; +} + +template A(){ + signal input a[10]; + signal output out; + component b = Bits2Num(10); + b.in <== a; + out <== b.out; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A_1::@A_1<[]>>} { +// CHECK-NEXT: poly.template @A_1 { +// CHECK-NEXT: struct.def @A_1 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @b : !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: struct.member @b$inputs : !pod.type<[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) -> !struct.type<@A_1::@A_1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A_1::@A_1<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !pod.type<[@count: index, @comp: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: pod.write %[[VAL_3]][@in] = %[[VAL_0]] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@count] : <[@count: index, @comp: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_4]], %[[VAL_5]] : index +// CHECK-NEXT: pod.write %[[VAL_2]][@count] = %[[VAL_6]] : <[@count: index, @comp: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_6]], %[[VAL_7]] : index +// CHECK-NEXT: scf.if %[[VAL_8]] { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@params] : <[@count: index, @comp: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@in] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = function.call @Bits2Num_0::@Bits2Num_0::@compute(%[[VAL_10]]) : (!array.type<10 x !felt.type<"bn128">>) -> !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: pod.write %[[VAL_2]][@comp] = %[[VAL_11]] : <[@count: index, @comp: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@comp] : <[@count: index, @comp: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_12]][@out] : <@Bits2Num_0::@Bits2Num_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_13]] : <@A_1::@A_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b$inputs] = %[[VAL_3]] : <@A_1::@A_1<[]>>, !pod.type<[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@comp] : <[@count: index, @comp: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, @params: !pod.type<[]>]>, !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_14]] : <@A_1::@A_1<[]>>, !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A_1::@A_1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@A_1::@A_1<[]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@out] : <@A_1::@A_1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@b] : <@A_1::@A_1<[]>>, !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@b$inputs] : <@A_1::@A_1<[]>>, !pod.type<[@in: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@in] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_16]] : !array.type<10 x !felt.type<"bn128">>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@out] : <@Bits2Num_0::@Bits2Num_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_17]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_19]][@in] : <[@in: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Bits2Num_0::@Bits2Num_0::@constrain(%[[VAL_18]], %[[VAL_22]]) : (!struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, !array.type<10 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Bits2Num_0 { +// CHECK-NEXT: struct.def @Bits2Num_0 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) -> !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.new : <@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_30:[0-9a-zA-Z_\.]+]] = %[[VAL_27]], %[[VAL_31:[0-9a-zA-Z_\.]+]] = %[[VAL_28]], %[[VAL_32:[0-9a-zA-Z_\.]+]] = %[[VAL_26]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_31]], %[[VAL_33]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_30]], %[[VAL_31]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_37:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_23]]{{\[}}%[[VAL_38]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_39]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_37]], %[[VAL_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_35]], %[[VAL_35]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_36]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_42]], %[[VAL_44]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_24]][@out] = %[[VAL_29]]#2 : <@Bits2Num_0::@Bits2Num_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_24]] : !struct.type<@Bits2Num_0::@Bits2Num_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !struct.type<@Bits2Num_0::@Bits2Num_0<[]>>, %[[VAL_46:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_45]][@out] : <@Bits2Num_0::@Bits2Num_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_53:[0-9a-zA-Z_\.]+]] = %[[VAL_50]], %[[VAL_54:[0-9a-zA-Z_\.]+]] = %[[VAL_51]], %[[VAL_55:[0-9a-zA-Z_\.]+]] = %[[VAL_49]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 10 : <"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_54]], %[[VAL_56]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_57]]) %[[VAL_53]], %[[VAL_54]], %[[VAL_55]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_58:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_59:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_60:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_46]]{{\[}}%[[VAL_61]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_62]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_60]], %[[VAL_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_58]], %[[VAL_58]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_59]], %[[VAL_66]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_65]], %[[VAL_67]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_52]]#2 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/fib_input.circom b/circom/tests/from_concrete/fib_input.circom new file mode 100644 index 000000000..eee54e748 --- /dev/null +++ b/circom/tests/from_concrete/fib_input.circom @@ -0,0 +1,104 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Fibonacci() { + signal input nth_fib; + signal output out; + + var a = 0; + var b = 1; + var next = 0; + + var counter = nth_fib; + while (counter > 2) { // unknown iteration count + next = a + b; + a = b; + b = next; + + counter--; + } + + out <-- (nth_fib == 0) ? 0 : (nth_fib == 1 ? 1 : a + b); +} + +component main = Fibonacci(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Fibonacci_0::@Fibonacci_0<[]>>} { +// CHECK-NEXT: poly.template @Fibonacci_0 { +// CHECK-NEXT: struct.def @Fibonacci_0 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Fibonacci_0::@Fibonacci_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Fibonacci_0::@Fibonacci_0<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:4 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_0]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_8]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_14]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_16]], %[[VAL_18]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_19]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_20]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]]#0, %[[VAL_5]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_25]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_27]] : <@Fibonacci_0::@Fibonacci_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_21]] : <@Fibonacci_0::@Fibonacci_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Fibonacci_0::@Fibonacci_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@Fibonacci_0::@Fibonacci_0<[]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_28]][@out] : <@Fibonacci_0::@Fibonacci_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]]:4 = scf.while (%[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_31]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_37:[0-9a-zA-Z_\.]+]] = %[[VAL_29]], %[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_33]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_37]], %[[VAL_39]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_40]]) %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_43]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_42]], %[[VAL_45]], %[[VAL_47]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_29]], %[[VAL_48]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_49]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_34]]#0, %[[VAL_34]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_52]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_29]], %[[VAL_53]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_54]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/inner_conditional_03.circom b/circom/tests/from_concrete/inner_conditional_03.circom new file mode 100644 index 000000000..e32da29b1 --- /dev/null +++ b/circom/tests/from_concrete/inner_conditional_03.circom @@ -0,0 +1,84 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional3(N) { + signal output out; + signal input in; + + var acc = 0; + for (var i = 1; i <= N; i++) { + if (in == 0) { // unknown condition + acc += i; + } else { + acc -= i; + } + } + + out <-- acc; +} + +component main = InnerConditional3(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional3_0::@InnerConditional3_0<[]>>} { +// CHECK-NEXT: poly.template @InnerConditional3_0 { +// CHECK-NEXT: struct.def @InnerConditional3_0 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@InnerConditional3_0::@InnerConditional3_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional3_0::@InnerConditional3_0<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_10]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_14]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_5]]#0 : <@InnerConditional3_0::@InnerConditional3_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerConditional3_0::@InnerConditional3_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional3_0::@InnerConditional3_0<[]>>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@out] : <@InnerConditional3_0::@InnerConditional3_0<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_26:[0-9a-zA-Z_\.]+]] = %[[VAL_23]], %[[VAL_27:[0-9a-zA-Z_\.]+]] = %[[VAL_24]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_27]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_29]]) %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_20]], %[[VAL_32]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_33]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_30]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_30]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_31]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_34]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/pass_and_constrain_arrays.circom b/circom/tests/from_concrete/pass_and_constrain_arrays.circom new file mode 100644 index 000000000..e0d77ba72 --- /dev/null +++ b/circom/tests/from_concrete/pass_and_constrain_arrays.circom @@ -0,0 +1,485 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function add(x1,y1,x2,y2) { + var a = 168700; + var d = 168696; + + var res[2]; + res[0] = (x1*y2 + y1*x2) / (1 + d*x1*x2*y1*y2); + res[1] = (y1*y2 - a*x1*x2) / (1 - d*x1*x2*y1*y2); + return res; +} + +function mul(base, k) { + var out[16][2]; + + var i; + var p[2]; + + var dbl[2] = base; + + for (i=0; i>} { +// CHECK-NEXT: function.def @add_1(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !array.type<2 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 168700 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 168696 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_9]]] = %[[VAL_7]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_12]]] = %[[VAL_10]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_1]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 168696 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_17]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_18]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_19]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_15]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_25]]] = %[[VAL_23]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_1]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 168700 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_27]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_28]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 168696 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_32]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_33]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_34]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_35]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_31]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_30]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_40]]] = %[[VAL_38]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_6]] : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @mul_0(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !array.type<16,2 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_46]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_44]]{{\[}}%[[VAL_47]]] = %[[VAL_45]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_44]]{{\[}}%[[VAL_50]]] = %[[VAL_48]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_52]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_54]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_56]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_57]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_58]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_60]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_62]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_63]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_64]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_66]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_68]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_69]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_70]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_71]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_72]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_73]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_74]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_75]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_76]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_77]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_78]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_79]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_80]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_81]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_82]]] = %[[VAL_44]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_86]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_84]]{{\[}}%[[VAL_87]]] = %[[VAL_85]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_89]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_84]]{{\[}}%[[VAL_90]]] = %[[VAL_88]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_93]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_91]]{{\[}}%[[VAL_94]]] = %[[VAL_92]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_96]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_91]]{{\[}}%[[VAL_97]]] = %[[VAL_95]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_100:[0-9a-zA-Z_\.]+]] = %[[VAL_41]], %[[VAL_101:[0-9a-zA-Z_\.]+]] = %[[VAL_98]]) : (!array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_102]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_101]], %[[VAL_103]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_104]]) %[[VAL_100]], %[[VAL_101]] : !array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_105:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>, %[[VAL_106:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_107]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_108]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_110]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_111]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_113]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_114]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_116]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_117]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = function.call @add_1(%[[VAL_109]], %[[VAL_112]], %[[VAL_115]], %[[VAL_118]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_106]], %[[VAL_120]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_119]], %[[VAL_121]] : !array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_123]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_125]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_124]], %[[VAL_126]]] = %[[VAL_122]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_128]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_130]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_129]], %[[VAL_131]]] = %[[VAL_127]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_134:[0-9a-zA-Z_\.]+]] = %[[VAL_132]], %[[VAL_135:[0-9a-zA-Z_\.]+]] = %[[VAL_84]]) : (!felt.type<"bn128">, !array.type<2 x !felt.type<"bn128">>) -> (!felt.type<"bn128">, !array.type<2 x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_134]], %[[VAL_136]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_137]]) %[[VAL_134]], %[[VAL_135]] : !felt.type<"bn128">, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_138:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_139:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>): +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_138]], %[[VAL_140]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_141]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_143]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_43]]{{\[}}%[[VAL_142]], %[[VAL_144]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_138]], %[[VAL_146]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_147]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_149]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_43]]{{\[}}%[[VAL_148]], %[[VAL_150]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_152]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_99]]#0{{\[}}%[[VAL_153]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_155:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_155]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_99]]#0{{\[}}%[[VAL_156]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = function.call @add_1(%[[VAL_145]], %[[VAL_151]], %[[VAL_154]], %[[VAL_157]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_159]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_158]]{{\[}}%[[VAL_160]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_163]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_162]], %[[VAL_164]]] = %[[VAL_161]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_165]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_158]]{{\[}}%[[VAL_166]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_168:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_169:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_169]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_168]], %[[VAL_170]]] = %[[VAL_167]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_172:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_138]], %[[VAL_171]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_172]], %[[VAL_158]] : !felt.type<"bn128">, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_43]] : !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Main_0 { +// CHECK-NEXT: struct.def @Main_0 { +// CHECK-NEXT: struct.member @out : !array.type<16,2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Main_0::@Main_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = struct.new : <@Main_0::@Main_0<[]>> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_176:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_177:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_177]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_178]]] = %[[VAL_176]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_179:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_180:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_181:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_180]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_181]]] = %[[VAL_179]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_182:[0-9a-zA-Z_\.]+]] = felt.const 5299619240641551281634865583518297030282874472190772894086521144482721001553 +// CHECK-NEXT: %[[VAL_183:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_184:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_183]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_184]]] = %[[VAL_182]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_185:[0-9a-zA-Z_\.]+]] = felt.const 16950150798460657717958625567821834550301663161624707787222815936182638968203 +// CHECK-NEXT: %[[VAL_186:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_187:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_186]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_187]]] = %[[VAL_185]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_188:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_189:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_190:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_191:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_192:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_191]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_189]]{{\[}}%[[VAL_192]]] = %[[VAL_190]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_193:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_194:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_195:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_194]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_189]]{{\[}}%[[VAL_195]]] = %[[VAL_193]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_196:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_197:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_197]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_198:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_199:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_198]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_199]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_200:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_201:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_200]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_201]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_202:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_203:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_202]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_203]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_204:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_205:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_204]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_205]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_206:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_207:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_206]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_207]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_208:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_209:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_208]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_209]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_210:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_211:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_210]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_211]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_212:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_213:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_212]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_213]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_214:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_215:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_214]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_215]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_216:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_217:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_216]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_217]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_218:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_219:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_218]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_219]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_220:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_221:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_220]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_221]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_222:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_223:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_222]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_223]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_224:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_225:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_224]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_225]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_226:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_227:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_226]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_227]]] = %[[VAL_189]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_228:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_229:[0-9a-zA-Z_\.]+]] = function.call @mul_0(%[[VAL_175]], %[[VAL_228]]) : (!array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128">) -> !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_230:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_231:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_232:[0-9a-zA-Z_\.]+]] = %[[VAL_230]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_233:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_234:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_232]], %[[VAL_233]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_234]]) %[[VAL_232]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_235:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_236:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_237:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_238:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_237]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_239:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_229]]{{\[}}%[[VAL_236]], %[[VAL_238]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_240:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_241:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_242:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_241]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_174]]{{\[}}%[[VAL_240]], %[[VAL_242]]] = %[[VAL_239]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_243:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_244:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_245:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_244]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_246:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_229]]{{\[}}%[[VAL_243]], %[[VAL_245]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_247:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_248:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_249:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_248]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_174]]{{\[}}%[[VAL_247]], %[[VAL_249]]] = %[[VAL_246]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_250:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_251:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_235]], %[[VAL_250]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_251]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_173]][@out] = %[[VAL_174]] : <@Main_0::@Main_0<[]>>, !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_173]] : !struct.type<@Main_0::@Main_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_252:[0-9a-zA-Z_\.]+]]: !struct.type<@Main_0::@Main_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_253:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_252]][@out] : <@Main_0::@Main_0<[]>>, !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_254:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_255:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_256:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_257:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_256]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_254]]{{\[}}%[[VAL_257]]] = %[[VAL_255]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_258:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_259:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_260:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_259]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_254]]{{\[}}%[[VAL_260]]] = %[[VAL_258]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_261:[0-9a-zA-Z_\.]+]] = felt.const 5299619240641551281634865583518297030282874472190772894086521144482721001553 +// CHECK-NEXT: %[[VAL_262:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_263:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_262]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_254]]{{\[}}%[[VAL_263]]] = %[[VAL_261]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_264:[0-9a-zA-Z_\.]+]] = felt.const 16950150798460657717958625567821834550301663161624707787222815936182638968203 +// CHECK-NEXT: %[[VAL_265:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_266:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_265]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_254]]{{\[}}%[[VAL_266]]] = %[[VAL_264]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_267:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_268:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_269:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_270:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_271:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_270]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_268]]{{\[}}%[[VAL_271]]] = %[[VAL_269]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_272:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_273:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_274:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_273]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_268]]{{\[}}%[[VAL_274]]] = %[[VAL_272]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_275:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_276:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_275]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_276]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_277:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_278:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_277]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_278]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_279:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_280:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_279]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_280]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_281:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_282:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_281]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_282]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_283:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_284:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_283]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_284]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_285:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_286:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_285]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_286]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_287:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_288:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_287]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_288]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_289:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_290:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_289]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_290]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_291:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_292:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_291]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_292]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_293:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_294:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_293]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_294]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_295:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_296:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_295]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_296]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_297:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_298:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_297]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_298]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_299:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_300:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_299]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_300]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_301:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_302:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_301]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_302]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_303:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_304:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_303]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_304]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_305:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_306:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_305]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_267]]{{\[}}%[[VAL_306]]] = %[[VAL_268]] : <16,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_307:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_308:[0-9a-zA-Z_\.]+]] = function.call @mul_0(%[[VAL_254]], %[[VAL_307]]) : (!array.type<2 x !felt.type<"bn128">>, !felt.type<"bn128">) -> !array.type<16,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_309:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_310:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_311:[0-9a-zA-Z_\.]+]] = %[[VAL_309]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_312:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_313:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_311]], %[[VAL_312]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_313]]) %[[VAL_311]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_314:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_315:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_314]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_316:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_317:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_316]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_318:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_308]]{{\[}}%[[VAL_315]], %[[VAL_317]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_319:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_314]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_320:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_321:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_320]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_322:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_253]]{{\[}}%[[VAL_319]], %[[VAL_321]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_322]], %[[VAL_318]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_323:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_314]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_324:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_325:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_324]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_326:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_308]]{{\[}}%[[VAL_323]], %[[VAL_325]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_327:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_314]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_328:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_329:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_328]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_330:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_253]]{{\[}}%[[VAL_327]], %[[VAL_329]]] : <16,2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_330]], %[[VAL_326]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_331:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_332:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_314]], %[[VAL_331]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_332]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/from_concrete/template_param_uses.circom b/circom/tests/from_concrete/template_param_uses.circom new file mode 100644 index 000000000..1ac930ec5 --- /dev/null +++ b/circom/tests/from_concrete/template_param_uses.circom @@ -0,0 +1,148 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(a) { + return a; +} + +template Template(m, n, c) { + var v[2][2] = m; + var x[c] = n; + var ret[2][2] = f(v); +} + +component main = Template([[0, 1], [2, 3]], [1, 0], 2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Template_0::@Template_0<[]>>} { +// CHECK-NEXT: function.def @f_0(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !felt.type<"bn128">>) -> !array.type<2,2 x !felt.type<"bn128">> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Template_0 { +// CHECK-NEXT: struct.def @Template_0 { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template_0::@Template_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Template_0::@Template_0<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_4]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_6]], %[[VAL_7]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_9]]{{\[}}%[[VAL_10]]] = %[[VAL_5]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_9]]{{\[}}%[[VAL_11]]] = %[[VAL_8]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_12]], %[[VAL_13]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_22]]] = %[[VAL_20]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_24]]] = %[[VAL_16]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_26]]] = %[[VAL_16]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_27]]{{\[}}%[[VAL_30]]] = %[[VAL_28]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_27]]{{\[}}%[[VAL_33]]] = %[[VAL_31]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_35]]{{\[}}%[[VAL_38]]] = %[[VAL_36]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_35]]{{\[}}%[[VAL_41]]] = %[[VAL_39]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_42]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_34]]{{\[}}%[[VAL_43]]] = %[[VAL_35]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_34]]{{\[}}%[[VAL_45]]] = %[[VAL_35]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = function.call @f_0(%[[VAL_9]]) : (!array.type<2,2 x !felt.type<"bn128">>) -> !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Template_0::@Template_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !struct.type<@Template_0::@Template_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_49]], %[[VAL_50]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_52]], %[[VAL_53]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_55]]{{\[}}%[[VAL_56]]] = %[[VAL_51]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_55]]{{\[}}%[[VAL_57]]] = %[[VAL_54]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_58]], %[[VAL_59]] : <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_64]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_62]]{{\[}}%[[VAL_65]]] = %[[VAL_63]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_62]]{{\[}}%[[VAL_68]]] = %[[VAL_66]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_69]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_61]]{{\[}}%[[VAL_70]]] = %[[VAL_62]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_71]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_61]]{{\[}}%[[VAL_72]]] = %[[VAL_62]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_75]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_73]]{{\[}}%[[VAL_76]]] = %[[VAL_74]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_78]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_73]]{{\[}}%[[VAL_79]]] = %[[VAL_77]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_81]]{{\[}}%[[VAL_84]]] = %[[VAL_82]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_86]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_81]]{{\[}}%[[VAL_87]]] = %[[VAL_85]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_88]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_80]]{{\[}}%[[VAL_89]]] = %[[VAL_81]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_90]] : !felt.type<"bn128"> +// CHECK-NEXT: array.insert %[[VAL_80]]{{\[}}%[[VAL_91]]] = %[[VAL_81]] : <2,2 x !felt.type<"bn128">>, <2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = function.call @f_0(%[[VAL_55]]) : (!array.type<2,2 x !felt.type<"bn128">>) -> !array.type<2,2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/lit.cfg.py b/circom/tests/lit.cfg.py new file mode 100644 index 000000000..33f843471 --- /dev/null +++ b/circom/tests/lit.cfg.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# Modified version of +# https://raw.githubusercontent.com/llvm/llvm-project/main/mlir/examples/standalone/test/lit.cfg.py +# from LLVM, which is licensed under Apache 2.0 with LLVM Exceptions. + +# -*- Python -*- + +import os +import platform +import re +import subprocess +import tempfile + +import lit.formats +import lit.util +from lit.llvm import llvm_config +from lit.llvm.subst import FindTool, ToolSubst + +# Configuration file for the 'lit' test runner. + +assert hasattr(config, "llvm_tools_dir") +assert hasattr(config, "circom_bin_dir") +assert hasattr(config, "extra_suffixes") +assert hasattr(config, "test_exec_root") +assert hasattr(config, "available_features") +# assert hasattr(config, 'test_source_root') + +# name: The name of this test suite. +config.name = "circom" + +config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = [".circom"] +config.suffixes.extend(config.extra_suffixes) + +# test_source_root: The root path where tests are located. + +# excludes: A list of directories to exclude from the testsuite. The 'Inputs' +# subdirectories contain auxiliary inputs for various tests in their parent +# directories. +config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt"] + + +# Set up environment variables + +llvm_config.with_system_environment(["HOME", "INCLUDE", "LIB", "TMP", "TEMP"]) + +llvm_config.use_default_substitutions() +llvm_config.with_environment("PATH", config.circom_bin_dir, append_path=True) +llvm_config.with_environment("PATH", config.llvm_tools_dir, append_path=True) +llvm_config.with_environment("FILECHECK_OPTS", "--dump-input-filter=all") + +tool_dirs = [config.circom_bin_dir, config.circom_src_dir] +tools = ["circom"] + +# Set up variable substitutions +llvm_config.add_tool_substitutions(tools, tool_dirs) +config.substitutions.append(("%PATH%", config.environment["PATH"])) diff --git a/circom/tests/lit.rs b/circom/tests/lit.rs new file mode 100644 index 000000000..62839a6cb --- /dev/null +++ b/circom/tests/lit.rs @@ -0,0 +1,118 @@ +#![allow(dead_code)] + +use std::path::Path; +use assert_cmd::Command; +use assert_fs::{NamedTempFile, prelude::FileWriteStr}; +use lazy_static::lazy_static; +use regex::Regex; + +const TEST_INPUT: &'static str = "%s"; +const TMP_FILE: &'static str = "%t"; +const CIRCOM: &'static str = "%circom"; + +type LitResult = Result>; + +fn marked_xfail(content: &str) -> bool { + lazy_static! { + static ref XFAIL: Regex = Regex::new(r"^//\s*XFAIL:.*$").unwrap(); + } + for line in content.lines() { + if XFAIL.is_match(line) { + return true; + } + } + return false; +} + +fn extract_runs(content: &str) -> Vec<&str> { + lazy_static! { + static ref RUN: Regex = Regex::new(r"^//\s*RUN:(.*)$").unwrap(); + static ref END: Regex = Regex::new(r"^//\s*END\.$").unwrap(); + } + let mut runs = Vec::new(); + for line in content.lines() { + if END.is_match(line) { + break; + } else if let Some(captures) = RUN.captures(line) { + if let Some(group) = captures.get(1) { + runs.push(group.as_str()); + } + } + } + if runs.is_empty() { + panic!("Unsupported test encountered. RUN declaration missing!") + } + runs +} + +fn write_test(content: &str, name: &str) -> LitResult { + let file = NamedTempFile::new(format!("{}.circom", name).as_str())?; + file.write_str(content)?; + Ok(file) +} + +struct LitTest<'a> { + expected_failure: bool, + run_commands: Vec<&'a str>, + test_input: NamedTempFile, + name: &'a str, +} + +impl<'a> LitTest<'a> { + pub fn create(content: &'a str, name: &'a str) -> LitResult { + Ok(LitTest { + expected_failure: marked_xfail(content), + run_commands: extract_runs(content), + test_input: write_test(content, name)?, + name, + }) + } + + fn execute_expecting_success(&self, cmd: &mut Command) -> LitResult<()> { + cmd.assert().success(); + Ok(()) + } + + fn execute_expecting_failure(&self, cmd: &mut Command) -> LitResult<()> { + cmd.assert().failure(); + Ok(()) + } + + fn prepare_command(&self, run_command: &str, tmp_file: &Path) -> String { + run_command + .replace( + TEST_INPUT, + format!("\"{}\"", self.test_input.path().to_str().unwrap()).as_str(), + ) + .replace(TMP_FILE, format!("\"{}\"", tmp_file.to_str().unwrap()).as_str()) + .replace(CIRCOM, env!("CARGO_BIN_EXE_circom")) + } + + pub fn execute(&self) -> LitResult<()> { + // Create a single temp file/directory to be shared across all RUN commands + // that is deleted when it goes out of scope at the end of this function. + let tmp_file = NamedTempFile::new(self.name)?; + + // Execute all RUN commands in sequence + for run_command in &self.run_commands { + let cmd = self.prepare_command(run_command, tmp_file.path()); + let mut sh = Command::new("sh"); + sh.arg("-c").arg(cmd); + if self.expected_failure { + self.execute_expecting_failure(&mut sh)?; + } else { + self.execute_expecting_success(&mut sh)?; + } + } + Ok(()) + } +} + +/// Emulates a lit test +#[inline] +fn lit_test(content: &str, name: &str) -> LitResult<()> { + LitTest::create(content, name)?.execute() +} + +// build.rs generates this file with the discovered circom tests in this crate +include!(concat!(env!("OUT_DIR"), "/discovered_tests.in")); diff --git a/circom/tests/lit.site.cfg.py b/circom/tests/lit.site.cfg.py new file mode 100644 index 000000000..9621a2a38 --- /dev/null +++ b/circom/tests/lit.site.cfg.py @@ -0,0 +1,44 @@ +import os +import shutil +import subprocess +import tempfile +from pathlib import Path + +# test_source_root: The root path where tests are located. +config.test_source_root = Path(__file__).parent + +config.llvm_tools_dir = subprocess.check_output( + ["llvm-config", "--bindir"], text=True +).rstrip() +config.available_features = {"circom"} + +circom_path = config.test_source_root.parent / "target" / "debug" / "circom" +circom_path = circom_path.resolve() if circom_path.exists() else None +assert circom_path is not None, "circom not found on PATH" + + +config.circom_bin_dir = str(Path(circom_path).parent) +config.circom_src_dir = str(config.test_source_root.parent) + +config.extra_suffixes = [".txt"] + +import lit.llvm + +lit.llvm.initialize(lit_config, config) + + +def _run_lit(): + # Let the main config do the real work. + lit_config.load_config(config, config.test_source_root / "lit.cfg.py") + + +# test_exec_root: The root path where tests should be run. +override_test_exec_root = os.environ.get("CIRCOM_TEST_RUN", None) +if override_test_exec_root is None: + with tempfile.TemporaryDirectory(prefix="circom_integration_test") as test_exec_root: + config.test_exec_root = test_exec_root + _run_lit() +else: + assert Path(override_test_exec_root).is_dir() + config.test_exec_root = override_test_exec_root + _run_lit() diff --git a/circom/tests/loops/assign_in_loop_01.circom b/circom/tests/loops/assign_in_loop_01.circom new file mode 100644 index 000000000..f26675c1c --- /dev/null +++ b/circom/tests/loops/assign_in_loop_01.circom @@ -0,0 +1,144 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Inner() { + signal input in; + signal output out; + + out <-- in & 1; +} + +template Num2Bits(n) { + signal input in; + signal output out[n]; + + component c[n]; + for (var i = 0; i < n; i++) { + c[i] = Inner(); + c[i].in <-- in; + out[i] <-- c[i].out; + } +} + +component main = Num2Bits(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Num2Bits::@Num2Bits<[3]>>} { +// CHECK-NEXT: poly.template @Inner { +// CHECK-NEXT: struct.def @Inner { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Inner::@Inner<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Inner::@Inner<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Inner::@Inner<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Inner::@Inner<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Inner::@Inner<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@Inner::@Inner<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @c : !array.type<@n x !struct.type<@Inner::@Inner<[]>>> +// CHECK-NEXT: struct.member @c$inputs : !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_12]], %[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_13]]) : (!array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>, !felt.type<"bn128">) -> (!array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_16]], %[[VAL_9]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_17]]) %[[VAL_15]], %[[VAL_16]] : !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_21]], @params = %[[VAL_20]] } : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_23]]] = %[[VAL_22]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_18]]{{\[}}%[[VAL_24]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_25]][@in] = %[[VAL_7]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_18]]{{\[}}%[[VAL_26]]] = %[[VAL_25]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_27]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@count] : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_29]], %[[VAL_30]] : index +// CHECK-NEXT: pod.write %[[VAL_28]][@count] = %[[VAL_31]] : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_31]], %[[VAL_32]] : index +// CHECK-NEXT: scf.if %[[VAL_33]] { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@params] : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = function.call @Inner::@Inner::@compute(%[[VAL_35]]) : (!felt.type<"bn128">) -> !struct.type<@Inner::@Inner<[]>> +// CHECK-NEXT: pod.write %[[VAL_28]][@comp] = %[[VAL_36]] : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>, !struct.type<@Inner::@Inner<[]>> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_37]]] = %[[VAL_28]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_38]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_39]][@comp] : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>, !struct.type<@Inner::@Inner<[]>> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_40]][@out] : <@Inner::@Inner<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_42]]] = %[[VAL_41]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_19]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_44]] : !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_8]][@c$inputs] = %[[VAL_14]]#0 : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.new : <@n x !struct.type<@Inner::@Inner<[]>>> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_49:[0-9a-zA-Z_\.]+]] = %[[VAL_47]] to %[[VAL_46]] step %[[VAL_48]] { +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_49]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_50]][@comp] : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]>, !struct.type<@Inner::@Inner<[]>> +// CHECK-NEXT: array.write %[[VAL_45]]{{\[}}%[[VAL_49]]] = %[[VAL_51]] : <@n x !struct.type<@Inner::@Inner<[]>>>, !struct.type<@Inner::@Inner<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_8]][@c] = %[[VAL_45]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !struct.type<@Inner::@Inner<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_8]][@out] = %[[VAL_10]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_52:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_53:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_52]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_52]][@c] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !struct.type<@Inner::@Inner<[]>>> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_52]][@c$inputs] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_60:[0-9a-zA-Z_\.]+]] = %[[VAL_58]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_60]], %[[VAL_54]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_61]]) %[[VAL_60]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_62:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Inner::@Inner<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_62]], %[[VAL_65]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_66]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_68]] to %[[VAL_67]] step %[[VAL_69]] { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_56]]{{\[}}%[[VAL_70]]] : <@n x !struct.type<@Inner::@Inner<[]>>>, !struct.type<@Inner::@Inner<[]>> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_57]]{{\[}}%[[VAL_70]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_72]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Inner::@Inner::@constrain(%[[VAL_71]], %[[VAL_73]]) : (!struct.type<@Inner::@Inner<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/assign_in_loop_02.circom b/circom/tests/loops/assign_in_loop_02.circom new file mode 100644 index 000000000..b11fc3add --- /dev/null +++ b/circom/tests/loops/assign_in_loop_02.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template Inner(i) { + signal input in; + signal output out; + + out <-- in & i; +} + +template Num2Bits(n) { + signal input in; + signal output out[n]; + + component c[n]; + for (var i = 0; i < n; i++) { + c[i] = Inner(i); + c[i].in <-- in; + out[i] <-- c[i].out; + } +} + +component main = Num2Bits(3); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/loops/assign_in_loop_03.circom b/circom/tests/loops/assign_in_loop_03.circom new file mode 100644 index 000000000..ab07b4f1f --- /dev/null +++ b/circom/tests/loops/assign_in_loop_03.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template Inner(i,j) { + signal input in; + signal output out; + + out <-- (in >> i) & j; +} + +template Num2Bits(n) { + signal input in; + signal output out[n]; + + component c[n]; + for (var i = 0; i < n; i++) { + c[i] = Inner(i, i+1); + c[i].in <-- in; + out[i] <-- c[i].out; + } +} + +component main = Num2Bits(3); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/loops/assign_in_loop_04.circom b/circom/tests/loops/assign_in_loop_04.circom new file mode 100644 index 000000000..584bbcb74 --- /dev/null +++ b/circom/tests/loops/assign_in_loop_04.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template Inner(i) { + signal input in; + signal output out; + + out <-- (in >> i) & 1; +} + +template Num2Bits(n) { + signal input in; + signal output out[n]; + + component c[n]; + for (var i = 0; i < n; i++) { + c[i] = Inner(i); + c[i].in <-- in; + out[i] <-- c[i].out; + } +} + +component main = Num2Bits(3); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/loops/call_inside_loop.circom b/circom/tests/loops/call_inside_loop.circom new file mode 100644 index 000000000..b203b6c2a --- /dev/null +++ b/circom/tests/loops/call_inside_loop.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function fun(a, n, b, c, d, e, f, g) { + var x[5]; + for (var i = 0; i < n; i++) { + x[i] = a[i] + b + c + d + e + f; + } + return x[0] + x[2] + x[4]; +} + +template CallInLoop(n, m) { + signal input in; + signal output out; + var a[n]; + for (var i = 0; i < n; i++) { + a[i] = m + in; + } + var b[n]; + for (var i = 0; i < n; i++) { + b[i] = fun(a, n, m, m, m, m, m, m); + } + out <-- b[0]; +} + +component main = CallInLoop(2, 3); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/loops/confusing_merge.circom b/circom/tests/loops/confusing_merge.circom new file mode 100644 index 000000000..d9bc2db33 --- /dev/null +++ b/circom/tests/loops/confusing_merge.circom @@ -0,0 +1,108 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Foo(N) { + signal input inp[N]; + signal output outp[N]; + + signal internal[N]; + + for (var i = 0; i < N; i++) { + internal[i] <== inp[i]; + } + + for (var i = 0; i < N; i++) { + internal[i] ==> outp[i]; + } +} + +component main = Foo(3); + +// COM: Lit variables cannot be set at the "llzk.nondet : !array.type<@N x !felt.type<"bn128">>" instances because their +// COM: ordering is non-deterministic. The lit variable is instead set at the first use of the MLIR value. +// +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Foo::@Foo<[3]>>} { +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: struct.member @outp : !array.type<@N x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @internal : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) -> !struct.type<@Foo::@Foo<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = llzk.nondet : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = llzk.nondet : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_5]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_10]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_4:[0-9a-zA-Z_\.]+]]{{\[}}%[[VAL_12]]] = %[[VAL_11]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_15]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_17]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_4]]{{\[}}%[[VAL_20]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3:[0-9a-zA-Z_\.]+]]{{\[}}%[[VAL_22]]] = %[[VAL_21]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_19]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@internal] = %[[VAL_4]] : <@Foo::@Foo<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: struct.writem %[[VAL_1]][@outp] = %[[VAL_3]] : <@Foo::@Foo<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Foo::@Foo<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[@N]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@outp] : <@Foo::@Foo<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@internal] : <@Foo::@Foo<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_32:[0-9a-zA-Z_\.]+]] = %[[VAL_30]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_32]], %[[VAL_27]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_33]]) %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_34:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_35]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_37]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_38]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_34]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_41]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_43]], %[[VAL_27]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_44]]) %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_46]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_48]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_49]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/fib_input.circom b/circom/tests/loops/fib_input.circom new file mode 100644 index 000000000..74c786584 --- /dev/null +++ b/circom/tests/loops/fib_input.circom @@ -0,0 +1,89 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Fibonacci() { + signal input nth_fib; + signal output out; + + var a = 0; + var b = 1; + var next = 0; + + var counter = nth_fib; + while (counter > 2) { // unknown iteration count + next = a + b; + a = b; + b = next; + + counter--; + } + + out <-- (nth_fib == 0) ? 0 : (nth_fib == 1 ? 1 : a + b); +} + +component main = Fibonacci(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Fibonacci::@Fibonacci<[]>>} { +// CHECK-NEXT: poly.template @Fibonacci { +// CHECK-NEXT: struct.def @Fibonacci { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Fibonacci::@Fibonacci<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Fibonacci::@Fibonacci<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:4 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_0]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_8]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_14]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_16]], %[[VAL_18]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_19]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_20]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_23]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_24]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]]#0, %[[VAL_5]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_21]] : <@Fibonacci::@Fibonacci<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Fibonacci::@Fibonacci<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@Fibonacci::@Fibonacci<[]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_28]][@out] : <@Fibonacci::@Fibonacci<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]]:4 = scf.while (%[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_31]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_32]], %[[VAL_37:[0-9a-zA-Z_\.]+]] = %[[VAL_29]], %[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_33]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_37]], %[[VAL_39]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_40]]) %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_43]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_42]], %[[VAL_45]], %[[VAL_47]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/fib_template.circom b/circom/tests/loops/fib_template.circom new file mode 100644 index 000000000..a5b32d473 --- /dev/null +++ b/circom/tests/loops/fib_template.circom @@ -0,0 +1,109 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template FibonacciTmpl(N) { + signal output out; + + var a = 0; + var b = 1; + var next = 0; + + var counter = N; + while (counter > 2) { // known iteration count + next = a + b; + a = b; + b = next; + + counter--; + } + + if (N == 0) { + out <-- 0; + } else if (N == 1) { + out <-- 1; + } else { + out <-- a + b; + } +} + +component main = FibonacciTmpl(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@FibonacciTmpl::@FibonacciTmpl<[5]>>} { +// CHECK-NEXT: poly.template @FibonacciTmpl { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @FibonacciTmpl { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@FibonacciTmpl::@FibonacciTmpl<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@FibonacciTmpl::@FibonacciTmpl<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B0:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_X0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]]:4 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_B1:[0-9a-zA-Z_\.]+]] = %[[V_B0]], %[[V_C1:[0-9a-zA-Z_\.]+]] = %[[V_N]], %[[V_X1:[0-9a-zA-Z_\.]+]] = %[[V_X0]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[V_C1]], %[[V_10]]) +// CHECK-NEXT: scf.condition(%[[V_11]]) %[[V_A1]], %[[V_B1]], %[[V_C1]], %[[V_X1]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_C2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_X2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_X3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_B2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_C3:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_C2]], %[[V_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_B2]], %[[V_X3]], %[[V_C3]], %[[V_X3]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_N]], %[[V_19]]) +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = scf.if %[[V_20]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writem %[[SELF]][@out] = %[[V_22]] : <@FibonacciTmpl::@FibonacciTmpl<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_N]], %[[V_23]]) +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = scf.if %[[V_24]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: struct.writem %[[SELF]][@out] = %[[V_26]] : <@FibonacciTmpl::@FibonacciTmpl<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_26]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.add %[[V_5]]#0, %[[V_5]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[SELF]][@out] = %[[V_27]] : <@FibonacciTmpl::@FibonacciTmpl<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_27]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_25]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@FibonacciTmpl::@FibonacciTmpl<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@FibonacciTmpl::@FibonacciTmpl<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readm %[[SELF]][@out] : <@FibonacciTmpl::@FibonacciTmpl<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B0:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_X0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]]:4 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_B1:[0-9a-zA-Z_\.]+]] = %[[V_B0]], %[[V_C1:[0-9a-zA-Z_\.]+]] = %[[V_N]], %[[V_X1:[0-9a-zA-Z_\.]+]] = %[[V_X0]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_39:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[V_C1]], %[[V_38]]) +// CHECK-NEXT: scf.condition(%[[V_39]]) %[[V_A1]], %[[V_B1]], %[[V_C1]], %[[V_X1]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_C2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_X2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_X3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_B2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_C3:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_C2]], %[[V_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_B2]], %[[V_X3]], %[[V_C3]], %[[V_X3]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_48:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_N]], %[[V_47]]) +// CHECK-NEXT: scf.if %[[V_48]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_N]], %[[V_51]]) +// CHECK-NEXT: scf.if %[[V_52]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/fixed_idx_nested_lhs.circom b/circom/tests/loops/fixed_idx_nested_lhs.circom new file mode 100644 index 000000000..f571c349c --- /dev/null +++ b/circom/tests/loops/fixed_idx_nested_lhs.circom @@ -0,0 +1,85 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template FixIdxNested() { + var arr[9] = [8, 7, 6, 5, 4, 3, 2, 1, 0]; + signal out[9]; + for (var i = 0; i < 9; i++) { + out[arr[i]] <-- arr[i]; + } +} + +component main = FixIdxNested(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@FixIdxNested::@FixIdxNested<[]>>} { +// CHECK-NEXT: poly.template @FixIdxNested { +// CHECK-NEXT: struct.def @FixIdxNested { +// CHECK-NEXT: struct.member @out : !array.type<9 x !felt.type<"bn128">> +// CHECK-NEXT: function.def @compute() -> !struct.type<@FixIdxNested::@FixIdxNested<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@FixIdxNested::@FixIdxNested<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_14]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_16]], %[[VAL_17]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_13]]{{\[}}%[[VAL_20]]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_13]]{{\[}}%[[VAL_22]]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_24]]] = %[[VAL_21]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_19]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_1]] : <@FixIdxNested::@FixIdxNested<[]>>, !array.type<9 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@FixIdxNested::@FixIdxNested<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !struct.type<@FixIdxNested::@FixIdxNested<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_27]][@out] : <@FixIdxNested::@FixIdxNested<[]>>, !array.type<9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]], %[[VAL_29]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_31]], %[[VAL_32]], %[[VAL_33]], %[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]], %[[VAL_39]] : <9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_41]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_43]], %[[VAL_44]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_45]]) %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_46]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_48]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/fixed_idx_nested_rhs.circom b/circom/tests/loops/fixed_idx_nested_rhs.circom new file mode 100644 index 000000000..c1b5a0003 --- /dev/null +++ b/circom/tests/loops/fixed_idx_nested_rhs.circom @@ -0,0 +1,108 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +pragma circom 2.0.0; + +template FixIdxNested() { + signal input in[16]; + signal output out[16]; + + var arr[16] = [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11]; + + for (var i = 0; i < 16; i++) { + out[i] <== in[arr[i]]; + } +} + +component main = FixIdxNested(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@FixIdxNested::@FixIdxNested<[]>>} { +// CHECK-NEXT: poly.template @FixIdxNested { +// CHECK-NEXT: struct.def @FixIdxNested { +// CHECK-NEXT: struct.member @out : !array.type<16 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<16 x !felt.type<"bn128">>) -> !struct.type<@FixIdxNested::@FixIdxNested<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@FixIdxNested::@FixIdxNested<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<16 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]], %[[VAL_3]] : <16 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]], %[[VAL_13]], %[[VAL_14]], %[[VAL_15]], %[[VAL_16]], %[[VAL_17]], %[[VAL_18]], %[[VAL_19]], %[[VAL_20]] : <16 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_24:[0-9a-zA-Z_\.]+]] = %[[VAL_22]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_24]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_26]]) %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_21]]{{\[}}%[[VAL_28]]] : <16 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_30]]] : <16 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_32]]] = %[[VAL_31]] : <16 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_2]] : <@FixIdxNested::@FixIdxNested<[]>>, !array.type<16 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@FixIdxNested::@FixIdxNested<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !struct.type<@FixIdxNested::@FixIdxNested<[]>>, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !array.type<16 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_35]][@out] : <@FixIdxNested::@FixIdxNested<[]>>, !array.type<16 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]], %[[VAL_38]] : <16 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_40]], %[[VAL_41]], %[[VAL_42]], %[[VAL_43]], %[[VAL_44]], %[[VAL_45]], %[[VAL_46]], %[[VAL_47]], %[[VAL_48]], %[[VAL_49]], %[[VAL_50]], %[[VAL_51]], %[[VAL_52]], %[[VAL_53]], %[[VAL_54]], %[[VAL_55]] : <16 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_59:[0-9a-zA-Z_\.]+]] = %[[VAL_57]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_59]], %[[VAL_60]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_61]]) %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_62:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_56]]{{\[}}%[[VAL_63]]] : <16 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_64]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_36]]{{\[}}%[[VAL_65]]] : <16 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_37]]{{\[}}%[[VAL_67]]] : <16 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_68]], %[[VAL_66]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_62]], %[[VAL_69]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/for_known.circom b/circom/tests/loops/for_known.circom new file mode 100644 index 000000000..c196cb747 --- /dev/null +++ b/circom/tests/loops/for_known.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ForKnown(N) { + signal output out; + + var acc = 0; + for (var i = 1; i <= N; i++) { + acc += i; + } + + out <-- acc; +} + +component main = ForKnown(10); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ForKnown::@ForKnown<[10]>>} { +// CHECK-NEXT: poly.template @ForKnown { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @ForKnown { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ForKnown::@ForKnown<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ForKnown::@ForKnown<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_4]]#0 : <@ForKnown::@ForKnown<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ForKnown::@ForKnown<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@ForKnown::@ForKnown<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@ForKnown::@ForKnown<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_16]], %[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_17]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_20]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_19]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_24]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/for_unknown.circom b/circom/tests/loops/for_unknown.circom new file mode 100644 index 000000000..0dc2dfe5e --- /dev/null +++ b/circom/tests/loops/for_unknown.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ForUnknown() { + signal input in; + signal output out; + + var acc = 0; + for (var i = 1; i <= in; i++) { + acc += i; + } + + out <-- acc; +} + +component main = ForUnknown(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ForUnknown::@ForUnknown<[]>>} { +// CHECK-NEXT: poly.template @ForUnknown { +// CHECK-NEXT: struct.def @ForUnknown { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ForUnknown::@ForUnknown<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ForUnknown::@ForUnknown<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_0]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_4]]#0 : <@ForUnknown::@ForUnknown<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ForUnknown::@ForUnknown<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@ForUnknown::@ForUnknown<[]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@ForUnknown::@ForUnknown<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_16]], %[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_17]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_20]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_19]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_24]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/for_unknown_index.circom b/circom/tests/loops/for_unknown_index.circom new file mode 100644 index 000000000..7c6dc8c61 --- /dev/null +++ b/circom/tests/loops/for_unknown_index.circom @@ -0,0 +1,65 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ForUnknownIndex() { + signal input in; + signal input arr[10]; + signal output out; + + var acc = 0; + for (var i = 1; i <= in; i++) { + acc += i; + } + + // non-quadractic constraint + // out <== arr[acc]; + out <-- arr[acc]; +} + +component main = ForUnknownIndex(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>>} { +// CHECK-NEXT: poly.template @ForUnknownIndex { +// CHECK-NEXT: struct.def @ForUnknownIndex { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) -> !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@ForUnknownIndex::@ForUnknownIndex<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_7]], %[[VAL_0]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_5]]#0 : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_14]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_15]] : <@ForUnknownIndex::@ForUnknownIndex<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@out] : <@ForUnknownIndex::@ForUnknownIndex<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_20]], %[[VAL_24:[0-9a-zA-Z_\.]+]] = %[[VAL_21]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_24]], %[[VAL_17]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_23]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_28]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/init_nonzero.circom b/circom/tests/loops/init_nonzero.circom new file mode 100644 index 000000000..639090077 --- /dev/null +++ b/circom/tests/loops/init_nonzero.circom @@ -0,0 +1,119 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Ensure that non-zero initialization of for-loop iteration variable is handled properly. +template NonZeroInit() { + signal input a[9]; + signal output b[9]; + + for (var i = 4; i < 7; i++) { + b[i] <-- a[i]; + } + for (var i = 7; i < 9; i++) { + b[i] <-- a[i]; + } + for (var i = 0; i < 4; i++) { + b[i] <-- a[i]; + } +} + +component main = NonZeroInit(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@NonZeroInit::@NonZeroInit<[]>>} { +// CHECK-NEXT: poly.template @NonZeroInit { +// CHECK-NEXT: struct.def @NonZeroInit { +// CHECK-NEXT: struct.member @b : !array.type<9 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<9 x !felt.type<"bn128">>) -> !struct.type<@NonZeroInit::@NonZeroInit<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@NonZeroInit::@NonZeroInit<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_5]], %[[VAL_6]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_9]]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_14]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_16]], %[[VAL_17]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_20]]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_22]]] = %[[VAL_21]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_19]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_27:[0-9a-zA-Z_\.]+]] = %[[VAL_25]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_27]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_29]]) %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_31]]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_33]]] = %[[VAL_32]] : <9 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_30]], %[[VAL_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_2]] : <@NonZeroInit::@NonZeroInit<[]>>, !array.type<9 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@NonZeroInit::@NonZeroInit<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_36:[0-9a-zA-Z_\.]+]]: !struct.type<@NonZeroInit::@NonZeroInit<[]>>, %[[VAL_37:[0-9a-zA-Z_\.]+]]: !array.type<9 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_36]][@b] : <@NonZeroInit::@NonZeroInit<[]>>, !array.type<9 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_39]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_41]], %[[VAL_42]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_43]]) %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_46]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_49:[0-9a-zA-Z_\.]+]] = %[[VAL_47]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_49]], %[[VAL_50]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_51]]) %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_52:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_52]], %[[VAL_53]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_54]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_57:[0-9a-zA-Z_\.]+]] = %[[VAL_55]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_57]], %[[VAL_58]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_59]]) %[[VAL_57]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_60:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_60]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_01.circom b/circom/tests/loops/inner_conditional_01.circom new file mode 100644 index 000000000..1aba9d299 --- /dev/null +++ b/circom/tests/loops/inner_conditional_01.circom @@ -0,0 +1,94 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional1(N) { + signal output out; + + var acc = 0; + for (var i = 1; i <= N; i++) { + if (i < 5) { + acc += i; + } else { + acc -= i; + } + } + //Values at loop header per iteration + // N, acc, i + // 10, 0, 1 + // 10, 1, 2 + // 10, 3, 3 + // 10, 6, 4 + // 10, 10, 5 + // 10, 5, 6 + // 10, -1, 7 + // 10, -8, 8 + // 10, -16, 9 + // 10, -25, 10 + + out <-- acc; +} + +component main = InnerConditional1(10); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional1::@InnerConditional1<[10]>>} { +// CHECK-NEXT: poly.template @InnerConditional1 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional1 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerConditional1::@InnerConditional1<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional1::@InnerConditional1<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_11]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_4]]#0 : <@InnerConditional1::@InnerConditional1<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@InnerConditional1::@InnerConditional1<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional1::@InnerConditional1<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@out] : <@InnerConditional1::@InnerConditional1<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_20]], %[[VAL_24:[0-9a-zA-Z_\.]+]] = %[[VAL_21]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_24]], %[[VAL_18]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_23]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_27]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_29]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_30]], %[[VAL_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_02.circom b/circom/tests/loops/inner_conditional_02.circom new file mode 100644 index 000000000..c6d2ebd4a --- /dev/null +++ b/circom/tests/loops/inner_conditional_02.circom @@ -0,0 +1,151 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional2(N, T) { + signal output out; + + var acc = 1; + for (var i = 1; i <= N; i++) { + if (T == 0) { + acc += i; + } else { + acc *= i; + } + } + + out <-- acc; +} + +template runner() { + signal output out; + + component a = InnerConditional2(4, 0); + component b = InnerConditional2(5, 1); + + out <-- a.out + b.out; +} + +component main = runner(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@runner::@runner<[]>>} { +// CHECK-NEXT: poly.template @InnerConditional2 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: poly.param @T +// CHECK-NEXT: struct.def @InnerConditional2 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerConditional2::@InnerConditional2<[@N, @T]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional2::@InnerConditional2<[@N, @T]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @T : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_7]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_2]], %[[VAL_11]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_12]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_5]]#0 : <@InnerConditional2::@InnerConditional2<[@N, @T]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@InnerConditional2::@InnerConditional2<[@N, @T]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional2::@InnerConditional2<[@N, @T]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @T : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@out] : <@InnerConditional2::@InnerConditional2<[@N, @T]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_25:[0-9a-zA-Z_\.]+]] = %[[VAL_22]], %[[VAL_26:[0-9a-zA-Z_\.]+]] = %[[VAL_23]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_26]], %[[VAL_19]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_27]]) %[[VAL_25]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_20]], %[[VAL_30]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_31]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_28]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_28]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_29]], %[[VAL_35]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @runner { +// CHECK-NEXT: struct.def @runner { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[]> +// CHECK-NEXT: struct.member @b : !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>> +// CHECK-NEXT: struct.member @b$inputs : !pod.type<[]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@runner::@runner<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = struct.new : <@runner::@runner<[]>> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_40]], @T = %[[VAL_41]] } : <[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = function.call @InnerConditional2::@InnerConditional2::@compute() : () -> !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_44]] } : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_46]], @T = %[[VAL_47]] } : <[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = function.call @InnerConditional2::@InnerConditional2::@compute() : () -> !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_50]] } : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@comp] : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]>, !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_52]][@out] : <@InnerConditional2::@InnerConditional2<[4, 0]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@comp] : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]>, !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_54]][@out] : <@InnerConditional2::@InnerConditional2<[5, 1]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_53]], %[[VAL_55]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_37]][@out] = %[[VAL_56]] : <@runner::@runner<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_37]][@a$inputs] = %[[VAL_38]] : <@runner::@runner<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@comp] : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]>, !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>> +// CHECK-NEXT: struct.writem %[[VAL_37]][@a] = %[[VAL_57]] : <@runner::@runner<[]>>, !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>> +// CHECK-NEXT: struct.writem %[[VAL_37]][@b$inputs] = %[[VAL_39]] : <@runner::@runner<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@comp] : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]>, !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>> +// CHECK-NEXT: struct.writem %[[VAL_37]][@b] = %[[VAL_58]] : <@runner::@runner<[]>>, !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>> +// CHECK-NEXT: function.return %[[VAL_37]] : !struct.type<@runner::@runner<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_59:[0-9a-zA-Z_\.]+]]: !struct.type<@runner::@runner<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_59]][@out] : <@runner::@runner<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_59]][@a] : <@runner::@runner<[]>>, !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_59]][@a$inputs] : <@runner::@runner<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_59]][@b] : <@runner::@runner<[]>>, !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_59]][@b$inputs] : <@runner::@runner<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_65]], @T = %[[VAL_66]] } : <[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = pod.new { @N = %[[VAL_69]], @T = %[[VAL_70]] } : <[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>>, @params: !pod.type<[@N: !felt.type<"bn128">, @T: !felt.type<"bn128">]>]> +// CHECK-NEXT: function.call @InnerConditional2::@InnerConditional2::@constrain(%[[VAL_61]]) : (!struct.type<@InnerConditional2::@InnerConditional2<[4, 0]>>) -> () +// CHECK-NEXT: function.call @InnerConditional2::@InnerConditional2::@constrain(%[[VAL_63]]) : (!struct.type<@InnerConditional2::@InnerConditional2<[5, 1]>>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_03.circom b/circom/tests/loops/inner_conditional_03.circom new file mode 100644 index 000000000..fc711b393 --- /dev/null +++ b/circom/tests/loops/inner_conditional_03.circom @@ -0,0 +1,83 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional3(N) { + signal output out; + signal input in; + + var acc = 0; + for (var i = 1; i <= N; i++) { + if (in == 0) { // unknown condition + acc += i; + } else { + acc -= i; + } + } + + out <-- acc; +} + +component main = InnerConditional3(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional3::@InnerConditional3<[3]>>} { +// CHECK-NEXT: poly.template @InnerConditional3 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional3 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@InnerConditional3::@InnerConditional3<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional3::@InnerConditional3<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_8]]) %[[V_A1]], %[[V_I1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_IN]], %[[V_11]]) +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = scf.if %[[V_12]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_13]], %[[V_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[SELF]][@out] = %[[V_5]]#0 : <@InnerConditional3::@InnerConditional3<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerConditional3::@InnerConditional3<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional3::@InnerConditional3<[@N]>>, %[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[SELF]][@out] : <@InnerConditional3::@InnerConditional3<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_26]]) %[[V_A1]], %[[V_I1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_IN]], %[[V_29]]) +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = scf.if %[[V_30]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_33]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_35:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_31]], %[[V_35]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_04.circom b/circom/tests/loops/inner_conditional_04.circom new file mode 100644 index 000000000..be5aca480 --- /dev/null +++ b/circom/tests/loops/inner_conditional_04.circom @@ -0,0 +1,76 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional4(N) { + signal output out[N]; + signal input in; + + for (var i = 0; i < N; i++) { + if (i < 3) { + out[i] <-- -in; + } else { + out[i] <-- in; + } + } +} + +component main = InnerConditional4(6); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional4::@InnerConditional4<[6]>>} { +// CHECK-NEXT: poly.template @InnerConditional4 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional4 { +// CHECK-NEXT: struct.member @out : !array.type<@N x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@InnerConditional4::@InnerConditional4<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional4::@InnerConditional4<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_8]], %[[VAL_9]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_10]] { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_12]]] = %[[VAL_11]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_13]]] = %[[VAL_0]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@InnerConditional4::@InnerConditional4<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerConditional4::@InnerConditional4<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional4::@InnerConditional4<[@N]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@out] : <@InnerConditional4::@InnerConditional4<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_20]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_22]], %[[VAL_18]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_23]]) %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_24]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_26]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_24]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_05.circom b/circom/tests/loops/inner_conditional_05.circom new file mode 100644 index 000000000..c006bd07a --- /dev/null +++ b/circom/tests/loops/inner_conditional_05.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template InnerConditional5(N, T) { + signal output out[N]; + + for (var i = 0; i < N; i++) { + if (T == 0) { + out[i] <-- 777; + } else { + out[i] <-- 999; + } + } +} + +template runner() { + signal output out; + + component a = InnerConditional5(4, 0); + component b = InnerConditional5(5, 1); + + out <-- a.out[1] + b.out[0]; +} + +component main = runner(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/loops/inner_conditional_06.circom b/circom/tests/loops/inner_conditional_06.circom new file mode 100644 index 000000000..7ebb55ef9 --- /dev/null +++ b/circom/tests/loops/inner_conditional_06.circom @@ -0,0 +1,89 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional6(N) { + signal output out[N]; + signal input in[N]; + + for (var i = 0; i < N; i++) { + var x; + if (in[i] == 0) { + x = 999; + } else { + x = 888; + } + out[i] <-- x; + } +} + +component main = InnerConditional6(4); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional6::@InnerConditional6<[4]>>} { +// CHECK-NEXT: poly.template @InnerConditional6 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional6 { +// CHECK-NEXT: struct.member @out : !array.type<@N x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) -> !struct.type<@InnerConditional6::@InnerConditional6<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional6::@InnerConditional6<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_10]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_11]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_17]]] = %[[VAL_14]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@InnerConditional6::@InnerConditional6<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerConditional6::@InnerConditional6<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional6::@InnerConditional6<[@N]>>, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@out] : <@InnerConditional6::@InnerConditional6<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_26:[0-9a-zA-Z_\.]+]] = %[[VAL_24]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_26]], %[[VAL_22]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_27]]) %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_21]]{{\[}}%[[VAL_30]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_31]], %[[VAL_32]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_33]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: scf.yield %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: scf.yield %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_28]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_07.circom b/circom/tests/loops/inner_conditional_07.circom new file mode 100644 index 000000000..2c6f81b20 --- /dev/null +++ b/circom/tests/loops/inner_conditional_07.circom @@ -0,0 +1,152 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional7(N) { + signal output out; + + var a[N]; + for (var i = 0; i < N; i++) { + // Values of 'a' at the header per iteration: + // i=0: [0, 0, 0, 0] + // i=1: [-111, -111, -111] + // i=2: [-222, -222, -222] + // NOTE: Technically there are no negative values, it's instead wrapped modulo the field prime + for (var j = 0; j < N; j++) { + if (i > 1) { + a[j] += 999; + } else { + a[j] -= 111; + } + } + } + // At this point, 'a[x] = 777' for all 'x', so 'out = 1554' + out <-- a[0] + a[1]; +} + +component main = InnerConditional7(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional7::@InnerConditional7<[3]>>} { +// CHECK-NEXT: poly.template @InnerConditional7 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional7 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerConditional7::@InnerConditional7<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional7::@InnerConditional7<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = array.len %[[V_A]], %[[V_4]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_8:[0-9a-zA-Z_\.]+]] = %[[V_6]] to %[[V_5]] step %[[V_7]] { +// CHECK-NEXT: array.write %[[V_A]]{{\[}}%[[V_8]]] = %[[V_2]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_13]]) %[[V_A1]], %[[V_I1]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A3:[0-9a-zA-Z_\.]+]] = %[[V_A2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_20]]) %[[V_A3]], %[[V_J1]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[V_I2]], %[[V_23]]) +// CHECK-NEXT: %[[V_A5:[0-9a-zA-Z_\.]+]] = scf.if %[[V_24]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = array.read %[[V_A4]]{{\[}}%[[V_26]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.add %[[V_27]], %[[V_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: array.write %[[V_A4]]{{\[}}%[[V_30]]] = %[[V_29]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = array.read %[[V_A4]]{{\[}}%[[V_31]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.const 111 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_32]], %[[V_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: array.write %[[V_A4]]{{\[}}%[[V_35]]] = %[[V_34]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_17]]#0, %[[V_I3]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_40]] +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_41]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_43]] +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_44]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.add %[[V_42]], %[[V_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[V_0]][@out] = %[[V_46]] : <@InnerConditional7::@InnerConditional7<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[V_0]] : !struct.type<@InnerConditional7::@InnerConditional7<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_47:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional7::@InnerConditional7<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_87:[0-9a-zA-Z_\.]+]] = struct.readm %[[V_47]][@out] : <@InnerConditional7::@InnerConditional7<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_49:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = array.len %[[V_A]], %[[V_51]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_55:[0-9a-zA-Z_\.]+]] = %[[V_53]] to %[[V_52]] step %[[V_54]] { +// CHECK-NEXT: array.write %[[V_A]]{{\[}}%[[V_55]]] = %[[V_49]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_57:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_60]]) %[[V_A1]], %[[V_I1]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A3:[0-9a-zA-Z_\.]+]] = %[[V_A2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_67]]) %[[V_A3]], %[[V_J1]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_70:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_71:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[V_I2]], %[[V_70]]) +// CHECK-NEXT: %[[V_A5:[0-9a-zA-Z_\.]+]] = scf.if %[[V_71]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[V_73:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: %[[V_74:[0-9a-zA-Z_\.]+]] = array.read %[[V_A4]]{{\[}}%[[V_73]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_75:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[V_76:[0-9a-zA-Z_\.]+]] = felt.add %[[V_74]], %[[V_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_77:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: array.write %[[V_A4]]{{\[}}%[[V_77]]] = %[[V_76]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_78:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: %[[V_79:[0-9a-zA-Z_\.]+]] = array.read %[[V_A4]]{{\[}}%[[V_78]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_80:[0-9a-zA-Z_\.]+]] = felt.const 111 +// CHECK-NEXT: %[[V_81:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_79]], %[[V_80]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_82:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: array.write %[[V_A4]]{{\[}}%[[V_82]]] = %[[V_81]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_83:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_83]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_85:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_64]]#0, %[[V_I3]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_08.circom b/circom/tests/loops/inner_conditional_08.circom new file mode 100644 index 000000000..153560bd3 --- /dev/null +++ b/circom/tests/loops/inner_conditional_08.circom @@ -0,0 +1,153 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// like inner_conditional_7 but with 'i' and 'j' uses swapped (and a larger constant) +template InnerConditional8(N) { + signal output out; + + var a[N]; + for (var i = 0; i < N; i++) { + // Values of 'a' at the header per iteration: + // i=0: [0, 0, 0, 0] + // i=1: [1776, 0, 0, 0] + // i=2: [1776, 1776, 0, 0] + // i=3: [1776, 1776, 1776, 0] + for (var j = 0; j < N; j++) { + if (j > 1) { + a[i] += 999; + } else { + a[i] -= 111; + } + } + } + // At this point, 'a[x] = 1776' for all 'x', so 'out = 3552' + out <-- a[0] + a[1]; +} + +component main = InnerConditional8(4); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional8::@InnerConditional8<[4]>>} { +// CHECK-NEXT: poly.template @InnerConditional8 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional8 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerConditional8::@InnerConditional8<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional8::@InnerConditional8<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = array.len %[[V_A]], %[[V_4]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_8:[0-9a-zA-Z_\.]+]] = %[[V_6]] to %[[V_5]] step %[[V_7]] { +// CHECK-NEXT: array.write %[[V_A]]{{\[}}%[[V_8]]] = %[[V_2]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_13]]) %[[V_A1]], %[[V_I1]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A3:[0-9a-zA-Z_\.]+]] = %[[V_A2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_20]]) %[[V_A3]], %[[V_J1]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[V_J2]], %[[V_23]]) +// CHECK-NEXT: %[[V_A5:[0-9a-zA-Z_\.]+]] = scf.if %[[V_24]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = array.read %[[V_A4]]{{\[}}%[[V_26]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.add %[[V_27]], %[[V_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: array.write %[[V_A4]]{{\[}}%[[V_30]]] = %[[V_29]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = array.read %[[V_A4]]{{\[}}%[[V_31]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.const 111 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_32]], %[[V_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: array.write %[[V_A4]]{{\[}}%[[V_35]]] = %[[V_34]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_17]]#0, %[[V_I3]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_40]] +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_41]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_43]] +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_44]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.add %[[V_42]], %[[V_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[V_0]][@out] = %[[V_46]] : <@InnerConditional8::@InnerConditional8<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[V_0]] : !struct.type<@InnerConditional8::@InnerConditional8<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_47:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional8::@InnerConditional8<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_87:[0-9a-zA-Z_\.]+]] = struct.readm %[[V_47]][@out] : <@InnerConditional8::@InnerConditional8<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_49:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = array.len %[[V_A]], %[[V_51]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_55:[0-9a-zA-Z_\.]+]] = %[[V_53]] to %[[V_52]] step %[[V_54]] { +// CHECK-NEXT: array.write %[[V_A]]{{\[}}%[[V_55]]] = %[[V_49]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_56:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_57:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_58:[0-9a-zA-Z_\.]+]] = %[[V_A]], %[[V_59:[0-9a-zA-Z_\.]+]] = %[[V_56]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_59]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_60]]) %[[V_58]], %[[V_59]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_61:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_62:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_65:[0-9a-zA-Z_\.]+]] = %[[V_61]], %[[V_66:[0-9a-zA-Z_\.]+]] = %[[V_63]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_66]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_67]]) %[[V_65]], %[[V_66]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_68:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[V_69:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_70:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_71:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[V_69]], %[[V_70]]) +// CHECK-NEXT: %[[V_72:[0-9a-zA-Z_\.]+]] = scf.if %[[V_71]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[V_73:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_62]] +// CHECK-NEXT: %[[V_74:[0-9a-zA-Z_\.]+]] = array.read %[[V_68]]{{\[}}%[[V_73]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_75:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[V_76:[0-9a-zA-Z_\.]+]] = felt.add %[[V_74]], %[[V_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_77:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_62]] +// CHECK-NEXT: array.write %[[V_68]]{{\[}}%[[V_77]]] = %[[V_76]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_68]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_78:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_62]] +// CHECK-NEXT: %[[V_79:[0-9a-zA-Z_\.]+]] = array.read %[[V_68]]{{\[}}%[[V_78]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_80:[0-9a-zA-Z_\.]+]] = felt.const 111 +// CHECK-NEXT: %[[V_81:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_79]], %[[V_80]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_82:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_62]] +// CHECK-NEXT: array.write %[[V_68]]{{\[}}%[[V_82]]] = %[[V_81]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_68]] : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_83:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_84:[0-9a-zA-Z_\.]+]] = felt.add %[[V_69]], %[[V_83]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_72]], %[[V_84]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_85:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_86:[0-9a-zA-Z_\.]+]] = felt.add %[[V_62]], %[[V_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_64]]#0, %[[V_86]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_09.circom b/circom/tests/loops/inner_conditional_09.circom new file mode 100644 index 000000000..15d427e94 --- /dev/null +++ b/circom/tests/loops/inner_conditional_09.circom @@ -0,0 +1,176 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerConditional9(N) { + signal output out; + + var a[N]; + for (var i = 0; i < N; i++) { + // Values of 'a' at the header per iteration: + // i=0: [0, 0, 0, 0] + // i=1: [3996, 0, 0, 0] + // i=2: [3996, 3996, 0, 0] + // i=3: [3996, 3996, 3552, 0] + if (i < 2) { + // runs when i∈{0,1} + for (var j = 0; j < N; j++) { + a[i] += 999; + } + } else { + // runs when i∈{2,3} + for (var j = 0; j < N; j++) { + a[i] += 888; + } + } + } + // At this point, 'a = [3996, 3996, 3552, 3552]', so 'out = 7992' + out <-- a[0] + a[1]; +} + +component main = InnerConditional9(4); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional9::@InnerConditional9<[4]>>} { +// CHECK-NEXT: poly.template @InnerConditional9 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional9 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerConditional9::@InnerConditional9<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional9::@InnerConditional9<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_3]], %[[VAL_4]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_6]] to %[[VAL_5]] step %[[VAL_7]] { +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_8]]] = %[[VAL_2]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_9]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_12]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_13]]) %[[VAL_11]], %[[VAL_12]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_15]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_17]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_14]], %[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_22]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_23]]) %[[VAL_21]], %[[VAL_22]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_26]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_24]]{{\[}}%[[VAL_30]]] = %[[VAL_29]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_24]], %[[VAL_32]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_20]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_14]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_33]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_36]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_35]], %[[VAL_36]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_38]]{{\[}}%[[VAL_40]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_38]]{{\[}}%[[VAL_44]]] = %[[VAL_43]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_38]], %[[VAL_46]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_34]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_15]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_48]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]#0{{\[}}%[[VAL_50]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_52]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]#0{{\[}}%[[VAL_53]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_51]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_55]] : <@InnerConditional9::@InnerConditional9<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@InnerConditional9::@InnerConditional9<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional9::@InnerConditional9<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_56]][@out] : <@InnerConditional9::@InnerConditional9<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_60]], %[[VAL_61]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_65:[0-9a-zA-Z_\.]+]] = %[[VAL_63]] to %[[VAL_62]] step %[[VAL_64]] { +// CHECK-NEXT: array.write %[[VAL_60]]{{\[}}%[[VAL_65]]] = %[[VAL_59]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_68:[0-9a-zA-Z_\.]+]] = %[[VAL_60]], %[[VAL_69:[0-9a-zA-Z_\.]+]] = %[[VAL_66]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_69]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_70]]) %[[VAL_68]], %[[VAL_69]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_71:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_72:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_72]], %[[VAL_73]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_74]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_78:[0-9a-zA-Z_\.]+]] = %[[VAL_71]], %[[VAL_79:[0-9a-zA-Z_\.]+]] = %[[VAL_76]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_79]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_80]]) %[[VAL_78]], %[[VAL_79]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_81:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_82:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_81]]{{\[}}%[[VAL_83]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_84]], %[[VAL_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_81]]{{\[}}%[[VAL_87]]] = %[[VAL_86]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_82]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_81]], %[[VAL_89]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_77]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_92:[0-9a-zA-Z_\.]+]] = %[[VAL_71]], %[[VAL_93:[0-9a-zA-Z_\.]+]] = %[[VAL_90]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_93]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_94]]) %[[VAL_92]], %[[VAL_93]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_95:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_96:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_95]]{{\[}}%[[VAL_97]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_98]], %[[VAL_99]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_95]]{{\[}}%[[VAL_101]]] = %[[VAL_100]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_96]], %[[VAL_102]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_95]], %[[VAL_103]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_91]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_72]], %[[VAL_104]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_75]], %[[VAL_105]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_10.circom b/circom/tests/loops/inner_conditional_10.circom new file mode 100644 index 000000000..eea6f0306 --- /dev/null +++ b/circom/tests/loops/inner_conditional_10.circom @@ -0,0 +1,169 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sigma() { + signal input inp; + signal output out; +} + +template Poseidon() { + signal input inp; + + component sigmaF[2]; + for (var i=0; i<4; i++) { + if (i < 1 || i >= 3) { + var k = i < 1 ? 0 : 1; + sigmaF[k] = Sigma(); + sigmaF[k].inp <== inp; + } + } +} + +component main = Poseidon(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Poseidon::@Poseidon<[]>>} { +// CHECK-NEXT: poly.template @Poseidon { +// CHECK-NEXT: struct.def @Poseidon { +// CHECK-NEXT: struct.member @sigmaF : !array.type<2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: struct.member @sigmaF$inputs : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Poseidon::@Poseidon<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Poseidon::@Poseidon<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>) -> (!felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_10]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_13]], %[[VAL_15]] : i1, i1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_16]] -> (!array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_18]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_19]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_24]], @params = %[[VAL_23]] } : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_26]]] = %[[VAL_25]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_27]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_28]][@inp] = %[[VAL_0]] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_29]]] = %[[VAL_28]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_30]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@count] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_32]], %[[VAL_33]] : index +// CHECK-NEXT: pod.write %[[VAL_31]][@count] = %[[VAL_34]] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_34]], %[[VAL_35]] : index +// CHECK-NEXT: scf.if %[[VAL_36]] { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@params] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = function.call @Sigma::@Sigma::@compute(%[[VAL_38]]) : (!felt.type<"bn128">) -> !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: pod.write %[[VAL_31]][@comp] = %[[VAL_39]] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_40]]] = %[[VAL_31]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_11]] : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_11]] : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_42]], %[[VAL_17]] : !felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@sigmaF$inputs] = %[[VAL_5]]#1 : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.new : <2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_47:[0-9a-zA-Z_\.]+]] = %[[VAL_45]] to %[[VAL_44]] step %[[VAL_46]] { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_47]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@comp] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_47]]] = %[[VAL_49]] : <2 x !struct.type<@Sigma::@Sigma<[]>>>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@sigmaF] = %[[VAL_43]] : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Poseidon::@Poseidon<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_50:[0-9a-zA-Z_\.]+]]: !struct.type<@Poseidon::@Poseidon<[]>>, %[[VAL_51:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@sigmaF] : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@sigmaF$inputs] : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_56:[0-9a-zA-Z_\.]+]] = %[[VAL_54]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_56]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_58]]) %[[VAL_56]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_59:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_59]], %[[VAL_60]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_59]], %[[VAL_62]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_61]], %[[VAL_63]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_64]] { +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_59]], %[[VAL_65]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_66]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_68]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_69]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_53]]{{\[}}%[[VAL_72]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_73]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_74]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_59]], %[[VAL_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_76]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_80:[0-9a-zA-Z_\.]+]] = %[[VAL_78]] to %[[VAL_77]] step %[[VAL_79]] { +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_52]]{{\[}}%[[VAL_80]]] : <2 x !struct.type<@Sigma::@Sigma<[]>>>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_53]]{{\[}}%[[VAL_80]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_82]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Sigma::@Sigma::@constrain(%[[VAL_81]], %[[VAL_83]]) : (!struct.type<@Sigma::@Sigma<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sigma { +// CHECK-NEXT: struct.def @Sigma { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_84:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Sigma::@Sigma<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = struct.new : <@Sigma::@Sigma<[]>> +// CHECK-NEXT: function.return %[[VAL_85]] : !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_86:[0-9a-zA-Z_\.]+]]: !struct.type<@Sigma::@Sigma<[]>>, %[[VAL_87:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_86]][@out] : <@Sigma::@Sigma<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_11.circom b/circom/tests/loops/inner_conditional_11.circom new file mode 100644 index 000000000..29e679525 --- /dev/null +++ b/circom/tests/loops/inner_conditional_11.circom @@ -0,0 +1,206 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sigma() { + signal input inp; + signal output out; +} + +// Equivalent to inner_conditional_10 but refactored slightly. +template Poseidon() { + signal input inp; + + component sigmaF[2]; + + for (var i=0; i<4; i++) { + if (i < 1) { + sigmaF[0] = Sigma(); + sigmaF[0].inp <== inp; + } else if (i >= 3) { + sigmaF[1] = Sigma(); + sigmaF[1].inp <== inp; + } + } +} + +component main = Poseidon(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Poseidon::@Poseidon<[]>>} { +// CHECK-NEXT: poly.template @Poseidon { +// CHECK-NEXT: struct.def @Poseidon { +// CHECK-NEXT: struct.member @sigmaF : !array.type<2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: struct.member @sigmaF$inputs : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Poseidon::@Poseidon<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Poseidon::@Poseidon<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>) -> (!felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_16]], @params = %[[VAL_15]] } : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_21]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_22]][@inp] = %[[VAL_0]] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_24]]] = %[[VAL_22]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_26]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_27]][@count] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_28]], %[[VAL_29]] : index +// CHECK-NEXT: pod.write %[[VAL_27]][@count] = %[[VAL_30]] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_30]], %[[VAL_31]] : index +// CHECK-NEXT: scf.if %[[VAL_32]] { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_27]][@params] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_22]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = function.call @Sigma::@Sigma::@compute(%[[VAL_34]]) : (!felt.type<"bn128">) -> !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: pod.write %[[VAL_27]][@comp] = %[[VAL_35]] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_37]]] = %[[VAL_27]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_11]] : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_10]], %[[VAL_38]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_39]] -> (!array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_42]], @params = %[[VAL_41]] } : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_45]]] = %[[VAL_43]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_46]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_47]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_48]][@inp] = %[[VAL_0]] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_50]]] = %[[VAL_48]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_52]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_53]][@count] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_54]], %[[VAL_55]] : index +// CHECK-NEXT: pod.write %[[VAL_53]][@count] = %[[VAL_56]] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_56]], %[[VAL_57]] : index +// CHECK-NEXT: scf.if %[[VAL_58]] { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_53]][@params] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = function.call @Sigma::@Sigma::@compute(%[[VAL_60]]) : (!felt.type<"bn128">) -> !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: pod.write %[[VAL_53]][@comp] = %[[VAL_61]] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_63]]] = %[[VAL_53]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_11]] : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_11]] : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_40]] : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_65]], %[[VAL_14]] : !felt.type<"bn128">, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@sigmaF$inputs] = %[[VAL_5]]#1 : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = array.new : <2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_68]] to %[[VAL_67]] step %[[VAL_69]] { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_70]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@comp] : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: array.write %[[VAL_66]]{{\[}}%[[VAL_70]]] = %[[VAL_72]] : <2 x !struct.type<@Sigma::@Sigma<[]>>>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@sigmaF] = %[[VAL_66]] : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Poseidon::@Poseidon<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_73:[0-9a-zA-Z_\.]+]]: !struct.type<@Poseidon::@Poseidon<[]>>, %[[VAL_74:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_73]][@sigmaF] : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !struct.type<@Sigma::@Sigma<[]>>> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_73]][@sigmaF$inputs] : <@Poseidon::@Poseidon<[]>>, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_79:[0-9a-zA-Z_\.]+]] = %[[VAL_77]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_79]], %[[VAL_80]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_81]]) %[[VAL_79]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_82:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_82]], %[[VAL_83]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_84]] { +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_87]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_76]]{{\[}}%[[VAL_88]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_89]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_90]], %[[VAL_74]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_82]], %[[VAL_91]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_92]] { +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sigma::@Sigma<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_95]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_76]]{{\[}}%[[VAL_96]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_97]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_98]], %[[VAL_74]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_82]], %[[VAL_99]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_100]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_104:[0-9a-zA-Z_\.]+]] = %[[VAL_102]] to %[[VAL_101]] step %[[VAL_103]] { +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_75]]{{\[}}%[[VAL_104]]] : <2 x !struct.type<@Sigma::@Sigma<[]>>>, !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_76]]{{\[}}%[[VAL_104]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_106]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Sigma::@Sigma::@constrain(%[[VAL_105]], %[[VAL_107]]) : (!struct.type<@Sigma::@Sigma<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sigma { +// CHECK-NEXT: struct.def @Sigma { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_108:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Sigma::@Sigma<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = struct.new : <@Sigma::@Sigma<[]>> +// CHECK-NEXT: function.return %[[VAL_109]] : !struct.type<@Sigma::@Sigma<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_110:[0-9a-zA-Z_\.]+]]: !struct.type<@Sigma::@Sigma<[]>>, %[[VAL_111:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_110]][@out] : <@Sigma::@Sigma<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_conditional_12.circom b/circom/tests/loops/inner_conditional_12.circom new file mode 100644 index 000000000..3a003c201 --- /dev/null +++ b/circom/tests/loops/inner_conditional_12.circom @@ -0,0 +1,172 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// This test contains inner loops with different iteration counts. Further, one is +// independent of the outer loop induction variable and the other loop depends on it. +template InnerConditional12(N) { + signal output out; + + var a[N]; + for (var i = 0; i < N; i++) { + if (i < 2) { + // runs when i∈{0,1} + for (var j = 0; j < N; j++) { + a[i] += 999; + } + } else { + // runs when i∈{2,3} + for (var j = 0; j < i; j++) { + a[i] += 888; + } + } + } + out <-- a[0] + a[1]; +} + +component main = InnerConditional12(4); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerConditional12::@InnerConditional12<[4]>>} { +// CHECK-NEXT: poly.template @InnerConditional12 { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerConditional12 { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerConditional12::@InnerConditional12<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional12::@InnerConditional12<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_3]], %[[VAL_4]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_6]] to %[[VAL_5]] step %[[VAL_7]] { +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_8]]] = %[[VAL_2]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_3]], %[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_9]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_12]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_13]]) %[[VAL_11]], %[[VAL_12]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_15]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_17]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_14]], %[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_22]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_23]]) %[[VAL_21]], %[[VAL_22]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_26]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_24]]{{\[}}%[[VAL_30]]] = %[[VAL_29]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_24]], %[[VAL_32]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_20]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_35:[0-9a-zA-Z_\.]+]] = %[[VAL_14]], %[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_33]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_36]], %[[VAL_15]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_35]], %[[VAL_36]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_38]]{{\[}}%[[VAL_40]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_38]]{{\[}}%[[VAL_44]]] = %[[VAL_43]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_38]], %[[VAL_46]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_34]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_15]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_48]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]#0{{\[}}%[[VAL_50]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_52]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]#0{{\[}}%[[VAL_53]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_51]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@out] = %[[VAL_55]] : <@InnerConditional12::@InnerConditional12<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@InnerConditional12::@InnerConditional12<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional12::@InnerConditional12<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_56]][@out] : <@InnerConditional12::@InnerConditional12<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_60]], %[[VAL_61]] : <@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_65:[0-9a-zA-Z_\.]+]] = %[[VAL_63]] to %[[VAL_62]] step %[[VAL_64]] { +// CHECK-NEXT: array.write %[[VAL_60]]{{\[}}%[[VAL_65]]] = %[[VAL_59]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_68:[0-9a-zA-Z_\.]+]] = %[[VAL_60]], %[[VAL_69:[0-9a-zA-Z_\.]+]] = %[[VAL_66]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_69]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_70]]) %[[VAL_68]], %[[VAL_69]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_71:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_72:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_72]], %[[VAL_73]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_74]] -> (!array.type<@N x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_78:[0-9a-zA-Z_\.]+]] = %[[VAL_71]], %[[VAL_79:[0-9a-zA-Z_\.]+]] = %[[VAL_76]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_79]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_80]]) %[[VAL_78]], %[[VAL_79]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_81:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_82:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_81]]{{\[}}%[[VAL_83]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_84]], %[[VAL_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_81]]{{\[}}%[[VAL_87]]] = %[[VAL_86]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_82]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_81]], %[[VAL_89]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_77]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_92:[0-9a-zA-Z_\.]+]] = %[[VAL_71]], %[[VAL_93:[0-9a-zA-Z_\.]+]] = %[[VAL_90]]) : (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_93]], %[[VAL_72]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_94]]) %[[VAL_92]], %[[VAL_93]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_95:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>, %[[VAL_96:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_95]]{{\[}}%[[VAL_97]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_98]], %[[VAL_99]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_95]]{{\[}}%[[VAL_101]]] = %[[VAL_100]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_96]], %[[VAL_102]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_95]], %[[VAL_103]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_91]]#0 : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_72]], %[[VAL_104]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_75]], %[[VAL_105]] : !array.type<@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_00.circom b/circom/tests/loops/inner_loop_00.circom new file mode 100644 index 000000000..3e69701b3 --- /dev/null +++ b/circom/tests/loops/inner_loop_00.circom @@ -0,0 +1,112 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(n, m) { + signal input in[m]; + signal output out; + var b[n]; + + for (var i = 0; i < n; i++) { + for (var j = 0; j < m; j++) { + b[i] = in[j]; + } + } + out <-- b[0]; +} + +component main = InnerLoops(2, 3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[2, 3]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.param @m +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_IN:[0-9a-zA-Z_\.]+]]: !array.type<@m x !felt.type<"bn128">>) -> !struct.type<@InnerLoops::@InnerLoops<[@n, @m]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@n, @m]>> +// CHECK-NEXT: %[[V_M:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_6]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_10:[0-9a-zA-Z_\.]+]] = %[[V_8]] to %[[V_7]] step %[[V_9]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_10]]] = %[[V_4]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B1:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_15]]) %[[V_B1]], %[[V_I1]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B2:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_I3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_20:[0-9a-zA-Z_\.]+]] = %[[V_B2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_M]]) +// CHECK-NEXT: scf.condition(%[[V_22]]) %[[V_20]], %[[V_J1]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_23:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_J2]] +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = array.read %[[V_IN]]{{\[}}%[[V_25]]] : <@m x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I3]] +// CHECK-NEXT: array.write %[[V_23]]{{\[}}%[[V_27]]] = %[[V_26]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_23]], %[[V_29]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I3]], %[[V_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_19]]#0, %[[V_31]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_32]] +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = array.read %[[V_12]]#0{{\[}}%[[V_33]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[V_1]][@out] = %[[V_34]] : <@InnerLoops::@InnerLoops<[@n, @m]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops::@InnerLoops<[@n, @m]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_IN:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@n, @m]>>, %[[V_36:[0-9a-zA-Z_\.]+]]: !array.type<@m x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_M:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = struct.readm %[[V_IN]][@out] : <@InnerLoops::@InnerLoops<[@n, @m]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_41]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_45:[0-9a-zA-Z_\.]+]] = %[[V_43]] to %[[V_42]] step %[[V_44]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_45]]] = %[[V_39]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_48:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_49:[0-9a-zA-Z_\.]+]] = %[[V_46]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_50:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_49]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_50]]) %[[V_48]], %[[V_49]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_51:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_52:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_55:[0-9a-zA-Z_\.]+]] = %[[V_51]], %[[V_56:[0-9a-zA-Z_\.]+]] = %[[V_53]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_57:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_56]], %[[V_M]]) +// CHECK-NEXT: scf.condition(%[[V_57]]) %[[V_55]], %[[V_56]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_58:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_59:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_59]] +// CHECK-NEXT: %[[V_61:[0-9a-zA-Z_\.]+]] = array.read %[[V_36]]{{\[}}%[[V_60]]] : <@m x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_52]] +// CHECK-NEXT: array.write %[[V_58]]{{\[}}%[[V_62]]] = %[[V_61]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]] = felt.add %[[V_59]], %[[V_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_58]], %[[V_64]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]] = felt.add %[[V_52]], %[[V_65]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_54]]#0, %[[V_66]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_01.circom b/circom/tests/loops/inner_loop_01.circom new file mode 100644 index 000000000..65279a32e --- /dev/null +++ b/circom/tests/loops/inner_loop_01.circom @@ -0,0 +1,109 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(n) { + signal input a[n]; + var b[n]; + + for (var i = 0; i < n; i++) { + for (var j = 0; j <= i; j++) { + b[i] += a[i - j]; + } + } +} + +component main = InnerLoops(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[2]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: function.def @compute(%[[V_A:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@InnerLoops::@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_5]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_9:[0-9a-zA-Z_\.]+]] = %[[V_7]] to %[[V_6]] step %[[V_8]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_9]]] = %[[V_3]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B1:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_14]]) %[[V_B1]], %[[V_I1]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B2:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B3:[0-9a-zA-Z_\.]+]] = %[[V_B2]], %[[V_J2:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_J2]], %[[V_I2]]) +// CHECK-NEXT: scf.condition(%[[V_21]]) %[[V_B3]], %[[V_J2]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B4:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_J3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = array.read %[[V_B4]]{{\[}}%[[V_24]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_I2]], %[[V_J3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_26]] +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_27]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.add %[[V_25]], %[[V_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: array.write %[[V_B4]]{{\[}}%[[V_30]]] = %[[V_29]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J3]], %[[V_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_B4]], %[[V_32]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_18]]#0, %[[V_34]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_A:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@n]>>, %[[V_36:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_40]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_44:[0-9a-zA-Z_\.]+]] = %[[V_42]] to %[[V_41]] step %[[V_43]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_44]]] = %[[V_38]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_47:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_48:[0-9a-zA-Z_\.]+]] = %[[V_45]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_49:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_48]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_49]]) %[[V_47]], %[[V_48]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_50:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_51:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_54:[0-9a-zA-Z_\.]+]] = %[[V_50]], %[[V_55:[0-9a-zA-Z_\.]+]] = %[[V_52]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_56:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_55]], %[[V_51]]) +// CHECK-NEXT: scf.condition(%[[V_56]]) %[[V_54]], %[[V_55]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_57:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_58:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_59:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_51]] +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = array.read %[[V_57]]{{\[}}%[[V_59]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_61:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_51]], %[[V_58]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_61]] +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = array.read %[[V_36]]{{\[}}%[[V_62]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]] = felt.add %[[V_60]], %[[V_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_51]] +// CHECK-NEXT: array.write %[[V_57]]{{\[}}%[[V_65]]] = %[[V_64]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = felt.add %[[V_58]], %[[V_66]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_57]], %[[V_67]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_68:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_69:[0-9a-zA-Z_\.]+]] = felt.add %[[V_51]], %[[V_68]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_53]]#0, %[[V_69]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_02.circom b/circom/tests/loops/inner_loop_02.circom new file mode 100644 index 000000000..cce4e5747 --- /dev/null +++ b/circom/tests/loops/inner_loop_02.circom @@ -0,0 +1,244 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(n) { + signal input a[n]; + var b[n]; + + // Manually unrolled loop from inner_loops_01.circom + // for (var i = 0; i < n; i++) { + + var i = 0; + for (var j = 0; j <= i; j++) { + b[i] = a[i - j]; + } + i++; // 1 + for (var j = 0; j <= i; j++) { + b[i] = a[i - j]; + } + i++; // 2 + for (var j = 0; j <= i; j++) { + b[i] = a[i - j]; + } + i++; // 3 + for (var j = 0; j <= i; j++) { + b[i] = a[i - j]; + } + i++; // 4 + for (var j = 0; j <= i; j++) { + b[i] = a[i - j]; + } + i++; // 5 +} + +component main = InnerLoops(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[5]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@InnerLoops::@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_4]], %[[VAL_5]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] { +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_13:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_14:[0-9a-zA-Z_\.]+]] = %[[VAL_11]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_14]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_15]]) %[[VAL_13]], %[[VAL_14]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_10]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_19]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_21]]] = %[[VAL_20]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_16]], %[[VAL_23]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_28:[0-9a-zA-Z_\.]+]] = %[[VAL_12]]#0, %[[VAL_29:[0-9a-zA-Z_\.]+]] = %[[VAL_26]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_29]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_30]]) %[[VAL_28]], %[[VAL_29]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_32:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_25]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_34]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_31]]{{\[}}%[[VAL_36]]] = %[[VAL_35]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_32]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_31]], %[[VAL_38]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_27]]#0, %[[VAL_44:[0-9a-zA-Z_\.]+]] = %[[VAL_41]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_44]], %[[VAL_40]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_45]]) %[[VAL_43]], %[[VAL_44]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_40]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_48]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_49]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_46]]{{\[}}%[[VAL_51]]] = %[[VAL_50]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_47]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_46]], %[[VAL_53]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_58:[0-9a-zA-Z_\.]+]] = %[[VAL_42]]#0, %[[VAL_59:[0-9a-zA-Z_\.]+]] = %[[VAL_56]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_59]], %[[VAL_55]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_60]]) %[[VAL_58]], %[[VAL_59]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_61:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_62:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_55]], %[[VAL_62]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_63]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_64]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_61]]{{\[}}%[[VAL_66]]] = %[[VAL_65]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_62]], %[[VAL_67]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_61]], %[[VAL_68]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_55]], %[[VAL_69]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_73:[0-9a-zA-Z_\.]+]] = %[[VAL_57]]#0, %[[VAL_74:[0-9a-zA-Z_\.]+]] = %[[VAL_71]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_74]], %[[VAL_70]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_75]]) %[[VAL_73]], %[[VAL_74]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_76:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_77:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_70]], %[[VAL_77]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_78]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_79]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_76]]{{\[}}%[[VAL_81]]] = %[[VAL_80]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_77]], %[[VAL_82]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_76]], %[[VAL_83]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_70]], %[[VAL_84]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_86:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@n]>>, %[[VAL_87:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_90]], %[[VAL_91]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_95:[0-9a-zA-Z_\.]+]] = %[[VAL_93]] to %[[VAL_92]] step %[[VAL_94]] { +// CHECK-NEXT: array.write %[[VAL_90]]{{\[}}%[[VAL_95]]] = %[[VAL_89]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_99:[0-9a-zA-Z_\.]+]] = %[[VAL_90]], %[[VAL_100:[0-9a-zA-Z_\.]+]] = %[[VAL_97]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_100]], %[[VAL_96]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_101]]) %[[VAL_99]], %[[VAL_100]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_102:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_103:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_96]], %[[VAL_103]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_104]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_87]]{{\[}}%[[VAL_105]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_96]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_102]]{{\[}}%[[VAL_107]]] = %[[VAL_106]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_103]], %[[VAL_108]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_102]], %[[VAL_109]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_96]], %[[VAL_110]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_114:[0-9a-zA-Z_\.]+]] = %[[VAL_98]]#0, %[[VAL_115:[0-9a-zA-Z_\.]+]] = %[[VAL_112]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_115]], %[[VAL_111]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_116]]) %[[VAL_114]], %[[VAL_115]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_117:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_118:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_111]], %[[VAL_118]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_119]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_87]]{{\[}}%[[VAL_120]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_111]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_117]]{{\[}}%[[VAL_122]]] = %[[VAL_121]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_118]], %[[VAL_123]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_117]], %[[VAL_124]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_111]], %[[VAL_125]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_129:[0-9a-zA-Z_\.]+]] = %[[VAL_113]]#0, %[[VAL_130:[0-9a-zA-Z_\.]+]] = %[[VAL_127]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_130]], %[[VAL_126]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_131]]) %[[VAL_129]], %[[VAL_130]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_132:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_133:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_126]], %[[VAL_133]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_134]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_87]]{{\[}}%[[VAL_135]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_126]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_132]]{{\[}}%[[VAL_137]]] = %[[VAL_136]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_133]], %[[VAL_138]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_132]], %[[VAL_139]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_126]], %[[VAL_140]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_144:[0-9a-zA-Z_\.]+]] = %[[VAL_128]]#0, %[[VAL_145:[0-9a-zA-Z_\.]+]] = %[[VAL_142]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_145]], %[[VAL_141]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_146]]) %[[VAL_144]], %[[VAL_145]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_147:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_148:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_141]], %[[VAL_148]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_149]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_87]]{{\[}}%[[VAL_150]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_141]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_147]]{{\[}}%[[VAL_152]]] = %[[VAL_151]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_148]], %[[VAL_153]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_147]], %[[VAL_154]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_155:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_141]], %[[VAL_155]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_159:[0-9a-zA-Z_\.]+]] = %[[VAL_143]]#0, %[[VAL_160:[0-9a-zA-Z_\.]+]] = %[[VAL_157]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_160]], %[[VAL_156]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_161]]) %[[VAL_159]], %[[VAL_160]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_162:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_163:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_156]], %[[VAL_163]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_164]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_87]]{{\[}}%[[VAL_165]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_156]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_162]]{{\[}}%[[VAL_167]]] = %[[VAL_166]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_168:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_169:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_163]], %[[VAL_168]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_162]], %[[VAL_169]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_156]], %[[VAL_170]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_03.circom b/circom/tests/loops/inner_loop_03.circom new file mode 100644 index 000000000..17ee383ae --- /dev/null +++ b/circom/tests/loops/inner_loop_03.circom @@ -0,0 +1,243 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(n) { + signal input a[n]; + var b[n]; + + for (var j = 0; j <= 0; j++) { + b[0] = a[0 - j]; + } + for (var j = 0; j <= 1; j++) { + b[1] = a[1 - j]; + } + for (var j = 0; j <= 2; j++) { + b[2] = a[2 - j]; + } + for (var j = 0; j <= 3; j++) { + b[3] = a[3 - j]; + } + for (var j = 0; j <= 4; j++) { + b[4] = a[4 - j]; + } +} + +component main = InnerLoops(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[5]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@InnerLoops::@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_4]], %[[VAL_5]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] { +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_13:[0-9a-zA-Z_\.]+]] = %[[VAL_10]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_13]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_15]]) %[[VAL_12]], %[[VAL_13]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_18]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_20]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_23]]] = %[[VAL_21]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_16]], %[[VAL_25]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_28:[0-9a-zA-Z_\.]+]] = %[[VAL_11]]#0, %[[VAL_29:[0-9a-zA-Z_\.]+]] = %[[VAL_26]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_29]], %[[VAL_30]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_31]]) %[[VAL_28]], %[[VAL_29]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_32:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_33:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_34]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_36]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_39]]] = %[[VAL_37]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_33]], %[[VAL_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]], %[[VAL_41]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_44:[0-9a-zA-Z_\.]+]] = %[[VAL_27]]#0, %[[VAL_45:[0-9a-zA-Z_\.]+]] = %[[VAL_42]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_45]], %[[VAL_46]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_47]]) %[[VAL_44]], %[[VAL_45]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_48:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_49:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_50]], %[[VAL_49]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_52]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_54]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_48]]{{\[}}%[[VAL_55]]] = %[[VAL_53]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_49]], %[[VAL_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_48]], %[[VAL_57]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_60:[0-9a-zA-Z_\.]+]] = %[[VAL_43]]#0, %[[VAL_61:[0-9a-zA-Z_\.]+]] = %[[VAL_58]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_61]], %[[VAL_62]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_63]]) %[[VAL_60]], %[[VAL_61]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_64:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_65:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_66]], %[[VAL_65]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_68]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_64]]{{\[}}%[[VAL_71]]] = %[[VAL_69]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_65]], %[[VAL_72]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_64]], %[[VAL_73]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_76:[0-9a-zA-Z_\.]+]] = %[[VAL_59]]#0, %[[VAL_77:[0-9a-zA-Z_\.]+]] = %[[VAL_74]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_77]], %[[VAL_78]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_79]]) %[[VAL_76]], %[[VAL_77]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_80:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_81:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_82]], %[[VAL_81]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_84]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_86]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_80]]{{\[}}%[[VAL_87]]] = %[[VAL_85]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_81]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_80]], %[[VAL_89]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_90:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@n]>>, %[[VAL_91:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_94]], %[[VAL_95]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_99:[0-9a-zA-Z_\.]+]] = %[[VAL_97]] to %[[VAL_96]] step %[[VAL_98]] { +// CHECK-NEXT: array.write %[[VAL_94]]{{\[}}%[[VAL_99]]] = %[[VAL_93]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_102:[0-9a-zA-Z_\.]+]] = %[[VAL_94]], %[[VAL_103:[0-9a-zA-Z_\.]+]] = %[[VAL_100]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_103]], %[[VAL_104]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_105]]) %[[VAL_102]], %[[VAL_103]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_106:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_107:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_108]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_109]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_91]]{{\[}}%[[VAL_110]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_112]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_106]]{{\[}}%[[VAL_113]]] = %[[VAL_111]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_107]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_106]], %[[VAL_115]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_118:[0-9a-zA-Z_\.]+]] = %[[VAL_101]]#0, %[[VAL_119:[0-9a-zA-Z_\.]+]] = %[[VAL_116]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_119]], %[[VAL_120]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_121]]) %[[VAL_118]], %[[VAL_119]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_122:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_123:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_124]], %[[VAL_123]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_125]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_91]]{{\[}}%[[VAL_126]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_128]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_122]]{{\[}}%[[VAL_129]]] = %[[VAL_127]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_123]], %[[VAL_130]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_122]], %[[VAL_131]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_134:[0-9a-zA-Z_\.]+]] = %[[VAL_117]]#0, %[[VAL_135:[0-9a-zA-Z_\.]+]] = %[[VAL_132]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_135]], %[[VAL_136]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_137]]) %[[VAL_134]], %[[VAL_135]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_138:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_139:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_140]], %[[VAL_139]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_141]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_91]]{{\[}}%[[VAL_142]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_144]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_138]]{{\[}}%[[VAL_145]]] = %[[VAL_143]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_139]], %[[VAL_146]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_138]], %[[VAL_147]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_150:[0-9a-zA-Z_\.]+]] = %[[VAL_133]]#0, %[[VAL_151:[0-9a-zA-Z_\.]+]] = %[[VAL_148]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_151]], %[[VAL_152]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_153]]) %[[VAL_150]], %[[VAL_151]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_154:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_155:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_156]], %[[VAL_155]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_157]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_91]]{{\[}}%[[VAL_158]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_160]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_154]]{{\[}}%[[VAL_161]]] = %[[VAL_159]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_155]], %[[VAL_162]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_154]], %[[VAL_163]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_166:[0-9a-zA-Z_\.]+]] = %[[VAL_149]]#0, %[[VAL_167:[0-9a-zA-Z_\.]+]] = %[[VAL_164]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_168:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_169:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_167]], %[[VAL_168]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_169]]) %[[VAL_166]], %[[VAL_167]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_170:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_171:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_172:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_172]], %[[VAL_171]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_173]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_91]]{{\[}}%[[VAL_174]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_176:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_177:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_176]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_170]]{{\[}}%[[VAL_177]]] = %[[VAL_175]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_179:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_171]], %[[VAL_178]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_170]], %[[VAL_179]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_04.circom b/circom/tests/loops/inner_loop_04.circom new file mode 100644 index 000000000..e9fac66d4 --- /dev/null +++ b/circom/tests/loops/inner_loop_04.circom @@ -0,0 +1,105 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(n) { + signal input a[n]; + var b[n]; + var j; + for (var i = 0; i < n; i++) { + for (j = 0; j <= i; j++) { + b[i] = a[i - j]; + } + } +} + +component main = InnerLoops(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[2]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: function.def @compute(%[[V_A:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@InnerLoops::@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_5]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_9:[0-9a-zA-Z_\.]+]] = %[[V_7]] to %[[V_6]] step %[[V_8]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_9]]] = %[[V_3]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_B1:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_16]]) %[[V_B1]], %[[V_I1]], %[[V_J1]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B2:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B3:[0-9a-zA-Z_\.]+]] = %[[V_B2]], %[[V_J3:[0-9a-zA-Z_\.]+]] = %[[V_20]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_J3]], %[[V_I2]]) +// CHECK-NEXT: scf.condition(%[[V_24]]) %[[V_B3]], %[[V_J3]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B4:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_J4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_I2]], %[[V_J4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_27]] +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_28]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: array.write %[[V_B4]]{{\[}}%[[V_30]]] = %[[V_29]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J5:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J4]], %[[V_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_B4]], %[[V_J5]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_21]]#0, %[[V_I3]], %[[V_21]]#1 : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_A:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@n]>>, %[[V_36:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_40]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_44:[0-9a-zA-Z_\.]+]] = %[[V_42]] to %[[V_41]] step %[[V_43]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_44]]] = %[[V_38]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_48:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_49:[0-9a-zA-Z_\.]+]] = %[[V_46]], %[[V_50:[0-9a-zA-Z_\.]+]] = %[[V_45]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_49]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_51]]) %[[V_48]], %[[V_49]], %[[V_50]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_52:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_53:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_54:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_55:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_56:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_57:[0-9a-zA-Z_\.]+]] = %[[V_52]], %[[V_58:[0-9a-zA-Z_\.]+]] = %[[V_55]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_59:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_58]], %[[V_53]]) +// CHECK-NEXT: scf.condition(%[[V_59]]) %[[V_57]], %[[V_58]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_60:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[V_61:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_53]], %[[V_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_62]] +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]] = array.read %[[V_36]]{{\[}}%[[V_63]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_53]] +// CHECK-NEXT: array.write %[[V_60]]{{\[}}%[[V_65]]] = %[[V_64]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = felt.add %[[V_61]], %[[V_66]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_60]], %[[V_67]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_68:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_69:[0-9a-zA-Z_\.]+]] = felt.add %[[V_53]], %[[V_68]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_56]]#0, %[[V_69]], %[[V_56]]#1 : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_05.circom b/circom/tests/loops/inner_loop_05.circom new file mode 100644 index 000000000..1c6052ca6 --- /dev/null +++ b/circom/tests/loops/inner_loop_05.circom @@ -0,0 +1,90 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n*n]; + + for (var i = 0; i < n; i++) { + for (var j = 0; j < n; j++) { + out[i*n + j] <-- in; + } + } +} + +component main = Num2Bits(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Num2Bits::@Num2Bits<[2]>>} { +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.expr @"n_Mul_n@300" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@"n_Mul_n@300" x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@300" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@"n_Mul_n@300" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_10]]) %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_14:[0-9a-zA-Z_\.]+]] = %[[VAL_12]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_14]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_15]]) %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_11]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_19]]] = %[[VAL_2]] : <@"n_Mul_n@300" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_3]][@out] = %[[VAL_6]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@"n_Mul_n@300" x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@300" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_24]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@"n_Mul_n@300" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_31:[0-9a-zA-Z_\.]+]] = %[[VAL_29]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_31]], %[[VAL_26]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_32]]) %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_33:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_34]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_36]], %[[VAL_26]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_33]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_42]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_06.circom b/circom/tests/loops/inner_loop_06.circom new file mode 100644 index 000000000..337bd0608 --- /dev/null +++ b/circom/tests/loops/inner_loop_06.circom @@ -0,0 +1,107 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(n) { + signal input a[n]; + var b[n]; + + for (var i = 0; i < n; i++) { + for (var j = 0; j <= i; j++) { + b[i] += a[i]; + } + } +} + +component main = InnerLoops(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[2]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@InnerLoops::@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_4]], %[[VAL_5]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] { +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_13:[0-9a-zA-Z_\.]+]] = %[[VAL_10]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_13]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_14]]) %[[VAL_12]], %[[VAL_13]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_15]], %[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_17]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_20]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_19]], %[[VAL_20]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_22]]{{\[}}%[[VAL_24]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_26]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_22]]{{\[}}%[[VAL_29]]] = %[[VAL_28]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_22]], %[[VAL_31]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_18]]#0, %[[VAL_33]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerLoops::@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_34:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@n]>>, %[[VAL_35:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_38]], %[[VAL_39]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_41]] to %[[VAL_40]] step %[[VAL_42]] { +// CHECK-NEXT: array.write %[[VAL_38]]{{\[}}%[[VAL_43]]] = %[[VAL_37]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_46:[0-9a-zA-Z_\.]+]] = %[[VAL_38]], %[[VAL_47:[0-9a-zA-Z_\.]+]] = %[[VAL_44]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_47]], %[[VAL_36]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_48]]) %[[VAL_46]], %[[VAL_47]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_49:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_50:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_53:[0-9a-zA-Z_\.]+]] = %[[VAL_49]], %[[VAL_54:[0-9a-zA-Z_\.]+]] = %[[VAL_51]]) : (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) -> (!array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_54]], %[[VAL_50]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_55]]) %[[VAL_53]], %[[VAL_54]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_57:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_56]]{{\[}}%[[VAL_58]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_35]]{{\[}}%[[VAL_60]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_59]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_56]]{{\[}}%[[VAL_63]]] = %[[VAL_62]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_57]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_56]], %[[VAL_65]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_50]], %[[VAL_66]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_52]]#0, %[[VAL_67]] : !array.type<@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_07.circom b/circom/tests/loops/inner_loop_07.circom new file mode 100644 index 000000000..cf5841fd6 --- /dev/null +++ b/circom/tests/loops/inner_loop_07.circom @@ -0,0 +1,84 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(N) { + signal output out; + var a = 0; + for (var i = 0; i < N; i++) { + for (var j = 0; j < N; j++) { + a += 99; + } + } + out <-- a; +} + +component main = InnerLoops(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[2]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerLoops::@InnerLoops<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_7]]) %[[V_A1]], %[[V_I1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A3:[0-9a-zA-Z_\.]+]] = %[[V_A2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_14]]) %[[V_A3]], %[[V_J1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[V_A5:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A4]], %[[V_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_11]]#0, %[[V_I3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[SELF]][@out] = %[[V_4]]#0 : <@InnerLoops::@InnerLoops<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerLoops::@InnerLoops<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = struct.readm %[[SELF]][@out] : <@InnerLoops::@InnerLoops<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_30]]) %[[V_A1]], %[[V_I1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A3:[0-9a-zA-Z_\.]+]] = %[[V_A2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_37]]) %[[V_A3]], %[[V_J1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[V_A5:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A4]], %[[V_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_44]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_34]]#0, %[[V_I3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_08.circom b/circom/tests/loops/inner_loop_08.circom new file mode 100644 index 000000000..2b2191254 --- /dev/null +++ b/circom/tests/loops/inner_loop_08.circom @@ -0,0 +1,95 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(N) { + signal output out; + var a = 0; + var b = 1000; + for (var i = 0; i < N; i++) { + b += a; + for (var j = 0; j < N; j++) { + a += 99; + } + b -= 5; + } + out <-- a; +} + +component main = InnerLoops(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[2]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@InnerLoops::@InnerLoops<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B0:[0-9a-zA-Z_\.]+]] = felt.const 1000 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_B1:[0-9a-zA-Z_\.]+]] = %[[V_B0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_9]]) %[[V_A1]], %[[V_B1]], %[[V_I1]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_B3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_B2]], %[[V_A2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A3:[0-9a-zA-Z_\.]+]] = %[[V_A2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_18]]) %[[V_A3]], %[[V_J1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[V_A5:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A4]], %[[V_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[V_B4:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_B3]], %[[V_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_15]]#0, %[[V_B4]], %[[V_I3]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[SELF]][@out] = %[[V_5]]#0 : <@InnerLoops::@InnerLoops<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerLoops::@InnerLoops<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@N]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[V_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[SELF]][@out] : <@InnerLoops::@InnerLoops<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_A0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B0:[0-9a-zA-Z_\.]+]] = felt.const 1000 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[V_A1:[0-9a-zA-Z_\.]+]] = %[[V_A0]], %[[V_B1:[0-9a-zA-Z_\.]+]] = %[[V_B0]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_38]]) %[[V_A1]], %[[V_B1]], %[[V_I1]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_B3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_B2]], %[[V_A2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_J0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_A3:[0-9a-zA-Z_\.]+]] = %[[V_A2]], %[[V_J1:[0-9a-zA-Z_\.]+]] = %[[V_J0]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_J1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_47]]) %[[V_A3]], %[[V_J1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[V_50:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[V_A5:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A4]], %[[V_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_J3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_J2]], %[[V_52]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[V_B4:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_B3]], %[[V_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[V_56:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[V_44]]#0, %[[V_B4]], %[[V_I3]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/inner_loop_09.circom b/circom/tests/loops/inner_loop_09.circom new file mode 100644 index 000000000..8ea529e8e --- /dev/null +++ b/circom/tests/loops/inner_loop_09.circom @@ -0,0 +1,104 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template InnerLoops(N) { + signal input in[N]; + signal output out; + signal output out2[N]; + var a = 0; + var b = 0; + for (var i = 0; i < N; i++) { + out2[i] <-- in[i] + b; + for (var j = 0; j < N; j++) { + a += 99; + } + b += 5; + } + out <-- a; +} + +component main = InnerLoops(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InnerLoops::@InnerLoops<[2]>>} { +// CHECK-NEXT: poly.template @InnerLoops { +// CHECK-NEXT: poly.param @N +// CHECK-NEXT: struct.def @InnerLoops { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @out2 : !array.type<@N x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) -> !struct.type<@InnerLoops::@InnerLoops<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops::@InnerLoops<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_4]], %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_6]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_15]]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@N x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_12]], %[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_22]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_23]]) %[[VAL_21]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_24]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_27]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_20]]#0, %[[VAL_31]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_7]]#0 : <@InnerLoops::@InnerLoops<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out2] = %[[VAL_3]] : <@InnerLoops::@InnerLoops<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerLoops::@InnerLoops<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_34:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops::@InnerLoops<[@N]>>, %[[VAL_35:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_34]][@out] : <@InnerLoops::@InnerLoops<[@N]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_34]][@out2] : <@InnerLoops::@InnerLoops<[@N]>>, !array.type<@N x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_39]], %[[VAL_44:[0-9a-zA-Z_\.]+]] = %[[VAL_40]], %[[VAL_45:[0-9a-zA-Z_\.]+]] = %[[VAL_41]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_45]], %[[VAL_36]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_46]]) %[[VAL_43]], %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_48:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_49:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_52:[0-9a-zA-Z_\.]+]] = %[[VAL_47]], %[[VAL_53:[0-9a-zA-Z_\.]+]] = %[[VAL_50]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_53]], %[[VAL_36]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_54]]) %[[VAL_52]], %[[VAL_53]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_55:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_56:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_55]], %[[VAL_57]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_56]], %[[VAL_59]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_58]], %[[VAL_60]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_48]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_49]], %[[VAL_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_51]]#0, %[[VAL_62]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/known_function.circom b/circom/tests/loops/known_function.circom new file mode 100644 index 000000000..e3e4623f7 --- /dev/null +++ b/circom/tests/loops/known_function.circom @@ -0,0 +1,102 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function funWithLoop(n) { + var acc = 0; + for (var i = 1; i <= n; i++) { + acc += i; + } + return acc; +} + +template KnownFunctionArgs() { + signal output out[3]; + + out[0] <-- funWithLoop(4); // 0 + 1 + 2 + 3 + 4 = 10 + out[1] <-- funWithLoop(5); // 0 + 1 + 2 + 3 + 4 + 5 = 15 + + var acc = 1; + for (var i = 2; i <= funWithLoop(3); i++) { // 0 + 1 + 2 + 3 = 6 + acc *= i; + } + out[2] <-- acc; // 1 * 2 * 3 * 4 * 5 * 6 = 720 +} + +component main = KnownFunctionArgs(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@KnownFunctionArgs::@KnownFunctionArgs<[]>>} { +// CHECK-NEXT: function.def @funWithLoop(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_4:[0-9a-zA-Z_\.]+]] = %[[VAL_1]], %[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_5]], %[[VAL_0]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_6]]) %[[VAL_4]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_9]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]]#0 : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @KnownFunctionArgs { +// CHECK-NEXT: struct.def @KnownFunctionArgs { +// CHECK-NEXT: struct.member @out : !array.type<3 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@KnownFunctionArgs::@KnownFunctionArgs<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@KnownFunctionArgs::@KnownFunctionArgs<[]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = function.call @funWithLoop(%[[VAL_14]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_17]]] = %[[VAL_15]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = function.call @funWithLoop(%[[VAL_18]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_21]]] = %[[VAL_19]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_25:[0-9a-zA-Z_\.]+]] = %[[VAL_22]], %[[VAL_26:[0-9a-zA-Z_\.]+]] = %[[VAL_23]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @funWithLoop(%[[VAL_27]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_26]], %[[VAL_28]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_29]]) %[[VAL_25]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_30]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_31]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]], %[[VAL_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_36]]] = %[[VAL_24]]#0 : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_12]][@out] = %[[VAL_13]] : <@KnownFunctionArgs::@KnownFunctionArgs<[]>>, !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@KnownFunctionArgs::@KnownFunctionArgs<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_37:[0-9a-zA-Z_\.]+]]: !struct.type<@KnownFunctionArgs::@KnownFunctionArgs<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_37]][@out] : <@KnownFunctionArgs::@KnownFunctionArgs<[]>>, !array.type<3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_42:[0-9a-zA-Z_\.]+]] = %[[VAL_39]], %[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_40]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = function.call @funWithLoop(%[[VAL_44]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_43]], %[[VAL_45]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_46]]) %[[VAL_42]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_48:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_48]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_49]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/known_signal_value.circom b/circom/tests/loops/known_signal_value.circom new file mode 100644 index 000000000..334ef21e5 --- /dev/null +++ b/circom/tests/loops/known_signal_value.circom @@ -0,0 +1,108 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template accumulate() { + signal input i; + signal output o; + var r = 0; + while (r < i) { + r++; + } + o <-- r; +} + +template KnownLoopViaSignal() { + signal output y; + + component a = accumulate(); + a.i <-- 5; + y <-- a.o; +} + +component main = KnownLoopViaSignal(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>} { +// CHECK-NEXT: poly.template @KnownLoopViaSignal { +// CHECK-NEXT: struct.def @KnownLoopViaSignal { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@KnownLoopViaSignal::@KnownLoopViaSignal<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@KnownLoopViaSignal::@KnownLoopViaSignal<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = pod.new : <[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_3]], @params = %[[VAL_2]] } : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: pod.write %[[VAL_1]][@i] = %[[VAL_5]] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@count] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_6]], %[[VAL_7]] : index +// CHECK-NEXT: pod.write %[[VAL_4]][@count] = %[[VAL_8]] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_8]], %[[VAL_9]] : index +// CHECK-NEXT: scf.if %[[VAL_10]] { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@params] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_1]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = function.call @accumulate::@accumulate::@compute(%[[VAL_12]]) : (!felt.type<"bn128">) -> !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: pod.write %[[VAL_4]][@comp] = %[[VAL_13]] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@comp] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@o] : <@accumulate::@accumulate<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@y] = %[[VAL_15]] : <@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@a$inputs] = %[[VAL_1]] : <@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@comp] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: struct.writem %[[VAL_0]][@a] = %[[VAL_16]] : <@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@KnownLoopViaSignal::@KnownLoopViaSignal<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@y] : <@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@a] : <@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@a$inputs] : <@KnownLoopViaSignal::@KnownLoopViaSignal<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_20]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @accumulate::@accumulate::@constrain(%[[VAL_19]], %[[VAL_23]]) : (!struct.type<@accumulate::@accumulate<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @accumulate { +// CHECK-NEXT: struct.def @accumulate { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@accumulate::@accumulate<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.new : <@accumulate::@accumulate<[]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_28:[0-9a-zA-Z_\.]+]] = %[[VAL_26]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_28]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_29]]) %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_30]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_25]][@o] = %[[VAL_27]] : <@accumulate::@accumulate<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_25]] : !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_33:[0-9a-zA-Z_\.]+]]: !struct.type<@accumulate::@accumulate<[]>>, %[[VAL_34:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_33]][@o] : <@accumulate::@accumulate<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_36]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_38]], %[[VAL_34]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_39]]) %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_42]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/needs_stack_ctx_A.circom b/circom/tests/loops/needs_stack_ctx_A.circom new file mode 100644 index 000000000..cf9b92841 --- /dev/null +++ b/circom/tests/loops/needs_stack_ctx_A.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function fun(in, len) { + var sum = 0; + for (var j = 0; j < len; j++) { + sum += in[j]; + } + return sum; +} + +template NeedsStackContext(max) { + signal input in[max]; + signal output out[max]; + for (var i = 0; i < max; i++) { + out[i] <-- fun(in, i); + } +} + +component main = NeedsStackContext(3); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/loops/needs_stack_ctx_B.circom b/circom/tests/loops/needs_stack_ctx_B.circom new file mode 100644 index 000000000..5d3240ea7 --- /dev/null +++ b/circom/tests/loops/needs_stack_ctx_B.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function fun(in, len) { + var sum = 0; + for (var j = 0; j < len; j++) { + sum += in[j]; + } + return sum; +} + +template Ark(t) { + signal input in[t]; + signal output out[t]; + + for (var i = 0; i < t; i++) { + out[i] <-- fun(in, i); + } +} + +template NeedsStackContext(a, b) { + signal input in[a][b]; + signal output out[a][b]; + component arks[a]; + for (var j = 0; j < a; j++) { + arks[j] = Ark(b); + arks[j].in <-- in[j]; + } + for (var k = 0; k < a; k++) { + out[k] <-- arks[k].out; + } +} + +component main = NeedsStackContext(3, 2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/loops/simple_sum.circom b/circom/tests/loops/simple_sum.circom new file mode 100644 index 000000000..44490b0b0 --- /dev/null +++ b/circom/tests/loops/simple_sum.circom @@ -0,0 +1,54 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + var sum = 0; + for (var i = 0; i < 18446744073709551616; i++) { + sum += i; + } +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_4:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_1]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 18446744073709551616 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_4]], %[[VAL_6]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_4]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_15]], %[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_14]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 18446744073709551616 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_17]], %[[VAL_19]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_20]]) %[[VAL_17]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_21]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_25]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/simple_variant_idx.circom b/circom/tests/loops/simple_variant_idx.circom new file mode 100644 index 000000000..32ca33974 --- /dev/null +++ b/circom/tests/loops/simple_variant_idx.circom @@ -0,0 +1,67 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template SimpleVariantIdx(n) { + signal input in; + signal output out[n]; + + var lc; + for (var i = 0; i < n; i++) { + out[i] <-- in; + lc = out[i]; + } +} + +component main = SimpleVariantIdx(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SimpleVariantIdx::@SimpleVariantIdx<[3]>>} { +// CHECK-NEXT: poly.template @SimpleVariantIdx { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @SimpleVariantIdx { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SimpleVariantIdx::@SimpleVariantIdx<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@SimpleVariantIdx::@SimpleVariantIdx<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_12]]] = %[[VAL_0]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_13]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_16]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@SimpleVariantIdx::@SimpleVariantIdx<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@SimpleVariantIdx::@SimpleVariantIdx<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@SimpleVariantIdx::@SimpleVariantIdx<[@n]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@out] : <@SimpleVariantIdx::@SimpleVariantIdx<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_24:[0-9a-zA-Z_\.]+]] = %[[VAL_22]], %[[VAL_25:[0-9a-zA-Z_\.]+]] = %[[VAL_21]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_24]], %[[VAL_19]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_26]]) %[[VAL_24]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_20]]{{\[}}%[[VAL_29]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/unknown_index_from_array.circom b/circom/tests/loops/unknown_index_from_array.circom new file mode 100644 index 000000000..4509f32d3 --- /dev/null +++ b/circom/tests/loops/unknown_index_from_array.circom @@ -0,0 +1,65 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Example(n) { + signal input a[n]; + signal input b[n]; + signal output c[n]; + + for(var i = 0; i < n; i++) { + c[i] <-- a[b[2]]; + } +} + +component main = Example(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Example::@Example<[3]>>} { +// CHECK-NEXT: poly.template @Example { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Example { +// CHECK-NEXT: struct.member @c : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Example::@Example<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Example::@Example<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_5]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_11]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_13]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_15]]] = %[[VAL_14]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_4]] : <@Example::@Example<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Example::@Example<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@Example::@Example<[@n]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@c] : <@Example::@Example<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_25:[0-9a-zA-Z_\.]+]] = %[[VAL_23]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_25]], %[[VAL_21]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_26]]) %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/unknown_index_from_function.circom b/circom/tests/loops/unknown_index_from_function.circom new file mode 100644 index 000000000..d77e9c188 --- /dev/null +++ b/circom/tests/loops/unknown_index_from_function.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function identity(n) { + return n; +} + +template Example(n) { + signal input a[n]; + signal input b; + signal output c[n]; + + for(var i = 0; i < n; i++) { + c[i] <-- a[identity(b)]; + } +} + +component main = Example(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Example::@Example<[3]>>} { +// CHECK-NEXT: function.def @identity(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Example { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Example { +// CHECK-NEXT: struct.member @c : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Example::@Example<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Example::@Example<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_8]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = function.call @identity(%[[VAL_2]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_12]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_5]]{{\[}}%[[VAL_14]]] = %[[VAL_13]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_3]][@c] = %[[VAL_5]] : <@Example::@Example<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Example::@Example<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@Example::@Example<[@n]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@c] : <@Example::@Example<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_24:[0-9a-zA-Z_\.]+]] = %[[VAL_22]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_24]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/unknown_local_array_index.circom b/circom/tests/loops/unknown_local_array_index.circom new file mode 100644 index 000000000..6627f3c85 --- /dev/null +++ b/circom/tests/loops/unknown_local_array_index.circom @@ -0,0 +1,148 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ForUnknownIndex() { + signal input in; + signal output out; + + var arr1[10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + var arr2[10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + var arr3[10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + var acc = 0; + for (var i = 1; i <= in; i++) { + acc += i; + } + + // non-quadractic constraint + // out <== arr[acc]; + out <-- arr2[acc % 10]; +} + +component main = ForUnknownIndex(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>>} { +// CHECK-NEXT: poly.template @ForUnknownIndex { +// CHECK-NEXT: struct.def @ForUnknownIndex { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ForUnknownIndex::@ForUnknownIndex<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]], %[[VAL_2]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_4]], %[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]], %[[VAL_13]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]], %[[VAL_15]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_17]], %[[VAL_18]], %[[VAL_19]], %[[VAL_20]], %[[VAL_21]], %[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_26]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]], %[[VAL_28]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_30]], %[[VAL_31]], %[[VAL_32]], %[[VAL_33]], %[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]], %[[VAL_39]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_44:[0-9a-zA-Z_\.]+]] = %[[VAL_41]], %[[VAL_45:[0-9a-zA-Z_\.]+]] = %[[VAL_42]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_45]], %[[VAL_0]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_46]]) %[[VAL_44]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_48:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_48]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_49]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_43]]#0, %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_27]]{{\[}}%[[VAL_54]]] : <10 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_55]] : <@ForUnknownIndex::@ForUnknownIndex<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@ForUnknownIndex::@ForUnknownIndex<[]>>, %[[VAL_57:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_56]][@out] : <@ForUnknownIndex::@ForUnknownIndex<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_59]], %[[VAL_59]], %[[VAL_59]], %[[VAL_59]], %[[VAL_59]], %[[VAL_59]], %[[VAL_59]], %[[VAL_59]], %[[VAL_59]], %[[VAL_59]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_61]], %[[VAL_62]], %[[VAL_63]], %[[VAL_64]], %[[VAL_65]], %[[VAL_66]], %[[VAL_67]], %[[VAL_68]], %[[VAL_69]], %[[VAL_70]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_72]], %[[VAL_72]], %[[VAL_72]], %[[VAL_72]], %[[VAL_72]], %[[VAL_72]], %[[VAL_72]], %[[VAL_72]], %[[VAL_72]], %[[VAL_72]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_74]], %[[VAL_75]], %[[VAL_76]], %[[VAL_77]], %[[VAL_78]], %[[VAL_79]], %[[VAL_80]], %[[VAL_81]], %[[VAL_82]], %[[VAL_83]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_85]], %[[VAL_85]], %[[VAL_85]], %[[VAL_85]], %[[VAL_85]], %[[VAL_85]], %[[VAL_85]], %[[VAL_85]], %[[VAL_85]], %[[VAL_85]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_87]], %[[VAL_88]], %[[VAL_89]], %[[VAL_90]], %[[VAL_91]], %[[VAL_92]], %[[VAL_93]], %[[VAL_94]], %[[VAL_95]], %[[VAL_96]] : <10 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_101:[0-9a-zA-Z_\.]+]] = %[[VAL_98]], %[[VAL_102:[0-9a-zA-Z_\.]+]] = %[[VAL_99]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_102]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_103]]) %[[VAL_101]], %[[VAL_102]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_104:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_105:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_104]], %[[VAL_105]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_105]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_106]], %[[VAL_108]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/unknown_loop_component.circom b/circom/tests/loops/unknown_loop_component.circom new file mode 100644 index 000000000..2fa02c73b --- /dev/null +++ b/circom/tests/loops/unknown_loop_component.circom @@ -0,0 +1,120 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template nbits() { + signal input in; + signal output out; + var n = 1; + var r = 0; + while (n-1 < in) { + r++; + n *= 2; + } + out <-- r; +} + +template UnknownLoopComponent() { + signal input num; + signal output bits; + + component nb = nbits(); + nb.in <-- num; + bits <-- nb.out; +} + +component main = UnknownLoopComponent(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownLoopComponent::@UnknownLoopComponent<[]>>} { +// CHECK-NEXT: poly.template @UnknownLoopComponent { +// CHECK-NEXT: struct.def @UnknownLoopComponent { +// CHECK-NEXT: struct.member @bits : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @nb : !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: struct.member @nb$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@UnknownLoopComponent::@UnknownLoopComponent<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownLoopComponent::@UnknownLoopComponent<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_4]], @params = %[[VAL_3]] } : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_2]][@in] = %[[VAL_0]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_5]][@count] : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_6]], %[[VAL_7]] : index +// CHECK-NEXT: pod.write %[[VAL_5]][@count] = %[[VAL_8]] : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_8]], %[[VAL_9]] : index +// CHECK-NEXT: scf.if %[[VAL_10]] { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_5]][@params] : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_2]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = function.call @nbits::@nbits::@compute(%[[VAL_12]]) : (!felt.type<"bn128">) -> !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: pod.write %[[VAL_5]][@comp] = %[[VAL_13]] : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]>, !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_5]][@comp] : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]>, !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@out] : <@nbits::@nbits<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@bits] = %[[VAL_15]] : <@UnknownLoopComponent::@UnknownLoopComponent<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@nb$inputs] = %[[VAL_2]] : <@UnknownLoopComponent::@UnknownLoopComponent<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_5]][@comp] : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]>, !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@nb] = %[[VAL_16]] : <@UnknownLoopComponent::@UnknownLoopComponent<[]>>, !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownLoopComponent::@UnknownLoopComponent<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownLoopComponent::@UnknownLoopComponent<[]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@bits] : <@UnknownLoopComponent::@UnknownLoopComponent<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@nb] : <@UnknownLoopComponent::@UnknownLoopComponent<[]>>, !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_17]][@nb$inputs] : <@UnknownLoopComponent::@UnknownLoopComponent<[]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@nbits::@nbits<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @nbits::@nbits::@constrain(%[[VAL_20]], %[[VAL_24]]) : (!struct.type<@nbits::@nbits<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @nbits { +// CHECK-NEXT: struct.def @nbits { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@nbits::@nbits<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.new : <@nbits::@nbits<[]>> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_30:[0-9a-zA-Z_\.]+]] = %[[VAL_27]], %[[VAL_31:[0-9a-zA-Z_\.]+]] = %[[VAL_28]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_30]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_33]], %[[VAL_25]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_30]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_36]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_35]], %[[VAL_39]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_40]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_26]][@out] = %[[VAL_29]]#1 : <@nbits::@nbits<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_26]] : !struct.type<@nbits::@nbits<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !struct.type<@nbits::@nbits<[]>>, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@out] : <@nbits::@nbits<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_47:[0-9a-zA-Z_\.]+]] = %[[VAL_44]], %[[VAL_48:[0-9a-zA-Z_\.]+]] = %[[VAL_45]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_47]], %[[VAL_49]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_50]], %[[VAL_42]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_51]]) %[[VAL_47]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_52:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_53:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_53]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_52]], %[[VAL_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_57]], %[[VAL_55]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/unknown_loop_index.circom b/circom/tests/loops/unknown_loop_index.circom new file mode 100644 index 000000000..dcc470b89 --- /dev/null +++ b/circom/tests/loops/unknown_loop_index.circom @@ -0,0 +1,408 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} + +template LessThan(n) { + assert(n <= 252); + signal input in[2]; + signal output out; + + component n2b = Num2Bits(n+1); + + n2b.in <== in[0]+ (1< in) { + n--; + } + + in === counter; + + out <-- counter; +} + +template UnknownLoopIndex(n) { + signal input idx; + signal input choices[n]; + signal output out; + + component lt = LessThan(n); + lt.in[0] <== idx; + lt.in[1] <== n; + lt.out === 1; + + component c = CountDown(n); + c.in <== idx; + + // This constraint will be unknown (error[T20462]) + // out <== choices[c.out]; + out <-- choices[c.out]; +} + +component main = UnknownLoopIndex(100); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownLoopIndex::@UnknownLoopIndex<[100]>>} { +// CHECK-NEXT: poly.template @CountDown { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @CountDown { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@CountDown::@CountDown<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_2]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_2]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_4]], %[[VAL_0]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_4]] : <@CountDown::@CountDown<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@CountDown::@CountDown<[@n]>>, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_11]][@out] : <@CountDown::@CountDown<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_13]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_16]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_19]]) %[[VAL_18]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_20]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @LessThan { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.expr @"n_Add_1@633" { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_25]], %[[VAL_23]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_26]], "assertion failed" +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @LessThan { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n2b : !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: struct.member @n2b$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.new : <@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_30]], %[[VAL_33]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_34]], "assertion failed" +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_35]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_37]], @params = %[[VAL_36]] } : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_38]] : (!pod.type<[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_40]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_41]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.shl %[[VAL_43]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_42]], %[[VAL_44]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_46]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_47]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_45]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_32]][@in] = %[[VAL_49]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_39]][@count] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_50]], %[[VAL_51]] : index +// CHECK-NEXT: pod.write %[[VAL_39]][@count] = %[[VAL_52]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_52]], %[[VAL_53]] : index +// CHECK-NEXT: scf.if %[[VAL_54]] { +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_39]][@params] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_32]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits::@Num2Bits::@compute(%[[VAL_56]]) : (!felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: pod.write %[[VAL_39]][@comp] = %[[VAL_57]] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_39]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_59]][@out] : <@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_60]]{{\[}}%[[VAL_61]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_58]], %[[VAL_62]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_29]][@out] = %[[VAL_63]] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_29]][@n2b$inputs] = %[[VAL_32]] : <@LessThan::@LessThan<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_39]][@comp] : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: struct.writem %[[VAL_29]][@n2b] = %[[VAL_64]] : <@LessThan::@LessThan<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: function.return %[[VAL_29]] : !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_65:[0-9a-zA-Z_\.]+]]: !struct.type<@LessThan::@LessThan<[@n]>>, %[[VAL_66:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_65]][@out] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_65]][@n2b] : <@LessThan::@LessThan<[@n]>>, !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_65]][@n2b$inputs] : <@LessThan::@LessThan<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_67]], %[[VAL_72]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_73]], "assertion failed" +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Add_1@633_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_74]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_77]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_66]]{{\[}}%[[VAL_78]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.shl %[[VAL_80]], %[[VAL_67]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_79]], %[[VAL_81]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_83]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_66]]{{\[}}%[[VAL_84]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_82]], %[[VAL_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_87]], %[[VAL_86]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_70]][@out] : <@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_89]]{{\[}}%[[VAL_90]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_88]], %[[VAL_91]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_69]], %[[VAL_92]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Num2Bits::@Num2Bits::@constrain(%[[VAL_70]], %[[VAL_93]]) : (!struct.type<@Num2Bits::@Num2Bits<[@"n_Add_1@633"]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"n_Add_1@633_0" { +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.const 252 : <"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_96]], %[[VAL_94]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_97]], "assertion failed" +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_96]], %[[VAL_95]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_98]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Num2Bits { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Num2Bits { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_99:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Num2Bits::@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_107:[0-9a-zA-Z_\.]+]] = %[[VAL_104]], %[[VAL_108:[0-9a-zA-Z_\.]+]] = %[[VAL_105]], %[[VAL_109:[0-9a-zA-Z_\.]+]] = %[[VAL_103]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_108]], %[[VAL_101]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_110]]) %[[VAL_107]], %[[VAL_108]], %[[VAL_109]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_111:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_112:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_113:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_99]], %[[VAL_112]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_114]], %[[VAL_115]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_112]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_102]]{{\[}}%[[VAL_117]]] = %[[VAL_116]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_112]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_102]]{{\[}}%[[VAL_118]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_119]], %[[VAL_104]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_113]], %[[VAL_120]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_111]], %[[VAL_111]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_112]], %[[VAL_123]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_122]], %[[VAL_124]], %[[VAL_121]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_100]][@out] = %[[VAL_102]] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_100]] : !struct.type<@Num2Bits::@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_125:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits::@Num2Bits<[@n]>>, %[[VAL_126:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_125]][@out] : <@Num2Bits::@Num2Bits<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_133:[0-9a-zA-Z_\.]+]] = %[[VAL_130]], %[[VAL_134:[0-9a-zA-Z_\.]+]] = %[[VAL_131]], %[[VAL_135:[0-9a-zA-Z_\.]+]] = %[[VAL_129]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_134]], %[[VAL_127]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_136]]) %[[VAL_133]], %[[VAL_134]], %[[VAL_135]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_137:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_138:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_139:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_128]]{{\[}}%[[VAL_140]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_128]]{{\[}}%[[VAL_142]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_143]], %[[VAL_144]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_141]], %[[VAL_145]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_146]], %[[VAL_147]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_128]]{{\[}}%[[VAL_148]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_149]], %[[VAL_130]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_139]], %[[VAL_150]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_137]], %[[VAL_137]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_138]], %[[VAL_153]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_152]], %[[VAL_154]], %[[VAL_151]] : !felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_132]]#2, %[[VAL_126]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @UnknownLoopIndex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @UnknownLoopIndex { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @c : !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: struct.member @c$inputs : !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: struct.member @lt : !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: struct.member @lt$inputs : !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_155:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_156:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@UnknownLoopIndex::@UnknownLoopIndex<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>> +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = pod.new : <[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_161]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_163]], @params = %[[VAL_162]] } : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_160]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_166]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_165]]{{\[}}%[[VAL_167]]] = %[[VAL_155]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_160]][@in] = %[[VAL_165]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_168:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_164]][@count] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_169:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_168]], %[[VAL_169]] : index +// CHECK-NEXT: pod.write %[[VAL_164]][@count] = %[[VAL_170]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_171:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_172:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_170]], %[[VAL_171]] : index +// CHECK-NEXT: scf.if %[[VAL_172]] { +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_164]][@params] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_160]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = function.call @LessThan::@LessThan::@compute(%[[VAL_174]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_164]][@comp] = %[[VAL_175]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_176:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_160]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_177:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_178:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_177]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_176]]{{\[}}%[[VAL_178]]] = %[[VAL_158]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_160]][@in] = %[[VAL_176]] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_179:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_164]][@count] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_180:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_181:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_179]], %[[VAL_180]] : index +// CHECK-NEXT: pod.write %[[VAL_164]][@count] = %[[VAL_181]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_182:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_183:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_181]], %[[VAL_182]] : index +// CHECK-NEXT: scf.if %[[VAL_183]] { +// CHECK-NEXT: %[[VAL_184:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_164]][@params] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_185:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_160]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_186:[0-9a-zA-Z_\.]+]] = function.call @LessThan::@LessThan::@compute(%[[VAL_185]]) : (!array.type<2 x !felt.type<"bn128">>) -> !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_164]][@comp] = %[[VAL_186]] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_187:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_188:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_187]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_189:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_190:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_189]], @params = %[[VAL_188]] } : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_159]][@in] = %[[VAL_155]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_191:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_190]][@count] : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_192:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_193:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_191]], %[[VAL_192]] : index +// CHECK-NEXT: pod.write %[[VAL_190]][@count] = %[[VAL_193]] : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_194:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_195:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_193]], %[[VAL_194]] : index +// CHECK-NEXT: scf.if %[[VAL_195]] { +// CHECK-NEXT: %[[VAL_196:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_190]][@params] : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_197:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_159]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_198:[0-9a-zA-Z_\.]+]] = function.call @CountDown::@CountDown::@compute(%[[VAL_197]]) : (!felt.type<"bn128">) -> !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_190]][@comp] = %[[VAL_198]] : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_199:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_190]][@comp] : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: %[[VAL_200:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_199]][@out] : <@CountDown::@CountDown<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_201:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_200]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_202:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_156]]{{\[}}%[[VAL_201]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_157]][@out] = %[[VAL_202]] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_157]][@c$inputs] = %[[VAL_159]] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_203:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_190]][@comp] : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_157]][@c] = %[[VAL_203]] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_157]][@lt$inputs] = %[[VAL_160]] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_204:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_164]][@comp] : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_157]][@lt] = %[[VAL_204]] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: function.return %[[VAL_157]] : !struct.type<@UnknownLoopIndex::@UnknownLoopIndex<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_205:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, %[[VAL_206:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_207:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_208:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_209:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_205]][@out] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_210:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_205]][@c] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !struct.type<@CountDown::@CountDown<[@n]>> +// CHECK-NEXT: %[[VAL_211:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_205]][@c$inputs] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_212:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_205]][@lt] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !struct.type<@LessThan::@LessThan<[@n]>> +// CHECK-NEXT: %[[VAL_213:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_205]][@lt$inputs] : <@UnknownLoopIndex::@UnknownLoopIndex<[@n]>>, !pod.type<[@in: !array.type<2 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_214:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_215:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_214]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_216:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@LessThan::@LessThan<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_217:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_213]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_218:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_219:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_218]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_220:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_217]]{{\[}}%[[VAL_219]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_220]], %[[VAL_206]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_221:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_213]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_222:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_223:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_222]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_224:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_221]]{{\[}}%[[VAL_223]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_224]], %[[VAL_208]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_225:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_212]][@out] : <@LessThan::@LessThan<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_226:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_225]], %[[VAL_226]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_227:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_228:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_227]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_229:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@CountDown::@CountDown<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_230:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_211]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_230]], %[[VAL_206]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_231:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_211]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @CountDown::@CountDown::@constrain(%[[VAL_210]], %[[VAL_231]]) : (!struct.type<@CountDown::@CountDown<[@n]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: %[[VAL_232:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_213]][@in] : <[@in: !array.type<2 x !felt.type<"bn128">>]>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @LessThan::@LessThan::@constrain(%[[VAL_212]], %[[VAL_232]]) : (!struct.type<@LessThan::@LessThan<[@n]>>, !array.type<2 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/unknown_loop_oob.circom b/circom/tests/loops/unknown_loop_oob.circom new file mode 100644 index 000000000..9f7bf850d --- /dev/null +++ b/circom/tests/loops/unknown_loop_oob.circom @@ -0,0 +1,111 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template accumulate() { + signal input i; + signal output o; + var r = 0; + while (r < i) { + r++; + } + o <-- r; +} + +template UnknownLoopOOB() { + signal input m; // Could be out of bounds + signal input n[2]; + signal output y; + + component a = accumulate(); + a.i <-- m; + y <-- n[a.o]; +} + +component main = UnknownLoopOOB(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UnknownLoopOOB::@UnknownLoopOOB<[]>>} { +// CHECK-NEXT: poly.template @UnknownLoopOOB { +// CHECK-NEXT: struct.def @UnknownLoopOOB { +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) -> !struct.type<@UnknownLoopOOB::@UnknownLoopOOB<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownLoopOOB::@UnknownLoopOOB<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_5]], @params = %[[VAL_4]] } : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_3]][@i] = %[[VAL_0]] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@count] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_7]], %[[VAL_8]] : index +// CHECK-NEXT: pod.write %[[VAL_6]][@count] = %[[VAL_9]] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_9]], %[[VAL_10]] : index +// CHECK-NEXT: scf.if %[[VAL_11]] { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@params] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @accumulate::@accumulate::@compute(%[[VAL_13]]) : (!felt.type<"bn128">) -> !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: pod.write %[[VAL_6]][@comp] = %[[VAL_14]] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@comp] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@o] : <@accumulate::@accumulate<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_17]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@y] = %[[VAL_18]] : <@UnknownLoopOOB::@UnknownLoopOOB<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@a$inputs] = %[[VAL_3]] : <@UnknownLoopOOB::@UnknownLoopOOB<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@comp] : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@a] = %[[VAL_19]] : <@UnknownLoopOOB::@UnknownLoopOOB<[]>>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@UnknownLoopOOB::@UnknownLoopOOB<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownLoopOOB::@UnknownLoopOOB<[]>>, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_22:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@y] : <@UnknownLoopOOB::@UnknownLoopOOB<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@a] : <@UnknownLoopOOB::@UnknownLoopOOB<[]>>, !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@a$inputs] : <@UnknownLoopOOB::@UnknownLoopOOB<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@accumulate::@accumulate<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_25]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @accumulate::@accumulate::@constrain(%[[VAL_24]], %[[VAL_28]]) : (!struct.type<@accumulate::@accumulate<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @accumulate { +// CHECK-NEXT: struct.def @accumulate { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@accumulate::@accumulate<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.new : <@accumulate::@accumulate<[]>> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_33:[0-9a-zA-Z_\.]+]] = %[[VAL_31]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_33]], %[[VAL_29]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_33]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_35]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_37]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_30]][@o] = %[[VAL_32]] : <@accumulate::@accumulate<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_30]] : !struct.type<@accumulate::@accumulate<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !struct.type<@accumulate::@accumulate<[]>>, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_38]][@o] : <@accumulate::@accumulate<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_41]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_43]], %[[VAL_39]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_44]]) %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/variant_idx_in_loop_01.circom b/circom/tests/loops/variant_idx_in_loop_01.circom new file mode 100644 index 000000000..433bc2831 --- /dev/null +++ b/circom/tests/loops/variant_idx_in_loop_01.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template VariantIndex(n) { + signal input in; + signal output out[n]; + + for (var i = 0; i> i); + } +} + +component main = VariantIndex(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@VariantIndex::@VariantIndex<[2]>>} { +// CHECK-NEXT: poly.template @VariantIndex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @VariantIndex { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@VariantIndex::@VariantIndex<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@VariantIndex::@VariantIndex<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_10]]] = %[[VAL_9]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@VariantIndex::@VariantIndex<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@VariantIndex::@VariantIndex<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@VariantIndex::@VariantIndex<[@n]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@VariantIndex::@VariantIndex<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_17]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_19]], %[[VAL_15]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_20]]) %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_21]], %[[VAL_22]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/variant_idx_in_loop_02.circom b/circom/tests/loops/variant_idx_in_loop_02.circom new file mode 100644 index 000000000..8961dcfd4 --- /dev/null +++ b/circom/tests/loops/variant_idx_in_loop_02.circom @@ -0,0 +1,89 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template VariantIndex(n) { + signal input in; + signal output out; + + var temp[n]; + for (var i = 0; i> i); + } + out <-- temp[0] + temp[1]; +} + +component main = VariantIndex(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@VariantIndex::@VariantIndex<[2]>>} { +// CHECK-NEXT: poly.template @VariantIndex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @VariantIndex { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@VariantIndex::@VariantIndex<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@VariantIndex::@VariantIndex<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_4]], %[[VAL_5]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_9:[0-9a-zA-Z_\.]+]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] { +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_9]]] = %[[VAL_3]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_10]], %[[VAL_13:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">>) -> (!felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_12]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_14]]) %[[VAL_12]], %[[VAL_13]] : !felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>): +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_15]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_20]], %[[VAL_16]] : !felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]#1{{\[}}%[[VAL_22]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]#1{{\[}}%[[VAL_25]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_27]] : <@VariantIndex::@VariantIndex<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@VariantIndex::@VariantIndex<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@VariantIndex::@VariantIndex<[@n]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_28]][@out] : <@VariantIndex::@VariantIndex<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_33]], %[[VAL_34]] : <@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_36]] to %[[VAL_35]] step %[[VAL_37]] { +// CHECK-NEXT: array.write %[[VAL_33]]{{\[}}%[[VAL_38]]] = %[[VAL_32]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_39]], %[[VAL_42:[0-9a-zA-Z_\.]+]] = %[[VAL_33]]) : (!felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">>) -> (!felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">>) { +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_41]], %[[VAL_30]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_43]]) %[[VAL_41]], %[[VAL_42]] : !felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_45:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>): +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_29]], %[[VAL_44]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_45]]{{\[}}%[[VAL_47]]] = %[[VAL_46]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_44]], %[[VAL_48]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_49]], %[[VAL_45]] : !felt.type<"bn128">, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/loops/variant_idx_in_loop_03.circom b/circom/tests/loops/variant_idx_in_loop_03.circom new file mode 100644 index 000000000..8e181d8f6 --- /dev/null +++ b/circom/tests/loops/variant_idx_in_loop_03.circom @@ -0,0 +1,73 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template VariantIndex(n) { + signal input in; + signal output out[n*n]; + + var x = 1; + for (var i = 0; i> i); + } +} + +component main = VariantIndex(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@VariantIndex::@VariantIndex<[2]>>} { +// CHECK-NEXT: poly.template @VariantIndex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.expr @"n_Mul_n@304" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @VariantIndex { +// CHECK-NEXT: struct.member @out : !array.type<@"n_Mul_n@304" x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@VariantIndex::@VariantIndex<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@VariantIndex::@VariantIndex<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@304" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@"n_Mul_n@304" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_8]], %[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_7]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_12]]) %[[VAL_10]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_2]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_17]]] = %[[VAL_16]] : <@"n_Mul_n@304" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_19]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_3]][@out] = %[[VAL_6]] : <@VariantIndex::@VariantIndex<[@n]>>, !array.type<@"n_Mul_n@304" x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@VariantIndex::@VariantIndex<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !struct.type<@VariantIndex::@VariantIndex<[@n]>>, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@304" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_20]][@out] : <@VariantIndex::@VariantIndex<[@n]>>, !array.type<@"n_Mul_n@304" x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_28:[0-9a-zA-Z_\.]+]] = %[[VAL_26]], %[[VAL_29:[0-9a-zA-Z_\.]+]] = %[[VAL_25]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_28]], %[[VAL_22]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_30]]) %[[VAL_28]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_32:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_32]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_31]], %[[VAL_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_35]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/misc/array_computation.circom b/circom/tests/misc/array_computation.circom new file mode 100644 index 000000000..f7d403c26 --- /dev/null +++ b/circom/tests/misc/array_computation.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function array_computation(x, n) { + var ret[2]; + var i; + for (i = 0; i < n \ 2; i++) { + ret[0] += x[i]; + } + for (i = n \ 2; i < n; i++) { + ret[1] += x[i]; + } + return ret; +} + +template Caller() { + signal input inp[5]; + signal output outp[2] <== array_computation(inp, 5); +} + +component main = Caller(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/misc/circomlib_smtprocessor.circom b/circom/tests/misc/circomlib_smtprocessor.circom new file mode 100644 index 000000000..db4cccd03 --- /dev/null +++ b/circom/tests/misc/circomlib_smtprocessor.circom @@ -0,0 +1,219 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.6; + +template SMTProcessorSM() { + signal input prev_new1; + signal input prev_na; +} + +template SMTProcessor(nLevels) { + signal input enabled; + + component sm[nLevels]; + for (var i=0; i>} { +// CHECK-NEXT: poly.template @SMTProcessor { +// CHECK-NEXT: poly.param @nLevels +// CHECK-NEXT: struct.def @SMTProcessor { +// CHECK-NEXT: struct.member @sm : !array.type<@nLevels x !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>> +// CHECK-NEXT: struct.member @sm$inputs : !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SMTProcessor::@SMTProcessor<[@nLevels]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@SMTProcessor::@SMTProcessor<[@nLevels]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @nLevels : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_5]], %[[VAL_8:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">, !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>) -> (!felt.type<"bn128">, !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_13]], @params = %[[VAL_12]] } : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_14]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_10]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_17]] -> (!array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_20]]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_21]][@prev_new1] = %[[VAL_19]] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_22]]] = %[[VAL_21]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_23]]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@count] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_25]], %[[VAL_26]] : index +// CHECK-NEXT: pod.write %[[VAL_24]][@count] = %[[VAL_27]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_27]], %[[VAL_28]] : index +// CHECK-NEXT: scf.if %[[VAL_29]] { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@params] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@prev_new1] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_21]][@prev_na] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = function.call @SMTProcessorSM::@SMTProcessorSM::@compute(%[[VAL_31]], %[[VAL_32]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: pod.write %[[VAL_24]][@comp] = %[[VAL_33]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_34]]] = %[[VAL_24]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_35]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_37]]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_38]][@prev_na] = %[[VAL_36]] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_39]]] = %[[VAL_38]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_40]]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_41]][@count] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_42]], %[[VAL_43]] : index +// CHECK-NEXT: pod.write %[[VAL_41]][@count] = %[[VAL_44]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_44]], %[[VAL_45]] : index +// CHECK-NEXT: scf.if %[[VAL_46]] { +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_41]][@params] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_38]][@prev_new1] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_38]][@prev_na] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = function.call @SMTProcessorSM::@SMTProcessorSM::@compute(%[[VAL_48]], %[[VAL_49]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: pod.write %[[VAL_41]][@comp] = %[[VAL_50]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_51]]] = %[[VAL_41]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_11]] : !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_53]]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_54]][@prev_new1] = %[[VAL_52]] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_55]]] = %[[VAL_54]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_56]]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_57]][@count] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_58]], %[[VAL_59]] : index +// CHECK-NEXT: pod.write %[[VAL_57]][@count] = %[[VAL_60]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_60]], %[[VAL_61]] : index +// CHECK-NEXT: scf.if %[[VAL_62]] { +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_57]][@params] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_54]][@prev_new1] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_54]][@prev_na] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = function.call @SMTProcessorSM::@SMTProcessorSM::@compute(%[[VAL_64]], %[[VAL_65]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: pod.write %[[VAL_57]][@comp] = %[[VAL_66]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_67]]] = %[[VAL_57]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_69]]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_70]][@prev_na] = %[[VAL_68]] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_71]]] = %[[VAL_70]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_72]]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_73]][@count] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_74]], %[[VAL_75]] : index +// CHECK-NEXT: pod.write %[[VAL_73]][@count] = %[[VAL_76]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_76]], %[[VAL_77]] : index +// CHECK-NEXT: scf.if %[[VAL_78]] { +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_73]][@params] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@prev_new1] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_70]][@prev_na] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = function.call @SMTProcessorSM::@SMTProcessorSM::@compute(%[[VAL_80]], %[[VAL_81]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: pod.write %[[VAL_73]][@comp] = %[[VAL_82]] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_83]]] = %[[VAL_73]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_11]] : !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_84]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_85]], %[[VAL_18]] : !felt.type<"bn128">, !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@sm$inputs] = %[[VAL_6]]#1 : <@SMTProcessor::@SMTProcessor<[@nLevels]>>, !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = array.new : <@nLevels x !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = poly.read_const @nLevels : index +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_90:[0-9a-zA-Z_\.]+]] = %[[VAL_88]] to %[[VAL_87]] step %[[VAL_89]] { +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_90]]] : <@nLevels x !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_91]][@comp] : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]>, !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: array.write %[[VAL_86]]{{\[}}%[[VAL_90]]] = %[[VAL_92]] : <@nLevels x !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>>, !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@sm] = %[[VAL_86]] : <@SMTProcessor::@SMTProcessor<[@nLevels]>>, !array.type<@nLevels x !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@SMTProcessor::@SMTProcessor<[@nLevels]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_93:[0-9a-zA-Z_\.]+]]: !struct.type<@SMTProcessor::@SMTProcessor<[@nLevels]>>, %[[VAL_94:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = poly.read_const @nLevels : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_93]][@sm] : <@SMTProcessor::@SMTProcessor<[@nLevels]>>, !array.type<@nLevels x !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_93]][@sm$inputs] : <@SMTProcessor::@SMTProcessor<[@nLevels]>>, !array.type<@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_100:[0-9a-zA-Z_\.]+]] = %[[VAL_98]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_100]], %[[VAL_95]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_101]]) %[[VAL_100]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_102:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_102]], %[[VAL_105]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_106]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_102]], %[[VAL_107]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_108]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = poly.read_const @nLevels : index +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_112:[0-9a-zA-Z_\.]+]] = %[[VAL_110]] to %[[VAL_109]] step %[[VAL_111]] { +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_96]]{{\[}}%[[VAL_112]]] : <@nLevels x !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>>, !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_97]]{{\[}}%[[VAL_112]]] : <@nLevels x !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>>, !pod.type<[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_114]][@prev_new1] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_114]][@prev_na] : <[@prev_new1: !felt.type<"bn128">, @prev_na: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @SMTProcessorSM::@SMTProcessorSM::@constrain(%[[VAL_113]], %[[VAL_115]], %[[VAL_116]]) : (!struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SMTProcessorSM { +// CHECK-NEXT: struct.def @SMTProcessorSM { +// CHECK-NEXT: function.def @compute(%[[VAL_117:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_118:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = struct.new : <@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: function.return %[[VAL_119]] : !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_120:[0-9a-zA-Z_\.]+]]: !struct.type<@SMTProcessorSM::@SMTProcessorSM<[]>>, %[[VAL_121:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_122:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/misc/poseidon.circom b/circom/tests/misc/poseidon.circom new file mode 100644 index 000000000..084fe6b5b --- /dev/null +++ b/circom/tests/misc/poseidon.circom @@ -0,0 +1,952 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function POSEIDON_C(t) { + if (t==5) { + return + [ + 0xeb544fee2815dda7f53e29ccac98ed7d889bb4ebd47c3864f3c2bd81a6da891, + 0x554d736315b8662f02fdba7dd737fbca197aeb12ea64713ba733f28475128cb, + 0x2f83b9df259b2b68bcd748056307c37754907df0c0fb0035f5087c58d5e8c2d4, + 0x2ca70e2e8d7f39a12447ac83052451b461f15f8b41a75ef31915208f5aba9683, + 0x1cb5f9319be6a45e91b04d7222271c94994196f12ed22c5d4ec719cb83ecfea9, + 0xa9c0b1916a8e41d360d02e6e2e5d1b98c34dfcec769429c851867e46e126fa3, + 0x1dd6ba3731e49d21e8d36e9d4d1edad245ebf9bdd9ebb60a252e4804a6390f6a, + 0x24ae2a67c3d521c11a11b7112abbdee30647107b808866a980837d0d7da4e3e0, + 0xd20c9310b5c14d9ef12866af5a45eae3ca9be16d200497066c8b2ee96781d70, + 0xe047c9821fe94d55d400d763a66c4c6169993abed543c7284b4a35430019445, + 0x29474ab799b1e13948eff41d2ce79bfad335d09110157076988ac207e10c81dd, + 0x3899f139d0dc4b281be3b74ab4c70789b7f41e7aca47ea2722a20d79afbca93, + 0x1866624f761ab8dd7a91c5f37af5e47639951d5acb6b1bbf3b96ca273f71029d, + 0x13c119f36718f7d5f09ad8541325a13acf6b34db6d9ee2af7ea06061240f3009, + 0xe4a1008158077402b11f13c08890b739643cc8e93fa44487b5a1575dd867fd7, + 0xef505fd44ac10a251b670dafe14cabd9ada9e3002210ac9c3876f37de4e7ad8, + 0x1d31e4e2a5978b7491c43d367470a5a5d1445b6b8129a5b9a6fd238405720de5, + 0xa979ad5428d481cb624d9d504524a9694ca5cb4421b5d1dc6af2c030fbeac39, + 0xf7fccd2ec8bc6ed9ce3682f38aa291deea9373f4995778bf762ade36d6ab2a0, + 0x2691b924dfa123005f7c078d9bf8706defe99c2ba99bd6ee53b153e9fec7bb80, + 0x2077df6510b4860e56b913bef3a80dbc464b0e4678add60dea7a9517463220b, + 0x29ee09d8af9d24ca49350ce2e0aa47d00a3dc21bafbfac1c9ba61c58e2993e8c, + 0x8b292c661d427506b9a01916624f3cde332aaced9f1a494a733cea6f25bfaad, + 0x2583699ce536a757b22e4713edfbb050092c84abc72c90ad87393a1da9a4cf90, + 0x1e3f1b660223d65ad88999475374f6e25fd4148eb8110a0b12cffa19657b0b66, + 0x20f3ecbb37c34aec79131455461259e59b222f0ee8e02f3194cf62a9ad4c3448, + 0xdf4f5088e4444fbf87d553ba62dbda95696d8b9cf6210b1c85513b1776fbc64, + 0x2b348effd4c9cef00a1cf4dd67dd664b2ffe361a807c589a252c63bcbfc6833, + 0x1ba1e522fcb153676cd8f20e82256f0327c000fa96b1b462fc84b556f26a86c7, + 0x294c44df8e68c96144e964c37bbc5766764ed3550aff80dbe9d3fa74419fe50, + 0x313716eec6dcd8a602ca040700498dc04c77dfe2194753c59bc818c1d2636a1, + 0x287dec74696d663e2359f68225de955384d960bbafb90967429a442e19e3ec61, + 0x25e42f72c6be0942311ba097cf365683db4962c8204fec9213f0f8f72c1946be, + 0x12b6881b96654fe1768c242acd5399b08639f081a94896f5ea6da70b6b475c91, + 0xdfc2b54546fd3267d7be55c716cb243ef18118ed9498c8270449bd9418afdb5, + 0x27dd55fe0d5c0ff56ad4890fa029c27c5f36d04cdc73899ab99b2872b28eedf0, + 0xc60962711aef16e7a2ce59f587443ec8b41ef8dcfccb38188adcbddd32f173f, + 0x2edc09feb267c6b586e62fffe32bf5f16c28b585986b81116684b7e8b40d42d2, + 0xaf8386859db252ff295a19466d8d100622c90502137aa1cd4c4bcc9656d11e2, + 0x121f218392f73d4c16abe382102a459e6c080b3ca4eda51a23e651a13a680550, + 0x1ea38273f5d59e65061f8c775c571ffc75ef67d29405b5e02913cb3019d56f8e, + 0x9bd2349005699bcc0ac35b627e2f8f08bfc3b0bf30b146f37742ac1556187fe, + 0x91c505b1e92448c11aea22aaac4d44f6a7f2132f89e91b7f55f9404696c1433, + 0xb316f1c29689d4f490f7fcdd5e9f2d256d443ba14cda4bb799b0573a931a99f, + 0x2049251919a8f3f4398188b81f99d2e2d0e3f5359cfa55bdf3aa75fdadf367e7, + 0x1fe7f9eb6788101908814168e3e4cf7a899a105bf9e584af0064188a4aac55bd, + 0x158e6579b0388153b0acd630ea94de8f6d966d529c2d01b9e9b1c67c1ec1d570, + 0x1994f82f27153afb9de2aa3f4be05c4b2c487e393dcedca2566aa6b7fbc3696b, + 0x1b6250553e8629a5a8a40b568432ce7dbd83c87603eeccc8dad572ccebef6e1d, + 0x20296940a7d1eded2ae79fd78fa2ac11abb2210bf24542feabee71f0d0d7c9e, + 0x2553943f9e0ffce9c297cd31c29f1fa5f01883cc9e504fded7a905032c170c89, + 0x1c56eb362896c2f00ad18faeaf04d577f5feb4db4e077965c38f2eaf5f7be08c, + 0xed8857205e0680055de7e822b6f7d62ac0f75fef67da1ff7b7735208885cf90, + 0x118f91185a09355f9d8c3f556367a2bebe79e7d9528a8d72a592681671aac75, + 0x2a71e6a67abdb25a78010fe6fe0a20d1d84e21cba75ad55937dc1834c13af0c5, + 0x2327dbc05997ce8575680e4b8929d4e9ed25fb9204277d603061986dbee57e0, + 0xe05235e01f21cc3f2971c382d18c14e41785a5ec8d447cd93d13281792e6d6e, + 0x98afa2ea7ff065b2adfc4ab00f3b04496c1e490eab264d2370b107e5a49204e, + 0x27bddb7bf06eaa63419adae44209dd25a4e35edcb863b009bd34ccc4905d204b, + 0x2704406bc806f4ccb19085cb9d3771b12ab5ce7aabf0601e9e06a2bc98837ade, + 0x21c75c54664b9fec86756aa9027261975244f42cf91c9cc0b33c2a62b756a3ef, + 0x2be84c1d84c16038ea5f933290699daaaa8164c5ea39a02bcbddc66cf69fe8ec, + 0x2c970e41d48649cf013c676c8c688ac165563720d1d5f32628ac5b239488a96e, + 0xe1ad2660a2e958daa1f2654b3a37fee60546ca0327150733070742edc806435, + 0x2060ee7fdf775fc7e389a55376374c9e35d5c8763d597f426304e236f577b829, + 0x1e0116818c843ed86f09daee0a581af10d52deeadad77656e736eac08e6f0f17, + 0xa89c1498ef25a383d886bb58424e6940ac399e3e557e9de951a697c54a7576c, + 0x303743d6f36d925e1097483350f5bd2cb297d4ec9239209f63c516b849a67e6, + 0x8cf44446d968430232df175d462b9c9b0e2e2c37e8406764cb96c7c3446018d, + 0x2419811cbaeb3f551b0a9232eee5d53e3769fbcf5239533074375f1b00777f16, + 0x4237c622626db376b774849dbbe876809082f1b13f5824f4c58369f27fe7b6, + 0x1e5b490c72eeb607e114a5cb87a8494b178937cdee34b9e8e947342c14454558, + 0x4265333e59e1a5ff749203cb4a5d1415a72862c61380b1c242d0f32ca15b97a, + 0x189deaf74258451ac4da682532be43d24a5c683293c1ff7486de26d35d982e86, + 0x4ec516b0fd42fa53a34905cfdedaad021b36399d03d8263ae08c46af3eca76f, + 0x2ce1c8a00845a82b3aa1b6642fc988578576cef86196525e6d595c7701ad700d, + 0x247816fd0d34f9d3b396917478605c94a1c052a6ed663bdc344e7aee9686b6b4, + 0xc676dbe6c494d5609c444de622bcf60cf555091a507fce86477019daea987d, + 0x1cb395ade530fc2407aa7b2148d2dfaee30f4ddf258fc149cce3c5cde80a85d5, + 0x190e1494e3cfdada3b9e65d8fe3c1ec769540da023f9ec2e56259f6a56890b0e, + 0x18f2941b2335138336c351a792343222a845ee0a2ea5a3b9160c1d6d9b229fe2, + 0x14ea23ce8b2312e07df57e0aece1da5d2c0e01f757e6a5c86ab5e403688544dc, + 0x2818ad1005f4efb5d554361a29f85ea10940d6e71f38e8369beff3563a660bbe, + 0x23ce3a9a522915a281793977b49054c37d65f90b841e0ca90817bab49d79db4a, + 0x6c2ed2be876309a9b3b44ece37b1c42382927dd04249658a3d41e3f38d5e022, + 0x18b6740f72d77ebcf642b945ca2ed6c8a9853a3749d7fab6051e4ca36f44fc42, + 0x1feacb9eb2a6878061374d069a9dae328369ee63e75a1b99cdb06a48b0d9976a, + 0x1a44ee4565a967647300c75ed2b2543d8d45d5477fd606a356d1073bd13831d2, + 0x41f3b3b5b1050c16bf3d62d87d5d273b067da484679103231ed65a18da9fe48, + 0x1fd958cc4fe0a290bd0fbfb8b8a513acb5898d63bc0d7e585b7d081c49eb5659, + 0x175daba07c5edbf84f09c87a8c34dd73325943a48fc12cb839dca47512561d2e, + 0x9cf0a4e6e31dc24dfd5a5a27a77833e477d5b2d92cff5fc5ccad9528c43ba78, + 0x12d49465bd4120cbf78e5a3414d44c6530bc963bd701c54d4c6418a6cebe80b1, + 0x101b2f2b675804d3b26b2bd1e07c7365af0bfc2edf010916eefb39e28215d44a, + 0x114fc65faba09a59749e0b5f111930783529a0638456216232cb7e5a339736aa, + 0x1dff99b52799afc802c2bbf9b67dd044d3cb51017dc4f88358ddd67366d3a9f5, + 0x290f4496a52dd4dda59edccd7325038bbdc0554ad3a9a0be7931c91062a67027, + 0x91e8704663c516c3b96721d2033d985089fb992dca48c8ddcb97d7d15c7e188, + 0x2dce22599de04196a0169fc211d0f9c8692643aa09728eadf6d50bb534c0e323, + 0x29a7ff0720e170c0e67efde72795328fecef66daada5f0e2ca858a8c6135fd48 + ]; + } else { + assert(0); + return [0]; + } +} + +function POSEIDON_M(t) { + if (t==5) { + return + [ + [ + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x2a70b9f1d4bbccdbc03e17c1d1dcdb02052903dc6609ea6969f661b2eb74c839, + 0x2f69a7198e1fbcc7dea43265306a37ed55b91bff652ad69aa4fa8478970d401d, + 0xc3f050a6bf5af151981e55e3e1a29a13c3ffa4550bd2514f1afd6c5f721f830, + 0x2a20e3a4a0e57d92f97c9d6186c6c3ea7c5e55c20146259be2f78c2ccc2e3595 + ], + [ + 0x25fb50b65acf4fb047cbd3b1c17d97c7fe26ea9ca238d6e348550486e91c7765, + 0x281154651c921e746315a9934f1b8a1bba9f92ad8ef4b979115b8e2e991ccd7a, + 0x1c1edd62645b73ad931ab80e37bbb267ba312b34140e716d6a3747594d3052, + 0xdec54e6dbf75205fa75ba7992bd34f08b2efe2ecd424a73eda7784320a1a36e, + 0x1049f8210566b51faafb1e9a5d63c0ee701673aed820d9c4403b01feb727a549 + ], + [ + 0x293d617d7da72102355f39ebf62f91b06deb5325f367a4556ea1e31ed5767833, + 0x28c2be2f8264f95f0b53c732134efa338ccd8fdb9ee2b45fb86a894f7db36c37, + 0x15b98ce93e47bc64ce2f2c96c69663c439c40c603049466fa7f9a4b228bfc32b, + 0x1c482a25a729f5df20225815034b196098364a11f4d988fb7cc75cf32d8136fa, + 0x2ecac687ef5b4b568002bd9d1b96b4bef357a69e3e86b5561b9299b82d69c8e + ], + [ + 0x104d0295ab00c85e960111ac25da474366599e575a9b7edf6145f14ba6d3c1c4, + 0x21888041e6febd546d427c890b1883bb9b626d8cb4dc18dcc4ec8fa75e530a13, + 0x12c7e2adfa524e5958f65be2fbac809fcba8458b28e44d9265051de33163cf9c, + 0x2625ce48a7b39a4252732624e4ab94360812ac2fc9a14a5fb8b607ae9fd8514a, + 0x2d3a1aea2e6d44466808f88c9ba903d3bdcb6b58ba40441ed4ebcf11bbe1e37b + ], + [ + 0xaaa35e2c84baf117dea3e336cd96a39792b3813954fe9bf3ed5b90f2f69c977, + 0x14ddb5fada0171db80195b9592d8cf2be810930e3ea4574a350d65e2cbff4941, + 0x2efc2b90d688134849018222e7b8922eaf67ce79816ef468531ec2de53bbd167, + 0x7f017a7ebd56dd086f7cd4fd710c509ed7ef8e300b9a8bb9fb9f28af710251f, + 0x14074bb14c982c81c9ad171e4f35fe49b39c4a7a72dbb6d9c98d803bfed65e64 + ] + ]; + } else { + assert(0); + return [[0]]; + } +} + +function POSEIDON_P(t) { + if (t==5) { + return + [ + [ + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x12873658ecf188d299b8ccd568eb14a4d4307c5caa95633dc296f05cfc966598, + 0x153cf8033d8e8a15cde2c5e6b93da4018c5954d00a9274ec5ec6d5101ea22761, + 0x9f15a882446823fdca6f6ab15dd8e250d90c84470516671afbdfb0de80fb00e, + 0x167c655bf6cf3e0fb64c9075773bc862b24b4ce2f69c8ec198add2758a2ce216 + ], + [ + 0x25fb50b65acf4fb047cbd3b1c17d97c7fe26ea9ca238d6e348550486e91c7765, + 0x27f9160806de9ef57ddb4243f839e4b7e8bb293ac176fdc5b5419ed73a07999f, + 0x16fceedd703bbbc2bc6f1d792e501939105b044b1b904d3b110110da983ccdc2, + 0x2eaa925d06b6f5a77c0d5cb20598742791495cec84593a57ee9fc4c9115ae7ca, + 0x279b324735fbc883e24f191ca7039f9986115b9e6fcf4946cf45f08ceda2dc8c + ], + [ + 0x293d617d7da72102355f39ebf62f91b06deb5325f367a4556ea1e31ed5767833, + 0x97d71f1fd579a0d0f436a6b36165cd23a9fcab03ad25e7872cdb09b4a0ea0dc, + 0x1a9fd26611128d592d594f51c251dbf4eff6dccecbcf2ebf310e34bed661337e, + 0x21eb30a57e5912ab06d18573fc546b2bf3be840d5f5ede01f91dd2bbb578dcc2, + 0x13abaf72889b31372b1e6f48759371ef65bc57d28ac2f60e6d227eb008b96ced + ], + [ + 0x104d0295ab00c85e960111ac25da474366599e575a9b7edf6145f14ba6d3c1c4, + 0x19bb8abf6a012cc7b8b974039c6be6df31446a51702b39a8d90ae4be7ec33ec9, + 0x11075889bc0dcc9d6f06af3012f04aadcf9049de04fc775f8fa091702e70b9bb, + 0x2e4cb25599a3dbf07de338827b28d16b9c8fcab8fffe8f2a16161be6a521a358, + 0xc7a700b33fb23fc642e0e8671deb84d05ded8ccbc968d15171182e158684e85 + ], + [ + 0xaaa35e2c84baf117dea3e336cd96a39792b3813954fe9bf3ed5b90f2f69c977, + 0x2b5d28e8d648bffe0fab59e3c7d983a4099fa0a4c548df0006e6d0f4e20206c1, + 0x1a96c37c461ab8a38ee15bc2784c5096d30d1482e57c2f861bab95584b90d84a, + 0xdbdd3171308bfcd3cb8b8a676592858b8652e902142beb8fe4145002fba8e0f, + 0x17ac4855f295a3b8fb8ceded7f4b39290647a0145af56b03b01e957808d66fa7 + ] + ]; + } else { + assert(0); + return [[0]]; + } +} + +function POSEIDON_S(t) { + if (t==5) { + return + [ + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x351d582486c39726623750495e297970b0e19787b49173a9388a0d5b08788ab, + 0x10e1a3beae297a472a31a3b51514c664abb12ec2d15860a29d2a9352d3ce8ba, + 0x12395fabf1c14664faf3ecc72a84623c1d5cb7b5e5744e602c886a5773e5f06d, + 0x9b91873151f00b299a173a5b736f73fbe2ce543f0b4d237565bd58758935cfd, + 0x1d53cf618f93c90852172c773264b8f49e938bf22791cff829e95ff6942299b, + 0x1a78e48450798918f254396fa7417bf2c5ff69259200c2a8d53af5f2c4d8ba1e, + 0xd3d4c1eb9828c87afeca8ea128d1d533750cf555c6b70d70a8520ccf16feb, + 0x3af062fcd1ca71ba6de0ca4436f1a5a0698a3f49abbe4ecf3daa0ea2e4dc84c, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1ad1ba4f26d401924b25657414256b59767284a692897ba5cd53a7f5322005fc, + 0x1133694747d2cb4890f4f5982564eac6019ca5a9091b302d2c10b87297041d5f, + 0x1b46c42ff1aa475972e26f559a88164024234f7b392039fb2a2171be631bd8de, + 0x1954aa0a79f14968c817000929e2e744262871011f238d986086e7d9574936b4, + 0x7aced898db99ae9796fc7191a103b9de4c77f0e08ecea6aa593974f652ac4ba, + 0x18a5a098d914f6221726d42ada7683a1605e20217a09489c9b2d84c3cdd2c39e, + 0x13e00cf4ab3b1e028165af8d41019ca20a21aeb40926592a180f9806083eb5a0, + 0x478f72938b528ec79defe09215b46320801fb752ddc88d638a48790561b4e2b, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x8ff43b0eda2134077b96d7e4cc37a6871254910ab4c58a4bcc78c1bc6ce3937, + 0x27d7fa3ab9c438b6ad1ec5a60e8ad91aad02e4d2908ff7192e5ac0cebd91f928, + 0x1e9ac8ae7cf2b40d629999251be50d9771391326a664dfc206f2abec8efbe56f, + 0x2d6b56b4849de82b636d81f2e98476bb6c35cbc8962137b615bff86f8f261971, + 0x21e70566d2f4bb8728fdb3749da99a2143a0d90bb6c1b0d6d02125aa1fe63092, + 0x1c01c050dd9b0b8ec8e5ee1eaecac1171bf69a8f3d477eb7ec3a605b010d4ea5, + 0x282e8dde73a0dc74ee10816aacc1dba10c142109c14ad7954eb7b56ca268a16e, + 0x767cf96b16035a96d19fcc57edcd92e746d226cfe84b733454c7ff9a16d25cc, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x18974b9f253b6bb681f82ed0f2d3e6b4f70ca858468b7f1bd3ded1a581ec21d9, + 0x18153fe8966abfc450a25222a6a27f6175fe851989776dcd2111a5a2de99c2da, + 0x2da1e2e775539490c2fe2c827e65c00e382a4a5d6c49a8374381d39c627f36c, + 0x2608589b9cd3f4c12b4e832e05fc5ebdcb403cd6560a8d7ece8d17ac94e79e06, + 0x5b4b074edb1366d35bf1c1ec1451a36cff351b407a8d30d563471bde491f146, + 0x856cc5a00bc37dd0217920da66c5765dea0644555e35822d7fd464d9eb38096, + 0x2021dde3ed193bdade457c9db5ab799e6b3fca640669d2f6295b4852f54d446e, + 0x12e3785f05f36bb797b2c9f03c5a55fab52e88b0550b7155d6013b706574d41b, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x118084c76d1340b3980add4df3878df4ffc6e9fe26f0b5c4e2f9579ee6fe2c1a, + 0xb9dacae8623c514c622c85753ef7b994d9102ab46620f956c26e9c62fe53050, + 0xdaea6d18a826bb2ba972ae16dc621cb8fdf9ecd531ee3c9f9d0b4012da6769f, + 0x8f2a5df6437e253b579921fc3208b3c176e5a18dde267a4f85b7afb7f79ceed, + 0x6a91d7c75e34fd43d9aa53b7d2793e4d5d70a5fecc5fd5653b162ba2631aa68, + 0x207579e33c36af2d20d759996c313f78dc339878c0a289d5db58b6b3d6069c56, + 0x25562540cd12084b3392c8cb8cda95ffc9c2dd6f8a75054ce16acf87ba871b9f, + 0xbbf47feca60b93dd0501ee0fc294c2a82b103817b4acf0af6979183afcdc87, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1e0814ce223453b24804a1ab4ce39815e35aed2fe9f7510e6288abb9d8b15711, + 0x18f9aa2721d95963399ae18d6d95a6f81b78b528e750554eb2613d6dabbd72a9, + 0x2ea329822ad302ab8831c559c64080e7ba6bf4c98ea0caf9cddd929bbb5875a6, + 0x29910d86bc27b38a93bca80677a3647c01cb5262ed19cd0c00872925a046a338, + 0x21c0a05ace45015ea895e01d630ce6b7423f3c211d26e8b6ef54d3dfc0660ab, + 0x25e7c463042f520ed2ff8c68be30bdddbd7ea5cdd7a91224bc6a32a3f5c0fcc, + 0xdf5b7e7663197f911e0dcd1ff4237ffdf080234e9b92201538ae7db6b6a7d21, + 0x94d0fcd9592b4771d2b9bfbc2bd78defef3b6ca923c68382650f9d63ce37c85, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x270361108967ed0391a49d4b7cd6af9bdd544e8bf048658c4fab36aa12407263, + 0x18bbf89e7cae93044c847bea654101914dbdd1656483e54e07ae332857821961, + 0x4aa47a0ede64ecdfa83507a2b8947b4b587758d75239071f6b4d3d66777bc1e, + 0x182d30ec988fca803ff7def1470c06aa6a596f56710184909fda17d354d3ce02, + 0x223c27171e456846dffc59cb1a53c761afddf85582e4c70ebafddf10eb1f8448, + 0x7d46dc97554a25edb78ea4d862c48bc5a08e9ea1eb369c5c8c2e0903114c915, + 0x1bf473a2e982e519523b486d264941d8e32cbbad362bdbf736d7ac04c4d2a964, + 0x12277b175bf54c3f2b0a57eb189e77714cf21630ace1fcf44d39397aae5b6da5, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x15c0a2cfffac7feab86a185031f489a9f83e89e3337a84b51dfe6fbb57feb15b, + 0x2b83199ec584185de52190f5a415c1fbb9efd4bb9eae4c4e2763abcf99359ccc, + 0x28601b9940a312c65b02adccb76937ef645d4e451c940ad4241b2b0f4925d7da, + 0x1a187b4875be24a2420729e016901b94ec0566c8a6936978c3f21e8d611996f7, + 0x10ed1fd44722d10bc7e44824e64978d36e68d56de2a465a0201b8e31065d5c57, + 0x2c4b6867179a949d377a9bfd3efe48456f7e70f02d859c78684a3573486dc227, + 0x15cb2c17aabecf7aa0f61655a8bb35a7afd87d0e20ac38fe21a07da7b388dfb1, + 0x4dff03c742111aab3e61f4166a733e87699c1ffa889fce179316e39f7d845ec, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x17baac874eaaa88de70cc92d5a72e00be4ec0e5e99ba7cbac2b0d98cead394a1, + 0x1dba338b4779919a2fb22ba949a050c85a73983d5ea4752cd73f664fe05f6247, + 0x1e33e7fcc41f32f90de5771d69e58f4486290c7b856becd5cd967d8e7739f719, + 0x10877ad7cf0652a2ff93977d3862e3ffb5d87d0c040f02a4f98612afafc9b604, + 0x115d3675856ff59ebcc9110defb9d6c70df6af533d4b0875d0e5eec430350595, + 0x3d74b961ba9013a874e7bcdc782f478da6097537549db7e6af4702ca749dae5, + 0x2563411e29867500a8fd18e4eba3c9a2b7992e44d263c29f7e06000f74887cdd, + 0x285501c4e0ee1b3c3e6b1b29160d6ec0f1af4dc2f36a4a1e654b7b47899738d7, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x209194489c86891b179e33103a81d969c5c75e941dc30e7228c04ed8ba68704f, + 0x2c7b2835cb79c29852926900cc168b2315aaf03a89532dd9fc162c2f7ddad845, + 0x1df181c420308c5b0bf00ee21c16f248edff686e9e835869d1022dcd4a8a635a, + 0x1ffabdffee2481d8cc1233506f708d9acabc1d758bb99c329142c866ef4c7474, + 0x8955ed55c2bec07027e4355a694a9b6ae1d9d50126563b29d8a074ea65540f5, + 0x1ab4b24a4db7c5758471846eb375163e7587791417cbc355b6ce93b64fd01da2, + 0x25a3439d2838ad459270bc633164f3a68215e11217eef1d605ecfa8b1805c609, + 0x65240d63179ae83013295a8251fbe17dd988ca5c84761ccb6a6ecb2bfbf02a6, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0xc1a91ddc1b24113bf876a2ef895c3315cd1b109cfa569b79f3155ee12b1c564, + 0x146fe8e28539ae36ae921069f4af83f675f1b68399efceb18989cab5fdd36ac9, + 0x1ba8c3cfa46425412e30790090980db139810efed7bdba7a38adf75e1a0d3641, + 0x83a58c9d889b74e66636bb8418db624726b0f1374f59eafb4d269ba1ed234d8, + 0x2b812a76ea526ce18df6032192ef033541aaf99d1c61839a0edf0336142a25f3, + 0x295f6d35fb9e57a50a5d913600538030ba8c09e021c16aa8634488cc8eeba645, + 0x1de69ba07e3b9f90c87eb67b1f64660c71befe5138061dbebb752296032542e6, + 0x24734fa363e52c64ee0162a86578ca899796e89caa1a3a3533b0965665208f59, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x2d5fb67efe93e8386fac071fe3522be1dade9dbf1d1030417a7b51e3122111f4, + 0x19522e9228feae7473e317fd7958a021a0b042a81b75da6dbf7568b857afab75, + 0x2bc3f6f0df0c7305afe83fd9ef0b708e129fbe889fce42cb695b33b290479342, + 0x783e5635eea0e623bb8c406909f0db77ad4f9302d4828b51015d6512818690e, + 0x2d61b243f02e21edaed6fd2e4969ddb95a6d0da8db17d115a9a3b0d8885bccaa, + 0x55e1a09f4bd4809a86e67f99279fc06ca89a4468df1ea25d76fe0ad36ccea12, + 0x17871eed22b0cdd2de61e55ba5c9f4e37da63ed0376420bb1ee7f077a0d7a85e, + 0x20fd0af1329bbdd70d4d835d18c915d98956c5f0a4252cfb81c1fe02fe130091, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x2e5b840ca8faeba6ec3613e22eb7b071d9633c83cef659ed96b2f6a8fcc6b262, + 0x5f5385703edf7c4a388ce585a81fe7cc92ff49e900b5601865a352c61d6b111, + 0x21e83ab5e95d369ccd30cc1c55cd5844cb1cc78ffcf0c8bc91c9c0d9937a3fab, + 0xa67b7ed9c37946306ac525597f1275b30a5d004ca50258c3d992284d90d724d, + 0x214f52f9f73a2c2d5425c9610461303d839dfe71891489c376c2ea3b5d868b27, + 0x241e0d267f7d1d899656929cdebd850f70ace216d9ac10253ab720bf40da0c7e, + 0x2229153475b7a6b282e110b10e8aed1fbc2a05a37352e954f40d85205fbd8bef, + 0xf30d0b7cd8ef10e895cdfab3faa4f4c1a61a5f4eba688634540619c84782d2d, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x265589a340c71d49e1660d3dd43d1869408176d8b9110ae8c369078de8ff7aef, + 0x1bcadf844bd1a2e7f4f464991dd651b9f15630c94977d35ebc3e85801252ac2e, + 0xc112b1c56ea288e8518cc039fe050649cf40b7ab98de8fdbc56eb7ef6bcfdde, + 0xf020b9ef75af8ddd505cd3947e11a04270be15daacbfa76fa04d9005283aa77, + 0xa426601ce9415e666acbbaf2a7cc8ef7ef7d07538d84b1a53da24c19c601688, + 0x10a1af65503614381fe2003123aee9008ab97d69739dc462e72a8be04594618e, + 0x2d792f9fe5f0ad658dddadfe3893d158012d84b3837b7415e188131595b060d2, + 0x2daa42d04e0b62fcb3869031bf382c3b9f8a98f7f5bf7421d0b63c2598f5f65e, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x144c0395157a1bfcfb5cc4187f271096784dbcdbfcb6a28c31ce8a06f908c0ac, + 0x1af8cdba0eccc83c16019622667527645e94c9ce64add4415df7f40446277a09, + 0x213c7a7ad6237e7211530c210a8d6f46a25bee433bade010591e6adf42fcd906, + 0x224f1ca24803c0119ad0e6c41a64968e064a83f5821972f2a5c9d5895da4ea42, + 0x10944d95ff5a3699efdde41ead13344937e3b1b93ba73a1531246ca4b99aad2b, + 0xcb2508b0a3395fabbeee5286ce5f1839c006ebbc09d94f475924923d8079ec2, + 0x284a14b1007ff6c5c0f8f7d8d0e4b19fe2d4a7094103912134b0f563a672acd1, + 0x2bc0bce43d55bfe1a27eed426980cf9055b0dbd42e8de516e77580b9d9a9060f, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x28e9b1884a4619b38b6f8a384368c358b7f210f3412c4481a26ae296f73c5c72, + 0x2123ca1f119a35e7d4e1b323fd2942d12ed020ccff3a7ad6b65af90457f00614, + 0x4aaffb0ba008fb9a82fc0700beaab2ce39efa895acdc280252f01e31035b8ad, + 0x1f1e16f8ec9261c82443b9b31cd908015e2c2d2314629a22639af1f37e1073d8, + 0x134928ff5ebe5e019214ef937b7f7a28248285d583613ea2bedfc66b5e2ae924, + 0x87fc99e11e63deec9d55047ac98030c57a4f09228cfa7749a3e1c7ab5f212e7, + 0x9429bcc52d6c43814df5b07fa116f8875299500a36ef791b592a64e27cca486, + 0x2267b1dfa5d26e6f0a80bc8b4c0026f0204bd4fd06c7725544d7760354e401f4, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x16fb3cdb76d21d3448c9988f428d198c8e5a640035ac2caff7aa7964b34ff1dd, + 0x80dd9d263a6698479df06bc98fc64594478028b61047ff93c425b29b092d37c, + 0x165f4f2d302a24eea5f46abeec4ab03d21e3d013865085e515bebcb2684af340, + 0xa8a3f3abf28f457c62045789fdcd302f0df1049b6ec521db2b7e72e8d9516cb, + 0x2342f103587a005c977578b12810378f9014bae831809cf7ad59ef3aed48aae9, + 0x1d1308e311e7ab846e158769c12213013eca377f396061aada6220f29eb1b7d8, + 0x2563949aeabfaa782be07dad903ebff5c913893761b75a3f8402a1e2bea5a998, + 0xcb371898d8d2e1f5bbc32dc21782704a73e415e0c9f6387157b48746bbf6ebe, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1231d40e7c6fe5bd47010ebe4f63d186369cdb6e8823aec194093a0b4cbc6845, + 0x18943696a4da551747068255f57a0437ba0ec36ad7e5c92cbed9c49a9775b97, + 0x1fa58f378160dd4af40e3b01bfe32dfd34f2cdca527973c194a53af30bc40670, + 0xc976561eeade533c5579041bcf5e8272e4af95efe3af9e5372250fdc5ad8966, + 0xcacfeeae1a8359ae9fcf831f315a4b8c576d579eae86b1b09823656231d3bff, + 0x109b0647298eaec354e4a155308192b5facfb586bc2fe63f073cd221a2106fb4, + 0x69d4744aca289d123baa0e6754c5232202dbcdfadd0ee8d14dd19a7bea39781, + 0x156195fe27df23b8184fc58a30e1a9bafcb9cc9fa9ce071163a26dccfb7c6ad0, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x20fed35942cbdc86df51a49ca26055bb25e232a42476ef06997af8911560fc12, + 0xcf20343957a55345423dd3450c2fd74039f1a82c6c02446dd64c4569d31471e, + 0x1e177139b05dbe38a56c40f919dc1f07126bad03049fc4025d77b4bc34c25ebf, + 0x144d1944a849fdc2aba8ab2a4368d57911e9614e3956d9326ca493c83050e932, + 0x162f08d305fe4f1b0a9bb1acf1223bde3405aecbf2356508841b85f1180cbc1d, + 0x21d68868502ab599c7c5f2a54d65be40ed5caec1613a98b2a98c5f8117415d97, + 0x984adb0c5263193be4027c68c6f3a6dbf7e22cf199dc4358b52968b0a248789, + 0x2883f3a940a8c10f7f347a8011b0f0d7f6e0a4a82eff568fffc7524235d1e4f3, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0xd89a340993be3d3ba07d2fa8432d412730e8ebf2bbfacbf9378c0c4d3d1e692, + 0x105e4a12836770bdbab24c85e7a63572c77556fffafc8f55a0e3f6e7383f7b02, + 0x4b09e851bb6dbbffd0780af3f99cbb707f5e8a073810b28b1b59794c8b117a8, + 0x2c8031907c10e1df2bfdd2589dd502a012a2292202e67954091ca57d21906d41, + 0x88a360cf4c5e26faddcba291dd2553906abd82fecb0ffaff4f3f544f672d703, + 0xe9e8d8ba62712e7f95840b5651f32912e84f146bbd57c566c178084cbb155f8, + 0x38fbaea7dd737c642ec414759bdeca4250d31d6011140e7e45e86c12c6f6fe2, + 0x96dea6e0d6411dd0c18e516511b03d8506c4901c52dbc2772c3d47bdbf461ab, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x2d60fb66fb133b0507b6b41ed7fbd5278c4ba9fa0679d2889da9672d21f07037, + 0x2567f444cfdbbf4d7799d5b50c8c582e1d2038a11969b3eb2b60aebafb1efc7b, + 0x2401d941b4fafe3311bc6cd9fb0bef62ad9b59e731c1ec4e6b0ca5e2c685bb2c, + 0x28747741579283853ed4e6525da70a4312769f7040db4f098eb7d9214fc8fae6, + 0x1104899fde3f530cf99500f20c4fb9d479e512cd70a15def442a4ea92bcb9743, + 0x4ddfde1b1aacb33977d4b3020db51b834f6197a18505e3ccfb37fa8a3a8764c, + 0x2bfa6913d62c8aea04ccc3e50229220efe3b9af6a568194ece56c065e3cac8aa, + 0x5cb914ec7b72436cf25adcbd0550c2db3c9c09aac565d46f96ac156fa72a90e, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x145dcc8a89ffd8fddf4e5bbe377a3b7649ab6faaddf5eb580ced3e0421b7077c, + 0x21f1b7c169a0744e49718346cbd390dbe3287f5de3897acdaaf2e4bbc1f3e80b, + 0xeeae34b5e8e48d2ba6bd062c803ced1dd1165cce5f8f0574ff7caf4e6eaf6b4, + 0x2827170c30a7f570a12f37aa0434e01a4aed9b5d37f1815029d5de89a8ff75e2, + 0xe328161a29c0376af526c8004597fbe018328d6d0c89503eaee36f59a4cee8a, + 0xd6617ff29ac941a779f907e749603cb36778fef6644b8684fa40055c8d978c6, + 0x169506e0877092fdef32109c064d251c4d6a50257ab9c032bd79801fa23094ba, + 0x8c516740479e1a852294e8cbbcbf83b4d7095b69758aaa9f1a368004dc1742a, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x281bcec4cbcefa9e18a1aca1dfeacd7f7ec527df5deb6c002f541ee4c0f2bcda, + 0x13ea58a6a82bf43f6d4c2619b87da0ce760410b68b77f694685e3f34ff47b86c, + 0x1ed604569bf581c71e4180d59a78dd48e2103006ac045566e44162656c36080b, + 0x15d25d19f8fb93c9272f10ea525e787c758f98c5bca884e6317ed21a292abf6f, + 0x1696701143abf5794f370a122fc60dafa4f0c241e8607983222bc72d1d8d1439, + 0x1471c9bea5d880676ffb53255487c1af57a0476b77eba56204a4a3780b109b50, + 0x1ec18e953909ee6e34dcbdde64fd6ae8b99817ffef4811551a27924b714cc00d, + 0x89ad915c65eb1cc1633229dd97f098a2f86e7ea44ee6d94f3fe5f08682c807f, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x7e3bb22a5d8f517b12b42d68ab707d809ce83671dd9a933313d251889a9d55, + 0x142b4285cb7ac7249975ddf59d177aeed1a94e0412002d83861ce061e3f38621, + 0x24839c6f8fade0c2ef1e248f64c4d81e324caa4ef4052916a31c5d1da484ec43, + 0xac0879ac864dc7bf40955a3f4a19cf37846fcfa9289ae59c8f8c7c174c5a57e, + 0x44832eb1eb4ac43192406deb4a37ea61e2d110468762d3a31f01f3c6c1f8208, + 0x2b3f948fb289860a26e995d14c6f8aba2089511c7ba58a310e6cbcb533f2dca2, + 0x118bfa7e2d1386301f187c1b1eda2f48c0a03de15e370b5be0f431a0b574681e, + 0x1399fb352bfce7874d22e0fcb24553b96ab59b85364c0c3c9b0135d4970c2349, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x2f25d0089300da1d286c5efcc9cf22d095af8f615b76cbee09b9ba651d737311, + 0x84eaaf7a0b07cf4992c7d05790c42cc742a7bed021c48f614b988d99f141e3d, + 0xcbb53527ad34cbd3c4d59504fa47c87eb5be078155e58ac3f1e4b3f45dd1cec, + 0x14e8e0d80d2af6efd3cc60741cbc21f6da3a42e2429322bc209097b1d22d26b4, + 0x149dc605f3ad39d4b470a132191d2739df2ad19fb71e067f6ede3f9da3172922, + 0x1533cdda4fe346f0a3e538172b8d5636d3b4b502047cb268015b2088f12b9897, + 0x1da21ab47505b1ed4358160f3cd24a01330718bb901beedde8cca37839805c62, + 0x130eee6229e6346096e121bda6bd3892aba85c363deda16ae8c8efc6ba721b18, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1d0669bb3cb83ad8702a76855510918ef300df2416cdf04a83061a915a6fbdeb, + 0x113f0f0db699267055417d499467b7502e23179a0c23787b7e8130967c95080d, + 0x28cd66f5e2046799405a9c14282070ef66fd409507971fcbac16032499bada61, + 0x230173d8146362d28c0cc9bc2c72a64afa7741b77653726017932821c1dc9502, + 0x4b23de911f3a1d3f32366c35a7f293837e1e7e8287b8abde423b2b3ab81c187, + 0xd4bcdd5ff441637f977dba6d523ef1f6178ba245cda76e429a91b0ca994db04, + 0x93bfbfaa8f3a8718603066321ce48219b55558f33e0f8645a93a41e6f4d3e2d, + 0x24e21e25f0b3d0e754bfd0e91e62b5fcd232e756ab34cbed6b4ab709dfca551a, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x146990fff0e10b8d70a4411f57f9e74a03d2ac0127b216400b82c8c644038303, + 0x1ddaefaba77bcd9c5ba0671b0a34a4cb37b7c689bdca187d90212f188ba4a87c, + 0xa7c37e8eab27e5edcfc3d6682b0267e3d9250fe470980956f5e3e5993ddaaaf, + 0x2d81f6984b67d7cc74b35aa9d673878f05d517085812190798dd24a510b8d6b5, + 0x7c46458e45e15ce1338bbe98ed3c0726664d8ae0c965bf0fc79ae31ad04a349, + 0x16dd83567c7289d8ea0e62df8620df74f3c987d5f162b6b0a24ab09837b5d2da, + 0x21819b9d78ef5e05c535a83e7d709f80f3ab5e8d733146139015ec4e34b29f1b, + 0x2340a29e4e4d4e920f39a6a32149b54307f918a2e179e6c7288cd02834c0e44c, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1ced784118aac9880c9fbbee28972453b36abc3d967b20e9b0a886af86d64305, + 0x237848c6b0c87f794b30cad5a3cebfe2c6c9173f7a258f4ea139252338a3ea5a, + 0x27c636aa956756d9ee04b355abe2fad8d703b1721fcf73b17a77751813c8abb4, + 0x1b40358386698e21d43bd3950c00f81d6ae340eff9ac0821a213f8fabd142d09, + 0x695487b95b15feda7188ab9bd8072f0edfcdaea2dfbe06596f8c037bf52145b, + 0x1df336831b6745c8f22a80c252ba12b24e2bb1e7fd3615cc96145d898dbf3220, + 0x264b7a66fcd41995c19f021b71fcb1abd59986c55377a82ad92e79a1165e58f5, + 0x208defb2122d53224aacda9868250ca3b39f78b13c9d150a14d75a886a1a42bc, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1627fe11fe92ca5b0841cd804a211151ca6c0d2706b2a17ec17a7fd224a48a13, + 0xd483b82a1ed0d6788ec73c257cacba9738eae7232e365df112b15a93459627e, + 0x24c490612a1636b43f902459851afb3cfc37d71db5e9ffd247116d5cbd34f9fc, + 0x2f2d08c8cab748b056307066141837d5cf195104459a91084768548346c8593f, + 0x42d3a4f87d782326b0c097a03de01ac1698954c8f300af708fa79a92e84790f, + 0x2b064fb3fabe9deb1593d253ecc7a12fcfae3193e8f7e16ec563876f92e4c62c, + 0x22b8060d8bd295c3a201655ef3891a481e21748554dcb7613ce6c0a532628e5a, + 0x1526148f85ce610667aa96af20059f0b02c8a9d4d463b27f0711db72a545503a, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x12d2a42d255875bd671d376e499cd79393db48c414f967d53388f60b4e180986, + 0x22d0f27c6767b64adf2eb09ed595e2f2c211a3504a8de66ac01991c81c0e2669, + 0x1fbcdf3ca6f2e0739b571248a9a994ea913375db065ed255b5eca3fde587dc91, + 0x402b7640d18feac0c700cbea0d8f527a7c3fb44a110c4d7cff21deae9a70e40, + 0x27f6c76f1b519e71c3888f1ece7cce4e0f99f231ddccc7798a31b0dee0c68206, + 0x2fe908cd208699c9d8e3b0c09f5c0fd58716d0eb50017aa7d12df08b53d963ff, + 0x1922a59ac83c1e2821afccf1610aa5fb0b3cf8eb3fe3f4957bac604c177fcffc, + 0x126fc5609db3cb254a05919034b8a7f9a0f85ec5abbde6f85068607250ea0ac0, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x28db35e17bb31e5be954d69f5bf8f6838651bb6f5c80f750b6b7017643b6c28c, + 0x26dd684288015065728c5e09454535a33e1537761d874102bd4ecd2baf40b384, + 0x1895d33a312becc17090e45df74981b4fc4b220d3aaa346c7e8485311cab159d, + 0x28bff25eb0f2a5c6d007a92a7c4d88bd9c12c5622d0ecc5a509c404048b7b5aa, + 0x130136f07c7e09acd49556f3a6bf0739c9efc0a1be738453af67b31e845c976e, + 0x259c455761e6b6420dadf3d6b64eb65493f989fb3e5698e7307a6d6075714ede, + 0x1c9ac464fb08828d02006c7529eb1e8f45eef54405ed2cfaa133bd697f618929, + 0x60abe65207efd0fda7b24719d35018f7607dd732e71c05077148f3d046dd180, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x2a41f347b5f6eebe8969667071926905a3e6e521b85a89006ec2f500ef42b000, + 0x17840fa3a180177731bce340ffeb5a3b6a68b94e3f870501e787edc5a94db63b, + 0x161ec94c3f624f928eadb0e2f2cf6b16430fba680a5a50590d89005a688d9b18, + 0x22d8be171b4571fb5b773c3a548fbb286b06e2701ce99b7630866e1bca6e2cc5, + 0x52464c9c7ad14525380aaea9b06e76f03db8edd220f90b03a9feef2fecfb978, + 0xbdee83b20d91ff9a0404d8b0593879c90f7be5a95a22c8e1b157d92c3cf4746, + 0x2d84235b4f4e04262d8f246123b8e631ad51a4e1051f41f1c89cc42b61717302, + 0x3d6b62f816bc4b464e2971cc6a7c1a585e519266c4627b1367ce7963cf93d86, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x16caff6fcc165168b3ed958d3aac8d09f98f670bdbb847ec0e9083a022d27692, + 0x2cd4beaac59c5306bf76ef7e06c81adff6de4bee730cd4676e966db45fc067d4, + 0x1b11abd8ee736830ffeda3782fc7a82623ecc5afa92c0ec9eeec58177ce8608b, + 0x2c80d3430e64bf850c9e10a22f6b781fd513af20d4705435bc870ec8cbe93cee, + 0x2d2f20d2ec0e52eec9fc2d0e49fef7a2454aa77a5055ebfc4d4449e8f83bd015, + 0x1e51c55a8d7a04be4edae4f7dfe6137e96370ee2a4ed459ec524b19de646e0b0, + 0x13d4327afa809e26c8f97e36ebb5be1d3992ec72d459760bbd25659790738f43, + 0x176222f47d8c0ff9e8a967920376793dbdb9dcc3a79b44fd25e1f43a755e6b81, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x5193cbcb59713c78012ede93db828e69336a197fdd30c6b7d03b1cfcdd6adfc, + 0x2bbe660fd34f6ac3f2545cba9f717d2eae9f8c60242851657f8661504a457c69, + 0x1fe59c3d2724f4158483406e6cd62aafa121c451f13e48ab3c857b2293333c3b, + 0x249c893f9de208601de45c9769ebad071eb864524003add0bde31fef7f4f91a7, + 0xc20c4a12bbd772e0dbb929b69b24751da26b0fa8639005ff786a25ac1a5fe96, + 0x1a0750fb1d27bcb326ceb3a3065a487cf7d513d8954f31dfb174fc5ed95ce55b, + 0x16d40c0ba7a7aa232eea997d45ec4f0567fb6814677b262aadfaefc91d409cad, + 0x25ccf860fc7237cc8721aaf1c717190db40ccd65bf65d108b16f851cebca736, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1a25b1a6b81926e968ffffa63979c900c1e82452c986ae599ef991ba214e6f67, + 0x2b344bbb50400ea76151bd0b68c3139955f101c701d32befddcfec1ca72df25e, + 0x1044e69af594eca5f9ca7ee28cc38d161d01037fe223412e2f10838bb9ffd1e5, + 0x1145f6f783af7d1e0ee3388f107ccc27609bb8314bce27b03dbf8d02843ab2c7, + 0x20ed7009a6093b160020318b0bbfbc9a9d14de64a3aa25936ddff0ffe3a3bc4b, + 0x24cc5ed4ff9d84fce95c1508e1c7852fe60a6def592f423bb79c229327be7627, + 0x1fc31b0e67cdf9efa9c0c312afe54b5158ada1511719c76953587b772f1c830c, + 0x1faf997032cbbadc0c6d30fc804d068faeccbdf7cc90155395b739e017081259, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1197c402e5f0a49c8f9b9a41af6fbcd013cf9adfe864613c1bdecb6201d9651e, + 0x3025d698462f4cf23c7d4d8a3eec4aa8e1a2184a43020122db653afc6d0deb4, + 0xecd1d402fafa3602052ab586f804ec15f1910542f35c608eb7a247d5a960cc9, + 0x30021619d0c0b402b429d4e962cc3c2a2d00c62c131144f7b3d7f8acae6975b8, + 0x2bcd05e889bf0fcd9dc4f5faf21c506cde15c316aaca47724e71bad0cf34b27d, + 0x1b890b4097a781900b40d9c1ffa06a5e8cd05ae8fe52e040db0e7085ca46b460, + 0x8081b53a974ee264310b279468093218cd5e5edec1b7da6b21ab35622242e48, + 0xe6632e8ec976cefca5b164e6c07ab40ac611fc723e8d8a14899a4cf4be3b2a, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x63a3f8dea9d024a6f6a851ecdb21bffbfe259ac17f6793c91e73823a82f4f76, + 0x23b2c6ec9af5d0188e75baab9990f663f8e929bcdac96cc09c4c0626274692a0, + 0x9a41c06730fe53d395c602113f1607fbe01425190d50d56e9f215658ad128cc, + 0x16571048fa92024a9345a0ffecc159e76602455a71bc7e9c9c01dc50d8aa1d6c, + 0x2a5a7e76dc76ad78878b3d3b4c74995e2ce77bc126e4d17db507161b049eadf6, + 0xa23738e129c11b285d81c1c01e3f87989c87600727e7593af45607f98fb18eb, + 0x2ce8d6eeaf4d6c7ca0922c4738e81dfba227e98fc3ed24e7696ebe9c2732a1ff, + 0x22e3bfb13d10368cee5175f5e2a2cc205d28021caf5fb2898de2389123178a5, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x66c43e71903c5b74ee8e90ec30a1d8cbff7d8c85d3c7b995a976036eef8c4b7, + 0x2fe5dfcdc9335f0c0dd3d08f4e783a5ff6c2e6fb7e2928840821e6da8d91570a, + 0x9accce03257f967a24c11ce718f21167d9d71a1cb60a4f50d0228418428300, + 0x224e2a85316b67bf2dc64549c505721a7858438a00e793fe76b961bfbfb67291, + 0x29f1447b45128f5c1cdb12a334509e0a991c0d2c9360a5cfc28af420c2ffffe0, + 0x1301ae5665bd3e87cb647f566ebcf2f2eb5bec4b257a77061a15dcc7b8b34abf, + 0xfc6599ea957e02f69b1ca585c7135425a6825867d0cdd2b06019f3c9398ba4f, + 0x22e91a30a945b960bd87d7b0d8290dcd5f5b1caa339c41aef323be1ba9c724e, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1c7c359c26eab655aa469e0f9a8cc3d8c7a8149ba9e9baeca9233b7edddd1d24, + 0x1ddf651a5d396b680828eaea1f252625db1988504765eb4aeae3274a19cef175, + 0xab486e3e737f40898d3fdd6bab09213c0056c2e090a90acc754574739159385, + 0xd0b817b8995913e3ddd08b576951bd47b45f536739fc9cc782e769fd17e0028, + 0x13471abd3a25160947928376fcd79dd5aa58672f8382745e3a040c2acb464974, + 0x7bff672e50ff1f20296b1838e5270229a7477110d9a7fb56580371ee4ba38c4, + 0x23f91ef1b8182e80c8a0f54c3a35ad51cb9a3bb61b07b3e34386f16f7f4b32bd, + 0x16c390b3fdf09c6c42e50b66557532cc5998cb5a8c15446d31813e7b70607ec5, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1976d21aee74f79c60d44e46029a5b04fc03ebfb9bdb09a0b5dbb89f677cd296, + 0x56e7cdc08bff8d8bc12dc72af3c84858dde68919cd991bdf513656eb0a0cfb2, + 0x22b7992d7c0ed349aab7531de2f12da4fdcf961215ba06f2b7577f5d42bfa85e, + 0x10f97fcc757a0d6d0d4e2d585ea5968b3faba9d6458d16bd366d081ab65ae95a, + 0x21bc046c3d727baf6e65b568653a398aeed8c95b73567feca3884a051ac001a2, + 0xe757fd0b77219a771723e071c9896062eedbb3da05cda25d39ead3cdf738491, + 0x1ed536c497ff36f612f326f3d03e97d30abf91605ba686af36ea04c19cce4f4a, + 0x2491340ecfed3f98ed6fd566034240e64a08e5a39a468c78b31e4734a679bd67, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x144b73c6f361a4c232674a08616b36c33f7bd667dcaeec35530a2e8fcb66103f, + 0xb244143540a248840ba5fa161cbfb2fb2dab97332073042cbf8a932144d27b, + 0xb5bedd8122560caf5a9dbd69f82f7439543bbad397d7cfb4ef7782f64ea4e12, + 0x1386c7e88c5c0bf880b28eb2735e221c2a88e7f871ca5d720c99b4287c528a67, + 0x277164cdc5187613b0a6d6450b56cabfc828f20dd7d07611edccbfc3d381c9ba, + 0x16bc97ee1ac4b1cea8f96e731dfa610212aec4a193015b94b0f2a1657d41f13, + 0xb341e4361f31734af9951c20a6aace08a3dc80f57379add9693b56b047b3480, + 0x263a060ba49fe4862df997994261e665c0406642c669c32dee4ae7a153fe1dbf, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0xdf811b70cab32c0ad0aacb576fd95b23424d3686d79e36feeff21d1aa047eb8, + 0xafae979e41c0cf7d662b4cf09e93dca70c0945c6759c4f8d2c935a6084eced, + 0x21ae12f1dbb152c33213efe9cdb6044574b3df8236be92341131435152115e5f, + 0x272fd8955a7524a09f77c28ce89d58caf7d883f9e4503ad6a37c0eab6bcbe468, + 0x3712cabfab0f6f3d23ac7beb815226883f409d60d798242c6d5e9dae8178fa6, + 0x15bc41d746b14885ba93dc7d00594ea2f174b3b3dbc1acd774335405c18b154f, + 0x9dded6d75c33754be1c1ebd2dbed077c1f1cb80938f0798bb2e25b054a52962, + 0x1d9fd7f273e141e48d7ae825a6d7758e351d80c4ed50139a659d52edff60d227, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0xca2cbfed626671d6e0f2e3d1b6b2ebb5e9048c2f1273223c34fe599429e1f2, + 0x1e9bb2efac004014858166710da5d764ab36b80e4e97500d784fe5cc2326fca1, + 0x23f8af81b77d2f06d566eb0d9096c4b267f498f92bac69d622dbed85bc8a8ada, + 0x11546811642965c71b3865d830809b7f402e02d1980c3219c4bbe48e8bd37811, + 0x2b872c434320ac521ac1e14867c05d88692de6ee063f402c28cba02adeaf9c51, + 0x1f80e8d09a04ffb20613cd83ea35fc1593f9a5d8db6c846d80dca53cd4ba5a94, + 0x117c4e17071565b51a2b97908f375ca0194dd595e9e873e8c0a158b59684ac70, + 0x14da94b9be3adb3c5f7cd04dd5c58f63e74245d5a1e6fce5de3d093d476f08a4, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x2ac96264771dd5b1762078d5132db23febb0d2edb19c7b24848d4b654e9ce670, + 0x5a23e8be5fe8e01b11d0c7a1dc85c909602604b15d620c7a51e2b017dd63830, + 0x172623676d3d38b2c68ba2b30c12c5818d874d83a98478cbec0b1d27f5dd7ece, + 0xb632ee1e8730d509691580805b890371ca2d51bce083faed1615a845481de7b, + 0x2a220ef566e4d54373e1a095231c10905a2f5e72edd2b6259b46ea45749203ce, + 0x2ebf4340ee05460d8298d52260b4ebae389357bd857cb638c41b2708cc333dc8, + 0x27e873b1750916366d2e7906ffe4f42e6dd7545bf534adc73a02b7410f7f8275, + 0x1ee70cb51ad6da4513ce42ff200e46dfa39992a90447f1004765711f3f5ad52e, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1021282efc84669e4a98956e30ad0043c6c19dfeb98d6d14bf027bcfa555f8af, + 0x1f1c2e424bdeadd277162d70faeb398d661225414a975a59be613e47274b73c3, + 0x1a71e415abc5ccd5bf268130070e89b1e61981cd54f6e5864b8cc4e1d50bb21b, + 0x29799820e28bb9c0a7bd0a2c6d6105e5c91f8f88b2823da7c57825067214dbb6, + 0x17377cb0195b95b473606c81a6bd5c807b22870afa6cb230a1048e9515e31db, + 0x2f73cf9f22e0431d5e7bbd907ebeb8553b4117ff1fc50d09fc7b75935ef41251, + 0x19cd57e77a99328260bd31fb993e7bb3fd27fdc21b2187fe3a4bac0ad664719c, + 0xd5ff1b6b5f33d6d568d9197d0df40d07abede20ae3a94a0292c01c304012713, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x26c7615e04be2355af0773fc0e025f86baab5f59d834ba005e099d95331d61b0, + 0x170ea6732c6d1b2ce3279f2d4990926fb8d279e4864d966ce6ee6c319739c2f5, + 0x2f870269a506f351fb0b9a9d677bad1bbc5e6ab40ff0afc1772f02ba395fdc8c, + 0x11986790a1cc239c92bd4b8d8a1b9baa76e1e49f847f16ede5f6398aa83e97c2, + 0x2510c2e5a39cd6c243ff590621941b221d2a2c5a79ed6e5bb90eb1008219239f, + 0x1ba5c05a828609b93a7e151338699af0b8b0aa96d3d5cc9e7d3785333fa03dcd, + 0x8648c03bd03b5f4dc3868ac1c47363d90010b9cb19933554fc7586b97b5fbda, + 0x69d0c72c5880618f66ad58d65f09e5fc488697c71d92135be291f55d496cb7, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x91acb34aa0d03afd0ea47c1d56965a9051b05eebff9af71e07c4554184f462a, + 0x2b3f75e8ca7118776d9676fe058565eb99b6e99bd23505a8fefc927e17cf5336, + 0x16d9ae82c0073fe1dcf35384c0dce87494b2400f9027ce1e64ee440a439fcbaa, + 0x2a11becd9333eb48f3027ad8f3c24fa1a0ea671a1020278ad84c863c322e8057, + 0xd569be295d5e44ccffd9d3ba84aaf6a0c178e8639689aa6c57214f00a6a9d90, + 0xa66025e45040fd45eb136eccc63e2d7fd237aae9b62e2330aaaa0bb44dbef48, + 0x2cda68234c7e22d8fd725d952d3c529b6997b68dc02065f6a047b6cabdd29e42, + 0x1f9ab3e8029afc72f56af02ced5a6b145ebc81444ed12e82c7ea547e9ad23650, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x18a2e32bb69277e0f5e82c2a60a008b3db5caf3c53a669215c0b3493b73c7138, + 0x224c4f2e98b4fa10d4ddc83f26ada461e5b4f412f94a1eb153be707470746fbf, + 0x1b2a8787e954d981add1b123a6f6c100a609e8135c0781ac9a1e7e326c4b0f4e, + 0x2da288c34f32d86d5dfc0b2dc9891091d396d36de9f70589b7beee769a058622, + 0x2e24d351b0d0e94f3e0f83eb60f2d476b8b64dcb47674290e87b27eda7f20180, + 0x14af016f9da2f982e82aec1ff6ee809445db2c6d85382f959508a31830dce9d7, + 0x91aeac9bde9ce64a54cbad523032180c2135b51ec4547ebcda08824bc9cdf9e, + 0xcaa07eac62d9f07c17f63f749b7047eaa1adda97f5716d76f23affd6d845dd3, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x554f06e31b164d9d7ada3e01c4bbf452fdccac121ca787b08ad50dd2928ed42, + 0xbf8f8d7702b1e8830bf126dc420158a624359067a0f6385068390b01d176601, + 0x1604d181baf488dce4f99bf63c065ed934ee29f0649af4dcc9ac2a2887e8690c, + 0xac951e2944f7532d4ddce72d31c8e91c0795cdfab82df338f172dd9bffdae43, + 0x24d53d9f665348c12bdc3425c2b83e24fbea3b66b0c9d119146ed5d5a1d1e9f3, + 0x1ee01a89a7ebc6b8e93f2ac2e60b9909e3e3d855852e0e113a72a118f56e2da3, + 0x2b78c3171a3c8ec6231bb7c208e5b7c2c90a85956a7f2a1f763cb6c883059938, + 0x7ca1e306d90787461696fc7e4a3938712312494329be76c8e2b402cc0d617e1, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1febc25f88aab92179ac3cefeea663f51562f6fc631bb236c04f5ef85b464784, + 0x1b1b065eb60dbd39a34da94414fdfa4415933a6bdba5c2de470ad8ccef1b28fd, + 0x60fcebd24cc08503ac4f80c0ffb87d0898f34bdce41420e84d941b5f7d352a, + 0x2a7b16d282447357a66d83fc5aafab7d3edbcb3f01105f193954c5ed496ae165, + 0xac39f59e76b9e296ef53921ae0436ab01217493f948bf6eca12b11ce46678b5, + 0x90b38aaae1df1873784a8966f1f62b68bbd93d34b0f4c637e208f9aeaedfc26, + 0x1ba601baf813cb2d40ed5674747b9e3d5760143501e0f21e31a7dd44b7135eb8, + 0x17695ec6204f10059ae5ca72c1332bc882cb7b4e161accd1ba9ef760b7365d5d, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0xab5930a06bb6b9a78e664dc5308da0b64d1e09e6c69fa583bc737245c5a469, + 0x16ab9a5de48bf089fba600dc70d2790ce0e8f79c1430566802f97fee43bc4e2b, + 0x75df7d5cdb7ccc175462dfad73927bf5a5f465e15ad267930c5ec846f42ae5e, + 0x23e4a7be74d0f0930279585aeaa432b5c28a4a2b21e3990aa45b5092f08d48ed, + 0x298795a8af97b8b3d378279e60b276b95227e66d74e2dc66cffa1c495af98c25, + 0x133d1455b6ea278f4acd91c65906bf75f2c90e41cdbefc2721b1e96adc5eeda7, + 0x24b722af1967cddac6a1745b71aca7bac72d436ed464e2b8ce55aa2ad5ff3502, + 0x276cb6e59cf4a06ff6775a537a4b04c6b42780c9c98a51ff634804b23acfac2b, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1044b0ed6b39096c0ff4d292c18632c909d1519149139b1cd192de01485dce3e, + 0x2dc918abffffceeb34cb17a8cb532f889d7dea98e9ad2686ef8e30936ecdb03a, + 0x2a5030937ec5690d090ad8b3d897541ecd187d2ee126fac5be6a280fbd4aa465, + 0x16961c105f85925010e0fdd445ee840dbd3370aab933ccfcb6e4b24a8826037, + 0x17262da0f8e41b6c42707dfbabec1d9f79ecdfdd25a32c2a640d3c5a4a3e8770, + 0x59935903a135cffc7c5e8cb06de7a0adbc6fe4f66b07a74172eb65951c6a345, + 0x1ea2228bc5f09dadfabf025e3d19db3cca4e448e60f2973605d2559a27b3bff8, + 0x1e35c4737f19de2debd3760ddc81e1f5857a01c42c86f4e264ba323f4165d5ed, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x5958875cfb17091058a7e29cbfe20d0d242ecdfbd3635279cd1e0c3a1144dd8, + 0x19fd165b2741329dc18d2a5b03d7b58eb3dcdf2c2b0870731a924387139033dd, + 0x11cea375386801203c61577504cdc68493716d023d116356def9ad9825be5887, + 0x2e911408231ad83ab40e44e28ee0b017a82f7e080a0d4bc1b42c52e9205ed13a, + 0x9d3f08c1d2cd4de393b703a7dd94df0540c91b59b288df6c1ad8ba0e51f179a, + 0xd8bac92c12807a3fb4b20ec11e083a88b953070c08c1ae9be28c80cddb29a50, + 0x22829b774491c0e3add8e7d2de8096cb55a1009ae9ce983b80c14972bc68b84e, + 0x2aec91a87a1731f6b2f534955aef3d09ca7e2ee2dbdb5e9a0d15db232557c621, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x281ec503552e6778fbafd7270951cfef153fcfeca0517410e495c62b81655f9e, + 0x98a3183f2ee18973943856c28e0dceb4392af147ca8b528ebee97577178bcf1, + 0x794a78e51b51af07808b643ed37bad31e6d6c68e5ef3171dcd06de598c6c29e, + 0x214a19348cd7cc8b1f985287e637e7987a3bedfa233f98dae9774daaea42fce9, + 0x130acc584954a9048597bda6395bd25ba02fce56102928cea7d5a6f520683ca4, + 0x1d09e1227434a4bcf72d8e91f5419ffd6da212a1d1ccb2a51b03e80aa258243, + 0x127397f2b156ba00d83847f03dd242007faf326271d0e2cd4f6dc84c961b19a6, + 0x971a3d373d35db8f181e0d7b26c33cff17e533e8f560d844694f853e7197e47, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x241c8bcc463ac96e3c1cc240ce83c44edcf9c781e258a2dad09d1976d9dc6dd6, + 0x1a0b489baf0a182599f458897aa340e57986dcde7bfd34738851092a75ae6e0e, + 0x102c886ce6381276fe52d15c51dbd571e94904a8ec4d4445d457d596442e443e, + 0x289f8a46d6792691caac00cd43dde74940f122c0e5ac202588349c2eee473f6b, + 0x2ed1721123242c33f23f809f6e431511594c10b0533afde3304ba62afc55f5ec, + 0xc730a9beb7b64f090a39929af4901900e772b0f817098adace287cc20dd9e84, + 0x480eb2f48521f46f5049f8d9d682d6f4060ff6c4190b2a22c40c27d0754b912, + 0x221d30bbccbb39bc23ffe2c8571a8cd1763cd48de6dfe21d7d8f2805db1e5066, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x22eff0bad54cf4f8fda9e07bfdae36713527756cff255aa481b730bd286cc90, + 0x220c6f4c23ec9272eafb522055494a1af4de6fe7456b39c5db851e1299b7a86e, + 0x18d66b43fd01a9cc88dd14b1b5d6c0d23b29ac28775ff60d3ccf36039de0963a, + 0x1f62901537c1c56f671fabbb4fc31fa743f3236c26f9f5c98ecbf332eda817df, + 0x130beece629451200a3de22eab4c45bb592aad667f9fc6729842971d4a802fcc, + 0x24411acb2c9c481c59adc41bc54fdb0fac658ed6e0b3636cdadfd12c386f8c98, + 0x2370059923938a3552819155a8b3816fb90cbde45871f6c122c190a27e7fdc43, + 0x217ccb823582bf7edbf4a6a64692e37928f2b02d79b43775abf304500dd2da46, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0xe2be5d3f9ee73557a1c421fe42cb29bdf8f4a58679a61496bf8a5a4f9bba3be, + 0x27639d7e461732f3baeb172103de2bc4a26708623919783fe54774153bdb59bf, + 0x159c005b660c7fb3551cbf624aadcec047ce72625673c866c5fb289f8c865fff, + 0x130d38734b549e833b50c550a90580c53248bb96731c0921ad6373316dfdaa8c, + 0x13ee4afd14334602b6791a7b8f49c4f4979d485b8d1b1119cb4a2a7c31a74f39, + 0x2d2647b74c63579e81a6270afc73e636e588996745ac0499dbeb6a7cf80a889e, + 0x1b884086fa3c4173be0fd5cb1c866c87e0f9ae4c3d9f1e3df630cb4c2fa59af5, + 0x2289328b5db5b2b2d00e76ff78815696e77eb19acdcb6c84279e65fcce29d15d, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0xbbd293300c70f612c8f5b7bcc6c4106246d2f713df02774a29742c31efeb4ae, + 0x278e2893fbb5d590fe623652e50086d58ab18d3015a59d6a61602b409252ec2b, + 0x2fcc41f73df0c835b0b514cbdd469af1e2b494f05269d15a6343af34668b18c9, + 0x378097f57525674b961d42a2f57a937c1fecaba4c673bdce345050d981b8fe0, + 0x15a6727e6f181a5da795ce173f1889e07f12892e13f889c4f8f6c71725ab9f62, + 0x1edc3a58673d364ba5906c3b39ff7f654c5d42f4ff94e6e75d2c500842846477, + 0x1aeddbe743c87ec10fe447a5d08ab5c73836eb214ef95a08ce91131b8e1a7f8, + 0x286e544456f114ce609d6b805b31064ab65482585699c91b9b3e83a75ca386b3, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x1477e88daf9348ea5f95cb08426f4285c654a897a2c4895333052fe2cdba34cb, + 0xc9918bbd089bc26c937ae2e0a92a1d8c87cf4480b055e43831a5e6a3acbe5b5, + 0x283c24f7bedf789f31465682347ff86b4c0fbb7f9aacbe5630021b41532f7931, + 0xb1e5f0ca68bafaf026438a4682a55c1df5d387c4c5f3e111ead3163eb5b2754, + 0x2d9da1df21f14ce401ff84b99fba07311a9da3cd7efc200695ab55b1233b9043, + 0xbb61e50ddc0821f0c03b3bed9476f580a02157b6a69a17f2afb0eff5f1e2a37, + 0x21dc1358f62ff3dc24cfc896fdfbae88b0074323dfc7b36d680edba89c470e4b, + 0xdc75868b6f7e6ec9c26221637eb789b9e4c2b892ce81b527c7da05627ec2dc8, + 0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1, + 0x8606e28acd8b2ee4c23a757886d7e99e407d177a58fb31b410ead7fbe1ef272, + 0x2f70d379513ce458231a8ee6b3029bcbbb4860ef48c104ddcffe65603d81592d, + 0x15315ba38b9e4c7a64a0844985e7b45db39eaec4c63b490cecfb19f02e102669, + 0x2a1529e4b1ca0cee97cde58af1536c4823f7e558bdc13f774e4ef3ec8454675b, + 0x2a70b9f1d4bbccdbc03e17c1d1dcdb02052903dc6609ea6969f661b2eb74c839, + 0x2f69a7198e1fbcc7dea43265306a37ed55b91bff652ad69aa4fa8478970d401d, + 0xc3f050a6bf5af151981e55e3e1a29a13c3ffa4550bd2514f1afd6c5f721f830, + 0x2a20e3a4a0e57d92f97c9d6186c6c3ea7c5e55c20146259be2f78c2ccc2e3595 + ]; + } else { + assert(0); + return [0]; + } +} + +template Sigma() { + signal input in; + signal output out; + + signal in2; + signal in4; + + in2 <== in*in; + in4 <== in2*in2; + + out <== in4*in; +} + +template Ark(t, C, r) { + signal input in[t]; + signal output out[t]; + + for (var i=0; i0) { + ark[0].in[j] <== inputs[j-1]; + } else { + ark[0].in[j] <== initialState; + } + } + + for (var r = 0; r < nRoundsF\2-1; r++) { + for (var j=0; j>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @cb : !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.member @cb$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_5]], @params = %[[VAL_4]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_3]][@a] = %[[VAL_0]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_7]], %[[VAL_8]] : index +// CHECK-NEXT: pod.write %[[VAL_6]][@count] = %[[VAL_9]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_9]], %[[VAL_10]] : index +// CHECK-NEXT: scf.if %[[VAL_11]] { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_13]], %[[VAL_14]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_6]][@comp] = %[[VAL_15]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: pod.write %[[VAL_3]][@b] = %[[VAL_1]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_16]], %[[VAL_17]] : index +// CHECK-NEXT: pod.write %[[VAL_6]][@count] = %[[VAL_18]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_18]], %[[VAL_19]] : index +// CHECK-NEXT: scf.if %[[VAL_20]] { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_22]], %[[VAL_23]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_6]][@comp] = %[[VAL_24]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@c] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@x] = %[[VAL_26]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_28]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@cb$inputs] = %[[VAL_3]] : <@A::@A<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@cb] = %[[VAL_29]] : <@A::@A<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_32:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@c] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@x] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@cb] : <@A::@A<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@cb$inputs] : <@A::@A<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_36]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_39]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_36]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_40]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_35]][@c] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_34]], %[[VAL_41]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 5 : <"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_34]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_36]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_36]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_35]], %[[VAL_44]], %[[VAL_45]]) : (!struct.type<@B::@B<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_46]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_48]][@c] = %[[VAL_49]] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_48]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_50:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_51:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_52:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@c] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_51]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_53]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/misc/signal_index2.circom b/circom/tests/misc/signal_index2.circom new file mode 100644 index 000000000..0831d8981 --- /dev/null +++ b/circom/tests/misc/signal_index2.circom @@ -0,0 +1,122 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input a; + signal input b; + signal output c; + + c <== a * b; +} + +template A() { + signal input a[2]; + signal input b[2]; + signal output c[4]; + signal x; + + for (var i = 0; i < 4; i++) { + if (i % 2 == 0) { + c[i] <== a[i \ 2]; + } else { + c[i] <== b[i \ 2]; + } + } + + x <== c[0]; +} + +component main {public [a, b]} = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @c : !array.type<4 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">> {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">> {llzk.pub}) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_7]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_11]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_13]] { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_9]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_16]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_9]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_21]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_23]]] = %[[VAL_22]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_27]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@x] = %[[VAL_28]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@c] = %[[VAL_3]] : <@A::@A<[]>>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_30:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">> {llzk.pub}, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type<"bn128">> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_29]][@c] : <@A::@A<[]>>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_29]][@x] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_36:[0-9a-zA-Z_\.]+]] = %[[VAL_34]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_36]], %[[VAL_37]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_38]]) %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_39]], %[[VAL_40]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_41]], %[[VAL_42]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_43]] { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_39]], %[[VAL_44]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_30]]{{\[}}%[[VAL_46]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_32]]{{\[}}%[[VAL_48]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_49]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_39]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_52]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_32]]{{\[}}%[[VAL_54]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_55]], %[[VAL_53]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_39]], %[[VAL_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_57]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_58]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_32]]{{\[}}%[[VAL_59]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_60]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/misc/unreachable_code_crash.circom b/circom/tests/misc/unreachable_code_crash.circom new file mode 100644 index 000000000..3713f9573 --- /dev/null +++ b/circom/tests/misc/unreachable_code_crash.circom @@ -0,0 +1,233 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.2; + +template OR() { + signal input a; +} + +template InvalidArgIndex(n, k) { + component has_prev_non_zero[k * n]; + for (var i = k - 1; i >= 0; i--) { + for (var j = n - 1; j >= 0; j--) { + has_prev_non_zero[n * i + j] = OR(); + if (i == k - 1 && j == n - 1) { + has_prev_non_zero[n * i + j].a <-- 99; + } else { + has_prev_non_zero[n * i + j].a <-- 33; + } + } + } +} + +component main = InvalidArgIndex(3, 2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@InvalidArgIndex::@InvalidArgIndex<[3, 2]>>} { +// CHECK-NEXT: poly.template @InvalidArgIndex { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.param @k +// CHECK-NEXT: poly.expr @"k_Mul_n@338" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @InvalidArgIndex { +// CHECK-NEXT: struct.member @has_prev_non_zero : !array.type<@"k_Mul_n@338" x !struct.type<@OR::@OR<[]>>> +// CHECK-NEXT: struct.member @has_prev_non_zero$inputs : !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @"k_Mul_n@338" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.new : <@"k_Mul_n@338" x !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.new : <@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_4]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_8]], %[[VAL_13:[0-9a-zA-Z_\.]+]] = %[[VAL_10]]) : (!array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128">) -> (!array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_13]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_15]]) %[[VAL_12]], %[[VAL_13]] : !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_6]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_16]], %[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128">) -> (!array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_22]], %[[VAL_23]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_24]]) %[[VAL_21]], %[[VAL_22]] : !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_28]], @params = %[[VAL_27]] } : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_30]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_7]]{{\[}}%[[VAL_32]]] = %[[VAL_29]] : <@"k_Mul_n@338" x !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_4]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_17]], %[[VAL_34]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_6]], %[[VAL_36]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_26]], %[[VAL_37]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_35]], %[[VAL_38]] : i1, i1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_39]] -> (!array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 99 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_42]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_25]]{{\[}}%[[VAL_44]]] : <@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_45]][@a] = %[[VAL_41]] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_46]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_25]]{{\[}}%[[VAL_48]]] = %[[VAL_45]] : <@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_49]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_50]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_7]]{{\[}}%[[VAL_51]]] : <@"k_Mul_n@338" x !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_52]][@count] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_53]], %[[VAL_54]] : index +// CHECK-NEXT: pod.write %[[VAL_52]][@count] = %[[VAL_55]] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_55]], %[[VAL_56]] : index +// CHECK-NEXT: scf.if %[[VAL_57]] { +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_52]][@params] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = function.call @OR::@OR::@compute(%[[VAL_59]]) : (!felt.type<"bn128">) -> !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: pod.write %[[VAL_52]][@comp] = %[[VAL_60]] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_61]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_7]]{{\[}}%[[VAL_63]]] = %[[VAL_52]] : <@"k_Mul_n@338" x !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_25]] : !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 33 : <"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_65]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_66]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_25]]{{\[}}%[[VAL_67]]] : <@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_68]][@a] = %[[VAL_64]] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_69]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_25]]{{\[}}%[[VAL_71]]] = %[[VAL_68]] : <@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_72]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_73]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_7]]{{\[}}%[[VAL_74]]] : <@"k_Mul_n@338" x !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_75]][@count] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_76]], %[[VAL_77]] : index +// CHECK-NEXT: pod.write %[[VAL_75]][@count] = %[[VAL_78]] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_78]], %[[VAL_79]] : index +// CHECK-NEXT: scf.if %[[VAL_80]] { +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_75]][@params] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_68]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = function.call @OR::@OR::@compute(%[[VAL_82]]) : (!felt.type<"bn128">) -> !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: pod.write %[[VAL_75]][@comp] = %[[VAL_83]] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_84]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_85]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_7]]{{\[}}%[[VAL_86]]] = %[[VAL_75]] : <@"k_Mul_n@338" x !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_25]] : !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_87]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_40]], %[[VAL_88]] : !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_17]], %[[VAL_89]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_20]]#0, %[[VAL_90]] : !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_3]][@has_prev_non_zero$inputs] = %[[VAL_11]]#0 : <@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>>, !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = array.new : <@"k_Mul_n@338" x !struct.type<@OR::@OR<[]>>> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = poly.read_const @"k_Mul_n@338" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_92]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_96:[0-9a-zA-Z_\.]+]] = %[[VAL_94]] to %[[VAL_93]] step %[[VAL_95]] { +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_7]]{{\[}}%[[VAL_96]]] : <@"k_Mul_n@338" x !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_97]][@comp] : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: array.write %[[VAL_91]]{{\[}}%[[VAL_96]]] = %[[VAL_98]] : <@"k_Mul_n@338" x !struct.type<@OR::@OR<[]>>>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_3]][@has_prev_non_zero] = %[[VAL_91]] : <@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>>, !array.type<@"k_Mul_n@338" x !struct.type<@OR::@OR<[]>>> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_99:[0-9a-zA-Z_\.]+]]: !struct.type<@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = poly.read_const @"k_Mul_n@338" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_99]][@has_prev_non_zero] : <@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>>, !array.type<@"k_Mul_n@338" x !struct.type<@OR::@OR<[]>>> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_99]][@has_prev_non_zero$inputs] : <@InvalidArgIndex::@InvalidArgIndex<[@n, @k]>>, !array.type<@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_100]], %[[VAL_105]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_108:[0-9a-zA-Z_\.]+]] = %[[VAL_106]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_108]], %[[VAL_109]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_110]]) %[[VAL_108]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_111:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_102]], %[[VAL_112]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_115:[0-9a-zA-Z_\.]+]] = %[[VAL_113]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_115]], %[[VAL_116]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_117]]) %[[VAL_115]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_118:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@OR::@OR<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_100]], %[[VAL_121]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_111]], %[[VAL_122]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_102]], %[[VAL_124]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_118]], %[[VAL_125]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_123]], %[[VAL_126]] : i1, i1 +// CHECK-NEXT: scf.if %[[VAL_127]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_118]], %[[VAL_128]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_129]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_111]], %[[VAL_130]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_131]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = poly.read_const @"k_Mul_n@338" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_132]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_136:[0-9a-zA-Z_\.]+]] = %[[VAL_134]] to %[[VAL_133]] step %[[VAL_135]] { +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_103]]{{\[}}%[[VAL_136]]] : <@"k_Mul_n@338" x !struct.type<@OR::@OR<[]>>>, !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_104]]{{\[}}%[[VAL_136]]] : <@"k_Mul_n@338" x !pod.type<[@a: !felt.type<"bn128">]>>, !pod.type<[@a: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_138]][@a] : <[@a: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @OR::@OR::@constrain(%[[VAL_137]], %[[VAL_139]]) : (!struct.type<@OR::@OR<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @OR { +// CHECK-NEXT: struct.def @OR { +// CHECK-NEXT: function.def @compute(%[[VAL_140:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@OR::@OR<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = struct.new : <@OR::@OR<[]>> +// CHECK-NEXT: function.return %[[VAL_141]] : !struct.type<@OR::@OR<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_142:[0-9a-zA-Z_\.]+]]: !struct.type<@OR::@OR<[]>>, %[[VAL_143:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith1.circom b/circom/tests/operators/arith1.circom new file mode 100644 index 000000000..f1f7e0ebe --- /dev/null +++ b/circom/tests/operators/arith1.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Arith1() { + signal input a; + signal input b; + signal input c; + signal output x; + x <== a + b + 10; +} + +component main = Arith1(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Arith1::@Arith1<[]>>} { +// CHECK-NEXT: poly.template @Arith1 { +// CHECK-NEXT: struct.def @Arith1 { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Arith1::@Arith1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Arith1::@Arith1<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_4]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@x] = %[[VAL_6]] : <@Arith1::@Arith1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Arith1::@Arith1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@Arith1::@Arith1<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_7]][@x] : <@Arith1::@Arith1<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith2.circom b/circom/tests/operators/arith2.circom new file mode 100644 index 000000000..86c2d8791 --- /dev/null +++ b/circom/tests/operators/arith2.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Arith2() { + signal input a; + signal input b; + signal input c; + signal output x; + x <== a * b * 10; +} + +component main = Arith2(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Arith2::@Arith2<[]>>} { +// CHECK-NEXT: poly.template @Arith2 { +// CHECK-NEXT: struct.def @Arith2 { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Arith2::@Arith2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Arith2::@Arith2<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_4]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@x] = %[[VAL_6]] : <@Arith2::@Arith2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Arith2::@Arith2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@Arith2::@Arith2<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_7]][@x] : <@Arith2::@Arith2<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_12]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith_add.circom b/circom/tests/operators/arith_add.circom new file mode 100644 index 000000000..0df2ca143 --- /dev/null +++ b/circom/tests/operators/arith_add.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArithAdd() { + signal input a; + signal input b; + signal output x; + x <== a + b; +} + +component main = ArithAdd(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArithAdd::@ArithAdd<[]>>} { +// CHECK-NEXT: poly.template @ArithAdd { +// CHECK-NEXT: struct.def @ArithAdd { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ArithAdd::@ArithAdd<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithAdd::@ArithAdd<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@x] = %[[VAL_3]] : <@ArithAdd::@ArithAdd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@ArithAdd::@ArithAdd<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithAdd::@ArithAdd<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@x] : <@ArithAdd::@ArithAdd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith_div.circom b/circom/tests/operators/arith_div.circom new file mode 100644 index 000000000..36a4b4c37 --- /dev/null +++ b/circom/tests/operators/arith_div.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Div() { + signal input in; + signal output out; + out <-- in / 5; +} + +component main = Div(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Div::@Div<[]>>} { +// CHECK-NEXT: poly.template @Div { +// CHECK-NEXT: struct.def @Div { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Div::@Div<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Div::@Div<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@Div::@Div<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Div::@Div<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Div::@Div<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@Div::@Div<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith_idiv.circom b/circom/tests/operators/arith_idiv.circom new file mode 100644 index 000000000..93884c567 --- /dev/null +++ b/circom/tests/operators/arith_idiv.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArithQuotient() { + signal input in; + signal output out <-- 1 \ in; +} + +component main = ArithQuotient(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArithQuotient::@ArithQuotient<[]>>} { +// CHECK-NEXT: poly.template @ArithQuotient { +// CHECK-NEXT: struct.def @ArithQuotient { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ArithQuotient::@ArithQuotient<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithQuotient::@ArithQuotient<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_2]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@ArithQuotient::@ArithQuotient<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithQuotient::@ArithQuotient<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithQuotient::@ArithQuotient<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@ArithQuotient::@ArithQuotient<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith_mod.circom b/circom/tests/operators/arith_mod.circom new file mode 100644 index 000000000..9af47e346 --- /dev/null +++ b/circom/tests/operators/arith_mod.circom @@ -0,0 +1,53 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArithRemainder() { + signal input in; + signal output out; + + signal inv; + + inv <-- in != 0 ? 1 % in : 0; + + out <== inv; + in * out === 0; +} + +component main = ArithRemainder(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArithRemainder::@ArithRemainder<[]>>} { +// CHECK-NEXT: poly.template @ArithRemainder { +// CHECK-NEXT: struct.def @ArithRemainder { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @inv : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ArithRemainder::@ArithRemainder<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithRemainder::@ArithRemainder<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_5]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@inv] = %[[VAL_4]] : <@ArithRemainder::@ArithRemainder<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_4]] : <@ArithRemainder::@ArithRemainder<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithRemainder::@ArithRemainder<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithRemainder::@ArithRemainder<[]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_8]][@out] : <@ArithRemainder::@ArithRemainder<[]>>, !felt.type<"bn128"> +// CHECK-DAG: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_8]][@inv] : <@ArithRemainder::@ArithRemainder<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith_neg.circom b/circom/tests/operators/arith_neg.circom new file mode 100644 index 000000000..817dffbe2 --- /dev/null +++ b/circom/tests/operators/arith_neg.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArithNeg() { + signal input a; + signal output x; + x <== -a; +} + +component main = ArithNeg(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArithNeg::@ArithNeg<[]>>} { +// CHECK-NEXT: poly.template @ArithNeg { +// CHECK-NEXT: struct.def @ArithNeg { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ArithNeg::@ArithNeg<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithNeg::@ArithNeg<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@x] = %[[VAL_2]] : <@ArithNeg::@ArithNeg<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithNeg::@ArithNeg<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithNeg::@ArithNeg<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@x] : <@ArithNeg::@ArithNeg<[]>>, !felt.type<"bn128"> +// CHECK-DAG: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith_pow.circom b/circom/tests/operators/arith_pow.circom new file mode 100644 index 000000000..04278dde2 --- /dev/null +++ b/circom/tests/operators/arith_pow.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArithPower() { + signal input in; + signal output out <-- in ** 2; +} + +component main = ArithPower(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArithPower::@ArithPower<[]>>} { +// CHECK-NEXT: poly.template @ArithPower { +// CHECK-NEXT: struct.def @ArithPower { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ArithPower::@ArithPower<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithPower::@ArithPower<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.pow %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@ArithPower::@ArithPower<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithPower::@ArithPower<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithPower::@ArithPower<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@ArithPower::@ArithPower<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/arith_sub.circom b/circom/tests/operators/arith_sub.circom new file mode 100644 index 000000000..f3b50e594 --- /dev/null +++ b/circom/tests/operators/arith_sub.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ArithSubtract() { + signal input a; + signal input b; + signal input c; + signal output x; + x <== a - (b - 10); +} + +component main = ArithSubtract(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ArithSubtract::@ArithSubtract<[]>>} { +// CHECK-NEXT: poly.template @ArithSubtract { +// CHECK-NEXT: struct.def @ArithSubtract { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ArithSubtract::@ArithSubtract<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithSubtract::@ArithSubtract<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_1]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_0]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@x] = %[[VAL_6]] : <@ArithSubtract::@ArithSubtract<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@ArithSubtract::@ArithSubtract<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithSubtract::@ArithSubtract<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_7]][@x] : <@ArithSubtract::@ArithSubtract<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_9]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_8]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/assert.circom b/circom/tests/operators/assert.circom new file mode 100644 index 000000000..d891ce43e --- /dev/null +++ b/circom/tests/operators/assert.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(nBits) { + assert(nBits <= 512); +} + +component main = A(32); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[32]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @nBits +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[@nBits]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@nBits]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @nBits : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 512 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_1]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[@nBits]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@nBits]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @nBits : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 512 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_5]], %[[VAL_6]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_7]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bitwise_and.circom b/circom/tests/operators/bitwise_and.circom new file mode 100644 index 000000000..f52e994b7 --- /dev/null +++ b/circom/tests/operators/bitwise_and.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BitwiseAnd() { + signal input v; + signal output type; + signal check_v; + type <-- v & 5; + check_v <== type*32; +} + +component main = BitwiseAnd(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BitwiseAnd::@BitwiseAnd<[]>>} { +// CHECK-NEXT: poly.template @BitwiseAnd { +// CHECK-NEXT: struct.def @BitwiseAnd { +// CHECK-NEXT: struct.member @type : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @check_v : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BitwiseAnd::@BitwiseAnd<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BitwiseAnd::@BitwiseAnd<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.bit_and %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseAnd::@BitwiseAnd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@check_v] = %[[VAL_5]] : <@BitwiseAnd::@BitwiseAnd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseAnd::@BitwiseAnd<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseAnd::@BitwiseAnd<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@type] : <@BitwiseAnd::@BitwiseAnd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@check_v] : <@BitwiseAnd::@BitwiseAnd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bitwise_comp.circom b/circom/tests/operators/bitwise_comp.circom new file mode 100644 index 000000000..a50130490 --- /dev/null +++ b/circom/tests/operators/bitwise_comp.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BitwiseComplement() { + signal input v; + signal output type; + signal check_v; + type <-- ~v; + check_v <== type*32; +} + +component main = BitwiseComplement(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BitwiseComplement::@BitwiseComplement<[]>>} { +// CHECK-NEXT: poly.template @BitwiseComplement { +// CHECK-NEXT: struct.def @BitwiseComplement { +// CHECK-NEXT: struct.member @type : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @check_v : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BitwiseComplement::@BitwiseComplement<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BitwiseComplement::@BitwiseComplement<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.bit_not %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@type] = %[[VAL_2]] : <@BitwiseComplement::@BitwiseComplement<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_2]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@check_v] = %[[VAL_4]] : <@BitwiseComplement::@BitwiseComplement<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseComplement::@BitwiseComplement<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseComplement::@BitwiseComplement<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@type] : <@BitwiseComplement::@BitwiseComplement<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@check_v] : <@BitwiseComplement::@BitwiseComplement<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_7]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bitwise_or.circom b/circom/tests/operators/bitwise_or.circom new file mode 100644 index 000000000..9c872a0f9 --- /dev/null +++ b/circom/tests/operators/bitwise_or.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BitwiseOr() { + signal input v; + signal output type; + signal check_v; + type <-- v | 5; + check_v <== type*32; +} + +component main = BitwiseOr(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BitwiseOr::@BitwiseOr<[]>>} { +// CHECK-NEXT: poly.template @BitwiseOr { +// CHECK-NEXT: struct.def @BitwiseOr { +// CHECK-NEXT: struct.member @type : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @check_v : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BitwiseOr::@BitwiseOr<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BitwiseOr::@BitwiseOr<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.bit_or %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseOr::@BitwiseOr<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@check_v] = %[[VAL_5]] : <@BitwiseOr::@BitwiseOr<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseOr::@BitwiseOr<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseOr::@BitwiseOr<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@type] : <@BitwiseOr::@BitwiseOr<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@check_v] : <@BitwiseOr::@BitwiseOr<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bitwise_shl.circom b/circom/tests/operators/bitwise_shl.circom new file mode 100644 index 000000000..5312c2222 --- /dev/null +++ b/circom/tests/operators/bitwise_shl.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BitwiseShiftLeft() { + signal input v; + signal output type; + type <-- v << 5; +} + +component main = BitwiseShiftLeft(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BitwiseShiftLeft::@BitwiseShiftLeft<[]>>} { +// CHECK-NEXT: poly.template @BitwiseShiftLeft { +// CHECK-NEXT: struct.def @BitwiseShiftLeft { +// CHECK-NEXT: struct.member @type : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BitwiseShiftLeft::@BitwiseShiftLeft<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BitwiseShiftLeft::@BitwiseShiftLeft<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.shl %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseShiftLeft::@BitwiseShiftLeft<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseShiftLeft::@BitwiseShiftLeft<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseShiftLeft::@BitwiseShiftLeft<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@type] : <@BitwiseShiftLeft::@BitwiseShiftLeft<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bitwise_shr.circom b/circom/tests/operators/bitwise_shr.circom new file mode 100644 index 000000000..7bfb82223 --- /dev/null +++ b/circom/tests/operators/bitwise_shr.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BitwiseShiftRight() { + signal input v; + signal output type; + type <-- v >> 5; +} + +component main = BitwiseShiftRight(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BitwiseShiftRight::@BitwiseShiftRight<[]>>} { +// CHECK-NEXT: poly.template @BitwiseShiftRight { +// CHECK-NEXT: struct.def @BitwiseShiftRight { +// CHECK-NEXT: struct.member @type : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BitwiseShiftRight::@BitwiseShiftRight<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BitwiseShiftRight::@BitwiseShiftRight<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseShiftRight::@BitwiseShiftRight<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseShiftRight::@BitwiseShiftRight<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseShiftRight::@BitwiseShiftRight<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@type] : <@BitwiseShiftRight::@BitwiseShiftRight<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bitwise_xor.circom b/circom/tests/operators/bitwise_xor.circom new file mode 100644 index 000000000..82a779b60 --- /dev/null +++ b/circom/tests/operators/bitwise_xor.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BitwiseXOR() { + signal input v; + signal output type; + signal check_v; + type <-- v ^ 5; + check_v <== type*32; +} + +component main = BitwiseXOR(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BitwiseXOR::@BitwiseXOR<[]>>} { +// CHECK-NEXT: poly.template @BitwiseXOR { +// CHECK-NEXT: struct.def @BitwiseXOR { +// CHECK-NEXT: struct.member @type : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @check_v : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BitwiseXOR::@BitwiseXOR<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BitwiseXOR::@BitwiseXOR<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.bit_xor %[[VAL_0]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseXOR::@BitwiseXOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@check_v] = %[[VAL_5]] : <@BitwiseXOR::@BitwiseXOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseXOR::@BitwiseXOR<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseXOR::@BitwiseXOR<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@type] : <@BitwiseXOR::@BitwiseXOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@check_v] : <@BitwiseXOR::@BitwiseXOR<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bool_and.circom b/circom/tests/operators/bool_and.circom new file mode 100644 index 000000000..9f62cab41 --- /dev/null +++ b/circom/tests/operators/bool_and.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BoolAnd() { + signal input a, b; + signal output out; + + out <-- a > 0 && b > 0; +} + +component main = BoolAnd(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BoolAnd::@BoolAnd<[]>>} { +// CHECK-NEXT: poly.template @BoolAnd { +// CHECK-NEXT: struct.def @BoolAnd { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BoolAnd::@BoolAnd<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@BoolAnd::@BoolAnd<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_1]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.and %[[VAL_4]], %[[VAL_6]] : i1, i1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_7]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_8]] : <@BoolAnd::@BoolAnd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@BoolAnd::@BoolAnd<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@BoolAnd::@BoolAnd<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out] : <@BoolAnd::@BoolAnd<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bool_not.circom b/circom/tests/operators/bool_not.circom new file mode 100644 index 000000000..0a48fec24 --- /dev/null +++ b/circom/tests/operators/bool_not.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BoolNot() { + signal input a, b; + signal output out; + + out <-- !(a < b); +} + +component main = BoolNot(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BoolNot::@BoolNot<[]>>} { +// CHECK-NEXT: poly.template @BoolNot { +// CHECK-NEXT: struct.def @BoolNot { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BoolNot::@BoolNot<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@BoolNot::@BoolNot<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.not %[[VAL_3]] : i1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_4]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_5]] : <@BoolNot::@BoolNot<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@BoolNot::@BoolNot<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BoolNot::@BoolNot<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@out] : <@BoolNot::@BoolNot<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/bool_or.circom b/circom/tests/operators/bool_or.circom new file mode 100644 index 000000000..0e347f047 --- /dev/null +++ b/circom/tests/operators/bool_or.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template BoolOr() { + signal input a, b; + signal output out; + + out <-- (a > 0 || b > 0); +} + +component main = BoolOr(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BoolOr::@BoolOr<[]>>} { +// CHECK-NEXT: poly.template @BoolOr { +// CHECK-NEXT: struct.def @BoolOr { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BoolOr::@BoolOr<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@BoolOr::@BoolOr<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_1]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_4]], %[[VAL_6]] : i1, i1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_7]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_8]] : <@BoolOr::@BoolOr<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@BoolOr::@BoolOr<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@BoolOr::@BoolOr<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out] : <@BoolOr::@BoolOr<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/cmp_eq.circom b/circom/tests/operators/cmp_eq.circom new file mode 100644 index 000000000..e9c689ab9 --- /dev/null +++ b/circom/tests/operators/cmp_eq.circom @@ -0,0 +1,75 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template CmpEQ(n) { + signal input a[n]; + signal output b[n]; + + if (n == 0) { + b[0] <== a[0]; + } else { + b[1] <== a[1]; + } +} + +component main = CmpEQ(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CmpEQ::@CmpEQ<[5]>>} { +// CHECK-NEXT: poly.template @CmpEQ { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @CmpEQ { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@CmpEQ::@CmpEQ<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpEQ::@CmpEQ<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_2]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_5]] { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_7]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_10]]] = %[[VAL_8]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_12]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_13]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpEQ::@CmpEQ<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpEQ::@CmpEQ<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpEQ::@CmpEQ<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@b] : <@CmpEQ::@CmpEQ<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_18]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_21]] { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_23]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_26]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_29]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_32]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/cmp_ge.circom b/circom/tests/operators/cmp_ge.circom new file mode 100644 index 000000000..5abd75f15 --- /dev/null +++ b/circom/tests/operators/cmp_ge.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template CmpGE(n) { + signal input a[n]; + signal output b[n]; + + for (var i = n-1; i >= 0; i--) { + b[i] <== a[i]; + } +} + +component main = CmpGE(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CmpGE::@CmpGE<[5]>>} { +// CHECK-NEXT: poly.template @CmpGE { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @CmpGE { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@CmpGE::@CmpGE<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpGE::@CmpGE<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_2]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_5]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_11]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_13]]] = %[[VAL_12]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_10]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpGE::@CmpGE<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpGE::@CmpGE<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpGE::@CmpGE<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@b] : <@CmpGE::@CmpGE<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_18]], %[[VAL_20]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_21]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_23]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_27]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_29]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/cmp_gt.circom b/circom/tests/operators/cmp_gt.circom new file mode 100644 index 000000000..06f6a2a81 --- /dev/null +++ b/circom/tests/operators/cmp_gt.circom @@ -0,0 +1,74 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template CmpGT(n) { + signal input a[n]; + signal output b[n]; + + for (var i = n; i > 0; i--) { + b[i-1] <== a[i-1]; + } +} + +component main = CmpGT(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CmpGT::@CmpGT<[5]>>} { +// CHECK-NEXT: poly.template @CmpGT { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @CmpGT { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@CmpGT::@CmpGT<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpGT::@CmpGT<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_5]], %[[VAL_6]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_11]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_8]], %[[VAL_13]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_12]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_8]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpGT::@CmpGT<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpGT::@CmpGT<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpGT::@CmpGT<[@n]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@b] : <@CmpGT::@CmpGT<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_20]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_23]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_27]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_29]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_21]]{{\[}}%[[VAL_33]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_34]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_35]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_36]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/cmp_le.circom b/circom/tests/operators/cmp_le.circom new file mode 100644 index 000000000..53d7f1326 --- /dev/null +++ b/circom/tests/operators/cmp_le.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template CmpLE(n) { + signal input a[n]; + signal output b[n]; + + for (var i = 0; i <= n-1; i++) { + b[i] <== a[i]; + } +} + +component main = CmpLE(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CmpLE::@CmpLE<[5]>>} { +// CHECK-NEXT: poly.template @CmpLE { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @CmpLE { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@CmpLE::@CmpLE<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpLE::@CmpLE<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_2]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_11]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_13]]] = %[[VAL_12]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpLE::@CmpLE<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpLE::@CmpLE<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpLE::@CmpLE<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@b] : <@CmpLE::@CmpLE<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_20]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_18]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_22]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_27]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_29]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_28]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_26]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/cmp_lt.circom b/circom/tests/operators/cmp_lt.circom new file mode 100644 index 000000000..ff9ae1ec6 --- /dev/null +++ b/circom/tests/operators/cmp_lt.circom @@ -0,0 +1,75 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template CmpLT(n) { + signal input a[n]; + signal output b[n]; + + if (n < 0) { + b[0] <== a[0]; + } else { + b[1] <== a[1]; + } +} + +component main = CmpLT(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CmpLT::@CmpLT<[5]>>} { +// CHECK-NEXT: poly.template @CmpLT { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @CmpLT { +// CHECK-NEXT: struct.member @b : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@CmpLT::@CmpLT<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpLT::@CmpLT<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_2]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_5]] { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_7]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_10]]] = %[[VAL_8]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_12]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_13]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpLT::@CmpLT<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpLT::@CmpLT<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpLT::@CmpLT<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@b] : <@CmpLT::@CmpLT<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_18]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_21]] { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_23]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_26]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_24]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_29]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_32]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/inline_switch_function.circom b/circom/tests/operators/inline_switch_function.circom new file mode 100644 index 000000000..d8527b172 --- /dev/null +++ b/circom/tests/operators/inline_switch_function.circom @@ -0,0 +1,50 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function InlineSwitch(cond, a, b) { + return cond ? a : b; +} + +template CallInlineSwitch() { + signal input in; + signal output out <-- InlineSwitch(in != 0, 1 / in, 0); +} + +component main = CallInlineSwitch(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@CallInlineSwitch::@CallInlineSwitch<[]>>} { +// CHECK-NEXT: function.def @InlineSwitch(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: scf.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @CallInlineSwitch { +// CHECK-NEXT: struct.def @CallInlineSwitch { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@CallInlineSwitch::@CallInlineSwitch<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@CallInlineSwitch::@CallInlineSwitch<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_6]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_9]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_11]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @InlineSwitch(%[[VAL_10]], %[[VAL_12]], %[[VAL_13]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@out] = %[[VAL_14]] : <@CallInlineSwitch::@CallInlineSwitch<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@CallInlineSwitch::@CallInlineSwitch<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@CallInlineSwitch::@CallInlineSwitch<[]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_15]][@out] : <@CallInlineSwitch::@CallInlineSwitch<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/operators/inline_switch_template.circom b/circom/tests/operators/inline_switch_template.circom new file mode 100644 index 000000000..952a86e98 --- /dev/null +++ b/circom/tests/operators/inline_switch_template.circom @@ -0,0 +1,77 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template MultByInv() { + signal input in; + signal output out; + + signal inv; + var temp; + + // Appears in both @compute and @constrain + temp = in != 0 ? 1 / in : 0; + // Appears only in @compute + inv <-- temp == 0 ? 0 : temp; + + out <== inv; + in * out === 0; +} + +component main = MultByInv(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@MultByInv::@MultByInv<[]>>} { +// CHECK-NEXT: poly.template @MultByInv { +// CHECK-NEXT: struct.def @MultByInv { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @inv : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@MultByInv::@MultByInv<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@MultByInv::@MultByInv<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_6]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_5]], %[[VAL_9]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_10]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@inv] = %[[VAL_11]] : <@MultByInv::@MultByInv<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_11]] : <@MultByInv::@MultByInv<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@MultByInv::@MultByInv<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@MultByInv::@MultByInv<[]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@MultByInv::@MultByInv<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@inv] : <@MultByInv::@MultByInv<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_14]], %[[VAL_18]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_19]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_21]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_15]], %[[VAL_16]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_14]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_25]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/assert.circom b/circom/tests/simple/assert.circom new file mode 100644 index 000000000..835eb20ea --- /dev/null +++ b/circom/tests/simple/assert.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n) { + signal input in; + assert(in > 0); +} + +component main = A(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[5]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_3]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_4]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_6]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/assert_known_false.circom b/circom/tests/simple/assert_known_false.circom new file mode 100644 index 000000000..c0e40c72a --- /dev/null +++ b/circom/tests/simple/assert_known_false.circom @@ -0,0 +1,52 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template UCO() { + for(var i = 0; i < 100; i++) { + 1 === 0; + } +} + +component main = UCO(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@UCO::@UCO<[]>>} { +// CHECK-NEXT: poly.template @UCO { +// CHECK-NEXT: struct.def @UCO { +// CHECK-NEXT: function.def @compute() -> !struct.type<@UCO::@UCO<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@UCO::@UCO<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_3:[0-9a-zA-Z_\.]+]] = %[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_3]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_5]]) %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_6]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@UCO::@UCO<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@UCO::@UCO<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_10]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_12]], %[[VAL_13]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_14]]) %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_16]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_15]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/blocks_01.circom b/circom/tests/simple/blocks_01.circom new file mode 100644 index 000000000..bd3a2ab2f --- /dev/null +++ b/circom/tests/simple/blocks_01.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// This test demonstrates that variables defined outside a block can be overwritten +// inside blocks because no "error[T3001]: False assert reached" is raised but if any +// of the asserts are changed, the error will be raised. +template B() { + var x = 5; + { + assert(x == 5); + x = 10; // overwrites value of x + assert(x == 10); + } + assert(x == 10); +} + +component main = B(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[]>>} { +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: function.def @compute() -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_4]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_6]], "assertion failed" +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_4]], %[[VAL_7]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_8]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_10]], %[[VAL_11]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_12]], "assertion failed" +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_13]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_15]], "assertion failed" +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_13]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_17]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/blocks_02.circom b/circom/tests/simple/blocks_02.circom new file mode 100644 index 000000000..bcc05a456 --- /dev/null +++ b/circom/tests/simple/blocks_02.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// This test demonstrates that variables can be shadowed inside blocks +// because no "error[T3001]: False assert reached" is raised but if any +// of the asserts are changed, the error will be raised. +template B() { + var x = 5; + { + assert(x == 5); + var x = 10; // shadows outer x + assert(x == 10); + } + assert(x == 5); +} + +component main = B(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[]>>} { +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: function.def @compute() -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_4]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_6]], "assertion failed" +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_7]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_8]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_10]], %[[VAL_11]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_12]], "assertion failed" +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_13]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_15]], "assertion failed" +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_10]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_17]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/blocks_03.circom b/circom/tests/simple/blocks_03.circom new file mode 100644 index 000000000..35ca6b902 --- /dev/null +++ b/circom/tests/simple/blocks_03.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// This test demonstrates that an inner block can both overwrite and shadow variables +// defined outside a block because no "error[T3001]: False assert reached" is raised +// but if any of the asserts are changed, the error will be raised. +template B() { + var x = 5; + { + assert(x == 5); + x = 15; // overwrites value of outer x + assert(x == 15); + var x = 10; // shadows outer x + assert(x == 10); + } + assert(x == 15); +} + +component main = B(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@B::@B<[]>>} { +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: function.def @compute() -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_4]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_6]], "assertion failed" +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_7]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_4]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_11]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_13]], %[[VAL_14]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_15]], "assertion failed" +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_16]], %[[VAL_17]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_18]], "assertion failed" +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_19]], %[[VAL_20]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_21]], "assertion failed" +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_16]], %[[VAL_22]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_23]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/bytecode_output.circom b/circom/tests/simple/bytecode_output.circom new file mode 100644 index 000000000..ed60f7833 --- /dev/null +++ b/circom/tests/simple/bytecode_output.circom @@ -0,0 +1,17 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs od -t x1 -N 4 | FileCheck %s +// END. + +pragma circom 2.0.0; + +// Verifies that the default bytecode output (i.e., no `--llzk_plaintext`) is MLIR bytecode +// by checking the 4-byte magic number at the start of the file. +template BytecodeOutput() { + signal input a; + signal output b; + b <== a; +} + +component main = BytecodeOutput(); + +// CHECK: 4d 4c ef 52 diff --git a/circom/tests/simple/bytecode_roundtrip.circom b/circom/tests/simple/bytecode_roundtrip.circom new file mode 100644 index 000000000..fee260256 --- /dev/null +++ b/circom/tests/simple/bytecode_roundtrip.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom, llzk-opt +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs llzk-opt | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Verifies that the default bytecode output (no --llzk_plaintext) round-trips +// correctly through llzk-opt, producing valid LLZK IR text. +template BytecodeOutput() { + signal input a; + signal output b; + b <== a; +} + +component main = BytecodeOutput(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@BytecodeOutput::@BytecodeOutput<[]>>} { +// CHECK-NEXT: poly.template @BytecodeOutput { +// CHECK-NEXT: struct.def @BytecodeOutput { +// CHECK-NEXT: struct.member @b : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@BytecodeOutput::@BytecodeOutput<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BytecodeOutput::@BytecodeOutput<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_0]] : <@BytecodeOutput::@BytecodeOutput<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BytecodeOutput::@BytecodeOutput<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@BytecodeOutput::@BytecodeOutput<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@b] : <@BytecodeOutput::@BytecodeOutput<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_and_return.circom b/circom/tests/simple/call_and_return.circom new file mode 100644 index 000000000..aec53d0ed --- /dev/null +++ b/circom/tests/simple/call_and_return.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template C() { + signal input in; + signal output out; + out <-- negative(in); +} + +function negative(n){ + if (n < 0) { + return 1; + } else { + return 0; + } +} + +component main = C(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@C::@C<[]>>} { +// CHECK-NEXT: function.def @negative(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@out] = %[[VAL_8]] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@out] : <@C::@C<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_01.circom b/circom/tests/simple/call_in_constraint_gen_01.circom new file mode 100644 index 000000000..8a2e1e18a --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_01.circom @@ -0,0 +1,43 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + return i + 1; +} + +template T() { + signal input inp; + signal output outp; + + outp <== f(inp); +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_3]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_4]][@outp] = %[[VAL_5]] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_4]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@outp] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_7]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_02.circom b/circom/tests/simple/call_in_constraint_gen_02.circom new file mode 100644 index 000000000..d120d156c --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_02.circom @@ -0,0 +1,48 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + var x = i; + var y = x*x + 1; + return y + 1; +} + +template T() { + signal input inp; + signal output outp; + + outp <== f(inp); +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_1]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_7]][@outp] = %[[VAL_8]] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@outp] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_10]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_03.circom b/circom/tests/simple/call_in_constraint_gen_03.circom new file mode 100644 index 000000000..75b84cb9b --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_03.circom @@ -0,0 +1,59 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + if (i < 10) { + return i + 2; + } else { + return i + 3; + } +} + +template T() { + signal input inp; + signal output outp; + + // error[T3001]: Non quadratic constraints are not allowed! + // This error does not occur when producing LLZK IR because it is only detected + // during the execution phase, which occurs after emitting LLZK IR. + outp <== f(inp); +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_8]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_9]][@outp] = %[[VAL_10]] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_11]][@outp] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_12]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_04.circom b/circom/tests/simple/call_in_constraint_gen_04.circom new file mode 100644 index 000000000..d56e988b2 --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_04.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + return i; +} + +template T() { + signal input inp1; + signal input inp2; + + inp1 === f(inp2); +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_6]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_05.circom b/circom/tests/simple/call_in_constraint_gen_05.circom new file mode 100644 index 000000000..6cce6c7b4 --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_05.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(a, b) { + return a + b; +} + +template T() { + signal input inp1; + signal input inp2; + signal input inp3; + + // Circom generates constraint "inp1 = inp2 + inp3" + inp1 === f(inp2, inp3); +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: function.def @compute(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: function.return %[[VAL_6]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_9]], %[[VAL_10]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_06.circom b/circom/tests/simple/call_in_constraint_gen_06.circom new file mode 100644 index 000000000..8a3bb078d --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_06.circom @@ -0,0 +1,44 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + return i; +} + +template T() { + signal input inp1; + signal input inp2; + signal input inp3; + signal temp; + + temp <-- f(inp1); + inp2 + inp3 === temp; +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: struct.member @temp : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_4]][@temp] = %[[VAL_5]] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_4]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@temp] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_07.circom b/circom/tests/simple/call_in_constraint_gen_07.circom new file mode 100644 index 000000000..e7909327a --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_07.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + return i; +} + +template T() { + signal input inp1; + signal input inp2; + signal input inp3; + + var temp = f(inp1); + inp2 + inp3 === temp; +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_4]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_7]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_08.circom b/circom/tests/simple/call_in_constraint_gen_08.circom new file mode 100644 index 000000000..6f2ef5555 --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_08.circom @@ -0,0 +1,46 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + return i; +} + +template T() { + signal input inp1; + signal input inp2; + signal output o1; + + o1 <-- 1; + var temp = f(inp1); + o1 + inp2 === temp; +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: struct.member @o1 : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: struct.writem %[[VAL_3]][@o1] = %[[VAL_4]] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@o1] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_7]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/call_in_constraint_gen_09.circom b/circom/tests/simple/call_in_constraint_gen_09.circom new file mode 100644 index 000000000..82298d3ec --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_09.circom @@ -0,0 +1,49 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function f(i) { + return i; +} + +template T() { + signal input inp1; + signal input inp2; + signal output o1; + signal temp; + + o1 <-- 1; + temp <-- f(inp1); + o1 + inp2 === temp; +} + +component main = T(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@T::@T<[]>>} { +// CHECK-NEXT: function.def @f(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @T { +// CHECK-NEXT: struct.def @T { +// CHECK-NEXT: struct.member @o1 : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @temp : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@T::@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@T::@T<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: struct.writem %[[VAL_3]][@o1] = %[[VAL_4]] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_1]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@temp] = %[[VAL_5]] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@T::@T<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@T::@T<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@o1] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@temp] : <@T::@T<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/constrain_before_assign.circom b/circom/tests/simple/constrain_before_assign.circom new file mode 100644 index 000000000..1e57e4524 --- /dev/null +++ b/circom/tests/simple/constrain_before_assign.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Template() { + signal output outp; + outp === 7; + outp <-- 7; +} + +component main = Template(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Template::@Template<[]>>} { +// CHECK-NEXT: poly.template @Template { +// CHECK-NEXT: struct.def @Template { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template::@Template<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Template::@Template<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: struct.writem %[[VAL_0]][@outp] = %[[VAL_1]] : <@Template::@Template<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Template::@Template<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Template::@Template<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@outp] : <@Template::@Template<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: constrain.eq %[[VAL_3]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/constraint.circom b/circom/tests/simple/constraint.circom new file mode 100644 index 000000000..2ef3c3f7b --- /dev/null +++ b/circom/tests/simple/constraint.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal input in; + signal output out; + out <== in; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_0]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/constraint_constant.circom b/circom/tests/simple/constraint_constant.circom new file mode 100644 index 000000000..1df90c467 --- /dev/null +++ b/circom/tests/simple/constraint_constant.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Simple2(a) { + signal output b; + + b <== a; +} + +component main = Simple2(10); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Simple2::@Simple2<[10]>>} { +// CHECK-NEXT: poly.template @Simple2 { +// CHECK-NEXT: poly.param @a +// CHECK-NEXT: struct.def @Simple2 { +// CHECK-NEXT: struct.member @b : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Simple2::@Simple2<[@a]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple2::@Simple2<[@a]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@b] = %[[VAL_1]] : <@Simple2::@Simple2<[@a]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Simple2::@Simple2<[@a]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Simple2::@Simple2<[@a]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@b] : <@Simple2::@Simple2<[@a]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/create_component.circom b/circom/tests/simple/create_component.circom new file mode 100644 index 000000000..9266f73b8 --- /dev/null +++ b/circom/tests/simple/create_component.circom @@ -0,0 +1,92 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B(n) { + signal input inB; +} + +template A(n) { + signal input inA; + component x = B(n * n); + x.inB <-- inA; +} + +component main = A(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[5]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.expr @"n_Mul_n@327" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_1]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @x : !struct.type<@B::@B<[@"n_Mul_n@327"]>> +// CHECK-NEXT: struct.member @x$inputs : !pod.type<[@inB: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@327" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new : <[@inB: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@327_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_7]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_9]], @params = %[[VAL_8]] } : <[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_10]] : (!pod.type<[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_6]][@inB] = %[[VAL_2]] : <[@inB: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_12]], %[[VAL_13]] : index +// CHECK-NEXT: pod.write %[[VAL_11]][@count] = %[[VAL_14]] : <[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_14]], %[[VAL_15]] : index +// CHECK-NEXT: scf.if %[[VAL_16]] { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@inB] : <[@inB: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_18]]) : (!felt.type<"bn128">) -> !struct.type<@B::@B<[@"n_Mul_n@327"]>> +// CHECK-NEXT: pod.write %[[VAL_11]][@comp] = %[[VAL_19]] : <[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[@"n_Mul_n@327"]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_3]][@x$inputs] = %[[VAL_6]] : <@A::@A<[@n]>>, !pod.type<[@inB: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_11]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@B::@B<[@"n_Mul_n@327"]>> +// CHECK-NEXT: struct.writem %[[VAL_3]][@x] = %[[VAL_20]] : <@A::@A<[@n]>>, !struct.type<@B::@B<[@"n_Mul_n@327"]>> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@327" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_21]][@x] : <@A::@A<[@n]>>, !struct.type<@B::@B<[@"n_Mul_n@327"]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_21]][@x$inputs] : <@A::@A<[@n]>>, !pod.type<[@inB: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_n@327_0" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_27]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[@"n_Mul_n@327_0"]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@inB] : <[@inB: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_25]], %[[VAL_30]]) : (!struct.type<@B::@B<[@"n_Mul_n@327"]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.expr @"n_Mul_n@327_0" { +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_31]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_32]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: function.def @compute(%[[VAL_33:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@B::@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[@n]>> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_34]] : !struct.type<@B::@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_36:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[@n]>>, %[[VAL_37:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/dump_parse.circom b/circom/tests/simple/dump_parse.circom new file mode 100644 index 000000000..af5d7f8e7 --- /dev/null +++ b/circom/tests/simple/dump_parse.circom @@ -0,0 +1,328 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --dump_parse -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --match-full-lines +// END. + +pragma circom 2.0.0; + +template HelloWorld() { + signal input a; + signal output b; + + b <== a; +} + +component main = HelloWorld(); + +//CHECK-LABEL: ProgramArchive { +// CHECK-NEXT: id_max: 8, +// CHECK-NEXT: file_id_main: [[FD:[0-9]+]], +//CHECK-LABEL: file_library: FileLibrary { +// CHECK-NEXT: files: SimpleFiles { +// CHECK-NEXT: files: [ +// CHECK-NEXT: SimpleFile { +// CHECK-NEXT: name: "", +// CHECK-NEXT: source: "", +// CHECK-NEXT: line_starts: [ +// CHECK-NEXT: 0, +// CHECK-NEXT: ], +// CHECK-NEXT: }, +// CHECK-NEXT: SimpleFile { +// CHECK-NEXT: name: {{.*}} +// CHECK-NEXT: source: {{.*}} +// COM: (--match-full-lines ensures this matches to the end to avoid match +// COM: problems caused by 'source' containing the full content of this file) +// CHECK-NEXT: line_starts: [ +// COM: (skip the long list of "line_starts") +//CHECK-LABEL: functions: {}, +//CHECK-LABEL: templates: { +// CHECK-NEXT: "HelloWorld": TemplateData { +// CHECK-NEXT: file_id: [[FD]], +// CHECK-NEXT: name: "HelloWorld", +// CHECK-NEXT: body: Block { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: stmts: [ +// CHECK-NEXT: InitializationBlock { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: xtype: Var, +// CHECK-NEXT: initializations: [], +// CHECK-NEXT: }, +// CHECK-NEXT: InitializationBlock { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: xtype: Component, +// CHECK-NEXT: initializations: [], +// CHECK-NEXT: }, +// CHECK-NEXT: InitializationBlock { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: xtype: Signal( +// CHECK-NEXT: Input, +// CHECK-NEXT: [], +// CHECK-NEXT: ), +// CHECK-NEXT: initializations: [ +// CHECK-NEXT: Declaration { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: xtype: Signal( +// CHECK-NEXT: Input, +// CHECK-NEXT: [], +// CHECK-NEXT: ), +// CHECK-NEXT: name: "a", +// CHECK-NEXT: dimensions: [], +// CHECK-NEXT: is_constant: true, +// CHECK-NEXT: is_anonymous: false, +// CHECK-NEXT: }, +// CHECK-NEXT: ], +// CHECK-NEXT: }, +// CHECK-NEXT: InitializationBlock { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: xtype: Signal( +// CHECK-NEXT: Output, +// CHECK-NEXT: [], +// CHECK-NEXT: ), +// CHECK-NEXT: initializations: [ +// CHECK-NEXT: Declaration { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: xtype: Signal( +// CHECK-NEXT: Output, +// CHECK-NEXT: [], +// CHECK-NEXT: ), +// CHECK-NEXT: name: "b", +// CHECK-NEXT: dimensions: [], +// CHECK-NEXT: is_constant: true, +// CHECK-NEXT: is_anonymous: false, +// CHECK-NEXT: }, +// CHECK-NEXT: ], +// CHECK-NEXT: }, +// CHECK-NEXT: Substitution { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: Some( +// CHECK-NEXT: Signal, +// CHECK-NEXT: ), +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: var: "b", +// CHECK-NEXT: access: [], +// CHECK-NEXT: op: AssignConstraintSignal, +// CHECK-NEXT: rhe: Variable { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: Some( +// CHECK-NEXT: Signal, +// CHECK-NEXT: ), +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: name: "a", +// CHECK-NEXT: access: [], +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: ], +// CHECK-NEXT: }, +// CHECK-NEXT: num_of_params: 0, +// CHECK-NEXT: name_of_params: [], +// CHECK-NEXT: param_location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: input_wires: { +// CHECK-NEXT: "a": WireData { +// CHECK-NEXT: wire_type: Signal, +// CHECK-NEXT: dimension: 0, +// CHECK-NEXT: tag_info: {}, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: output_wires: { +// CHECK-NEXT: "b": WireData { +// CHECK-NEXT: wire_type: Signal, +// CHECK-NEXT: dimension: 0, +// CHECK-NEXT: tag_info: {}, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: is_parallel: false, +// CHECK-NEXT: is_custom_gate: false, +// CHECK-NEXT: is_extern_c: false, +// CHECK-NEXT: input_declarations: [ +// CHECK-NEXT: ( +// CHECK-NEXT: "a", +// CHECK-NEXT: 0, +// CHECK-NEXT: ), +// CHECK-NEXT: ], +// CHECK-NEXT: output_declarations: [ +// CHECK-NEXT: ( +// CHECK-NEXT: "b", +// CHECK-NEXT: 0, +// CHECK-NEXT: ), +// CHECK-NEXT: ], +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: buses: {}, +// CHECK-NEXT: function_keys: {}, +// CHECK-NEXT: template_keys: { +// CHECK-NEXT: "HelloWorld", +// CHECK-NEXT: }, +// CHECK-NEXT: bus_keys: {}, +// CHECK-NEXT: public_inputs: [], +// CHECK-NEXT: initial_template_call: Call { +// CHECK-NEXT: meta: Meta { +// CHECK-NEXT: elem_id: {{[0-9]+}}, +// CHECK-NEXT: start: {{[0-9]+}}, +// CHECK-NEXT: end: {{[0-9]+}}, +// CHECK-NEXT: location: {{[0-9]+}}..{{[0-9]+}}, +// CHECK-NEXT: file_id: Some( +// CHECK-NEXT: [[FD]], +// CHECK-NEXT: ), +// CHECK-NEXT: component_inference: None, +// CHECK-NEXT: type_knowledge: TypeKnowledge { +// CHECK-NEXT: reduces_to: None, +// CHECK-NEXT: }, +// CHECK-NEXT: memory_knowledge: MemoryKnowledge { +// CHECK-NEXT: concrete_dimensions: None, +// CHECK-NEXT: full_length: None, +// CHECK-NEXT: abstract_memory_address: None, +// CHECK-NEXT: }, +// CHECK-NEXT: }, +// CHECK-NEXT: id: "HelloWorld", +// CHECK-NEXT: args: [], +// CHECK-NEXT: }, +// CHECK-NEXT: custom_gates: false, +// CHECK-NEXT: } diff --git a/circom/tests/simple/idiv_1.circom b/circom/tests/simple/idiv_1.circom new file mode 100644 index 000000000..3f665cc32 --- /dev/null +++ b/circom/tests/simple/idiv_1.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + var c = 1 \ -1; + assert(c == 0); // 0 quotient means circom uses unsigned division +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_1]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_4]], %[[VAL_5]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_6]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_9]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_8]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_11]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: bool.assert %[[VAL_13]], "assertion failed" +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/idiv_2.circom b/circom/tests/simple/idiv_2.circom new file mode 100644 index 000000000..60f560fa6 --- /dev/null +++ b/circom/tests/simple/idiv_2.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n) { + signal output c <== n \ 4; // this op can be used in a constraint only because it folds to a constant +} + +component main = A(12); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[12]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_1]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@c] = %[[VAL_3]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@c] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_5]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/load.circom b/circom/tests/simple/load.circom new file mode 100644 index 000000000..633635428 --- /dev/null +++ b/circom/tests/simple/load.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n) { + signal input in[3]; + signal output out; + var idx[3] = [ 2, 1, 0 ]; + + var x = in[idx[n]]; + out <-- x; +} + +component main = A(1); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[1]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<3 x !felt.type<"bn128">>) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_3]], %[[VAL_3]], %[[VAL_3]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_5]], %[[VAL_6]], %[[VAL_7]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_8]]{{\[}}%[[VAL_9]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_11]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_12]] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !array.type<3 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_13]][@out] : <@A::@A<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_17]], %[[VAL_17]], %[[VAL_17]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_19]], %[[VAL_20]], %[[VAL_21]] : <3 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_22]]{{\[}}%[[VAL_23]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_25]]] : <3 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/log.circom b/circom/tests/simple/log.circom new file mode 100644 index 000000000..28c0ba2b8 --- /dev/null +++ b/circom/tests/simple/log.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// COM: Setup and run the test +// RUN: rm -rf %t && mkdir %t +// RUN: %circom --stabilize --llzk --llzk_plaintext -o %t %s > %t/stdout.txt 2> %t/stderr.txt +// COM: Check stderr for the warning that's produced (but ignore the color codes) +// RUN: sed 's/\x1b\[[0-9;]*m//g' %t/stderr.txt | FileCheck %s --check-prefix=WARN +// COM: Check stdout for the generated IR +// RUN: sed -n 's/.*Written successfully:.* \(.*\)/\1/p' %t/stdout.txt | xargs cat | FileCheck %s --check-prefix=IR +// END. + +pragma circom 2.0.0; + +template A() { + log(1658); +} + +component main = A(); + +//WARN-LABEL: warning[T2038]: log calls are not currently supported in LLZK +// WARN-NEXT: ┌─ "{{.*}}log.circom":14:3 +// WARN-NEXT: │ +// WARN-NEXT: 14 │ log(1658); +// WARN-NEXT: │ ^^^^^^^^^^ here + +//IR-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// IR-NEXT: poly.template @A { +// IR-NEXT: struct.def @A { +// IR-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// IR-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// IR-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// IR-NEXT: } +// IR-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// IR-NEXT: function.return +// IR-NEXT: } +// IR-NEXT: } +// IR-NEXT: } +// IR-NEXT: } diff --git a/circom/tests/simple/loop_and_block.circom b/circom/tests/simple/loop_and_block.circom new file mode 100644 index 000000000..9c08d3b89 --- /dev/null +++ b/circom/tests/simple/loop_and_block.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(n) { + signal input inp[n]; + signal output out[n]; + + for ( var i = 0; i < n; i++ ) { + out[i] <-- inp[i]; + } +} + +component main = A(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[3]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@A::@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_4]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_9]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@A::@A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@n]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_14]][@out] : <@A::@A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_18]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_24]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/main_exprs.circom b/circom/tests/simple/main_exprs.circom new file mode 100644 index 000000000..de41da15a --- /dev/null +++ b/circom/tests/simple/main_exprs.circom @@ -0,0 +1,59 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.2.0; + +function f(x) { + return x + 1; +} +template A(a,b,c,d,e,f,g,h,i,j,k) {} + +component main = A( 3<<3, -12+24, 4*5, 20/4, 20\4, 7%3, 2^3, (1+2)*3, 6&2, 5!=2, !(1==1 ? 2<2 : 4*1^5&3) ); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[24, 12, 20, 5, 5, 1, 1, 9, 2, 1, 1]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @a +// CHECK-NEXT: poly.param @b +// CHECK-NEXT: poly.param @c +// CHECK-NEXT: poly.param @d +// CHECK-NEXT: poly.param @e +// CHECK-NEXT: poly.param @f +// CHECK-NEXT: poly.param @g +// CHECK-NEXT: poly.param @h +// CHECK-NEXT: poly.param @i +// CHECK-NEXT: poly.param @j +// CHECK-NEXT: poly.param @k +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[@a, @b, @c, @d, @e, @f, @g, @h, @i, @j, @k]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@a, @b, @c, @d, @e, @f, @g, @h, @i, @j, @k]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @b : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @c : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @d : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @e : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @f : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @g : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @h : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = poly.read_const @i : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @j : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[@a, @b, @c, @d, @e, @f, @g, @h, @i, @j, @k]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@a, @b, @c, @d, @e, @f, @g, @h, @i, @j, @k]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @b : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @c : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @d : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @e : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @f : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = poly.read_const @g : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @h : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = poly.read_const @i : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = poly.read_const @j : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/parallel_subcmp.circom b/circom/tests/simple/parallel_subcmp.circom new file mode 100644 index 000000000..61d0bb98e --- /dev/null +++ b/circom/tests/simple/parallel_subcmp.circom @@ -0,0 +1,53 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Foo() {} + +template Parallel() { + component foo = parallel Foo(); +} + +component main = Parallel(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Parallel::@Parallel<[]>>} { +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Foo::@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Parallel { +// CHECK-NEXT: struct.def @Parallel { +// CHECK-NEXT: struct.member @foo : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: struct.member @foo$inputs : !pod.type<[]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@Parallel::@Parallel<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Parallel::@Parallel<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = function.call @Foo::@Foo::@compute() : () -> !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_6]] } : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: struct.writem %[[VAL_2]][@foo$inputs] = %[[VAL_3]] : <@Parallel::@Parallel<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@foo] = %[[VAL_8]] : <@Parallel::@Parallel<[]>>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Parallel::@Parallel<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@Parallel::@Parallel<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@foo] : <@Parallel::@Parallel<[]>>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@foo$inputs] : <@Parallel::@Parallel<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: function.call @Foo::@Foo::@constrain(%[[VAL_10]]) : (!struct.type<@Foo::@Foo<[]>>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/parallel_template.circom b/circom/tests/simple/parallel_template.circom new file mode 100644 index 000000000..a3d4035de --- /dev/null +++ b/circom/tests/simple/parallel_template.circom @@ -0,0 +1,53 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template parallel Foo() {} + +template Parallel() { + component foo = Foo(); +} + +component main = Parallel(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Parallel::@Parallel<[]>>} { +// CHECK-NEXT: poly.template @Foo { +// CHECK-NEXT: struct.def @Foo { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Foo::@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo::@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo::@Foo<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Parallel { +// CHECK-NEXT: struct.def @Parallel { +// CHECK-NEXT: struct.member @foo : !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: struct.member @foo$inputs : !pod.type<[]> +// CHECK-NEXT: function.def @compute() -> !struct.type<@Parallel::@Parallel<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Parallel::@Parallel<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = function.call @Foo::@Foo::@compute() : () -> !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_6]] } : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: struct.writem %[[VAL_2]][@foo$inputs] = %[[VAL_3]] : <@Parallel::@Parallel<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@foo] = %[[VAL_8]] : <@Parallel::@Parallel<[]>>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Parallel::@Parallel<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@Parallel::@Parallel<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@foo] : <@Parallel::@Parallel<[]>>, !struct.type<@Foo::@Foo<[]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_9]][@foo$inputs] : <@Parallel::@Parallel<[]>>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Foo::@Foo<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: function.call @Foo::@Foo::@constrain(%[[VAL_10]]) : (!struct.type<@Foo::@Foo<[]>>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/read_from_output.circom b/circom/tests/simple/read_from_output.circom new file mode 100644 index 000000000..9c78e3d06 --- /dev/null +++ b/circom/tests/simple/read_from_output.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ReadFromOutput() { + signal input inp; + signal output outp; + signal intermediate; + outp <== inp; + intermediate <== outp; +} + +component main = ReadFromOutput(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ReadFromOutput::@ReadFromOutput<[]>>} { +// CHECK-NEXT: poly.template @ReadFromOutput { +// CHECK-NEXT: struct.def @ReadFromOutput { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @intermediate : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ReadFromOutput::@ReadFromOutput<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ReadFromOutput::@ReadFromOutput<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@outp] = %[[VAL_0]] : <@ReadFromOutput::@ReadFromOutput<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@intermediate] = %[[VAL_0]] : <@ReadFromOutput::@ReadFromOutput<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ReadFromOutput::@ReadFromOutput<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@ReadFromOutput::@ReadFromOutput<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@outp] : <@ReadFromOutput::@ReadFromOutput<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@intermediate] : <@ReadFromOutput::@ReadFromOutput<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/read_from_output_with_felt_cast.circom b/circom/tests/simple/read_from_output_with_felt_cast.circom new file mode 100644 index 000000000..108e6a31f --- /dev/null +++ b/circom/tests/simple/read_from_output_with_felt_cast.circom @@ -0,0 +1,43 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template ReadFromOutputWithFeltCast() { + signal input inp; + signal output outp; + signal intermediate; + outp <== inp == 0; + intermediate <== outp; +} + +component main = ReadFromOutputWithFeltCast(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>>} { +// CHECK-NEXT: poly.template @ReadFromOutputWithFeltCast { +// CHECK-NEXT: struct.def @ReadFromOutputWithFeltCast { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @intermediate : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_3]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@outp] = %[[VAL_4]] : <@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@intermediate] = %[[VAL_4]] : <@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@outp] : <@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_5]][@intermediate] : <@ReadFromOutputWithFeltCast::@ReadFromOutputWithFeltCast<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_6]], %[[VAL_9]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_10]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_11]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/simple_01.circom b/circom/tests/simple/simple_01.circom new file mode 100644 index 000000000..1e8748158 --- /dev/null +++ b/circom/tests/simple/simple_01.circom @@ -0,0 +1,38 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Simple3() { + signal input a; + signal output b; + signal output c; + + b <== a; + c <== a; +} + +component main = Simple3(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Simple3::@Simple3<[]>>} { +// CHECK-NEXT: poly.template @Simple3 { +// CHECK-NEXT: struct.def @Simple3 { +// CHECK-NEXT: struct.member @b : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Simple3::@Simple3<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple3::@Simple3<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@b] = %[[VAL_0]] : <@Simple3::@Simple3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@c] = %[[VAL_0]] : <@Simple3::@Simple3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Simple3::@Simple3<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Simple3::@Simple3<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@b] : <@Simple3::@Simple3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@c] : <@Simple3::@Simple3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/simple_02.circom b/circom/tests/simple/simple_02.circom new file mode 100644 index 000000000..5acdf638b --- /dev/null +++ b/circom/tests/simple/simple_02.circom @@ -0,0 +1,47 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Simple4(a) { + signal output b; + signal input c; + signal input d; + var x; + var y; + + x = a; + y = 11; + + b <== a; +} + +component main = Simple4(10); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Simple4::@Simple4<[10]>>} { +// CHECK-NEXT: poly.template @Simple4 { +// CHECK-NEXT: poly.param @a +// CHECK-NEXT: struct.def @Simple4 { +// CHECK-NEXT: struct.member @b : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Simple4::@Simple4<[@a]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple4::@Simple4<[@a]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: struct.writem %[[VAL_2]][@b] = %[[VAL_3]] : <@Simple4::@Simple4<[@a]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Simple4::@Simple4<[@a]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@Simple4::@Simple4<[@a]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_7]][@b] : <@Simple4::@Simple4<[@a]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/simple_03.circom b/circom/tests/simple/simple_03.circom new file mode 100644 index 000000000..af110ace2 --- /dev/null +++ b/circom/tests/simple/simple_03.circom @@ -0,0 +1,45 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Simple5() { + signal input a; + signal input b; + signal input c; + + signal output x; + signal output y; + signal output z; + + x <== a; + y <-- b; + y === b; +} + +component main = Simple5(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Simple5::@Simple5<[]>>} { +// CHECK-NEXT: poly.template @Simple5 { +// CHECK-NEXT: struct.def @Simple5 { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @z : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Simple5::@Simple5<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple5::@Simple5<[]>> +// CHECK-NEXT: struct.writem %[[VAL_3]][@x] = %[[VAL_0]] : <@Simple5::@Simple5<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@y] = %[[VAL_1]] : <@Simple5::@Simple5<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Simple5::@Simple5<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Simple5::@Simple5<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@x] : <@Simple5::@Simple5<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@y] : <@Simple5::@Simple5<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@z] : <@Simple5::@Simple5<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_5]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_6]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/simple_04.circom b/circom/tests/simple/simple_04.circom new file mode 100644 index 000000000..8f4878cf6 --- /dev/null +++ b/circom/tests/simple/simple_04.circom @@ -0,0 +1,124 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input a; + signal input b; + signal output c; + + c <== a * b; +} + +template A() { + signal input a; + signal input b; + signal input d; + signal output c; + signal x; + + component cb = B(); + cb.a <== a; + cb.b <== b; + + x <== cb.c; + c <== x * d; +} + +component main {public [a, b]} = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @cb : !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.member @cb$inputs : !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_6]], @params = %[[VAL_5]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_4]][@a] = %[[VAL_0]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_8]], %[[VAL_9]] : index +// CHECK-NEXT: pod.write %[[VAL_7]][@count] = %[[VAL_10]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_10]], %[[VAL_11]] : index +// CHECK-NEXT: scf.if %[[VAL_12]] { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_14]], %[[VAL_15]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_7]][@comp] = %[[VAL_16]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: pod.write %[[VAL_4]][@b] = %[[VAL_1]] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@count] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_17]], %[[VAL_18]] : index +// CHECK-NEXT: pod.write %[[VAL_7]][@count] = %[[VAL_19]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_19]], %[[VAL_20]] : index +// CHECK-NEXT: scf.if %[[VAL_21]] { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@params] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_4]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = function.call @B::@B::@compute(%[[VAL_23]], %[[VAL_24]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@B::@B<[]>> +// CHECK-NEXT: pod.write %[[VAL_7]][@comp] = %[[VAL_25]] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_26]][@c] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@x] = %[[VAL_27]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_27]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@c] = %[[VAL_28]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@cb$inputs] = %[[VAL_4]] : <@A::@A<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.writem %[[VAL_3]][@cb] = %[[VAL_29]] : <@A::@A<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_32:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_33:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@c] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@x] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@cb] : <@A::@A<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@cb$inputs] : <@A::@A<[]>>, !pod.type<[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_40]], %[[VAL_31]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_41]], %[[VAL_32]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_36]][@c] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_35]], %[[VAL_42]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_35]], %[[VAL_33]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_34]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@a] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_37]][@b] : <[@a: !felt.type<"bn128">, @b: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_36]], %[[VAL_44]], %[[VAL_45]]) : (!struct.type<@B::@B<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: struct.member @c : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_46]], %[[VAL_47]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_48]][@c] = %[[VAL_49]] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_48]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_50:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_51:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}, %[[VAL_52:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128"> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_50]][@c] : <@B::@B<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_51]], %[[VAL_52]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_53]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/simple_05.circom b/circom/tests/simple/simple_05.circom new file mode 100644 index 000000000..5635f8320 --- /dev/null +++ b/circom/tests/simple/simple_05.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal input a, b, d; + signal output out; + + out <== (a + b) * d; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_4]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_3]][@out] = %[[VAL_5]] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_6]][@out] : <@A::@A<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_8]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_11]], %[[VAL_9]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/trivially_empty.circom b/circom/tests/simple/trivially_empty.circom new file mode 100644 index 000000000..b5fbc2b34 --- /dev/null +++ b/circom/tests/simple/trivially_empty.circom @@ -0,0 +1,23 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template EmptyTemplate() { +} +component main = EmptyTemplate(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@EmptyTemplate::@EmptyTemplate<[]>>} { +// CHECK-NEXT: poly.template @EmptyTemplate { +// CHECK-NEXT: struct.def @EmptyTemplate { +// CHECK-NEXT: function.def @compute() -> !struct.type<@EmptyTemplate::@EmptyTemplate<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@EmptyTemplate::@EmptyTemplate<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@EmptyTemplate::@EmptyTemplate<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@EmptyTemplate::@EmptyTemplate<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/underscore_substitution_num.circom b/circom/tests/simple/underscore_substitution_num.circom new file mode 100644 index 000000000..4aec3c1cc --- /dev/null +++ b/circom/tests/simple/underscore_substitution_num.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + _ <-- 99; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/underscore_substitution_var.circom b/circom/tests/simple/underscore_substitution_var.circom new file mode 100644 index 000000000..572fe8b47 --- /dev/null +++ b/circom/tests/simple/underscore_substitution_var.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A() { + signal input in; + _ <-- in; +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/simple/var_consts.circom b/circom/tests/simple/var_consts.circom new file mode 100644 index 000000000..da17268f3 --- /dev/null +++ b/circom/tests/simple/var_consts.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Van276() { + var a = 168700; + var b = 999999; +} + +component main = Van276(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Van276::@Van276<[]>>} { +// CHECK-NEXT: poly.template @Van276 { +// CHECK-NEXT: struct.def @Van276 { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Van276::@Van276<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Van276::@Van276<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 168700 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 999999 +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Van276::@Van276<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Van276::@Van276<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 168700 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 999999 +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/array_copy_constraints.circom b/circom/tests/subcmps/array_copy_constraints.circom new file mode 100644 index 000000000..e4a04a63e --- /dev/null +++ b/circom/tests/subcmps/array_copy_constraints.circom @@ -0,0 +1,132 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sum(n) { + signal input inp[n]; + signal output outp; + + var acc = 0; + for (var i = 0; i < n; i++) { + acc += inp[i]; + } + + outp <== acc; +} + +template Caller(n) { + signal input inp[n]; + signal outp; + + component op = Sum(n); + op.inp <== inp; + + outp <== op.outp; +} + +component main = Caller(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller::@Caller<[5]>>} { +// CHECK-NEXT: poly.template @Caller { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Caller { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> +// CHECK-NEXT: struct.member @op : !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.member @op$inputs : !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Caller::@Caller<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller::@Caller<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_4]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_6]], @params = %[[VAL_5]] } : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_3]][@inp] = %[[VAL_0]] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@count] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_8]], %[[VAL_9]] : index +// CHECK-NEXT: pod.write %[[VAL_7]][@count] = %[[VAL_10]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_10]], %[[VAL_11]] : index +// CHECK-NEXT: scf.if %[[VAL_12]] { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@params] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_3]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = function.call @Sum::@Sum::@compute(%[[VAL_14]]) : (!array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_7]][@comp] = %[[VAL_15]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_16]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@outp] = %[[VAL_17]] : <@Caller::@Caller<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@op$inputs] = %[[VAL_3]] : <@Caller::@Caller<[@n]>>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@op] = %[[VAL_18]] : <@Caller::@Caller<[@n]>>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Caller::@Caller<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller::@Caller<[@n]>>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@outp] : <@Caller::@Caller<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@op] : <@Caller::@Caller<[@n]>>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@op$inputs] : <@Caller::@Caller<[@n]>>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_25]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: constrain.eq %[[VAL_28]], %[[VAL_20]] : !array.type<@n x !felt.type<"bn128">>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_23]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_22]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum::@Sum::@constrain(%[[VAL_23]], %[[VAL_30]]) : (!struct.type<@Sum::@Sum<[@n]>>, !array.type<@n x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sum { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Sum { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_37:[0-9a-zA-Z_\.]+]] = %[[VAL_34]], %[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_35]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_38]], %[[VAL_33]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_39]]) %[[VAL_37]], %[[VAL_38]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_41:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_42]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_43]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_45]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_44]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_32]][@outp] = %[[VAL_36]]#0 : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_32]] : !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum::@Sum<[@n]>>, %[[VAL_48:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_47]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_54:[0-9a-zA-Z_\.]+]] = %[[VAL_51]], %[[VAL_55:[0-9a-zA-Z_\.]+]] = %[[VAL_52]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_55]], %[[VAL_49]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_56]]) %[[VAL_54]], %[[VAL_55]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_57:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_58:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_58]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_48]]{{\[}}%[[VAL_59]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_57]], %[[VAL_60]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_58]], %[[VAL_62]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_61]], %[[VAL_63]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_50]], %[[VAL_53]]#0 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/conv_map2idx_A.circom b/circom/tests/subcmps/conv_map2idx_A.circom new file mode 100644 index 000000000..b922ec247 --- /dev/null +++ b/circom/tests/subcmps/conv_map2idx_A.circom @@ -0,0 +1,157 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.3; + +template GetWeight(A) { + signal input inp; +} + +template ComputeValue() { + component getWeights[2]; + + getWeights[0] = GetWeight(0); + getWeights[0].inp <-- 888; + + getWeights[1] = GetWeight(1); + getWeights[1].inp <-- 999; +} + +component main = ComputeValue(); + +// CHECK-LABEL: #[[$ATTR_0]] = affine_map<(d0) -> (d0)> +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ComputeValue::@ComputeValue<[]>>} { +// CHECK-NEXT: poly.template @ComputeValue { +// CHECK-NEXT: struct.def @ComputeValue { +// CHECK-NEXT: struct.member @getWeights : !array.type<2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>> +// CHECK-NEXT: struct.member @getWeights$inputs : !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@ComputeValue::@ComputeValue<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ComputeValue::@ComputeValue<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_3]] } : <[@A: index]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_5]], @params = %[[VAL_4]] } : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[0]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_6]] : (!pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[0]>>, @params: !pod.type<[@A: index]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_8]]] = %[[VAL_9]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 888 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_12]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_13]][@inp] = %[[VAL_10]] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_15]]] = %[[VAL_13]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_17]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@count] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_19]], %[[VAL_20]] : index +// CHECK-NEXT: pod.write %[[VAL_18]][@count] = %[[VAL_21]] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_21]], %[[VAL_22]] : index +// CHECK-NEXT: scf.if %[[VAL_23]] { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_18]][@params] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, !pod.type<[@A: index]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@A] : <[@A: index]>, index +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = function.call @GetWeight::@GetWeight::@compute(%[[VAL_25]]) {(%[[VAL_26]])} : (!felt.type<"bn128">) -> !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>> +// CHECK-NEXT: pod.write %[[VAL_18]][@comp] = %[[VAL_27]] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_29]]] = %[[VAL_18]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_30]] } : <[@A: index]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_32]], @params = %[[VAL_31]] } : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[1]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_33]] : (!pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[1]>>, @params: !pod.type<[@A: index]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_35]]] = %[[VAL_36]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 999 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_39]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_40]][@inp] = %[[VAL_37]] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_42]]] = %[[VAL_40]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_44]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@count] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, index +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_46]], %[[VAL_47]] : index +// CHECK-NEXT: pod.write %[[VAL_45]][@count] = %[[VAL_48]] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, index +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_48]], %[[VAL_49]] : index +// CHECK-NEXT: scf.if %[[VAL_50]] { +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@params] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, !pod.type<[@A: index]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_40]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@A] : <[@A: index]>, index +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = function.call @GetWeight::@GetWeight::@compute(%[[VAL_52]]) {(%[[VAL_53]])} : (!felt.type<"bn128">) -> !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>> +// CHECK-NEXT: pod.write %[[VAL_45]][@comp] = %[[VAL_54]] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_56]]] = %[[VAL_45]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@getWeights$inputs] = %[[VAL_2]] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.new : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_61:[0-9a-zA-Z_\.]+]] = %[[VAL_59]] to %[[VAL_58]] step %[[VAL_60]] { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_61]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_62]][@comp] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, @params: !pod.type<[@A: index]>]>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>> +// CHECK-NEXT: array.write %[[VAL_57]]{{\[}}%[[VAL_61]]] = %[[VAL_63]] : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@getWeights] = %[[VAL_57]] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ComputeValue::@ComputeValue<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_64:[0-9a-zA-Z_\.]+]]: !struct.type<@ComputeValue::@ComputeValue<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@getWeights] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@getWeights$inputs] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !pod.type<[@inp: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_67]] } : <[@A: index]> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_69]], @params = %[[VAL_68]] } : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[0]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_71]] } : <[@A: index]> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_73]], @params = %[[VAL_72]] } : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[1]>>, @params: !pod.type<[@A: index]>]> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_78:[0-9a-zA-Z_\.]+]] = %[[VAL_76]] to %[[VAL_75]] step %[[VAL_77]] { +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_65]]{{\[}}%[[VAL_78]]] : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_66]]{{\[}}%[[VAL_78]]] : <2 x !pod.type<[@inp: !felt.type<"bn128">]>>, !pod.type<[@inp: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_80]][@inp] : <[@inp: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @GetWeight::@GetWeight::@constrain(%[[VAL_79]], %[[VAL_81]]) : (!struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]]]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @GetWeight { +// CHECK-NEXT: poly.param @A +// CHECK-NEXT: struct.def @GetWeight { +// CHECK-NEXT: function.def @compute(%[[VAL_82:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@GetWeight::@GetWeight<[@A]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = struct.new : <@GetWeight::@GetWeight<[@A]>> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = poly.read_const @A : !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_83]] : !struct.type<@GetWeight::@GetWeight<[@A]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_85:[0-9a-zA-Z_\.]+]]: !struct.type<@GetWeight::@GetWeight<[@A]>>, %[[VAL_86:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = poly.read_const @A : !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/conv_map2idx_B.circom b/circom/tests/subcmps/conv_map2idx_B.circom new file mode 100644 index 000000000..5aa4b4ab2 --- /dev/null +++ b/circom/tests/subcmps/conv_map2idx_B.circom @@ -0,0 +1,153 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.3; + +template GetWeight(A, B) { + signal output x; //signal index 0 + signal output y; //signal index 1 + signal output out; //signal index 2 + out <-- A; +} + +template ComputeValue() { + component ws[2]; + ws[0] = GetWeight(999, 0); + ws[1] = GetWeight(888, 1); + + signal ret[2]; + ret[0] <== ws[0].out; + ret[1] <== ws[1].out; +} + +component main = ComputeValue(); + +// CHECK-LABEL: #[[$ATTR_0]] = affine_map<(d0) -> (d0)> +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@ComputeValue::@ComputeValue<[]>>} { +// CHECK-NEXT: poly.template @ComputeValue { +// CHECK-NEXT: struct.def @ComputeValue { +// CHECK-NEXT: struct.member @ret : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: struct.member @ws : !array.type<2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>> +// CHECK-NEXT: struct.member @ws$inputs : !array.type<2 x !pod.type<[]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@ComputeValue::@ComputeValue<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ComputeValue::@ComputeValue<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = arith.constant 999 : index +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_4]], @B = %[[VAL_5]] } : <[@A: index, @B: index]> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @GetWeight::@GetWeight::@compute() : () -> !struct.type<@GetWeight::@GetWeight<[999, 0]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_8]] } : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[999, 0]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_9]] : (!pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[999, 0]>>, @params: !pod.type<[@A: index, @B: index]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_11]]] = %[[VAL_12]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 888 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_13]], @B = %[[VAL_14]] } : <[@A: index, @B: index]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = function.call @GetWeight::@GetWeight::@compute() : () -> !struct.type<@GetWeight::@GetWeight<[888, 1]>> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.new { @comp = %[[VAL_17]] } : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[888, 1]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = poly.unifiable_cast %[[VAL_18]] : (!pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[888, 1]>>, @params: !pod.type<[@A: index, @B: index]>]>) -> !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_20]]] = %[[VAL_21]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_23]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_24]][@comp] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@out] : <@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_28]]] = %[[VAL_26]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_30]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@comp] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_32]][@out] : <@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_35]]] = %[[VAL_33]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_0]][@ws$inputs] = %[[VAL_3]] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !pod.type<[]>> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.new : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_40:[0-9a-zA-Z_\.]+]] = %[[VAL_38]] to %[[VAL_37]] step %[[VAL_39]] { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_40]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>>, !pod.type<[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_41]][@comp] : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, @params: !pod.type<[@A: index, @B: index]>]>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>> +// CHECK-NEXT: array.write %[[VAL_36]]{{\[}}%[[VAL_40]]] = %[[VAL_42]] : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@ws] = %[[VAL_36]] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>> +// CHECK-NEXT: struct.writem %[[VAL_0]][@ret] = %[[VAL_1]] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ComputeValue::@ComputeValue<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !struct.type<@ComputeValue::@ComputeValue<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@ret] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@ws] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@ws$inputs] : <@ComputeValue::@ComputeValue<[]>>, !array.type<2 x !pod.type<[]>> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 999 : index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_47]], @B = %[[VAL_48]] } : <[@A: index, @B: index]> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[999, 0]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.constant 888 : index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.new { @A = %[[VAL_51]], @B = %[[VAL_52]] } : <[@A: index, @B: index]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@GetWeight::@GetWeight<[888, 1]>>, @params: !pod.type<[@A: index, @B: index]>]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_45]]{{\[}}%[[VAL_56]]] : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_57]][@out] : <@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_44]]{{\[}}%[[VAL_60]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_61]], %[[VAL_58]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_62]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_45]]{{\[}}%[[VAL_63]]] : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@out] : <@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_66]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_44]]{{\[}}%[[VAL_67]]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_68]], %[[VAL_65]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_72:[0-9a-zA-Z_\.]+]] = %[[VAL_70]] to %[[VAL_69]] step %[[VAL_71]] { +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_45]]{{\[}}%[[VAL_72]]] : <2 x !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>>, !struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_46]]{{\[}}%[[VAL_72]]] : <2 x !pod.type<[]>>, !pod.type<[]> +// CHECK-NEXT: function.call @GetWeight::@GetWeight::@constrain(%[[VAL_73]]) : (!struct.type<@GetWeight::@GetWeight<[#[[$ATTR_0]], #[[$ATTR_0]]]>>) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @GetWeight { +// CHECK-NEXT: poly.param @A +// CHECK-NEXT: poly.param @B +// CHECK-NEXT: struct.def @GetWeight { +// CHECK-NEXT: struct.member @x : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @y : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@GetWeight::@GetWeight<[@A, @B]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = struct.new : <@GetWeight::@GetWeight<[@A, @B]>> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = poly.read_const @A : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = poly.read_const @B : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_75]][@out] = %[[VAL_76]] : <@GetWeight::@GetWeight<[@A, @B]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_75]] : !struct.type<@GetWeight::@GetWeight<[@A, @B]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_78:[0-9a-zA-Z_\.]+]]: !struct.type<@GetWeight::@GetWeight<[@A, @B]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = poly.read_const @A : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = poly.read_const @B : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_78]][@x] : <@GetWeight::@GetWeight<[@A, @B]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_78]][@y] : <@GetWeight::@GetWeight<[@A, @B]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_78]][@out] : <@GetWeight::@GetWeight<[@A, @B]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/conv_map2idx_C.circom b/circom/tests/subcmps/conv_map2idx_C.circom new file mode 100644 index 000000000..f4e781694 --- /dev/null +++ b/circom/tests/subcmps/conv_map2idx_C.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template SegmentMulFix(nWindows) { + signal input e[nWindows]; +} + +template EscalarMulFix() { + //Needs at least 2 subcomp to trigger the crash + component segments[2]; + for (var s = 0; s < 2; s++) { + + // s = 0, nseg = 9, nWindows = 9 + // s = 1, nseg = 4, nWindows = 6 + var nseg = (s == 0) ? 9 : 4; + var nWindows = (s == 0) ? 9 : 6; + + segments[s] = SegmentMulFix(nWindows); + + // Needs this split loop to trigger the crash + for (var i = 0; i < nseg; i++) { + //Runs 9 times for s=0 + //Runs 4 times for s=1 + segments[s].e[i] <-- 999; + } + for (var i = nseg; i < nWindows; i++) { + //Runs 0 times for s=0 //this is the case where the extracted body is generated but shouldn't be! + //Runs 2 times for s=1 + segments[s].e[i] <-- 888; + } + } +} + +component main = EscalarMulFix(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/subcmps/large_array_param.circom b/circom/tests/subcmps/large_array_param.circom new file mode 100644 index 000000000..89c819746 --- /dev/null +++ b/circom/tests/subcmps/large_array_param.circom @@ -0,0 +1,63 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function create_large_array(t) { + if (t == 2) { + return [ + 11, + 22, + 33, + 44 + ]; + } else if (t == 3) { + return [ + 11, + 22, + 33, + 44, + 55, + 66, + 77, + 88, + 99 + ]; + } else { + assert(0); + return [0]; + } +} + +template Mixer(t, S, r) { + signal input inp; + signal output out; + + var lc = 0; + for (var i = 0; i < t; i++) { + lc += S[t*r+i] * inp; + } + out <== lc; +} + +template Main(t) { + signal input inp; + signal output out; + + var S[t*t] = create_large_array(t); + + component mix[t]; + var lc = 0; + for (var r = 0; r < t; r++) { + mix[r] = Mixer(t, S, r); + mix[r].inp <-- inp; + lc += mix[r].out; + } + out <-- lc; +} + +component main = Main(2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/subcmps/mapped_01.circom b/circom/tests/subcmps/mapped_01.circom new file mode 100644 index 000000000..44cbbd938 --- /dev/null +++ b/circom/tests/subcmps/mapped_01.circom @@ -0,0 +1,45 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A(n) { + signal input a[n]; + signal input b[n]; + signal output c[n]; + + var i; + for (i = 0; i < n; i++) { + c[i] <== a[i] * b[i]; + } +} + +template B(n) { + signal input a[n * 4]; + signal output b[n]; + + component as[2]; + + as[0] = A(n * 2); + var i; + for (i = 0; i < n * 2; i++) { + as[0].a[i] <== a[i]; + as[0].b[i] <== a[i + n * 2]; + } + + as[1] = A(n); + for(i = 0; i < n; i++) { + as[1].a[i] <== as[0].c[i]; + as[1].b[i] <== as[0].c[i + n]; + } + + for (i = 0; i < n; i++) { + b[i] <== as[1].c[i]; + } +} + +component main = B(2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/subcmps/mapped_02.circom b/circom/tests/subcmps/mapped_02.circom new file mode 100644 index 000000000..7cb2927af --- /dev/null +++ b/circom/tests/subcmps/mapped_02.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A(n) { + signal input a[n]; + signal output c[n]; + + var i; + for (i = 0; i < n; i++) { + c[i] <== a[i] * 2; + } +} + +template B(n, m, j) { + signal input a[n][j]; + signal output b[n][j]; + signal input c[m][j]; + signal output d[m][j]; + + component as[2][j]; + + var i; + var k; + for (k = 0; k < j; k++) { + as[0][k] = A(n); + } + for (i = 0; i < n; i++) { + for (k = 0; k < j; k++) { + as[0][k].a[i] <== a[i][k]; + } + } + + + for (k = 0; k < j; k++) { + as[1][k] = A(m); + } + for(i = 0; i < m; i++) { + for (k = 0; k < j; k++) { + as[1][k].a[i] <== c[i][k]; + } + } + + for (i = 0; i < n; i++) { + for (k = 0; k < j; k++) { + b[i][k] <== as[0][k].c[i]; + } + } + + for (i = 0; i < m; i++) { + for (k = 0; k < j; k++) { + d[i][k] <== as[1][k].c[i]; + } + } +} + +component main = B(2, 3, 2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/subcmps/mapped_03.circom b/circom/tests/subcmps/mapped_03.circom new file mode 100644 index 000000000..f64f0e78b --- /dev/null +++ b/circom/tests/subcmps/mapped_03.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template ArrayOp(q) { + signal input inp[15]; + signal output outp[15]; + + for (var i = 0; i < 15; i++) { + outp[i] <== inp[i] + q; + } +} + +template Wrapper() { + signal input inp[15]; + signal output outp; + + component m[4]; + + for (var q = 0; q < 4; q++) { + // This test exhibits the behavior because the array of different subcomponents + // (differentiated by the template parameter changing) + m[q] = ArrayOp(q); + for (var i = 0; i < 15; i++) { + m[q].inp[i] <== inp[i]; + } + } + + outp <== m[2].outp[3]; +} + +component main = Wrapper(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/subcmps/mapped_04.circom b/circom/tests/subcmps/mapped_04.circom new file mode 100644 index 000000000..7a0dc4c68 --- /dev/null +++ b/circom/tests/subcmps/mapped_04.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template MatrixOp(q) { + signal input inp[5][3]; + signal output outp[5][3]; + + for (var i = 0; i < 5; i++) { + for (var j = 0; j < 3; j++) { + outp[i][j] <== inp[i][j] + q; + } + } +} + +template Wrapper() { + signal input inp[5][3]; + signal output outp; + + component m[4]; + + for (var q = 0; q < 4; q++) { + // This test exhibits the behavior because the array of different subcomponents + // (differentiated by the template parameter changing) + m[q] = MatrixOp(q); + for (var i = 0; i < 5; i++) { + for (var j = 0; j < 3; j++) { + m[q].inp[i][j] <== inp[i][j]; + } + } + } + + outp <== m[2].outp[1][2]; +} + +component main = Wrapper(); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/subcmps/multi_infix_in_loop.circom b/circom/tests/subcmps/multi_infix_in_loop.circom new file mode 100644 index 000000000..cc1ba1ce7 --- /dev/null +++ b/circom/tests/subcmps/multi_infix_in_loop.circom @@ -0,0 +1,24 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +template A(n) { + signal output x; + x <== n; +} + +template B(N) { + component comp[N][N]; + for(var i = 1; i < N; i++) { + for(var j = 0; j < N; j++) { + comp[i][j] = A(i * 4 + j); + } + } +} + +component main = B(2); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/subcmps/subcmps0A.circom b/circom/tests/subcmps/subcmps0A.circom new file mode 100644 index 000000000..e12905161 --- /dev/null +++ b/circom/tests/subcmps/subcmps0A.circom @@ -0,0 +1,145 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Like SubCmps1 but simpler (no constraints and fewer operations) +template IsZero() { + signal input in; + signal output out; + out <-- -in; +} + +template SubCmps0A(n) { + signal input ins[n]; + signal output outs[n]; + + component zeros[n]; + for (var i = 0; i < n; i++) { + zeros[i] = IsZero(); + zeros[i].in <-- ins[i]; + outs[i] <-- zeros[i].out; + } +} + +component main = SubCmps0A(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmps0A::@SubCmps0A<[2]>>} { +// CHECK-NEXT: poly.template @IsZero { +// CHECK-NEXT: struct.def @IsZero { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_2]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero::@IsZero<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmps0A { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @SubCmps0A { +// CHECK-NEXT: struct.member @outs : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @zeros : !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.member @zeros$inputs : !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@SubCmps0A::@SubCmps0A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmps0A::@SubCmps0A<[@n]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_14:[0-9a-zA-Z_\.]+]] = %[[VAL_12]], %[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_11]]) : (!felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) -> (!felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_14]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_16]]) %[[VAL_14]], %[[VAL_15]] : !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>): +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_20]], @params = %[[VAL_19]] } : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_22]]] = %[[VAL_21]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_6]]{{\[}}%[[VAL_23]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_18]]{{\[}}%[[VAL_25]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_26]][@in] = %[[VAL_24]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_18]]{{\[}}%[[VAL_27]]] = %[[VAL_26]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_28]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@count] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_30]], %[[VAL_31]] : index +// CHECK-NEXT: pod.write %[[VAL_29]][@count] = %[[VAL_32]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_32]], %[[VAL_33]] : index +// CHECK-NEXT: scf.if %[[VAL_34]] { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@params] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = function.call @IsZero::@IsZero::@compute(%[[VAL_36]]) : (!felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: pod.write %[[VAL_29]][@comp] = %[[VAL_37]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_38]]] = %[[VAL_29]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_39]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_40]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_9]]{{\[}}%[[VAL_43]]] = %[[VAL_42]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_44]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_45]], %[[VAL_18]] : !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_7]][@zeros$inputs] = %[[VAL_13]]#1 : <@SubCmps0A::@SubCmps0A<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = array.new : <@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_50:[0-9a-zA-Z_\.]+]] = %[[VAL_48]] to %[[VAL_47]] step %[[VAL_49]] { +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_50]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: array.write %[[VAL_46]]{{\[}}%[[VAL_50]]] = %[[VAL_52]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_7]][@zeros] = %[[VAL_46]] : <@SubCmps0A::@SubCmps0A<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_7]][@outs] = %[[VAL_9]] : <@SubCmps0A::@SubCmps0A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@SubCmps0A::@SubCmps0A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_53:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmps0A::@SubCmps0A<[@n]>>, %[[VAL_54:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_53]][@outs] : <@SubCmps0A::@SubCmps0A<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_53]][@zeros] : <@SubCmps0A::@SubCmps0A<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_53]][@zeros$inputs] : <@SubCmps0A::@SubCmps0A<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_61:[0-9a-zA-Z_\.]+]] = %[[VAL_59]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_61]], %[[VAL_55]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_62]]) %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_63:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_63]], %[[VAL_66]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_71:[0-9a-zA-Z_\.]+]] = %[[VAL_69]] to %[[VAL_68]] step %[[VAL_70]] { +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_57]]{{\[}}%[[VAL_71]]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_58]]{{\[}}%[[VAL_71]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_73]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @IsZero::@IsZero::@constrain(%[[VAL_72]], %[[VAL_74]]) : (!struct.type<@IsZero::@IsZero<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps0B.circom b/circom/tests/subcmps/subcmps0B.circom new file mode 100644 index 000000000..af647d7af --- /dev/null +++ b/circom/tests/subcmps/subcmps0B.circom @@ -0,0 +1,155 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +// Like SubCmps1 but simpler (no constraints and fewer operations) +template IsZero() { + signal input in; + signal output out; + out <-- -in; +} + +template SubCmps0B(n) { + signal input ins[n]; + signal output outs[n]; + var temp; + component zeros[n]; + for (var i = 0; i < n; i++) { + zeros[i] = IsZero(); + zeros[i].in <-- ins[i]; + outs[i] <-- zeros[i].out; + temp = zeros[i].out; + } +} + +component main = SubCmps0B(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmps0B::@SubCmps0B<[2]>>} { +// CHECK-NEXT: poly.template @IsZero { +// CHECK-NEXT: struct.def @IsZero { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_2]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero::@IsZero<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmps0B { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @SubCmps0B { +// CHECK-NEXT: struct.member @outs : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @zeros : !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.member @zeros$inputs : !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@SubCmps0B::@SubCmps0B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmps0B::@SubCmps0B<[@n]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_13]], %[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_12]], %[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_11]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) -> (!felt.type<"bn128">, !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_15]], %[[VAL_8]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_15]], %[[VAL_16]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>): +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_23]], @params = %[[VAL_22]] } : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_25]]] = %[[VAL_24]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_6]]{{\[}}%[[VAL_26]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_21]]{{\[}}%[[VAL_28]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_29]][@in] = %[[VAL_27]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_21]]{{\[}}%[[VAL_30]]] = %[[VAL_29]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_31]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_32]][@count] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_33]], %[[VAL_34]] : index +// CHECK-NEXT: pod.write %[[VAL_32]][@count] = %[[VAL_35]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_35]], %[[VAL_36]] : index +// CHECK-NEXT: scf.if %[[VAL_37]] { +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_32]][@params] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = function.call @IsZero::@IsZero::@compute(%[[VAL_39]]) : (!felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: pod.write %[[VAL_32]][@comp] = %[[VAL_40]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_10]]{{\[}}%[[VAL_41]]] = %[[VAL_32]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_42]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_43]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_44]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_9]]{{\[}}%[[VAL_46]]] = %[[VAL_45]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_47]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_49]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_19]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_52]], %[[VAL_50]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_7]][@zeros$inputs] = %[[VAL_14]]#2 : <@SubCmps0B::@SubCmps0B<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.new : <@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_57:[0-9a-zA-Z_\.]+]] = %[[VAL_55]] to %[[VAL_54]] step %[[VAL_56]] { +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_57]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_58]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: array.write %[[VAL_53]]{{\[}}%[[VAL_57]]] = %[[VAL_59]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_7]][@zeros] = %[[VAL_53]] : <@SubCmps0B::@SubCmps0B<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_7]][@outs] = %[[VAL_9]] : <@SubCmps0B::@SubCmps0B<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@SubCmps0B::@SubCmps0B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_60:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmps0B::@SubCmps0B<[@n]>>, %[[VAL_61:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_60]][@outs] : <@SubCmps0B::@SubCmps0B<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_60]][@zeros] : <@SubCmps0B::@SubCmps0B<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_60]][@zeros$inputs] : <@SubCmps0B::@SubCmps0B<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_69:[0-9a-zA-Z_\.]+]] = %[[VAL_67]], %[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_66]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_69]], %[[VAL_62]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_71]]) %[[VAL_69]], %[[VAL_70]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_72:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_73:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_64]]{{\[}}%[[VAL_76]]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_77]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_72]], %[[VAL_79]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_80]], %[[VAL_78]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_84:[0-9a-zA-Z_\.]+]] = %[[VAL_82]] to %[[VAL_81]] step %[[VAL_83]] { +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_64]]{{\[}}%[[VAL_84]]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_65]]{{\[}}%[[VAL_84]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_86]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @IsZero::@IsZero::@constrain(%[[VAL_85]], %[[VAL_87]]) : (!struct.type<@IsZero::@IsZero<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps0C.circom b/circom/tests/subcmps/subcmps0C.circom new file mode 100644 index 000000000..cf84d75b3 --- /dev/null +++ b/circom/tests/subcmps/subcmps0C.circom @@ -0,0 +1,149 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template IsZero() { + signal input in; + signal output out; + signal temp <-- -in; + out <-- temp * temp; +} + +template SubCmps0C(n) { + signal input ins[n]; + signal output outs[n]; + + component zeros[n]; + for (var i = 0; i < n; i++) { + zeros[i] = IsZero(); + zeros[i].in <-- ins[i]; + outs[i] <-- zeros[i].out; + } +} + +component main = SubCmps0C(2); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmps0C::@SubCmps0C<[2]>>} { +// CHECK-NEXT: poly.template @IsZero { +// CHECK-NEXT: struct.def @IsZero { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @temp : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@temp] = %[[VAL_2]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_2]], %[[VAL_2]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_3]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero::@IsZero<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@temp] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmps0C { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @SubCmps0C { +// CHECK-NEXT: struct.member @outs : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @zeros : !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.member @zeros$inputs : !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@SubCmps0C::@SubCmps0C<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmps0C::@SubCmps0C<[@n]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_14]], %[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_13]]) : (!felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) -> (!felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_16]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_16]], %[[VAL_17]] : !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>): +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_22]], @params = %[[VAL_21]] } : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_12]]{{\[}}%[[VAL_24]]] = %[[VAL_23]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_8]]{{\[}}%[[VAL_25]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_20]]{{\[}}%[[VAL_27]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_28]][@in] = %[[VAL_26]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_20]]{{\[}}%[[VAL_29]]] = %[[VAL_28]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_30]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@count] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_32]], %[[VAL_33]] : index +// CHECK-NEXT: pod.write %[[VAL_31]][@count] = %[[VAL_34]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_34]], %[[VAL_35]] : index +// CHECK-NEXT: scf.if %[[VAL_36]] { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@params] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = function.call @IsZero::@IsZero::@compute(%[[VAL_38]]) : (!felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: pod.write %[[VAL_31]][@comp] = %[[VAL_39]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_12]]{{\[}}%[[VAL_40]]] = %[[VAL_31]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_41]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_43]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_45]]] = %[[VAL_44]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_19]], %[[VAL_46]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_47]], %[[VAL_20]] : !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_9]][@zeros$inputs] = %[[VAL_15]]#1 : <@SubCmps0C::@SubCmps0C<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.new : <@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_52:[0-9a-zA-Z_\.]+]] = %[[VAL_50]] to %[[VAL_49]] step %[[VAL_51]] { +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_52]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_53]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: array.write %[[VAL_48]]{{\[}}%[[VAL_52]]] = %[[VAL_54]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_9]][@zeros] = %[[VAL_48]] : <@SubCmps0C::@SubCmps0C<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@outs] = %[[VAL_11]] : <@SubCmps0C::@SubCmps0C<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@SubCmps0C::@SubCmps0C<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_55:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmps0C::@SubCmps0C<[@n]>>, %[[VAL_56:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@outs] : <@SubCmps0C::@SubCmps0C<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@zeros] : <@SubCmps0C::@SubCmps0C<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_55]][@zeros$inputs] : <@SubCmps0C::@SubCmps0C<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_63:[0-9a-zA-Z_\.]+]] = %[[VAL_61]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_63]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_64]]) %[[VAL_63]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_65:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_65]], %[[VAL_68]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_69]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_73:[0-9a-zA-Z_\.]+]] = %[[VAL_71]] to %[[VAL_70]] step %[[VAL_72]] { +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_59]]{{\[}}%[[VAL_73]]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_60]]{{\[}}%[[VAL_73]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_75]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @IsZero::@IsZero::@constrain(%[[VAL_74]], %[[VAL_76]]) : (!struct.type<@IsZero::@IsZero<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps0D.circom b/circom/tests/subcmps/subcmps0D.circom new file mode 100644 index 000000000..935b0533e --- /dev/null +++ b/circom/tests/subcmps/subcmps0D.circom @@ -0,0 +1,173 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Add() { + signal input in1; + signal input in2; + signal output out; + out <-- in1 + in2; +} + +template SubCmps0D(n) { + signal input ins[n]; + signal output outs[n]; + + component a[n]; + for (var i = 0; i < n; i++) { + a[i] = Add(); + a[i].in1 <-- ins[i]; + a[i].in2 <-- ins[i]; + outs[i] <-- a[i].out; + } +} + +component main = SubCmps0D(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmps0D::@SubCmps0D<[3]>>} { +// CHECK-NEXT: poly.template @Add { +// CHECK-NEXT: struct.def @Add { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Add::@Add<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Add::@Add<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@out] = %[[VAL_3]] : <@Add::@Add<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Add::@Add<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_4]][@out] : <@Add::@Add<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmps0D { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @SubCmps0D { +// CHECK-NEXT: struct.member @outs : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @a : !array.type<@n x !struct.type<@Add::@Add<[]>>> +// CHECK-NEXT: struct.member @a$inputs : !array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@SubCmps0D::@SubCmps0D<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmps0D::@SubCmps0D<[@n]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_13]], %[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_14]]) : (!array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128">) -> (!array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_17]], %[[VAL_10]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_16]], %[[VAL_17]] : !array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_22]], @params = %[[VAL_21]] } : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_12]]{{\[}}%[[VAL_24]]] = %[[VAL_23]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_8]]{{\[}}%[[VAL_25]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_27]]] : <@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_28]][@in1] = %[[VAL_26]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_19]]{{\[}}%[[VAL_29]]] = %[[VAL_28]] : <@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_30]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@count] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_32]], %[[VAL_33]] : index +// CHECK-NEXT: pod.write %[[VAL_31]][@count] = %[[VAL_34]] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_34]], %[[VAL_35]] : index +// CHECK-NEXT: scf.if %[[VAL_36]] { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_31]][@params] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = function.call @Add::@Add::@compute(%[[VAL_38]], %[[VAL_39]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: pod.write %[[VAL_31]][@comp] = %[[VAL_40]] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_12]]{{\[}}%[[VAL_41]]] = %[[VAL_31]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_8]]{{\[}}%[[VAL_42]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_44]]] : <@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_45]][@in2] = %[[VAL_43]] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_19]]{{\[}}%[[VAL_46]]] = %[[VAL_45]] : <@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_47]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@count] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_49]], %[[VAL_50]] : index +// CHECK-NEXT: pod.write %[[VAL_48]][@count] = %[[VAL_51]] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_51]], %[[VAL_52]] : index +// CHECK-NEXT: scf.if %[[VAL_53]] { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_48]][@params] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_45]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = function.call @Add::@Add::@compute(%[[VAL_55]], %[[VAL_56]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: pod.write %[[VAL_48]][@comp] = %[[VAL_57]] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_12]]{{\[}}%[[VAL_58]]] = %[[VAL_48]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_59]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_60]][@comp] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_61]][@out] : <@Add::@Add<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_11]]{{\[}}%[[VAL_63]]] = %[[VAL_62]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_20]], %[[VAL_64]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_19]], %[[VAL_65]] : !array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_9]][@a$inputs] = %[[VAL_15]]#0 : <@SubCmps0D::@SubCmps0D<[@n]>>, !array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = array.new : <@n x !struct.type<@Add::@Add<[]>>> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_68]] to %[[VAL_67]] step %[[VAL_69]] { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_12]]{{\[}}%[[VAL_70]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_71]][@comp] : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]>, !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: array.write %[[VAL_66]]{{\[}}%[[VAL_70]]] = %[[VAL_72]] : <@n x !struct.type<@Add::@Add<[]>>>, !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_9]][@a] = %[[VAL_66]] : <@SubCmps0D::@SubCmps0D<[@n]>>, !array.type<@n x !struct.type<@Add::@Add<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_9]][@outs] = %[[VAL_11]] : <@SubCmps0D::@SubCmps0D<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@SubCmps0D::@SubCmps0D<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_73:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmps0D::@SubCmps0D<[@n]>>, %[[VAL_74:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_73]][@outs] : <@SubCmps0D::@SubCmps0D<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_73]][@a] : <@SubCmps0D::@SubCmps0D<[@n]>>, !array.type<@n x !struct.type<@Add::@Add<[]>>> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_73]][@a$inputs] : <@SubCmps0D::@SubCmps0D<[@n]>>, !array.type<@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_81:[0-9a-zA-Z_\.]+]] = %[[VAL_79]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_81]], %[[VAL_75]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_82]]) %[[VAL_81]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_83:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Add::@Add<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_83]], %[[VAL_86]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_87]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_91:[0-9a-zA-Z_\.]+]] = %[[VAL_89]] to %[[VAL_88]] step %[[VAL_90]] { +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_77]]{{\[}}%[[VAL_91]]] : <@n x !struct.type<@Add::@Add<[]>>>, !struct.type<@Add::@Add<[]>> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_78]]{{\[}}%[[VAL_91]]] : <@n x !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>>, !pod.type<[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_93]][@in1] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_93]][@in2] : <[@in1: !felt.type<"bn128">, @in2: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Add::@Add::@constrain(%[[VAL_92]], %[[VAL_94]], %[[VAL_95]]) : (!struct.type<@Add::@Add<[]>>, !felt.type<"bn128">, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps1.circom b/circom/tests/subcmps/subcmps1.circom new file mode 100644 index 000000000..4d602bc22 --- /dev/null +++ b/circom/tests/subcmps/subcmps1.circom @@ -0,0 +1,190 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template IsZero() { + signal input in; + signal output out; + + signal inv; + + inv <-- in != 0 ? 1 / in : 0; + + out <== -in * inv + 1; + in * out === 0; +} + +// Simple circuit that returns what signals are equal to 0 +template SubCmps1(n) { + signal input ins[n]; + signal output outs[n]; + + component zeros[n]; + var i; + for (i = 0; i < n; i++) { + zeros[i] = IsZero(); + zeros[i].in <== ins[i]; + outs[i] <== zeros[i].out; + } +} + +component main = SubCmps1(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmps1::@SubCmps1<[3]>>} { +// CHECK-NEXT: poly.template @IsZero { +// CHECK-NEXT: struct.def @IsZero { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @inv : !felt.type<"bn128"> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_5]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@inv] = %[[VAL_4]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_10]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_11]] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero::@IsZero<[]>>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_12]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_12]][@inv] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_13]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_16]], %[[VAL_15]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_18]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_19]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_13]], %[[VAL_14]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_21]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmps1 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @SubCmps1 { +// CHECK-NEXT: struct.member @outs : !array.type<@n x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @zeros : !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.member @zeros$inputs : !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@SubCmps1::@SubCmps1<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmps1::@SubCmps1<[@n]>> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.new : <@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_31:[0-9a-zA-Z_\.]+]] = %[[VAL_29]], %[[VAL_32:[0-9a-zA-Z_\.]+]] = %[[VAL_27]]) : (!felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) -> (!felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>) { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_31]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_33]]) %[[VAL_31]], %[[VAL_32]] : !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_34:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_35:[0-9a-zA-Z_\.]+]]: !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>>): +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_37]], @params = %[[VAL_36]] } : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_26]]{{\[}}%[[VAL_39]]] = %[[VAL_38]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_22]]{{\[}}%[[VAL_40]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_35]]{{\[}}%[[VAL_42]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: pod.write %[[VAL_43]][@in] = %[[VAL_41]] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_35]]{{\[}}%[[VAL_44]]] = %[[VAL_43]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_45]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_46]][@count] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_47]], %[[VAL_48]] : index +// CHECK-NEXT: pod.write %[[VAL_46]][@count] = %[[VAL_49]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_49]], %[[VAL_50]] : index +// CHECK-NEXT: scf.if %[[VAL_51]] { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_46]][@params] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_43]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = function.call @IsZero::@IsZero::@compute(%[[VAL_53]]) : (!felt.type<"bn128">) -> !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: pod.write %[[VAL_46]][@comp] = %[[VAL_54]] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_26]]{{\[}}%[[VAL_55]]] = %[[VAL_46]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_56]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_57]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_58]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_25]]{{\[}}%[[VAL_60]]] = %[[VAL_59]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_34]], %[[VAL_61]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_62]], %[[VAL_35]] : !felt.type<"bn128">, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_23]][@zeros$inputs] = %[[VAL_30]]#1 : <@SubCmps1::@SubCmps1<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = array.new : <@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_67:[0-9a-zA-Z_\.]+]] = %[[VAL_65]] to %[[VAL_64]] step %[[VAL_66]] { +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_67]]] : <@n x !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_68]][@comp] : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: array.write %[[VAL_63]]{{\[}}%[[VAL_67]]] = %[[VAL_69]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_23]][@zeros] = %[[VAL_63]] : <@SubCmps1::@SubCmps1<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: struct.writem %[[VAL_23]][@outs] = %[[VAL_25]] : <@SubCmps1::@SubCmps1<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_23]] : !struct.type<@SubCmps1::@SubCmps1<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_70:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmps1::@SubCmps1<[@n]>>, %[[VAL_71:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_70]][@outs] : <@SubCmps1::@SubCmps1<[@n]>>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_70]][@zeros] : <@SubCmps1::@SubCmps1<[@n]>>, !array.type<@n x !struct.type<@IsZero::@IsZero<[]>>> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_70]][@zeros$inputs] : <@SubCmps1::@SubCmps1<[@n]>>, !array.type<@n x !pod.type<[@in: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_79:[0-9a-zA-Z_\.]+]] = %[[VAL_77]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_79]], %[[VAL_72]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_80]]) %[[VAL_79]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_81:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@IsZero::@IsZero<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_81]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_71]]{{\[}}%[[VAL_84]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_81]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_75]]{{\[}}%[[VAL_86]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_87]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_88]], %[[VAL_85]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_81]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_74]]{{\[}}%[[VAL_89]]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_90]][@out] : <@IsZero::@IsZero<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_81]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_73]]{{\[}}%[[VAL_92]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_93]], %[[VAL_91]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_81]], %[[VAL_94]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_95]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = poly.read_const @n : index +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_99:[0-9a-zA-Z_\.]+]] = %[[VAL_97]] to %[[VAL_96]] step %[[VAL_98]] { +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_74]]{{\[}}%[[VAL_99]]] : <@n x !struct.type<@IsZero::@IsZero<[]>>>, !struct.type<@IsZero::@IsZero<[]>> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_75]]{{\[}}%[[VAL_99]]] : <@n x !pod.type<[@in: !felt.type<"bn128">]>>, !pod.type<[@in: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_101]][@in] : <[@in: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @IsZero::@IsZero::@constrain(%[[VAL_100]], %[[VAL_102]]) : (!struct.type<@IsZero::@IsZero<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps2.circom b/circom/tests/subcmps/subcmps2.circom new file mode 100644 index 000000000..3a464821c --- /dev/null +++ b/circom/tests/subcmps/subcmps2.circom @@ -0,0 +1,173 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.6; + +template Sum(n) { + signal input inp[n]; + signal output outp; + + var s = 0; + + for (var i = 0; i < n; i++) { + s += inp[i]; + } + + outp <== s; +} + +function nop(i) { + return i; +} + +template Caller() { + signal input inp[4]; + signal output outp; + + component s = Sum(4); + + for (var i = 0; i < 4; i++) { + s.inp[i] <== nop(inp[i]); + } + + outp <== s.outp; +} + +component main = Caller(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@Caller::@Caller<[]>>} { +// CHECK-NEXT: function.def @nop(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Caller { +// CHECK-NEXT: struct.def @Caller { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @s : !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: struct.member @s$inputs : !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<4 x !felt.type<"bn128">>) -> !struct.type<@Caller::@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller::@Caller<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_4]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 4 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_6]], @params = %[[VAL_5]] } : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_8]], %[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]>) -> (!felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]>) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_12]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_13]]) %[[VAL_10]], %[[VAL_11]] : !felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]>): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_16]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @nop(%[[VAL_17]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_19]]{{\[}}%[[VAL_20]]] = %[[VAL_18]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_15]][@inp] = %[[VAL_19]] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@count] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_21]], %[[VAL_22]] : index +// CHECK-NEXT: pod.write %[[VAL_7]][@count] = %[[VAL_23]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_23]], %[[VAL_24]] : index +// CHECK-NEXT: scf.if %[[VAL_25]] { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@params] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_15]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @Sum::@Sum::@compute(%[[VAL_27]]) : (!array.type<4 x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: pod.write %[[VAL_7]][@comp] = %[[VAL_28]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_30]], %[[VAL_15]] : !felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@outp] : <@Sum::@Sum<[4]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@outp] = %[[VAL_32]] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_2]][@s$inputs] = %[[VAL_9]]#1 : <@Caller::@Caller<[]>>, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: struct.writem %[[VAL_2]][@s] = %[[VAL_33]] : <@Caller::@Caller<[]>>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Caller::@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_34:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller::@Caller<[]>>, %[[VAL_35:[0-9a-zA-Z_\.]+]]: !array.type<4 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_34]][@outp] : <@Caller::@Caller<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_34]][@s] : <@Caller::@Caller<[]>>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_34]][@s$inputs] : <@Caller::@Caller<[]>>, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_39]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_44:[0-9a-zA-Z_\.]+]] = %[[VAL_42]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_44]], %[[VAL_45]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_46]]) %[[VAL_44]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_35]]{{\[}}%[[VAL_48]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = function.call @nop(%[[VAL_49]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_38]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_47]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_51]]{{\[}}%[[VAL_52]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_53]], %[[VAL_50]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_47]], %[[VAL_54]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_55]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_37]][@outp] : <@Sum::@Sum<[4]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_36]], %[[VAL_56]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_38]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum::@Sum::@constrain(%[[VAL_37]], %[[VAL_57]]) : (!struct.type<@Sum::@Sum<[4]>>, !array.type<4 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sum { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Sum { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_58:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_64:[0-9a-zA-Z_\.]+]] = %[[VAL_62]], %[[VAL_65:[0-9a-zA-Z_\.]+]] = %[[VAL_61]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_64]], %[[VAL_60]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_66]]) %[[VAL_64]], %[[VAL_65]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_67:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_68:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_58]]{{\[}}%[[VAL_69]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_68]], %[[VAL_70]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_67]], %[[VAL_72]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_73]], %[[VAL_71]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_59]][@outp] = %[[VAL_63]]#1 : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_59]] : !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_74:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum::@Sum<[@n]>>, %[[VAL_75:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_74]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_81:[0-9a-zA-Z_\.]+]] = %[[VAL_79]], %[[VAL_82:[0-9a-zA-Z_\.]+]] = %[[VAL_78]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_81]], %[[VAL_76]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_83]]) %[[VAL_81]], %[[VAL_82]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_84:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_85:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_84]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_75]]{{\[}}%[[VAL_86]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_85]], %[[VAL_87]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_84]], %[[VAL_89]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_90]], %[[VAL_88]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_77]], %[[VAL_80]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps3.circom b/circom/tests/subcmps/subcmps3.circom new file mode 100644 index 000000000..8077d4d29 --- /dev/null +++ b/circom/tests/subcmps/subcmps3.circom @@ -0,0 +1,178 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sum(n) { + signal input inp[n]; + signal output outp; + + var s = 0; + + for (var i = 0; i < n; i++) { + s += inp[i]; + } + + outp <== s; +} + +template SubCmps3() { + signal input inp[4]; + signal output outp; + + component s = Sum(4); + + for (var i = 0; i < 4; i++) { + s.inp[i] <== inp[i]; + if (i == 3) { + outp <== s.outp; + } + } +} + +component main = SubCmps3(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmps3::@SubCmps3<[]>>} { +// CHECK-NEXT: poly.template @SubCmps3 { +// CHECK-NEXT: struct.def @SubCmps3 { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @s : !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: struct.member @s$inputs : !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<4 x !felt.type<"bn128">>) -> !struct.type<@SubCmps3::@SubCmps3<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmps3::@SubCmps3<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = llzk.nondet : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_4]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant 4 : index +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_6]], @params = %[[VAL_5]] } : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_10:[0-9a-zA-Z_\.]+]] = %[[VAL_8]], %[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_12:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type<"bn128">, !felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]>) -> (!felt.type<"bn128">, !felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]>) { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_13]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_14]]) %[[VAL_10]], %[[VAL_11]], %[[VAL_12]] : !felt.type<"bn128">, !felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]>): +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_18]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_20]]{{\[}}%[[VAL_21]]] = %[[VAL_19]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_17]][@inp] = %[[VAL_20]] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@count] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_22]], %[[VAL_23]] : index +// CHECK-NEXT: pod.write %[[VAL_7]][@count] = %[[VAL_24]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_24]], %[[VAL_25]] : index +// CHECK-NEXT: scf.if %[[VAL_26]] { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@params] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = function.call @Sum::@Sum::@compute(%[[VAL_28]]) : (!array.type<4 x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: pod.write %[[VAL_7]][@comp] = %[[VAL_29]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_15]], %[[VAL_30]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_31]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_33]][@outp] : <@Sum::@Sum<[4]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@outp] = %[[VAL_34]] : <@SubCmps3::@SubCmps3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_34]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_15]], %[[VAL_35]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_36]], %[[VAL_32]], %[[VAL_17]] : !felt.type<"bn128">, !felt.type<"bn128">, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@s$inputs] = %[[VAL_9]]#2 : <@SubCmps3::@SubCmps3<[]>>, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@s] = %[[VAL_37]] : <@SubCmps3::@SubCmps3<[]>>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@SubCmps3::@SubCmps3<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmps3::@SubCmps3<[]>>, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !array.type<4 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_38]][@outp] : <@SubCmps3::@SubCmps3<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_38]][@s] : <@SubCmps3::@SubCmps3<[]>>, !struct.type<@Sum::@Sum<[4]>> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_38]][@s$inputs] : <@SubCmps3::@SubCmps3<[]>>, !pod.type<[@inp: !array.type<4 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_43]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sum::@Sum<[4]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_48:[0-9a-zA-Z_\.]+]] = %[[VAL_46]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 4 : <"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_48]], %[[VAL_49]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_50]]) %[[VAL_48]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_51:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_39]]{{\[}}%[[VAL_52]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_54]]{{\[}}%[[VAL_55]]] : <4 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_56]], %[[VAL_53]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 3 : <"bn128"> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_51]], %[[VAL_57]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_58]] { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_41]][@outp] : <@Sum::@Sum<[4]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_40]], %[[VAL_59]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_51]], %[[VAL_60]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_61]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_42]][@inp] : <[@inp: !array.type<4 x !felt.type<"bn128">>]>, !array.type<4 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum::@Sum::@constrain(%[[VAL_41]], %[[VAL_62]]) : (!struct.type<@Sum::@Sum<[4]>>, !array.type<4 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sum { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Sum { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_63:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_69:[0-9a-zA-Z_\.]+]] = %[[VAL_67]], %[[VAL_70:[0-9a-zA-Z_\.]+]] = %[[VAL_66]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_69]], %[[VAL_65]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_71]]) %[[VAL_69]], %[[VAL_70]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_72:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_73:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_72]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_63]]{{\[}}%[[VAL_74]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_73]], %[[VAL_75]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_72]], %[[VAL_77]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_78]], %[[VAL_76]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_64]][@outp] = %[[VAL_68]]#1 : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_64]] : !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_79:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum::@Sum<[@n]>>, %[[VAL_80:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_79]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_86:[0-9a-zA-Z_\.]+]] = %[[VAL_84]], %[[VAL_87:[0-9a-zA-Z_\.]+]] = %[[VAL_83]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_86]], %[[VAL_81]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_88]]) %[[VAL_86]], %[[VAL_87]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_89:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_90:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_89]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_80]]{{\[}}%[[VAL_91]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_90]], %[[VAL_92]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_89]], %[[VAL_94]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_95]], %[[VAL_93]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_82]], %[[VAL_85]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps4.circom b/circom/tests/subcmps/subcmps4.circom new file mode 100644 index 000000000..fdcec8860 --- /dev/null +++ b/circom/tests/subcmps/subcmps4.circom @@ -0,0 +1,255 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Sum(n) { + signal input inp[n]; + signal output outp; + + var s = 0; + + for (var i = 0; i < n; i++) { + s += inp[i]; + } + + outp <== s; +} + +template SubCmps4(n) { + signal input inp[n*2]; + signal output outp[2]; + + component a = Sum(n); + component b = Sum(n); + + for (var i = 0; i < n*2; i++) { + if (i % 2 == 0) { + a.inp[i\2] <== inp[i]; + } else { + b.inp[i\2] <== inp[i]; + } + } + outp[0] <-- a.outp; + outp[1] <-- b.outp; +} + +component main = SubCmps4(3); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmps4::@SubCmps4<[3]>>} { +// CHECK-NEXT: poly.template @SubCmps4 { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: poly.expr @"n_Mul_2@443" { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_1]], %[[VAL_0]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: poly.yield %[[VAL_2]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @SubCmps4 { +// CHECK-NEXT: struct.member @outp : !array.type<2 x !felt.type<"bn128">> {llzk.pub} +// CHECK-NEXT: struct.member @a : !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.member @a$inputs : !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: struct.member @b : !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.member @b$inputs : !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: function.def @compute(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !array.type<@"n_Mul_2@443" x !felt.type<"bn128">>) -> !struct.type<@SubCmps4::@SubCmps4<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmps4::@SubCmps4<[@n]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_2@443" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = llzk.nondet : !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.new : <[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_10]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_12]], @params = %[[VAL_11]] } : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_14]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_5]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_16]], @params = %[[VAL_15]] } : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_8]], %[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_9]], %[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_18]]) : (!pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128">) -> (!pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_22]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_20]], %[[VAL_21]], %[[VAL_22]] : !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_28]], %[[VAL_29]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_30]], %[[VAL_31]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_32]] -> (!pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>) { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_34]]] : <@"n_Mul_2@443" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_28]], %[[VAL_37]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_36]]{{\[}}%[[VAL_39]]] = %[[VAL_35]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_26]][@inp] = %[[VAL_36]] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@count] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_40]], %[[VAL_41]] : index +// CHECK-NEXT: pod.write %[[VAL_13]][@count] = %[[VAL_42]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_42]], %[[VAL_43]] : index +// CHECK-NEXT: scf.if %[[VAL_44]] { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@params] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = function.call @Sum::@Sum::@compute(%[[VAL_46]]) : (!array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_13]][@comp] = %[[VAL_47]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_26]], %[[VAL_27]] : !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_48]]] : <@"n_Mul_2@443" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_27]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_28]], %[[VAL_51]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_52]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_50]]{{\[}}%[[VAL_53]]] = %[[VAL_49]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: pod.write %[[VAL_27]][@inp] = %[[VAL_50]] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@count] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_54]], %[[VAL_55]] : index +// CHECK-NEXT: pod.write %[[VAL_17]][@count] = %[[VAL_56]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_56]], %[[VAL_57]] : index +// CHECK-NEXT: scf.if %[[VAL_58]] { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@params] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_27]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = function.call @Sum::@Sum::@compute(%[[VAL_60]]) : (!array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: pod.write %[[VAL_17]][@comp] = %[[VAL_61]] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_26]], %[[VAL_27]] : !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_28]], %[[VAL_62]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_33]]#0, %[[VAL_33]]#1, %[[VAL_63]] : !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_64]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_66]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_7]]{{\[}}%[[VAL_67]]] = %[[VAL_65]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_68]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_70]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_7]]{{\[}}%[[VAL_71]]] = %[[VAL_69]] : <2 x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_4]][@a$inputs] = %[[VAL_19]]#0 : <@SubCmps4::@SubCmps4<[@n]>>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_4]][@a] = %[[VAL_72]] : <@SubCmps4::@SubCmps4<[@n]>>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_4]][@b$inputs] = %[[VAL_19]]#1 : <@SubCmps4::@SubCmps4<[@n]>>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_17]][@comp] : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_4]][@b] = %[[VAL_73]] : <@SubCmps4::@SubCmps4<[@n]>>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: struct.writem %[[VAL_4]][@outp] = %[[VAL_7]] : <@SubCmps4::@SubCmps4<[@n]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: function.return %[[VAL_4]] : !struct.type<@SubCmps4::@SubCmps4<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_74:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmps4::@SubCmps4<[@n]>>, %[[VAL_75:[0-9a-zA-Z_\.]+]]: !array.type<@"n_Mul_2@443" x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = poly.read_const @"n_Mul_2@443" : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_74]][@outp] : <@SubCmps4::@SubCmps4<[@n]>>, !array.type<2 x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_74]][@a] : <@SubCmps4::@SubCmps4<[@n]>>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_74]][@a$inputs] : <@SubCmps4::@SubCmps4<[@n]>>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_74]][@b] : <@SubCmps4::@SubCmps4<[@n]>>, !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_74]][@b$inputs] : <@SubCmps4::@SubCmps4<[@n]>>, !pod.type<[@inp: !array.type<@n x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_83]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_86]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Sum::@Sum<[@n]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_91:[0-9a-zA-Z_\.]+]] = %[[VAL_89]]) : (!felt.type<"bn128">) -> !felt.type<"bn128"> { +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_76]], %[[VAL_92]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_91]], %[[VAL_93]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_94]]) %[[VAL_91]] : !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_95:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_95]], %[[VAL_96]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_97]], %[[VAL_98]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.if %[[VAL_99]] { +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_95]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_75]]{{\[}}%[[VAL_100]]] : <@"n_Mul_2@443" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_80]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_95]], %[[VAL_103]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_104]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_102]]{{\[}}%[[VAL_105]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_106]], %[[VAL_101]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_95]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_75]]{{\[}}%[[VAL_107]]] : <@"n_Mul_2@443" x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_82]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 2 : <"bn128"> +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_95]], %[[VAL_110]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_111]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_109]]{{\[}}%[[VAL_112]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_113]], %[[VAL_108]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_95]], %[[VAL_114]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_115]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_80]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum::@Sum::@constrain(%[[VAL_79]], %[[VAL_116]]) : (!struct.type<@Sum::@Sum<[@n]>>, !array.type<@n x !felt.type<"bn128">>) -> () +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_82]][@inp] : <[@inp: !array.type<@n x !felt.type<"bn128">>]>, !array.type<@n x !felt.type<"bn128">> +// CHECK-NEXT: function.call @Sum::@Sum::@constrain(%[[VAL_81]], %[[VAL_117]]) : (!struct.type<@Sum::@Sum<[@n]>>, !array.type<@n x !felt.type<"bn128">>) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @Sum { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Sum { +// CHECK-NEXT: struct.member @outp : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_118:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) -> !struct.type<@Sum::@Sum<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum::@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_124:[0-9a-zA-Z_\.]+]] = %[[VAL_122]], %[[VAL_125:[0-9a-zA-Z_\.]+]] = %[[VAL_121]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_124]], %[[VAL_120]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_126]]) %[[VAL_124]], %[[VAL_125]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_127:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_128:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_127]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_118]]{{\[}}%[[VAL_129]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_128]], %[[VAL_130]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_127]], %[[VAL_132]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_133]], %[[VAL_131]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_119]][@outp] = %[[VAL_123]]#1 : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_119]] : !struct.type<@Sum::@Sum<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_134:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum::@Sum<[@n]>>, %[[VAL_135:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_134]][@outp] : <@Sum::@Sum<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_141:[0-9a-zA-Z_\.]+]] = %[[VAL_139]], %[[VAL_142:[0-9a-zA-Z_\.]+]] = %[[VAL_138]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> (!felt.type<"bn128">, !felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_141]], %[[VAL_136]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.condition(%[[VAL_143]]) %[[VAL_141]], %[[VAL_142]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_144:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_145:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">): +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_144]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_135]]{{\[}}%[[VAL_146]]] : <@n x !felt.type<"bn128">>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_145]], %[[VAL_147]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_144]], %[[VAL_149]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: scf.yield %[[VAL_150]], %[[VAL_148]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_137]], %[[VAL_140]]#1 : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps5a.circom b/circom/tests/subcmps/subcmps5a.circom new file mode 100644 index 000000000..6311186fb --- /dev/null +++ b/circom/tests/subcmps/subcmps5a.circom @@ -0,0 +1,88 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Nop() { + signal input i; + signal output o; + o <== i; +} + +template SubCmp() { + signal input i; + signal output o; + component n = Nop(); + n.i <== i; + o <== n.o; +} + +component main = SubCmp(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmp::@SubCmp<[]>>} { +// CHECK-NEXT: poly.template @Nop { +// CHECK-NEXT: struct.def @Nop { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop::@Nop<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@o] = %[[VAL_0]] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop::@Nop<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmp { +// CHECK-NEXT: struct.def @SubCmp { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n : !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: struct.member @n$inputs : !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SubCmp::@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new : <[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_9]], @params = %[[VAL_8]] } : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_7]][@i] = %[[VAL_5]] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@count] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_11]], %[[VAL_12]] : index +// CHECK-NEXT: pod.write %[[VAL_10]][@count] = %[[VAL_13]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_13]], %[[VAL_14]] : index +// CHECK-NEXT: scf.if %[[VAL_15]] { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@params] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @Nop::@Nop::@compute(%[[VAL_17]]) : (!felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: pod.write %[[VAL_10]][@comp] = %[[VAL_18]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_6]][@o] = %[[VAL_20]] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_6]][@n$inputs] = %[[VAL_7]] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: struct.writem %[[VAL_6]][@n] = %[[VAL_21]] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: function.return %[[VAL_6]] : !struct.type<@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp::@SubCmp<[]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@o] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@n] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@n$inputs] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_29]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Nop::@Nop::@constrain(%[[VAL_25]], %[[VAL_31]]) : (!struct.type<@Nop::@Nop<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps5b.circom b/circom/tests/subcmps/subcmps5b.circom new file mode 100644 index 000000000..2922e1ded --- /dev/null +++ b/circom/tests/subcmps/subcmps5b.circom @@ -0,0 +1,84 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Nop() { + signal input i; + signal output o; + o <== i; +} + +template SubCmp() { + signal input i; + signal output o; + component n = Nop(); + n.i <-- i; + o <-- n.o; +} + +component main = SubCmp(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmp::@SubCmp<[]>>} { +// CHECK-NEXT: poly.template @Nop { +// CHECK-NEXT: struct.def @Nop { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop::@Nop<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@o] = %[[VAL_0]] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop::@Nop<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmp { +// CHECK-NEXT: struct.def @SubCmp { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n : !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: struct.member @n$inputs : !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SubCmp::@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new : <[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_9]], @params = %[[VAL_8]] } : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_7]][@i] = %[[VAL_5]] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@count] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_11]], %[[VAL_12]] : index +// CHECK-NEXT: pod.write %[[VAL_10]][@count] = %[[VAL_13]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_13]], %[[VAL_14]] : index +// CHECK-NEXT: scf.if %[[VAL_15]] { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@params] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @Nop::@Nop::@compute(%[[VAL_17]]) : (!felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: pod.write %[[VAL_10]][@comp] = %[[VAL_18]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_6]][@o] = %[[VAL_20]] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_6]][@n$inputs] = %[[VAL_7]] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: struct.writem %[[VAL_6]][@n] = %[[VAL_21]] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: function.return %[[VAL_6]] : !struct.type<@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp::@SubCmp<[]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@o] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@n] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@n$inputs] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Nop::@Nop::@constrain(%[[VAL_25]], %[[VAL_29]]) : (!struct.type<@Nop::@Nop<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps5c.circom b/circom/tests/subcmps/subcmps5c.circom new file mode 100644 index 000000000..684865376 --- /dev/null +++ b/circom/tests/subcmps/subcmps5c.circom @@ -0,0 +1,90 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Nop() { + signal input i; + signal output o; + o <== i; +} + +template SubCmp() { + signal input i; + signal output o; + component n = Nop(); + n.i <-- i; + n.i === i; + o <-- n.o; + o === n.o; +} + +component main = SubCmp(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmp::@SubCmp<[]>>} { +// CHECK-NEXT: poly.template @Nop { +// CHECK-NEXT: struct.def @Nop { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop::@Nop<[]>> +// CHECK-NEXT: struct.writem %[[VAL_1]][@o] = %[[VAL_0]] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop::@Nop<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_2]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmp { +// CHECK-NEXT: struct.def @SubCmp { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n : !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: struct.member @n$inputs : !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SubCmp::@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new : <[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_9]], @params = %[[VAL_8]] } : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: pod.write %[[VAL_7]][@i] = %[[VAL_5]] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@count] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_11]], %[[VAL_12]] : index +// CHECK-NEXT: pod.write %[[VAL_10]][@count] = %[[VAL_13]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_13]], %[[VAL_14]] : index +// CHECK-NEXT: scf.if %[[VAL_15]] { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@params] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !pod.type<[]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_7]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @Nop::@Nop::@compute(%[[VAL_17]]) : (!felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: pod.write %[[VAL_10]][@comp] = %[[VAL_18]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_19]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_6]][@o] = %[[VAL_20]] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_6]][@n$inputs] = %[[VAL_7]] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_10]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: struct.writem %[[VAL_6]][@n] = %[[VAL_21]] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: function.return %[[VAL_6]] : !struct.type<@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp::@SubCmp<[]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@o] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@n] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@n$inputs] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Nop::@Nop<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_29]], %[[VAL_23]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@o] : <@Nop::@Nop<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_30]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_26]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Nop::@Nop::@constrain(%[[VAL_25]], %[[VAL_31]]) : (!struct.type<@Nop::@Nop<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps6a.circom b/circom/tests/subcmps/subcmps6a.circom new file mode 100644 index 000000000..9d59db734 --- /dev/null +++ b/circom/tests/subcmps/subcmps6a.circom @@ -0,0 +1,93 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Nop(n) { + signal input i; + signal output o; + o <== i; +} + +template SubCmp() { + signal input i; + signal output o; + component n = Nop(1); + n.i <== i; + o <== n.o; +} + +component main = SubCmp(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmp::@SubCmp<[]>>} { +// CHECK-NEXT: poly.template @Nop { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Nop { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop::@Nop<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@o] = %[[VAL_0]] : <@Nop::@Nop<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Nop::@Nop<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop::@Nop<[@n]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@o] : <@Nop::@Nop<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmp { +// CHECK-NEXT: struct.def @SubCmp { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n : !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: struct.member @n$inputs : !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SubCmp::@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.new : <[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_10]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_12]], @params = %[[VAL_11]] } : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_9]][@i] = %[[VAL_7]] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@count] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_14]], %[[VAL_15]] : index +// CHECK-NEXT: pod.write %[[VAL_13]][@count] = %[[VAL_16]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_16]], %[[VAL_17]] : index +// CHECK-NEXT: scf.if %[[VAL_18]] { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@params] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_9]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = function.call @Nop::@Nop::@compute(%[[VAL_20]]) : (!felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: pod.write %[[VAL_13]][@comp] = %[[VAL_21]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@o] : <@Nop::@Nop<[1]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_8]][@o] = %[[VAL_23]] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_8]][@n$inputs] = %[[VAL_9]] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: struct.writem %[[VAL_8]][@n] = %[[VAL_24]] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp::@SubCmp<[]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@o] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@n] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@n$inputs] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_30]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_26]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_28]][@o] : <@Nop::@Nop<[1]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_34]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Nop::@Nop::@constrain(%[[VAL_28]], %[[VAL_35]]) : (!struct.type<@Nop::@Nop<[1]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps6b.circom b/circom/tests/subcmps/subcmps6b.circom new file mode 100644 index 000000000..1b9d5181b --- /dev/null +++ b/circom/tests/subcmps/subcmps6b.circom @@ -0,0 +1,89 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Nop(n) { + signal input i; + signal output o; + o <== i; +} + +template SubCmp() { + signal input i; + signal output o; + component n = Nop(1); + n.i <-- i; + o <-- n.o; +} + +component main = SubCmp(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmp::@SubCmp<[]>>} { +// CHECK-NEXT: poly.template @Nop { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Nop { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop::@Nop<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_1]][@o] = %[[VAL_0]] : <@Nop::@Nop<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Nop::@Nop<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop::@Nop<[@n]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@o] : <@Nop::@Nop<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmp { +// CHECK-NEXT: struct.def @SubCmp { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n : !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: struct.member @n$inputs : !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: function.def @compute(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SubCmp::@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.new : <[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_10]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_12]], @params = %[[VAL_11]] } : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: pod.write %[[VAL_9]][@i] = %[[VAL_7]] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@count] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = arith.subi %[[VAL_14]], %[[VAL_15]] : index +// CHECK-NEXT: pod.write %[[VAL_13]][@count] = %[[VAL_16]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, index +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.cmpi eq, %[[VAL_16]], %[[VAL_17]] : index +// CHECK-NEXT: scf.if %[[VAL_18]] { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@params] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !pod.type<[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_9]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = function.call @Nop::@Nop::@compute(%[[VAL_20]]) : (!felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: pod.write %[[VAL_13]][@comp] = %[[VAL_21]] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_22]][@o] : <@Nop::@Nop<[1]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_8]][@o] = %[[VAL_23]] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_8]][@n$inputs] = %[[VAL_9]] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_13]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: struct.writem %[[VAL_8]][@n] = %[[VAL_24]] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp::@SubCmp<[]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@o] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@n] : <@SubCmp::@SubCmp<[]>>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_25]][@n$inputs] : <@SubCmp::@SubCmp<[]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_30]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Nop::@Nop::@constrain(%[[VAL_28]], %[[VAL_33]]) : (!struct.type<@Nop::@Nop<[1]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcmps7.circom b/circom/tests/subcmps/subcmps7.circom new file mode 100644 index 000000000..c628117a7 --- /dev/null +++ b/circom/tests/subcmps/subcmps7.circom @@ -0,0 +1,102 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template Nop(n) { + signal input i; + signal output o; +} + +template SubCmp() { + signal input i; + signal output o; + component n[2]; + n[0] = Nop(1); + n[1] = Nop(1); + //n[0].i <== i; + //o <== n[0].o; +} + +component main = SubCmp(); + +// This test is not 100% testable because array support is not complete. +// Currently we only care about having the right type in the field definition. +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@SubCmp::@SubCmp<[]>>} { +// CHECK-NEXT: poly.template @Nop { +// CHECK-NEXT: poly.param @n +// CHECK-NEXT: struct.def @Nop { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@Nop::@Nop<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop::@Nop<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Nop::@Nop<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop::@Nop<[@n]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_3]][@o] : <@Nop::@Nop<[@n]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @SubCmp { +// CHECK-NEXT: struct.def @SubCmp { +// CHECK-NEXT: struct.member @o : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: struct.member @n : !array.type<2 x !struct.type<@Nop::@Nop<[1]>>> +// CHECK-NEXT: struct.member @n$inputs : !array.type<2 x !pod.type<[@i: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@SubCmp::@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.new : <2 x !pod.type<[@i: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_11]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_13]], @params = %[[VAL_12]] } : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_9]]{{\[}}%[[VAL_16]]] = %[[VAL_14]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_17]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_19]], @params = %[[VAL_18]] } : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_9]]{{\[}}%[[VAL_22]]] = %[[VAL_20]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: struct.writem %[[VAL_8]][@n$inputs] = %[[VAL_10]] : <@SubCmp::@SubCmp<[]>>, !array.type<2 x !pod.type<[@i: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.new : <2 x !struct.type<@Nop::@Nop<[1]>>> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_27:[0-9a-zA-Z_\.]+]] = %[[VAL_25]] to %[[VAL_24]] step %[[VAL_26]] { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_27]]] : <2 x !pod.type<[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>>, !pod.type<[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_28]][@comp] : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: array.write %[[VAL_23]]{{\[}}%[[VAL_27]]] = %[[VAL_29]] : <2 x !struct.type<@Nop::@Nop<[1]>>>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_8]][@n] = %[[VAL_23]] : <@SubCmp::@SubCmp<[]>>, !array.type<2 x !struct.type<@Nop::@Nop<[1]>>> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@SubCmp::@SubCmp<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp::@SubCmp<[]>>, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@o] : <@SubCmp::@SubCmp<[]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@n] : <@SubCmp::@SubCmp<[]>>, !array.type<2 x !struct.type<@Nop::@Nop<[1]>>> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_30]][@n$inputs] : <@SubCmp::@SubCmp<[]>>, !array.type<2 x !pod.type<[@i: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_35]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.new { @n = %[[VAL_38]] } : <[@n: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@Nop::@Nop<[1]>>, @params: !pod.type<[@n: !felt.type<"bn128">]>]> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_44:[0-9a-zA-Z_\.]+]] = %[[VAL_42]] to %[[VAL_41]] step %[[VAL_43]] { +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_33]]{{\[}}%[[VAL_44]]] : <2 x !struct.type<@Nop::@Nop<[1]>>>, !struct.type<@Nop::@Nop<[1]>> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_34]]{{\[}}%[[VAL_44]]] : <2 x !pod.type<[@i: !felt.type<"bn128">]>>, !pod.type<[@i: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_46]][@i] : <[@i: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @Nop::@Nop::@constrain(%[[VAL_45]], %[[VAL_47]]) : (!struct.type<@Nop::@Nop<[1]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/subcmps/subcomponent_memory.circom b/circom/tests/subcmps/subcomponent_memory.circom new file mode 100644 index 000000000..b32b00797 --- /dev/null +++ b/circom/tests/subcmps/subcomponent_memory.circom @@ -0,0 +1,127 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template B() { + signal input x; + signal input y[10]; +} + +template C() { + signal input f; +} + +template A() { + component b = B(); + component c[2][1]; + c[0][0] = C(); + c[1][0] = C(); +} + +component main = A(); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @b : !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.member @b$inputs : !pod.type<[@x: !felt.type<"bn128">, @y: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: struct.member @c : !array.type<2,1 x !struct.type<@C::@C<[]>>> +// CHECK-NEXT: struct.member @c$inputs : !array.type<2,1 x !pod.type<[@f: !felt.type<"bn128">]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@A::@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = pod.new : <[@x: !felt.type<"bn128">, @y: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !pod.type<[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !pod.type<[@f: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = arith.constant 11 : index +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_5]], @params = %[[VAL_4]] } : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_8]], @params = %[[VAL_7]] } : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_11]], %[[VAL_13]]] = %[[VAL_9]] : <2,1 x !pod.type<[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = pod.new { @count = %[[VAL_15]], @params = %[[VAL_14]] } : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 : <"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 : <"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] : !felt.type<"bn128"> +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_18]], %[[VAL_20]]] = %[[VAL_16]] : <2,1 x !pod.type<[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: struct.writem %[[VAL_0]][@b$inputs] = %[[VAL_1]] : <@A::@A<[]>>, !pod.type<[@x: !felt.type<"bn128">, @y: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_6]][@comp] : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.writem %[[VAL_0]][@b] = %[[VAL_21]] : <@A::@A<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: struct.writem %[[VAL_0]][@c$inputs] = %[[VAL_3]] : <@A::@A<[]>>, !array.type<2,1 x !pod.type<[@f: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !struct.type<@C::@C<[]>>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_27:[0-9a-zA-Z_\.]+]] = %[[VAL_25]] to %[[VAL_23]] step %[[VAL_26]] { +// CHECK-NEXT: scf.for %[[VAL_28:[0-9a-zA-Z_\.]+]] = %[[VAL_25]] to %[[VAL_24]] step %[[VAL_26]] { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_27]], %[[VAL_28]]] : <2,1 x !pod.type<[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>>, !pod.type<[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_29]][@comp] : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: array.write %[[VAL_22]]{{\[}}%[[VAL_27]], %[[VAL_28]]] = %[[VAL_30]] : <2,1 x !struct.type<@C::@C<[]>>>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_0]][@c] = %[[VAL_22]] : <@A::@A<[]>>, !array.type<2,1 x !struct.type<@C::@C<[]>>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A::@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@b] : <@A::@A<[]>>, !struct.type<@B::@B<[]>> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@b$inputs] : <@A::@A<[]>>, !pod.type<[@x: !felt.type<"bn128">, @y: !array.type<10 x !felt.type<"bn128">>]> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@c] : <@A::@A<[]>>, !array.type<2,1 x !struct.type<@C::@C<[]>>> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_31]][@c$inputs] : <@A::@A<[]>>, !array.type<2,1 x !pod.type<[@f: !felt.type<"bn128">]>> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@B::@B<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = pod.new : <[]> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = pod.new : <[@count: index, @comp: !struct.type<@C::@C<[]>>, @params: !pod.type<[]>]> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_33]][@x] : <[@x: !felt.type<"bn128">, @y: !array.type<10 x !felt.type<"bn128">>]>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_33]][@y] : <[@x: !felt.type<"bn128">, @y: !array.type<10 x !felt.type<"bn128">>]>, !array.type<10 x !felt.type<"bn128">> +// CHECK-NEXT: function.call @B::@B::@constrain(%[[VAL_32]], %[[VAL_42]], %[[VAL_43]]) : (!struct.type<@B::@B<[]>>, !felt.type<"bn128">, !array.type<10 x !felt.type<"bn128">>) -> () +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = arith.constant 2 : index +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_48:[0-9a-zA-Z_\.]+]] = %[[VAL_46]] to %[[VAL_44]] step %[[VAL_47]] { +// CHECK-NEXT: scf.for %[[VAL_49:[0-9a-zA-Z_\.]+]] = %[[VAL_46]] to %[[VAL_45]] step %[[VAL_47]] { +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_34]]{{\[}}%[[VAL_48]], %[[VAL_49]]] : <2,1 x !struct.type<@C::@C<[]>>>, !struct.type<@C::@C<[]>> +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_35]]{{\[}}%[[VAL_48]], %[[VAL_49]]] : <2,1 x !pod.type<[@f: !felt.type<"bn128">]>>, !pod.type<[@f: !felt.type<"bn128">]> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = pod.read %[[VAL_51]][@f] : <[@f: !felt.type<"bn128">]>, !felt.type<"bn128"> +// CHECK-NEXT: function.call @C::@C::@constrain(%[[VAL_50]], %[[VAL_52]]) : (!struct.type<@C::@C<[]>>, !felt.type<"bn128">) -> () +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @B { +// CHECK-NEXT: struct.def @B { +// CHECK-NEXT: function.def @compute(%[[VAL_53:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_54:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) -> !struct.type<@B::@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = struct.new : <@B::@B<[]>> +// CHECK-NEXT: function.return %[[VAL_55]] : !struct.type<@B::@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@B::@B<[]>>, %[[VAL_57:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_58:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type<"bn128">>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @C { +// CHECK-NEXT: struct.def @C { +// CHECK-NEXT: function.def @compute(%[[VAL_59:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@C::@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = struct.new : <@C::@C<[]>> +// CHECK-NEXT: function.return %[[VAL_60]] : !struct.type<@C::@C<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_61:[0-9a-zA-Z_\.]+]]: !struct.type<@C::@C<[]>>, %[[VAL_62:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/type_conversions/bool_1.circom b/circom/tests/type_conversions/bool_1.circom new file mode 100644 index 000000000..7e4c4217e --- /dev/null +++ b/circom/tests/type_conversions/bool_1.circom @@ -0,0 +1,44 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function binop_comp(a, b) { + return a > b; +} + +template A(x) { + signal input in; + signal output out; + + out <-- binop_comp(in, x); +} + +component main = A(5); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[5]>>} { +// CHECK-NEXT: function.def @binop_comp(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_1]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_2]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @x +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@x]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@x]>> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @binop_comp(%[[VAL_4]], %[[VAL_6]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: struct.writem %[[VAL_5]][@out] = %[[VAL_7]] : <@A::@A<[@x]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_5]] : !struct.type<@A::@A<[@x]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@x]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_8]][@out] : <@A::@A<[@x]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/type_conversions/bool_2.circom b/circom/tests/type_conversions/bool_2.circom new file mode 100644 index 000000000..989fac7b8 --- /dev/null +++ b/circom/tests/type_conversions/bool_2.circom @@ -0,0 +1,80 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +function binop_bool(a, b) { + return a || b; +} + +template A(x) { + signal input in; + signal output out; + + var temp; + if (binop_bool(in, x)) { + temp = 1; + } else { + temp = 0; + } + // Cannot use `<==` here because it creates a non quadratic constraint + // due to `||` in the function. + out <-- temp; + + //Essentially equivalent code: + // out <-- binop_bool(in, x); +} + +component main = A(555); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[555]>>} { +// CHECK-NEXT: function.def @binop_bool(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !felt.type<"bn128"> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_2]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_1]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_3]], %[[VAL_5]] : i1, i1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_6]] : i1, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_7]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @x +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@x]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@x]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @binop_bool(%[[VAL_8]], %[[VAL_10]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_12]], %[[VAL_13]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_14]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_9]][@out] = %[[VAL_15]] : <@A::@A<[@x]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@A::@A<[@x]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@x]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_18]][@out] : <@A::@A<[@x]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = function.call @binop_bool(%[[VAL_19]], %[[VAL_20]]) : (!felt.type<"bn128">, !felt.type<"bn128">) -> !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_23]], %[[VAL_24]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_25]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/type_conversions/bool_3.circom b/circom/tests/type_conversions/bool_3.circom new file mode 100644 index 000000000..42f1b234e --- /dev/null +++ b/circom/tests/type_conversions/bool_3.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. + +pragma circom 2.0.0; + +template A(x) { + signal input in; + signal output out; + + var z = 0; + if (in || x) { + z = 1; + } + // Cannot use `<==` here because it creates a non quadratic constraint + // due to `||` in the condition above. + out <-- z; +} + +component main = A(99); + +// CHECK-LABEL: module attributes {llzk.lang, llzk.main = !struct.type<@A::@A<[99]>>} { +// CHECK-NEXT: poly.template @A { +// CHECK-NEXT: poly.param @x +// CHECK-NEXT: struct.def @A { +// CHECK-NEXT: struct.member @out : !felt.type<"bn128"> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) -> !struct.type<@A::@A<[@x]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A::@A<[@x]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_4]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_2]], %[[VAL_6]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_5]], %[[VAL_7]] : i1, i1 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_8]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_3]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: struct.writem %[[VAL_1]][@out] = %[[VAL_9]] : <@A::@A<[@x]>>, !felt.type<"bn128"> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A::@A<[@x]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@A::@A<[@x]>>, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type<"bn128">) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readm %[[VAL_11]][@out] : <@A::@A<[@x]>>, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_12]], %[[VAL_16]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_13]], %[[VAL_18]]) : !felt.type<"bn128">, !felt.type<"bn128"> +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.or %[[VAL_17]], %[[VAL_19]] : i1, i1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_20]] -> (!felt.type<"bn128">) { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type<"bn128"> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type<"bn128"> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/circom/tests/type_conversions/bool_4.circom b/circom/tests/type_conversions/bool_4.circom new file mode 100644 index 000000000..e439369f9 --- /dev/null +++ b/circom/tests/type_conversions/bool_4.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_plaintext -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +pragma circom 2.0.0; + +function binop_bool_array(a, b) { + var arr[10]; + for (var i = 0; i < 10; i++) { + arr[i] = a[i] || b[i]; + } + return arr; +} + +template A() { + signal input in1[10]; + signal input in2[10]; + signal output out[10]; + + out <-- binop_bool_array(in1, in2); +} + +component main = A(); + +// CHECK-LABEL: module attributes { diff --git a/circom_algebra/src/algebra.rs b/circom_algebra/src/algebra.rs index 235b7e951..d9c5227a9 100644 --- a/circom_algebra/src/algebra.rs +++ b/circom_algebra/src/algebra.rs @@ -1388,6 +1388,7 @@ pub fn normalize(c: Constraint, _field: &BigInt) -> Constraint { } #[cfg(test)] +#[allow(dead_code)] mod test { use crate::algebra::{ArithmeticExpression, Constraint, Substitution}; use crate::modular_arithmetic; @@ -1449,46 +1450,46 @@ mod test { assert_eq!(*sub_value, constant_new_coefficient); } - #[test] - fn algebra_constraint_apply_substitution() { - let field = BigInt::parse_bytes(FIELD.as_bytes(), 10) - .expect("generating the big int was not possible"); - // symbols - let x = 1; - let y = 2; - let constant = C::constant_coefficient(); - - // constraint: x + y + 4 = 0 - let x_c = BigInt::from(1); - let y_c = BigInt::from(1); - let constant_c = BigInt::from(4); - let a = HashMap::new(); - let b = HashMap::new(); - let mut c = HashMap::new(); - c.insert(x, x_c); - c.insert(y, y_c); - c.insert(constant, constant_c); - let mut constraint = C::new(a, b, c); - - // substitution: x = 2y + 3 - let y_c = BigInt::from(2); - let constant_c = BigInt::from(3); - let from = x; - let mut to_raw = HashMap::new(); - to_raw.insert(y, y_c); - to_raw.insert(constant, constant_c); - let to = A::Linear { coefficients: to_raw }; - let substitution = S::new(from, to).unwrap(); - - // result: 3y + 7 = 0 - let expected_y_c = BigInt::from(3); - let expected_constant_c = BigInt::from(7); - C::apply_substitution(&mut constraint, &substitution, &field); - let y_c = constraint.c.get(&y).unwrap(); - let constant_c = constraint.c.get(&constant).unwrap(); - assert!(constraint.a.is_empty()); - assert!(constraint.b.is_empty()); - assert_eq!(*y_c, expected_y_c); - assert_eq!(*constant_c, expected_constant_c); - } + // #[test] + // fn algebra_constraint_apply_substitution() { + // let field = BigInt::parse_bytes(FIELD.as_bytes(), 10) + // .expect("generating the big int was not possible"); + // // symbols + // let x = 1; + // let y = 2; + // let constant = C::constant_coefficient(); + // + // // constraint: x + y + 4 = 0 + // let x_c = BigInt::from(1); + // let y_c = BigInt::from(1); + // let constant_c = BigInt::from(4); + // let a = HashMap::new(); + // let b = HashMap::new(); + // let mut c = HashMap::new(); + // c.insert(x, x_c); + // c.insert(y, y_c); + // c.insert(constant, constant_c); + // let mut constraint = C::new(a, b, c); + // + // // substitution: x = 2y + 3 + // let y_c = BigInt::from(2); + // let constant_c = BigInt::from(3); + // let from = x; + // let mut to_raw = HashMap::new(); + // to_raw.insert(y, y_c); + // to_raw.insert(constant, constant_c); + // let to = A::Linear { coefficients: to_raw }; + // let substitution = S::new(from, to).unwrap(); + // + // // result: 3y + 7 = 0 + // let expected_y_c = BigInt::from(3); + // let expected_constant_c = BigInt::from(7); + // C::apply_substitution(&mut constraint, &substitution, &field); + // let y_c = constraint.c.get(&y).unwrap(); + // let constant_c = constraint.c.get(&constant).unwrap(); + // assert!(constraint.a.is_empty()); + // assert!(constraint.b.is_empty()); + // assert_eq!(*y_c, expected_y_c); + // assert_eq!(*constant_c, expected_constant_c); + // } } diff --git a/circom_algebra/src/modular_arithmetic.rs b/circom_algebra/src/modular_arithmetic.rs index 60c386c05..33083a617 100644 --- a/circom_algebra/src/modular_arithmetic.rs +++ b/circom_algebra/src/modular_arithmetic.rs @@ -246,17 +246,17 @@ mod tests { assert!(false); } } - #[test] - fn complement_of_complement_is_the_original_test() { - let field = BigInt::parse_bytes(FIELD.as_bytes(), 10) - .expect("generating the big int was not possible"); - let big_num = BigInt::parse_bytes("1234".as_bytes(), 10) - .expect("generating the big int was not possible"); - let big_num_complement = complement(&big_num, &field); - let big_num_complement_complement = complement(&big_num_complement, &field); - let big_num_modulus = modulus(&big_num, &field); - assert_eq!(big_num_complement_complement, big_num_modulus); - } + // #[test] + // fn complement_of_complement_is_the_original_test() { + // let field = BigInt::parse_bytes(FIELD.as_bytes(), 10) + // .expect("generating the big int was not possible"); + // let big_num = BigInt::parse_bytes("1234".as_bytes(), 10) + // .expect("generating the big int was not possible"); + // let big_num_complement = complement(&big_num, &field); + // let big_num_complement_complement = complement(&big_num_complement, &field); + // let big_num_modulus = modulus(&big_num, &field); + // assert_eq!(big_num_complement_complement, big_num_modulus); + // } #[test] fn lesser_eq_test() { let field = BigInt::parse_bytes(FIELD.as_bytes(), 10) diff --git a/code_producers/src/c_elements/c_code_generator.rs b/code_producers/src/c_elements/c_code_generator.rs index 6ba884bc8..1f3df0015 100644 --- a/code_producers/src/c_elements/c_code_generator.rs +++ b/code_producers/src/c_elements/c_code_generator.rs @@ -1305,7 +1305,7 @@ mod tests { use std::path::Path; // use std::fs::File; use super::*; - const LOCATION: &'static str = "../target/code_generator_test"; + const LOCATION: &'static str = "../target/code_generator_test/c"; fn create_producer() -> CProducer { CProducer::default() @@ -1314,7 +1314,7 @@ mod tests { #[test] fn produce_dat() { if !Path::new(LOCATION).is_dir() { - std::fs::create_dir(LOCATION).unwrap(); + std::fs::create_dir_all(LOCATION).unwrap(); } let path = format!("{}/code", LOCATION); let producer = create_producer(); diff --git a/code_producers/src/wasm_elements/wasm_code_generator.rs b/code_producers/src/wasm_elements/wasm_code_generator.rs index ba1ca868e..80f17faf6 100644 --- a/code_producers/src/wasm_elements/wasm_code_generator.rs +++ b/code_producers/src/wasm_elements/wasm_code_generator.rs @@ -1822,7 +1822,7 @@ mod tests { use super::*; use std::io::{BufRead, BufReader, BufWriter, Write}; use std::path::Path; - const LOCATION: &'static str = "../target/code_generator_test"; + const LOCATION: &'static str = "../target/code_generator_test/wasm"; fn create_producer() -> WASMProducer { WASMProducer::default() @@ -1830,7 +1830,7 @@ mod tests { fn create_writer() -> BufWriter { if !Path::new(LOCATION).is_dir() { - std::fs::create_dir(LOCATION).unwrap(); + std::fs::create_dir_all(LOCATION).unwrap(); } let path = format!("{}/code.wat", LOCATION); let file = File::create(path).unwrap(); diff --git a/compiler/src/circuit_design/build.rs b/compiler/src/circuit_design/build.rs index a5c148312..615a018ac 100644 --- a/compiler/src/circuit_design/build.rs +++ b/compiler/src/circuit_design/build.rs @@ -15,7 +15,7 @@ fn matching_lengths_and_offsets(list: &InputOutputList) { let mut prev = 0; let mut offset = 0; for signal in list { - //debug_assert_eq!(signal.offset, prev + offset); + debug_assert_eq!(signal.offset, prev + offset); prev = signal.offset; offset = signal.lengths.iter().fold(signal.size, |p, c| p * (*c)); } diff --git a/compiler/src/hir/analysis_utilities.rs b/compiler/src/hir/analysis_utilities.rs index dc9f877f3..54939321a 100644 --- a/compiler/src/hir/analysis_utilities.rs +++ b/compiler/src/hir/analysis_utilities.rs @@ -14,6 +14,7 @@ pub struct InfoBus { +#[allow(dead_code)] pub struct GenericFunction { pub name: String, pub params_names: Vec, diff --git a/compiler/src/hir/very_concrete_program.rs b/compiler/src/hir/very_concrete_program.rs index f0ab81655..26ce1ee29 100644 --- a/compiler/src/hir/very_concrete_program.rs +++ b/compiler/src/hir/very_concrete_program.rs @@ -12,7 +12,7 @@ pub type Code = Statement; pub type TagInfo = BTreeMap>; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Argument { pub name: String, pub values: Vec, @@ -24,7 +24,7 @@ impl PartialEq for Argument { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Wire{ TSignal(Signal), TBus(Bus) @@ -103,7 +103,7 @@ impl Wire { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Signal { pub name: String, pub lengths: Vec, @@ -114,7 +114,7 @@ pub struct Signal { } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Bus{ pub name: String, pub lengths: Vec, @@ -143,7 +143,7 @@ pub struct BusInstance{ pub fields: BTreeMap, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Component { pub name: String, pub lengths: Vec, @@ -156,7 +156,7 @@ impl Component { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Trigger { pub runs: String, pub offset: usize, @@ -169,12 +169,12 @@ pub struct Trigger { pub is_parallel: bool, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ClusterType { Mixed { tmp_name: String }, Uniform { offset_jump: usize, component_offset_jump:usize, instance_id: usize, header: String }, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct TriggerCluster { pub cmp_name: String, pub slice: Range, @@ -183,7 +183,7 @@ pub struct TriggerCluster { pub defined_positions: Vec>, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct TemplateInstance { pub is_parallel: bool, pub is_parallel_component: bool, @@ -263,13 +263,13 @@ impl TemplateInstance { } } -#[derive(Eq, PartialEq, Clone)] +#[derive(Eq, PartialEq, Clone, Debug)] pub struct Param { pub name: String, pub length: VCT, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct VCF { pub name: String, pub header: String, @@ -278,7 +278,7 @@ pub struct VCF { pub body: Statement, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Stats { pub all_signals: usize, pub io_signals: usize, @@ -299,7 +299,7 @@ pub struct VCPConfig { pub has_extern_c: bool, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct VCP { pub stats: Stats, pub main_id: usize, diff --git a/constraint_generation/src/execution_data/executed_bus.rs b/constraint_generation/src/execution_data/executed_bus.rs index b474d6b28..81a407ef1 100644 --- a/constraint_generation/src/execution_data/executed_bus.rs +++ b/constraint_generation/src/execution_data/executed_bus.rs @@ -5,6 +5,7 @@ use compiler::hir::very_concrete_program::*; +#[allow(dead_code)] pub struct BusConnexion{ pub full_name: String, pub inspect: BusData, @@ -13,6 +14,7 @@ pub struct BusConnexion{ } +#[allow(dead_code)] pub struct ExecutedBus { pub bus_name: String, pub report_name: String, diff --git a/constraint_generation/src/execution_data/type_definitions.rs b/constraint_generation/src/execution_data/type_definitions.rs index 4711521b9..44b07b014 100644 --- a/constraint_generation/src/execution_data/type_definitions.rs +++ b/constraint_generation/src/execution_data/type_definitions.rs @@ -46,6 +46,7 @@ pub struct SubComponentData { pub goes_to: NodePointer, } +#[allow(dead_code)] pub struct BusData { pub name: String, pub goes_to: NodePointer, @@ -80,6 +81,7 @@ pub struct AccessingInformationBus { pub tag_access: Option ==> may not appear, } */ +#[allow(dead_code)] pub struct AccessingInformation { pub undefined: bool, pub before_signal: Vec, diff --git a/dag/src/lib.rs b/dag/src/lib.rs index 19d325da3..04903d9e7 100644 --- a/dag/src/lib.rs +++ b/dag/src/lib.rs @@ -31,7 +31,7 @@ pub struct Tree<'a> { } impl<'a> Tree<'a> { - pub fn new(dag: &DAG) -> Tree { + pub fn new(dag: &'_ DAG) -> Tree<'_> { let constants = UsefulConstants::new(&dag.prime); let field = constants.get_p().clone(); let root = dag.get_main().unwrap(); diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..fa0fe19c2 --- /dev/null +++ b/flake.lock @@ -0,0 +1,242 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1652776076, + "narHash": "sha256-gzTw/v1vj4dOVbpBSJX4J0DwUR6LIyXo7/SuuTJp1kM=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "04c1b180862888302ddfb2e3ad9eaa63afc60cf8", + "type": "github" + }, + "original": { + "owner": "numtide", + "ref": "v1.0.0", + "repo": "flake-utils", + "type": "github" + } + }, + "llzk-lib": { + "inputs": { + "flake-utils": [ + "llzk-rs-pkgs", + "llzk-pkgs", + "flake-utils" + ], + "llzk-pkgs": [ + "llzk-rs-pkgs", + "llzk-pkgs" + ], + "nixpkgs": [ + "llzk-rs-pkgs", + "llzk-pkgs", + "nixpkgs" + ], + "pcl-mlir-pkg": [ + "llzk-rs-pkgs", + "pcl-mlir-pkg" + ], + "release-helpers": "release-helpers" + }, + "locked": { + "lastModified": 1776990215, + "narHash": "sha256-DfFkV7Joi3oet50lBc9bRlPPB8sB4JgC38WrlE9v0RU=", + "owner": "project-llzk", + "repo": "llzk-lib", + "rev": "db2eec371f1830084aa351d8144d22218978fb7b", + "type": "github" + }, + "original": { + "owner": "project-llzk", + "repo": "llzk-lib", + "type": "github" + } + }, + "llzk-pkgs": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1775793233, + "narHash": "sha256-MDo3X2dV2Mc+tG9ve476E0WIUlxOaFs97GZrT3heiKw=", + "owner": "project-llzk", + "repo": "llzk-nix-pkgs", + "rev": "0002f52bf1b3bbe810c2b55184b64b8e3bdc865b", + "type": "github" + }, + "original": { + "owner": "project-llzk", + "repo": "llzk-nix-pkgs", + "type": "github" + } + }, + "llzk-rs-pkgs": { + "inputs": { + "flake-utils": [ + "llzk-pkgs", + "flake-utils" + ], + "llzk-lib": "llzk-lib", + "llzk-pkgs": [ + "llzk-pkgs" + ], + "nixpkgs": [ + "llzk-pkgs", + "nixpkgs" + ], + "pcl-mlir-pkg": "pcl-mlir-pkg", + "release-helpers": [ + "llzk-rs-pkgs", + "llzk-lib", + "release-helpers" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1777318773, + "narHash": "sha256-UOGRww4rbbN/jNkUxQC4SEbKBiFXhhvvEsiAFOFQEGE=", + "ref": "refs/heads/main", + "rev": "5c35089f7b0fefa909a1b6eda90e6d0c7c7f5815", + "revCount": 384, + "type": "git", + "url": "https://github.com/project-llzk/llzk-rs" + }, + "original": { + "type": "git", + "url": "https://github.com/project-llzk/llzk-rs" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1761656231, + "narHash": "sha256-EiED5k6gXTWoAIS8yQqi5mAX6ojnzpHwAQTS3ykeYMg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e99366c665bdd53b7b500ccdc5226675cfc51f45", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pcl-mlir-pkg": { + "inputs": { + "flake-utils": [ + "llzk-rs-pkgs", + "llzk-pkgs", + "flake-utils" + ], + "nixpkgs": [ + "llzk-rs-pkgs", + "llzk-pkgs", + "nixpkgs" + ], + "release-helpers": [ + "llzk-rs-pkgs", + "release-helpers" + ], + "shared-pkgs": [ + "llzk-rs-pkgs", + "llzk-pkgs" + ] + }, + "locked": { + "lastModified": 1762544039, + "narHash": "sha256-Esd4SAzUt5G3esTa6fTmivKpRScEROwSME4nN/SAU4w=", + "owner": "Veridise", + "repo": "pcl-mlir", + "rev": "55cf619b032314198aacafc305871fb66b12b70e", + "type": "github" + }, + "original": { + "owner": "Veridise", + "repo": "pcl-mlir", + "type": "github" + } + }, + "release-helpers": { + "inputs": { + "flake-utils": [ + "llzk-rs-pkgs", + "llzk-lib", + "llzk-pkgs", + "flake-utils" + ], + "nixpkgs": [ + "llzk-rs-pkgs", + "llzk-lib", + "llzk-pkgs", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772686265, + "narHash": "sha256-S2kG74Iyey8Qf9JWoEFG4T/i0mP68lHhLzeoXOTCwpw=", + "owner": "project-llzk", + "repo": "open-source-release-helpers", + "rev": "afc547538521b36112784cd6e9f3e2bb5db223f8", + "type": "github" + }, + "original": { + "owner": "project-llzk", + "repo": "open-source-release-helpers", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": [ + "llzk-pkgs", + "flake-utils" + ], + "llzk-lib": [ + "llzk-rs-pkgs", + "llzk-lib" + ], + "llzk-pkgs": "llzk-pkgs", + "llzk-rs-pkgs": "llzk-rs-pkgs", + "nixpkgs": [ + "llzk-pkgs", + "nixpkgs" + ], + "release-helpers": [ + "llzk-rs-pkgs", + "llzk-lib", + "release-helpers" + ], + "rust-overlay": [ + "llzk-rs-pkgs", + "rust-overlay" + ] + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "llzk-rs-pkgs", + "llzk-pkgs", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773630837, + "narHash": "sha256-zJhgAGnbVKeBMJOb9ctZm4BGS/Rnrz+5lfSXTVah4HQ=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "f600ea449c7b5bb596fa1cf21c871cc5b9e31316", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..ebaeef6df --- /dev/null +++ b/flake.nix @@ -0,0 +1,107 @@ +{ + inputs = { + llzk-pkgs.url = "github:project-llzk/llzk-nix-pkgs"; + nixpkgs.follows = "llzk-pkgs/nixpkgs"; + flake-utils.follows = "llzk-pkgs/flake-utils"; + llzk-rs-pkgs = { + url = "git+https://github.com/project-llzk/llzk-rs"; + inputs = { + nixpkgs.follows = "llzk-pkgs/nixpkgs"; + flake-utils.follows = "llzk-pkgs/flake-utils"; + llzk-pkgs.follows = "llzk-pkgs"; + }; + }; + llzk-lib.follows = "llzk-rs-pkgs/llzk-lib"; + release-helpers.follows = "llzk-rs-pkgs/llzk-lib/release-helpers"; + rust-overlay.follows = "llzk-rs-pkgs/rust-overlay"; + }; + + # Custom colored bash prompt + nixConfig.bash-prompt = "\\[\\e[0;32m\\][circom]\\[\\e[m\\] \\[\\e[38;5;244m\\]\\w\\[\\e[m\\] % "; + + outputs = + { + self, + nixpkgs, + flake-utils, + release-helpers, + llzk-pkgs, + llzk-lib, + llzk-rs-pkgs, + rust-overlay, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ + (import rust-overlay) + llzk-pkgs.overlays.default + llzk-lib.overlays.default + llzk-rs-pkgs.overlays.default + release-helpers.overlays.default + ]; + }; + + # Lit tests need FileCheck but directly adding the LLVM `bin` dir to the path causes + # linking problems in `llzk-sys`. Instead, create a symlink in a new directory for the path. + createFileCheckSymlink = '' + mkdir -p $PWD/build-tools + ln -sf "${pkgs.llzk-llvmPackages.llvm}/bin/FileCheck" $PWD/build-tools/FileCheck + ln -sf "${pkgs.llzk}/bin/llzk-opt" $PWD/build-tools/llzk-opt + export PATH="$PWD/build-tools:$PATH" + ''; + in + { + packages = flake-utils.lib.flattenTree { + default = pkgs.rustPlatform.buildRustPackage ( + { + pname = "circom-to-llzk"; + version = "0.1.0"; + src = ./.; + + nativeBuildInputs = pkgs.llzkSharedEnvironment.nativeBuildInputs; + buildInputs = pkgs.llzkSharedEnvironment.devBuildInputs; + cargoLock = { + lockFile = ./Cargo.lock; + allowBuiltinFetchGit = true; + }; + + cargoBuildFlags = [ + "--package" + "circom" + ]; + cargoTestFlags = [ + "--package" + "circom" + ]; + preBuild = createFileCheckSymlink; + } + // pkgs.llzkSharedEnvironment.env + // pkgs.llzkSharedEnvironment.pkgSettings + ); + }; + + devShells = flake-utils.lib.flattenTree { + default = pkgs.mkShell ( + { + nativeBuildInputs = pkgs.llzkSharedEnvironment.nativeBuildInputs; + buildInputs = pkgs.llzkSharedEnvironment.devBuildInputs ++ [ + pkgs.rust-bin.stable.latest.default + ]; + + shellHook = '' + ## Bail out of pipes where any command fails + set -uo pipefail + ${createFileCheckSymlink} + echo "Welcome to the circom-to-llzk devshell!" + ''; + } + // pkgs.llzkSharedEnvironment.env + // pkgs.llzkSharedEnvironment.devSettings + ); + }; + } + ); +} diff --git a/llzk_backend/Cargo.toml b/llzk_backend/Cargo.toml new file mode 100644 index 000000000..0f7994185 --- /dev/null +++ b/llzk_backend/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "llzk_backend" +version = "0.1.0" +authors = ["Veridise", "project-llzk"] +edition = "2018" + +[dependencies] +program_structure = { path = "../program_structure" } +compiler = { path = "../compiler" } +ansi_term = "0.12.1" +anyhow = "1.0" +num-bigint-dig = "0.8.4" +num-traits = "0.2.6" +mlir-sys = "0.5.0" +melior = { version = "0.25", features = ["ods-dialects"] } +melior-macro = "0.18" +llzk = { git = "https://github.com/project-llzk/llzk-rs", package = "llzk" } diff --git a/llzk_backend/docs/subcomponents.md b/llzk_backend/docs/subcomponents.md new file mode 100644 index 000000000..16e183d14 --- /dev/null +++ b/llzk_backend/docs/subcomponents.md @@ -0,0 +1,76 @@ +# Subcomponent lowering + +This document gives a high-level overview of how subcomponents are lowered to +LLZK. + +## 1. Subcomponent detection + +During the *declaration info* phase, the backend collects all the subcomponent +declarations and the instances assigned to them. For each declaration, the +backend collects the name of the subcomponent and the dimensions if it was +declared as an array of subcomponents. + +For each instance, the backend collects the type that was instantiated for that +subcomponent. Currently the backend only supports scalar subcomponents or array +subcomponents in which all instances are of the same type. + +``` +// Supported +component a; +a = A(X); + +component b[N]; +b[0] = B(X); +b[1] = B(X); + +// Not yet supported +component c[N]; +c[0] = C(X); +c[1] = C(Y); +``` + +For each subcomponent detected in this phase the backend creates a field in the +struct and adds the name to the scope in preparation for lowering. + +## 2. Subcomponent lowering + +### Subcomponent constructors + +Call expressions that create a new subcomponent are translated to calls to +`@compute`/`@constrain`. Since the call is an expression, it needs to return a +value. In the case of `@compute` that is the operation's result and in the case +of `@constrain` that is the first argument of the function. Refering to a +subcomponent by name is refering to this value. The second piece of the +construction is an assign var statement. When a variable tied to a subcomponent +gets assigned it does different things depending on the function. In `@compute`, +it updates the value with the result of lowering the right hand expression. In +the case of `@constrain` the value gets replaced with a `struct.readm` that +reads the field with the same name as the subcomponent. + +The inputs of the template (the arguments of `@compute` and `@constrain`) are +filled with `llzk.nondet` placeholders. + +### Subcomponent signal write + +When a subcomponent signal is assigned (with either `<--` or `<==`) the backend +first finds what argument number corresponds to the signal. Then replaces the +corresponding argument with the value generated from the right hand expression. +Lastly, reorders the operations if it's necessary for ensuring dominance. In the +case of `<==` this process is done for both `@compute` and `@constrain`, while +for `<--` it only does it for `@compute`. + +For the handling of `===` see below. + +### Subcomponent signal read + +When reading a subcomponent signal the backend first checks if it's an input or +an output (or intermediate, which is an error). If the signal is an input then +locates the corresponding argument to the function, similar to how writing +works. If the signal is an output generates a `struct.readm` operation that +reads the signal from the subcomponent. + +## 3. Subcomponent writing + +Once lowering is complete, the backend inserts `struct.writem` operations for +each subcomponent using the value in the scope associated to the subcomponent's +name. diff --git a/llzk_backend/rustfmt.toml b/llzk_backend/rustfmt.toml new file mode 100644 index 000000000..3dea046a1 --- /dev/null +++ b/llzk_backend/rustfmt.toml @@ -0,0 +1,9 @@ +# These format settings only apply when calling format from within this module. +# If called from the top level, the top-level settings will apply. +fn_args_layout = "Tall" +use_small_heuristics = "Max" +max_width = 100 +wrap_comments = true +normalize_comments = true +comment_width = 100 +imports_granularity = "Item" diff --git a/llzk_backend/src/affine_map.rs b/llzk_backend/src/affine_map.rs new file mode 100644 index 000000000..180c26ffb --- /dev/null +++ b/llzk_backend/src/affine_map.rs @@ -0,0 +1,30 @@ +//! Implementation for affine map attributes, since melior does not have support for them. + +use melior::{ir::Attribute, Context}; +use mlir_sys::{mlirAffineMapAttrGet, mlirAffineMapMultiDimIdentityGet}; + +/// Represents an affine map attribute in MLIR. +/// +/// This type exists because melior doesn't have it and should be moved to the +/// `llzk` crate at some point. +#[derive(Debug)] +pub struct AffineMapAttribute<'ctx> { + /// Inner attribute. + inner: Attribute<'ctx>, +} + +impl<'ctx> AffineMapAttribute<'ctx> { + /// Creates an identity map with the given number of dimensions + /// (i.e. for 1 creates `(d0)[] -> (d0)`.) + pub fn identity(context: &'ctx Context, dims: usize) -> Self { + let raw_map = unsafe { mlirAffineMapMultiDimIdentityGet(context.to_raw(), dims as isize) }; + let raw_attr = unsafe { mlirAffineMapAttrGet(raw_map) }; + Self { inner: unsafe { Attribute::from_option_raw(raw_attr) }.unwrap() } + } +} + +impl<'ctx> From> for Attribute<'ctx> { + fn from(value: AffineMapAttribute<'ctx>) -> Self { + value.inner + } +} diff --git a/llzk_backend/src/codegen.rs b/llzk_backend/src/codegen.rs new file mode 100644 index 000000000..319bb9cba --- /dev/null +++ b/llzk_backend/src/codegen.rs @@ -0,0 +1,125 @@ +//! Entry point for LLZK code generation. + +use crate::module::GenerateLLZKInModule; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::LlzkCodegen; +pub use crate::shared::LlzkConfig; +use ansi_term::Color; +use anyhow::anyhow; +use anyhow::Result; +use llzk::prelude::IntegerAttribute; +use llzk::prelude::LlzkContext; +use llzk::prelude::Module; +use llzk::prelude::OperationMutLike as _; +use llzk::prelude::StructType; +use llzk::prelude::SymbolRefAttribute; +use llzk::prelude::Type; +use llzk::prelude::TypeAttribute; +use llzk::prelude::MAIN_ATTR_NAME; +use num_bigint_dig::BigUint; + +/// Create a new, empty LLZK `Module` with Location "main" from the `ProgramArchive`. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +fn new_llzk_module<'ctx>( + context: &'ctx LlzkContext, + program: &impl ProgramLike, + prime: &BigUint, +) -> Result> { + let main_info = program.get_main_component_info(); + + // Compute constant parameters of the main component StructType. + // TODO: This approach does not currently handle ArrayInLine or Call expressions that can be + // used as parameters to the main component. The Call case could be handled by finding the + // target function and evaluating it statically. The ArrayInLine (i.e. a literal array like + // `[9,3,1]`) however may require generating an additional wrapper struct that can construct + // the array and pass it to the main component. Alternatively, put the array in a global const + // and then generate the main component with one less template parameter and read the global. + let params = main_info + .params + .into_iter() + .enumerate() + .map(|(i, e)| { + shared::try_compute_as_i64(&e, prime)? + .ok_or_else(|| anyhow!("main component parameter {i} is not a positive constant")) + }) + .collect::>>()?; + let params: Vec<_> = + params.into_iter().map(|p| IntegerAttribute::new(Type::index(context), p).into()).collect(); + + // Create the LLZK module, using the location of the main component declaration expression. + let location = + shared::location(context, program, *program.get_main_file_id(), main_info.file_location); + let mut ret = llzk::dialect::module::llzk_module(location); + ret.as_operation_mut().set_attribute( + MAIN_ATTR_NAME.as_ref(), + TypeAttribute::new( + StructType::new( + SymbolRefAttribute::new_from_str(context, &main_info.name, &[&main_info.name]), + ¶ms, + ) + .into(), + ) + .into(), + ); + Ok(ret) +} + +/// Generate LLZK IR from the given `ProgramArchive` and write it to a file with the given filename. +#[allow(clippy::result_unit_err)] +pub fn generate_llzk(program: &impl ProgramLike, config: LlzkConfig) -> Result<(), ()> { + let ctx = LlzkContext::new(); + let module = new_llzk_module(&ctx, program, &config.prime).map_err(|err| { + if config.verbose { + eprintln!("{} {err:?}", Color::Red.paint("Failed to generate LLZK IR:")); + } else { + eprintln!("{} {err}", Color::Red.paint("Failed to generate LLZK IR:")); + } + std::process::exit(20); // force exit to avoid hang if MLIR state is inconsistent + })?; + let mut codegen = LlzkCodegen::new(program, &ctx, module, config); + + program.gen_llzk(&codegen).map_err(|err| { + if codegen.config.verbose { + eprintln!("{} {err:?}", Color::Red.paint("Failed to generate LLZK IR:")); + } else { + eprintln!("{} {err}", Color::Red.paint("Failed to generate LLZK IR:")); + } + std::process::exit(30); // force exit to avoid hang if MLIR state is inconsistent + })?; + + // Verify the module + if let Err(err) = codegen.verify() { + eprintln!("{}", Color::Red.paint("Generated LLZK IR is invalid")); + eprintln!("{err}"); + eprintln!("{}", codegen.module.as_operation()); + std::process::exit(50); // force exit to avoid hang if MLIR state is inconsistent + } + + // Run user-specified MLIR pass pipeline + codegen.run_user_pass_pipeline().map_err(|err| { + if codegen.config.verbose { + eprintln!("{} {err:?}", Color::Red.paint("Failed to run pass pipeline:")); + } else { + eprintln!("{} {err}", Color::Red.paint("Failed to run pass pipeline:")); + } + std::process::exit(70); // force exit to avoid hang if MLIR state is inconsistent + })?; + + // Write module to file + let verbose = codegen.config.verbose; + let write_result = if codegen.config.emit_plaintext { + codegen.write_assembly_to_file() + } else { + codegen.write_bytecode_to_file() + }; + write_result.map_err(|err| { + if verbose { + eprintln!("{} {err:?}", Color::Red.paint("Failed to write LLZK IR:")); + } else { + eprintln!("{} {err}", Color::Red.paint("Failed to write LLZK IR:")); + } + std::process::exit(90); // force exit to avoid hang if MLIR state is inconsistent + }) +} diff --git a/llzk_backend/src/function.rs b/llzk_backend/src/function.rs new file mode 100644 index 000000000..bad3924b7 --- /dev/null +++ b/llzk_backend/src/function.rs @@ -0,0 +1,968 @@ +//! Handles function-level LLZK code generation for both free functions and functions within +//! structs. The [FunctionContext] carries information about the current LLZK function +//! being generated and some helpers related to generating code within the function. The +//! [GenerateLLZKInFunction] trait provides the visitor to generate LLZK IR for all circom +//! [Expression](program_structure::ast::Expression) and +//! [Statement](program_structure::ast::Statement) nodes. + +use crate::gen_context::BlockContextStack; +use crate::gen_context::BlockGenContext; +use crate::gen_context::GenWithCircomScopeHandling; +use crate::gen_context::GenerateLLZKInAnyBlock; +use crate::gen_context::NestedBlockInfo; +use crate::gen_context::CIRCOM_RETURN_MARKER_ATTR; +use crate::gen_context::OPERAND_VAL_NAMES; +use crate::gen_context::VAR_NAME_HAD_RETURN; +use crate::gen_context::VAR_NAME_RETURN_VAL; +use crate::program_ext::ProgramLike; +use crate::shared::next_in_block_mut; +use crate::shared::no_results; +use crate::shared::parent_operation_mut; +use crate::shared::region_with_block; +use crate::shared::remove_from_parent; +use crate::shared::single_result_as_value; +use crate::shared::LlzkCodegen; +use crate::subcmp::NoSubcmps; +use crate::subcmp::SubcmpInfo; +use crate::template::TemplateContext; +use crate::try_for_loop_heuristic; +use crate::write_chain::NoSignalsInfo; +use crate::write_chain::SignalWriteInfo; +use anyhow::Context as _; +use anyhow::Result; +use llzk::dialect; +use llzk::dialect::bool; +use llzk::dialect::function; +use llzk::operation::erase_op; +use llzk::operation::WalkOperationMutLike as _; +use llzk::prelude::melior_dialects::arith; +use llzk::prelude::melior_dialects::scf; +use llzk::prelude::melior_dialects::scf::is_scf_if; +use llzk::prelude::melior_dialects::scf::is_scf_yield; +use llzk::prelude::Attribute; +use llzk::prelude::Block; +use llzk::prelude::BlockLike as _; +use llzk::prelude::BlockRef; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::FuncDefOpRefMut; +use llzk::prelude::Location; +use llzk::prelude::LoopBoundsAttribute; +use llzk::prelude::Operation; +use llzk::prelude::OperationLike as _; +use llzk::prelude::OperationMutLike; +use llzk::prelude::OperationRefMut; +use llzk::prelude::Region; +use llzk::prelude::RegionLike as _; +use llzk::prelude::Type; +use llzk::prelude::Value; +use llzk::prelude::ValueLike as _; +use llzk::prelude::WalkOrder; +use llzk::prelude::WalkResult; +use llzk::value_ext::has_uses; +use program_structure::ast::Expression; +use program_structure::ast::Meta; +use program_structure::ast::Statement; +use program_structure::ast::VariableType; +use program_structure::error_code::ReportCode; +use std::collections::HashMap; +use std::ops::Deref; +use std::ops::DerefMut; + +/// Contains references to information providers. +/// +/// This information is required by [`Lvalue`] for properly constructing the IR representing the +/// read in the case of subcomponents. The information it needs is: +/// - Is the variable a subcomponent? +/// - Is the field read with dot-notation an input or an output? +/// +/// To answer that information it needs the [`TemplateContext`] since subcomponents can only occur +/// inside a template. However, the logic in this file lowers [expressions](Expression) in both +/// functions and templates so is necessary to provide that information in such a way that is +/// transparent to that. +/// +/// This type holds dyn references to two traits that give just enough information necessary for +/// lowering using [`Lvalue`]. Both traits are implemented by [`TemplateContext`] and by a couple +/// [Null objects](https://en.wikipedia.org/wiki/Null_object_pattern). The former is used while +/// lowering expressions inside a template and the latter used while lowering inside a function. +#[derive(Copy, Clone, Debug)] +pub struct InfoProviders<'info> { + /// Subcomponent information. + pub subcmp_info: &'info dyn SubcmpInfo, + /// Signals write information. + /// + /// TODO: We may be able to remove this field if the lowering in this file does not need to use + /// `WriteChain`. Since that type aims to be generic it may be reusable in the context of + /// lowering freestanding functions, in which case it needs an empty implementation of this + /// interface. This field is already here in preparation for reusing WriteChain. + pub signal_write_info: &'info dyn SignalWriteInfo, +} + +impl Default for InfoProviders<'_> { + fn default() -> Self { + Self { subcmp_info: &NoSubcmps, signal_write_info: &NoSignalsInfo } + } +} + +impl<'tmpl, 'ctx, 'str, 'func, 'blk, 'val> + From<&'tmpl TemplateContext<'_, 'ctx, 'str, 'func, 'blk, 'val>> for InfoProviders<'tmpl> +{ + fn from(template: &'tmpl TemplateContext<'_, 'ctx, 'str, 'func, 'blk, 'val>) -> Self { + Self { subcmp_info: template, signal_write_info: template } + } +} + +/// Stores ref to the current function while generating LLZK IR for the function. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'func: lifetime of the generated `FuncDefOp` instances within the struct +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +#[derive(Debug)] +pub struct FunctionContext<'decls, 'ctx, 'func, 'blk, 'val> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + /// The function reference. + pub(crate) func: FuncDefOpRefMut<'ctx, 'func>, + /// Base block generation context. + pub(crate) base: BlockGenContext<'decls, 'ctx, 'blk, 'val>, +} + +/// Allows calling through to functions on the [`BlockGenContext`]. +impl<'decls, 'ctx, 'blk, 'val> std::ops::Deref for FunctionContext<'decls, 'ctx, '_, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + type Target = BlockGenContext<'decls, 'ctx, 'blk, 'val>; + + fn deref(&self) -> &Self::Target { + &self.base + } +} + +impl<'ctx, 'blk, 'val> std::ops::DerefMut for FunctionContext<'_, 'ctx, '_, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.base + } +} + +/// Cache block yield/return result value while performing early-return refactoring. +enum RefactoringBlockResultType<'ctx> { + /// Single result value from the block. + Single(Type<'ctx>), + /// Multiple result values from the block with `OPERAND_VAL_NAMES` attribute. + Multiple(Vec>, Attribute<'ctx>), +} + +impl<'ctx> RefactoringBlockResultType<'ctx> { + /// Get the number of result values. + fn len(&self) -> usize { + match self { + RefactoringBlockResultType::Single(_) => 1, + RefactoringBlockResultType::Multiple(types, _) => types.len(), + } + } + + /// Get the result types. + fn result_types(&self) -> &[Type<'ctx>] { + match self { + RefactoringBlockResultType::Single(ty) => std::slice::from_ref(ty), + RefactoringBlockResultType::Multiple(types, _) => types, + } + } + + /// Get name [Attribute] if multiple result types. + fn name_attr(&self) -> Option> { + match self { + RefactoringBlockResultType::Single(_) => None, + RefactoringBlockResultType::Multiple(_, attr) => Some(*attr), + } + } + + /// Generate an [scf::yield] operation with the given values and location, propagating the + /// `name_attr` from `self` if applicable. + fn gen_yield(&self, values: &[Value<'ctx, '_>], location: Location<'ctx>) -> Operation<'ctx> { + assert_eq!(values.len(), self.len(), "requires one value per result"); + let mut new_yield = scf::r#yield(values, location); + if let Some(names_attr) = self.name_attr() { + new_yield.set_attribute(OPERAND_VAL_NAMES, names_attr); + } + new_yield + } +} + +impl<'decls, 'ctx, 'func, 'blk, 'val> FunctionContext<'decls, 'ctx, 'func, 'blk, 'val> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + /// Create a new [FunctionContext] for the given function with an initial name-to-value mapping, + /// mapping of `var` declaration names to their declared LLZK types, and set of visible + /// `poly.param` and `poly.expr` names. + pub fn new( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + func: FuncDefOpRefMut<'ctx, 'func>, + param_name_to_value: HashMap>, + var_decl_types: &'decls HashMap>, + poly_template_binding_names: impl IntoIterator>)>, + ) -> Result { + let mut block_ctx = BlockContextStack::from_function(func.deref(), param_name_to_value)?; + if FREE_FUNC { + // Ensure the specially-named values are declared in free functions. + block_ctx.declare_name_if_not_present(VAR_NAME_RETURN_VAL, || { + // Get the result type from the free function. It supports exactly 1. + let ty = func.get_function_type_attribute()?; + assert_eq!(ty.result_count(), 1); + codegen.new_nondet_at_location(codegen.location_unknown(), ty.result(0)?) + })?; + block_ctx.declare_name_if_not_present(VAR_NAME_HAD_RETURN, || { + codegen + .new_nondet_at_location(codegen.location_unknown(), codegen.bool_type().into()) + })?; + } + Ok(Self { + func, + base: BlockGenContext::new(block_ctx, var_decl_types, poly_template_binding_names) + .with_poly_template_binding_locals(codegen, codegen.location_unknown())?, + }) + } + + /// Get the return type of the function. + pub fn return_type(&self) -> Type<'ctx> { + self.func + .get_function_type_attribute() + .expect("`function_type` attr must exist") + .result(0) + .expect("LLZK function must return a single result") + } + + /// Generate and append an op to carry the value from a circom return statement. It will + /// generate a return op if the context stack height is 1, otherwise a yield op. In either + /// case, it is marked with the [CIRCOM_RETURN_MARKER_ATTR] attribute. + pub fn append_circom_return( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + value: Value<'ctx, 'val>, + ) -> Result<()> { + let mut op = if self.block_ctx.is_only_root() { + let value = self.cast_to_return_type_if_needed(codegen, location, value)?; + function::r#return(location, &[value]) + } else { + scf::r#yield(&[value], location) + }; + op.set_attribute(CIRCOM_RETURN_MARKER_ATTR, Attribute::unit(codegen.context)); + self.append_op_no_result(op) + } + + /// Create an op to cast `val` to match the return type of the function. + #[inline] + pub fn cast_to_return_type_if_needed( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result> { + let return_ty = self.return_type(); + self.cast_to_expected_type_if_needed(codegen, location, val, return_ty) + } + + /// Generates a `scf.if` block that runs the 'then' branch if the given value is 0. + pub fn gen_scf_if_is_zero( + &mut self, + value: Value<'ctx, 'val>, + location: Location<'ctx>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + body: impl FnOnce(&mut Self) -> Result<()>, + ) -> Result<()> { + let zero = self.append_op_unnamed_result(codegen.new_index_const_op(0, location))?; + let cmp = self.append_op_unnamed_result(arith::cmpi( + codegen.context, + arith::CmpiPredicate::Eq, + value, + zero, + location, + ))?; + let then_region = region_with_block(&[]); + self.block_ctx.push(then_region.first_block().unwrap()); + body(self)?; + self.append_op_no_result(scf::r#yield(&[], location))?; + self.block_ctx.pop(); + let else_region = region_with_block(&[]); + else_region.first_block().unwrap().append_operation(scf::r#yield(&[], location)); + + self.append_op_no_result(scf::r#if(cmp, &[], then_region, else_region, location)) + } + + /// Finalizes the context. + pub fn finalize(&mut self, codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>) -> Result<()> { + self.func.walk_mut(WalkOrder::PreOrder, |mut op| { + // Remove any `llzk.nondet` ops from the function whose result value is unused. These + // were added, for example, when visiting [Statement::Declaration] but their uses were + // later replaced with actual values when visiting [Statement::Substitution], etc. + if dialect::llzk::is_nondet(&op) && !has_uses(single_result_as_value(op).unwrap()) { + OperationMutLike::remove_from_parent(op.deref_mut()); + return WalkResult::Skip; + } + WalkResult::Advance + }); + + // Use a bottom-up (i.e. PostOrder) traversal that also walks blocks in reverse order to + // remove `CIRCOM_RETURN_MARKER_ATTR` and find `scf.return` ops located within `scf.if` ops. + let mut rets_in_if: Vec<_> = vec![]; + self.func.walk_rev_mut(WalkOrder::PostOrder, |mut op| { + if op.has_attribute(CIRCOM_RETURN_MARKER_ATTR) { + // Perform replacement of "if(..) return" pattern. + if function::is_func_return(&op) { + if let Some(parent) = parent_operation_mut(&op) { + if is_scf_if(&parent) { + // Cannot directly do the refactor here because it will invalidate the + // walk iterator. Instead, collect the pairs to process after the walk. + // But, must use raw objects since `op` is invalid outside this closure. + rets_in_if.push((op.to_raw(), parent.to_raw())); + return WalkResult::Skip; + } + } + } + + // Remove the `CIRCOM_RETURN_MARKER_ATTR` attribute because it is a temporary marker + // used to adjust the location of return statements to match LLZK requirements. + let r = op.remove_attribute(CIRCOM_RETURN_MARKER_ATTR); + assert!(r.is_ok(), "Must succeed due to the has_attribute check above"); + } + WalkResult::Advance + }); + for (ret, parent) in rets_in_if { + let ret_op = unsafe { OperationRefMut::from_raw(ret) }; + let parent_if_op = unsafe { OperationRefMut::from_raw(parent) }; + Self::refactor_return_in_if(codegen, ret_op, parent_if_op)?; + } + // One more pass to remove remaining `OPERAND_VAL_NAMES` attributes + self.func.walk_mut(WalkOrder::PreOrder, |mut op| { + let _ = op.remove_attribute(OPERAND_VAL_NAMES); + WalkResult::Advance + }); + Ok(()) + } + + /// Replace `parent_if_op` with a new `scf.if` op where the existing `ret_op` is changed to a + /// yield and all operations in the same block following `parent_if_op` are moved into the else + /// branch (with return op there also converted to a yield op). The new `scf.if` op result is + /// used in a new `scf.return` op added after the new `scf.if`. + fn refactor_return_in_if( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ret_op: OperationRefMut<'ctx, '_>, + mut parent_if_op: OperationRefMut<'ctx, '_>, + ) -> Result<()> { + assert!(function::is_func_return(&ret_op)); // precondition + assert!(is_scf_if(&parent_if_op)); // precondition + + // Move all ops after the `scf.if` into a new block for "else" branch of new `scf.if`. + let new_else_block = Block::new(&[]); + let new_else_result_info: RefactoringBlockResultType; + { + // Collect all ops before removing any to avoid invalidating references. + let mut following_ops = Vec::new(); + let mut cur = next_in_block_mut(&parent_if_op); + while let Some(op_ref) = cur { + following_ops.push(op_ref); + cur = next_in_block_mut(&op_ref); + } + let mut tail = following_ops.pop().context("expected at least a yield/return")?; + for op_ref in following_ops.iter_mut() { + new_else_block.append_operation(remove_from_parent(op_ref)); + } + // Special handling for the tail op: yield is just added, return is converted to yield. + let tail = remove_from_parent(&mut tail); + if is_scf_yield(&tail) { + let result_types = tail.operands().map(|v| v.r#type()).collect(); + let names = tail + .attribute(OPERAND_VAL_NAMES) + .expect("multi-value yield op must have names"); + new_else_result_info = RefactoringBlockResultType::Multiple(result_types, names); + no_results(new_else_block.append_operation(tail))?; + } else if function::is_func_return(&tail) { + assert_eq!(tail.operand_count(), 1, "circom functions must return a single value"); + let ret_val = tail.operand(0).unwrap(); + new_else_result_info = RefactoringBlockResultType::Single(ret_val.r#type()); + no_results( + new_else_block.append_operation(scf::r#yield(&[ret_val], tail.location())), + )?; + } else { + anyhow::bail!("expected either yield or return at end of block"); + } + } + + // Create "then" block for new `scf.if` and add yield converted from `ret_op`. + let new_then_block = Block::new(&[]); + { + assert_eq!(ret_op.operand_count(), 1, "circom functions must return a single value"); + let ret_val = ret_op.operand(0).unwrap(); + let mut yield_values = Vec::with_capacity(new_else_result_info.len()); + + // The blocks must yield the same number and type of values. So if the "else" block + // yields more than one value, need to add additional operands to yield here. + match &new_else_result_info { + RefactoringBlockResultType::Single(t) => { + if ret_val.r#type() != *t { + anyhow::bail!("type mismatch in return value between branches"); + } + yield_values.push(ret_val); + } + RefactoringBlockResultType::Multiple(result_types, names) => { + let location = ret_op.location(); + for (i, s) in codegen.attribute_to_list(*names)?.enumerate() { + if s == VAR_NAME_RETURN_VAL { + yield_values.push(ret_val); + } else if s == VAR_NAME_HAD_RETURN { + // Gen true constant since this case has a return. + yield_values.push(single_result_as_value( + new_then_block + .append_operation(codegen.new_bool_const_op(true, location)), + )?); + } else { + assert!(i <= result_types.len(), "more names than result types"); + // Fill other positions with `llzk.nondet` values of the expected type. + yield_values.push(single_result_as_value( + new_then_block.append_operation( + codegen.new_nondet_at_location(location, result_types[i])?, + ), + )?); + } + } + } + } + let new_yield = new_else_result_info.gen_yield(&yield_values, ret_op.location()); + no_results(new_then_block.append_operation(new_yield))?; + } + + // Create new `scf.if` op using the new "then" and "else" blocks. Replace `parent_if_op` + // with this new `scf.if` and then append a return/yield with the new `scf.if` results. + let blk = parent_if_op.block().context("expected parent block for original `if`")?; + let else_region = Region::new(); + else_region.append_block(new_else_block); + let then_region = Region::new(); + then_region.append_block(new_then_block); + let new_if_ref = blk.append_operation(scf::r#if( + parent_if_op.operand(0)?, + new_else_result_info.result_types(), + then_region, + else_region, + parent_if_op.location(), + )); + + // Add return if the destination block is the function def body, else yield. + let result_values: Vec<_> = new_if_ref.results().map(Value::from).collect(); + let op = if blk.parent_operation().is_some_and(|r| function::is_func_def(&r)) { + function::r#return(parent_if_op.location(), &result_values) + } else { + new_else_result_info.gen_yield(&result_values, parent_if_op.location()) + }; + no_results(blk.append_operation(op))?; + + // Finally, remove and drop the original `parent_if_op`. + let _drop = remove_from_parent(&mut parent_if_op); + + Ok(()) + } +} + +/// The [FunctionContext] directly accesses a single [BlockContextStack] for circom scope handling. +impl<'ctx, 'func, 'blk, 'val> GenWithCircomScopeHandling<'ctx, 'func, 'blk, 'val> + for FunctionContext<'_, 'ctx, 'func, 'blk, 'val> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type BlockType = BlockRef<'ctx, 'blk>; + type HandlerDataType = NestedBlockInfo<'ctx, 'blk, 'val>; + + fn stack_top(&self) -> Self::BlockType { + *self.block_ctx.top_block() + } + + fn stack_push(&mut self, block: Self::BlockType) { + self.block_ctx.push(block) + } + + fn stack_pop( + &mut self, + overwrite_handler: H, + overwrite_data: &mut Self::HandlerDataType, + ) -> Result<()> + where + H: Fn( + &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + &mut NestedBlockInfo<'ctx, 'blk, 'val>, + HashMap>, + ) -> Result<()>, + { + let popped = self.block_ctx.pop(); + overwrite_handler(&mut self.base, overwrite_data, popped) + } +} + +/// A trait to generate LLZK IR from the body of a circom function. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'func: lifetime of the generated `FuncDefOp` instances within the struct +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +pub trait GenerateLLZKInFunction<'ctx, 'func, 'blk, 'val> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + /// Output type of the generator function. + type Output; + + /// Generates LLZK IR from [Statement] nodes in a circom function. + fn gen_llzk_in_function<'info>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + info: InfoProviders<'info>, + ) -> Result; +} + +/// Output type of [GenerateLLZKInFunction] implemented for [Statement] indicating whether the +/// current statement causes abrupt termination of the current block (in other words, whether +/// the remaining statements in the same block should be skipped). +type SkipRestOfBlock = bool; + +/// Within a nested (i.e. non-root) block, get the Value wrapped within an `scf.yield` op that was +/// created from a circom return op. +fn get_val_of_circom_return_and_erase<'ctx, 'blk, 'val>( + block: BlockRef<'ctx, 'blk>, +) -> Option> +where + 'ctx: 'blk, + 'blk: 'val, +{ + if let Some(mut term) = block.terminator_mut() { + // Per `append_circom_return()`, the op generated from a circom return + // statement has the special attribute `CIRCOM_RETURN_MARKER_ATTR`. + if term.has_attribute(CIRCOM_RETURN_MARKER_ATTR) { + // ASSERT: This must be a `yield` not a `return` since it's generated + // within a nested block of an `if` or `while` statement. + assert!(is_scf_yield(&term)); + // ASSERT: Per `append_circom_return()` it has exactly one operand. + assert_eq!(term.operand_count(), 1); + let result = term.operand(0).unwrap(); + term.remove_from_parent(); + // To avoid "still has uses" native errors, must perform an explicit erase + // since there's no way to get the owned `Operation` out of the block. + erase_op(term); + return Some(result); + } + } + None +} + +/// Helper for [gen_if_then_else] to mangage the special return-related variables needed +/// when a circom [Statement::IfThenElse] contains a return statement. +#[allow(clippy::too_many_arguments)] +fn handle_unbalanced_return<'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + location: Location<'ctx>, + return_val: Value<'ctx, 'val>, + returning_block: BlockRef<'ctx, 'blk>, + returning_block_overwrites: &mut HashMap>, + nonreturning_block: BlockRef<'ctx, 'blk>, + nonreturning_block_overwrites: &mut HashMap>, +) -> Result<()> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + // Set `VAR_NAME_HAD_RETURN` in both maps: `true` in returning block, `false` in other. + returning_block_overwrites.insert( + VAR_NAME_HAD_RETURN.to_string(), + single_result_as_value( + returning_block.append_operation(codegen.new_bool_const_op(true, location)), + )?, + ); + nonreturning_block_overwrites.insert( + VAR_NAME_HAD_RETURN.to_string(), + single_result_as_value( + nonreturning_block.append_operation(codegen.new_bool_const_op(false, location)), + )?, + ); + + // Set return value in both maps. In the non-returning block, use the existing value in the + // block context, if present, otherwise create a new non-det value. + returning_block_overwrites.insert(VAR_NAME_RETURN_VAL.to_string(), return_val); + nonreturning_block_overwrites.insert( + VAR_NAME_RETURN_VAL.to_string(), + function.block_ctx.get_named_value(VAR_NAME_RETURN_VAL).cloned().or_else(|_| { + single_result_as_value( + nonreturning_block.append_operation( + codegen.new_nondet_at_location(location, return_val.r#type())?, + ), + ) + })?, + ); + Ok(()) +} + +/// Generate LLZK code that follows a circom `if-then-else` statement that has an unbalanced return +/// (i.e. one branch returns and the other does not) or `while`. Generates the following LLZK code: +/// ```llzk +/// VAR_NAME_RETURN_VAL = scf.if VAR_NAME_HAD_RETURN { +/// function.return VAR_NAME_RETURN_VAL +/// } +/// ``` +fn gen_unbalanced_return_extra<'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + location: Location<'ctx>, +) -> Result<()> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + let then_region = Region::new(); + let then_block = then_region.append_block(Block::new(&[])); + function.gen_in_given_block_with_new_circom_scope_and_merge_overwrites(then_block, |fc| { + let ret_val = fc.block_ctx.get_named_value(VAR_NAME_RETURN_VAL)?; + let value = fc.cast_to_return_type_if_needed(codegen, location, *ret_val)?; + let mut op = function::r#return(location, &[value]); + op.set_attribute(CIRCOM_RETURN_MARKER_ATTR, Attribute::unit(codegen.context)); + fc.append_op_no_result(op) + })?; + + let condition = *function.block_ctx.get_named_value(VAR_NAME_HAD_RETURN)?; + function.append_op_no_result(scf::r#if(condition, &[], then_region, Region::new(), location)) +} + +/// Generate LLZK code for a circom [Statement::IfThenElse]. +fn gen_if_then_else<'ctx, 'func, 'blk, 'val, 'info>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + info: InfoProviders<'info>, + meta: &Meta, + cond: &Expression, + if_case: &Statement, + else_case: &Option>, +) -> Result +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + // Initially, generate the blocks for the 'then' and 'else' cases naively. + let mut then_info = NestedBlockInfo::default(); + function.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + then_info.block, + |function| if_case.gen_llzk_in_function(codegen, function, info), + &mut then_info, + )?; + let mut else_info = NestedBlockInfo::default(); + if let Some(else_case) = else_case { + function.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + else_info.block, + |function| else_case.gen_llzk_in_function(codegen, function, info), + &mut else_info, + )?; + } + + let location = codegen.location_from_meta(meta); + let condition = cond.gen_llzk_in_block(codegen, function, info)?; + + // Check if one or both blocks end with a return in circom. The `scf.if` op used in LLZK cannot + // have returns nested within other blocks like circom allows. Use of `append_circom_return()` + // already ensures `scf.yield` is used instead of `function.return` when not in the root + // block but the `scf.if` additionally requires that both blocks yield the same number and type + // of values. Additionally, if one block returns and the other does not (in the circom code), + // an additional return state must be added to the values to be yielded from both branches and + // an additional `scf.if` must be added after the main `scf.if` to check the return state flag. + let then_return_opt = get_val_of_circom_return_and_erase(then_info.block); + let else_return_opt = get_val_of_circom_return_and_erase(else_info.block); + if let Some(then_return) = then_return_opt { + if let Some(else_return) = else_return_opt { + // Both return, just add the return value to both overwrite maps. + then_info.var_overwrites.insert(VAR_NAME_RETURN_VAL.to_string(), then_return); + else_info.var_overwrites.insert(VAR_NAME_RETURN_VAL.to_string(), else_return); + } else { + // Return in `then` block but not `else` block. + handle_unbalanced_return( + codegen, + function, + location, + then_return, + then_info.block, + &mut then_info.var_overwrites, + else_info.block, + &mut else_info.var_overwrites, + )?; + } + } else if let Some(else_return) = else_return_opt { + // Return in `else` block but not `then` block. + handle_unbalanced_return( + codegen, + function, + location, + else_return, + else_info.block, + &mut else_info.var_overwrites, + then_info.block, + &mut then_info.var_overwrites, + )?; + } + + function.gen_scf_if(codegen, location, condition, then_info, else_info)?; + + // Finally, if both blocks ended with a return, then add a new return/yield here. Else, + // if only one block returned, gen additional code to handle the unbalanced return. + if then_return_opt.is_some() && else_return_opt.is_some() { + let ret_val = function.block_ctx.get_named_value(VAR_NAME_RETURN_VAL)?; + function.append_circom_return(codegen, location, *ret_val)?; + // Since we added a return/yield here, the rest of current block is unreachable. + return Ok(true); + } else if then_return_opt.is_some() || else_return_opt.is_some() { + gen_unbalanced_return_extra(codegen, function, location)?; + } + Ok(false) +} + +/// Generate LLZK code for a circom [Statement::While]. +fn gen_while<'ctx, 'func, 'blk, 'val, 'info>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + info: InfoProviders<'info>, + meta: &Meta, + cond: &Expression, + body_stmt: &Statement, + loop_bounds: Option>, +) -> Result +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + // Generate the loop condition (i.e. "before") and body (i.e. "after") blocks naively. + let mut loop_cond_info = NestedBlockInfo::default(); + let cond_result = function.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + loop_cond_info.block, + |fc| cond.gen_llzk_in_block(codegen, fc, info), + &mut loop_cond_info, + )?; + let mut loop_body_info = NestedBlockInfo::default(); + function.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + loop_body_info.block, + |fc| body_stmt.gen_llzk_in_function(codegen, fc, info), + &mut loop_body_info, + )?; + + let location = codegen.location_from_meta(meta); + + // Check if loop body block ends with a return in circom. The `scf.while` op used in LLZK cannot + // have returns nested within other blocks like circom allows. Use of `append_circom_return()` + // already ensures `scf.yield` is used instead of `function.return` when not in the root block + // but the `scf.while` additionally requires that both blocks yield the same number and type + // of values. Additionally, if the loop body block returns (in the circom code), an additional + // return state must be added to the values to be yielded and an additional `scf.if` must be + // added after the `scf.while` to check the return state flag. + if let Some(early_return) = get_val_of_circom_return_and_erase(loop_body_info.block) { + // Within the loop body, set `VAR_NAME_HAD_RETURN` to `true` since a return occurs. + loop_body_info.var_overwrites.insert( + VAR_NAME_HAD_RETURN.to_string(), + single_result_as_value( + loop_body_info.block.append_operation(codegen.new_bool_const_op(true, location)), + )?, + ); + // In the current block, initialize the `VAR_NAME_HAD_RETURN` flag to `false` to capture the + // scenario where the loop body does not execute and thus the return within does not occur. + function.append_op_named_result( + codegen.new_bool_const_op(false, location), + VAR_NAME_HAD_RETURN.to_string(), + )?; + + // Add the return value to the overwrite map of the loop body. + loop_body_info.var_overwrites.insert(VAR_NAME_RETURN_VAL.to_string(), early_return); + // In the current block, ensure the return value variable is initialized, default to nondet. + if function.block_ctx.get_named_value(VAR_NAME_RETURN_VAL).is_err() { + function.append_op_named_result( + codegen.new_nondet_at_location(location, early_return.r#type())?, + VAR_NAME_RETURN_VAL.to_string(), + )?; + } + } + + // Generate the loop op. + let had_return = function.gen_scf_while( + codegen, + location, + cond_result, + loop_cond_info, + loop_body_info, + loop_bounds, + )?; + + // Finally, if the loop body contained a return statement, gen additional code to handle it. + if had_return { + gen_unbalanced_return_extra(codegen, function, location)?; + } + Ok(false) +} + +/// Generate LLZK code for a circom [Statement::InitializationBlock]. +/// This is needed to support the `try_for_loop_heuristic` macro. +#[inline] +fn gen_init_block<'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + info: InfoProviders<'_>, + initializations: &[Statement], +) -> Result<()> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + initializations.gen_llzk_in_function(codegen, function, info) +} + +impl<'ctx, 'func, 'blk, 'val> GenerateLLZKInFunction<'ctx, 'func, 'blk, 'val> for Statement +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + type Output = SkipRestOfBlock; + + fn gen_llzk_in_function<'info>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + info: InfoProviders<'info>, + ) -> Result { + let _guard = codegen.trace_statement(self); + match self { + Statement::InitializationBlock { xtype, initializations, .. } => { + if let VariableType::Signal(..) = xtype { + // per `type_analysis/src/analyzers/functions_free_of_template_elements.rs` + unreachable!("Template elements declared inside the function") + } + gen_init_block(codegen, function, info, initializations)?; + Ok(false) + } + Statement::Declaration { meta, xtype, name, dimensions, .. } => { + if VariableType::Var != *xtype { + // per `type_analysis/src/analyzers/functions_free_of_template_elements.rs` + unreachable!("Template elements declared inside the function") + } + function.gen_declaration(codegen, meta, name, dimensions)?; + Ok(false) + } + Statement::Block { meta, stmts } => { + function.gen_in_current_block_with_new_circom_scope_and_merge_overwrites( + |function| { + try_for_loop_heuristic!(codegen, function, meta, stmts, info); + // Fallback to standard block handling. + stmts.gen_llzk_in_function(codegen, function, info)?; + Ok(false) + }, + )?; + Ok(false) + } + Statement::Substitution { meta, var, access, op, rhe } => { + if op.is_signal_operator() { + // per `type_analysis/src/analyzers/functions_free_of_template_elements.rs` + unreachable!("Function uses template operators"); + } + function.handle_substitution_stmt_nonsignal( + codegen, info, meta, var, access, op, rhe, + )?; + Ok(false) + } + Statement::UnderscoreSubstitution { op, rhe, .. } => { + if op.is_signal_operator() { + // per `type_analysis/src/analyzers/functions_free_of_template_elements.rs` + unreachable!("Function uses template operators"); + } + // Just visit and drop the resulting Value since it's unused. + rhe.gen_llzk_in_block(codegen, function, info).map(drop)?; + Ok(false) + } + Statement::IfThenElse { meta, cond, if_case, else_case } => { + gen_if_then_else(codegen, function, info, meta, cond, if_case, else_case) + } + Statement::While { meta, cond, stmt } => { + gen_while(codegen, function, info, meta, cond, stmt, None) + } + Statement::Return { meta, value } => { + let value = value.gen_llzk_in_block(codegen, function, info)?; + let location = codegen.location_from_meta(meta); + function.append_circom_return(codegen, location, value)?; + // circom allows unreachable code after a return but it is not processed + // (e.g. `assert(1 == 0)` after a return does not cause an error as it normally + // would) so replicate the same behavior here by stopping processing after a + // return (which is also what MLIR expects, no code after a terminator op). + Ok(true) + } + Statement::Assert { meta, arg } => { + let cond = arg.gen_llzk_in_block(codegen, function, info)?; + function.append_assert(codegen, codegen.location_from_meta(meta), cond)?; + Ok(false) + } + Statement::LogCall { meta, .. } => { + codegen.emit_circom_warning( + meta, + "log calls are not currently supported in LLZK", + ReportCode::NotAllowedOperation, + ); + Ok(false) + } + Statement::MultSubstitution { .. } => { + unreachable!("removed by 'syntax_sugar_remover'") + } + Statement::ConstraintEquality { .. } => { + // per `type_analysis/src/analyzers/functions_free_of_template_elements.rs` + unreachable!("Function uses template operators"); + } + } + } +} + +impl<'ctx, 'func, 'blk, 'val> GenerateLLZKInFunction<'ctx, 'func, 'blk, 'val> for [Statement] +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + type Output = (); + + fn gen_llzk_in_function<'info>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + info: InfoProviders<'info>, + ) -> Result { + for s in self { + let skip_rest_of_block = s.gen_llzk_in_function(codegen, function, info)?; + if skip_rest_of_block { + break; + } + } + Ok(()) + } +} diff --git a/llzk_backend/src/function_ext.rs b/llzk_backend/src/function_ext.rs new file mode 100644 index 000000000..e84a9ae1d --- /dev/null +++ b/llzk_backend/src/function_ext.rs @@ -0,0 +1,116 @@ +//! Extensions for the [`FunctionData`] and [`VCF`] types. + +use crate::program_ext::ProgramLike; +use crate::shared::LlzkCodegen; +use anyhow::Result; +use compiler::hir::very_concrete_program::VCF; +use llzk::prelude::Location; +use llzk::prelude::Type; +use program_structure::ast::Statement; +use program_structure::function_data::FunctionData; +use std::slice; + +/// A trait that allows common handling of the structs used to represent a circom +/// function at different stages in the compilation process. +pub trait FunctionLike: std::fmt::Debug { + /// Generate the LLZK Location for the function definition. + fn get_location<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Location<'ctx>; + /// Get the name of the function. + fn get_name(&self) -> &str; + /// Get the names of the parameters of the function. + fn get_name_of_params(&self) -> Vec; + /// Get the types of parameters of the function. + fn get_type_of_params<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Vec>; + /// Get the type of the function return value. + fn get_type_of_return<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Type<'ctx>; + /// Get the body statements of the function. + fn get_body(&self) -> &[Statement]; +} + +impl FunctionLike for FunctionData { + fn get_location<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Location<'ctx> { + codegen.location(self.get_file_id(), self.get_param_location()) + } + fn get_name(&self) -> &str { + self.get_name() + } + fn get_name_of_params(&self) -> Vec { + self.get_name_of_params().clone() + } + // TODO: This just uses `felt.type` for param and return types but those must actually be + // determined based on the caller. Circom functions cannot accept or return components or + // busses so the only types allowed for params and return are `felt.type` and arrays of + // `felt.type`. The actual type used here also affects the dimensions of array types and + // which array read/write-like ops must be used when translating the body. This can be + // implemented properly once the LLZK `poly.template` proposal is implemented. + fn get_type_of_params<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Vec> { + vec![codegen.felt_type().into(); self.get_num_of_params()] + } + fn get_type_of_return<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Type<'ctx> { + codegen.felt_type().into() + } + fn get_body(&self) -> &[Statement] { + self.get_body_as_vec() + } +} + +impl FunctionLike for VCF { + fn get_location<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Location<'ctx> { + codegen.location_unknown() + } + fn get_name(&self) -> &str { + &self.header + } + fn get_name_of_params(&self) -> Vec { + self.params_types.iter().map(|p| p.name.clone()).collect() + } + fn get_type_of_params<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Vec> { + let base_type = codegen.felt_type().into(); + self.params_types + .iter() + .map(|p| codegen.type_from_dimension_consts(base_type, &p.length)) + .collect::>>() + .expect("In function parameter types") + } + fn get_type_of_return<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Type<'ctx> { + let base_type = codegen.felt_type().into(); + codegen + .type_from_dimension_consts(base_type, &self.return_type) + .expect("In function return type") + } + fn get_body(&self) -> &[Statement] { + // In VCF format, the function body is wrapped in a Block that conveys no additional + // information but will cause returns to generate `scf.yield` instead of `function.return`. + match &self.body { + Statement::Block { stmts, .. } => stmts, + b => slice::from_ref(b), + } + } +} diff --git a/llzk_backend/src/gen_context.rs b/llzk_backend/src/gen_context.rs new file mode 100644 index 000000000..e9669abe7 --- /dev/null +++ b/llzk_backend/src/gen_context.rs @@ -0,0 +1,2315 @@ +//! Handles circom var scoping and LLZK blocks stack management. + +use crate::function::InfoProviders; +use crate::function_ext::FunctionLike as _; +use crate::lvalue::Lvalue; +use crate::lvalue::Root; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::append_tail; +use crate::shared::dim_expr_name; +use crate::shared::is_bool; +use crate::shared::is_index; +use crate::shared::new_array_type; +use crate::shared::no_results; +use crate::shared::region_with_block; +use crate::shared::replace_uses_with_new_block_argument; +use crate::shared::single_result_as_value; +use crate::shared::ArrayDimension; +use crate::shared::ArrayDimensionResult; +use crate::shared::DimExprConverter; +use crate::shared::LlzkCodegen; +use crate::subcmp::names::COUNT; +use anyhow::anyhow; +use anyhow::ensure; +use anyhow::Context as _; +use anyhow::Result; +use llzk::dialect::array; +use llzk::dialect::array::ArrayCtor; +use llzk::dialect::bool; +use llzk::dialect::cast; +use llzk::dialect::constrain; +use llzk::dialect::felt; +use llzk::dialect::function; +use llzk::dialect::pod; +use llzk::dialect::poly; +use llzk::map_operands::MapOperandsBuilder; +use llzk::prelude::is_felt_type; +use llzk::prelude::melior_dialects::arith; +use llzk::prelude::melior_dialects::index; +use llzk::prelude::melior_dialects::scf; +use llzk::prelude::ArrayType; +use llzk::prelude::Attribute; +use llzk::prelude::Block; +use llzk::prelude::BlockLike as _; +use llzk::prelude::BlockRef; +use llzk::prelude::FlatSymbolRefAttribute; +use llzk::prelude::FuncDefOp; +use llzk::prelude::IntegerAttribute; +use llzk::prelude::Location; +use llzk::prelude::LoopBoundsAttribute; +use llzk::prelude::Operation; +use llzk::prelude::OperationLike as _; +use llzk::prelude::OperationMutLike as _; +use llzk::prelude::OperationRef; +use llzk::prelude::PodType; +use llzk::prelude::Region; +use llzk::prelude::RegionLike as _; +use llzk::prelude::StringAttribute; +use llzk::prelude::StructType; +use llzk::prelude::TemplateExprOp; +use llzk::prelude::Type; +use llzk::prelude::Value; +use llzk::prelude::ValueLike as _; +use llzk::prelude::FUNC_NAME_COMPUTE; +use llzk::prelude::FUNC_NAME_CONSTRAIN; +use llzk::typing::types_unify; +use llzk::value_ext::OwningValueRange; +use llzk::value_ext::ValueRange; +use melior::dialect::ods::math; +use melior::ir::AttributeLike as _; +use melior::ir::TypeLike as _; +use num_bigint_dig::BigInt; +use num_traits::Zero; +use program_structure::ast::Access; +use program_structure::ast::AssignOp; +use program_structure::ast::Expression; +use program_structure::ast::ExpressionInfixOpcode; +use program_structure::ast::ExpressionPrefixOpcode; +use program_structure::ast::Meta; +use program_structure::error_code::ReportCode; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::convert::TryInto; +use std::iter::zip; + +/// Special variable name used to reference the return Value throughout the +/// conversion of circom return locations to LLZK return locations. +pub(crate) const VAR_NAME_RETURN_VAL: &str = "**return_val**"; +/// Special variable name used to reference the status of whether or not a circom block +/// had a `return` when translating to an LLZK block that cannot contain a `return`. +pub(crate) const VAR_NAME_HAD_RETURN: &str = "**had_return**"; +/// LLZK attribute used to mark yield/return ops generated from circom return statements. +pub(crate) const CIRCOM_RETURN_MARKER_ATTR: &str = "from_circom_return"; +/// LLZK attribute used to attach comma-separated list of variable names for the operands +/// of an `scf.yield` op. +pub(crate) const OPERAND_VAL_NAMES: &str = "operand_val_names"; + +/// Single frame in the [BlockContextStack]. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +#[derive(Debug)] +pub struct BlockContext<'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Reference to a block in LLZK IR. + block: BlockRef<'ctx, 'blk>, + /// For variables declared in the current scope, maps circom variable name to current LLZK SSA + /// Value stored for that variable. These are local to the current block and go out of scope + /// when the block is popped. + scope_local_name_to_value: HashMap>, + /// For variables declared in an outer scope, maps circom variable name to current LLZK SSA + /// Value stored for that variable. These are preserved when the block is popped because they + /// overwrite the value of existing variables in outer scopes. + overwriting_name_to_value: HashMap>, + /// Queue of operations that need to be appended before the context goes out of scope. + op_queue: Vec>, +} + +impl<'ctx, 'blk, 'val> BlockContext<'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Create a new empty [BlockContext] for the given block. + fn new(block: BlockRef<'ctx, 'blk>) -> Self { + Self { + block, + scope_local_name_to_value: Default::default(), + overwriting_name_to_value: Default::default(), + op_queue: Default::default(), + } + } + + /// Set the scope local declarations with the mapping of parameter names to values. + fn with_params(mut self, param_name_to_value: HashMap>) -> Self { + self.scope_local_name_to_value = param_name_to_value; + self + } + + /// Get the LLZK IR SSA Value for the given circom var name. Check the local scope first since + /// the names there may shadow the same name from parent scope(s). + fn get(&self, name: &str) -> Option<&Value<'ctx, 'val>> { + self.scope_local_name_to_value + .get(name) + .or_else(|| self.overwriting_name_to_value.get(name)) + } + + /// Set the LLZK IR SSA Value for the given circom var name. If the name is declared in the + /// local scope, assign the new value there since it's shadowing the same name from parent + /// scope(s). Otherwise, assign the new value in the overwriting map. + fn insert(&mut self, name: String, value: Value<'ctx, 'val>) { + if let Some(v) = self.scope_local_name_to_value.get_mut(&name) { + *v = value; + } else { + self.overwriting_name_to_value.insert(name, value); + } + } + + /// Return true iff the given name is declared in the local scope of this block context. + fn declares(&self, name: &str) -> bool { + self.scope_local_name_to_value.contains_key(name) + } +} + +/// Stack of blocks where the top block is the current block where code should be appended and the +/// previous block in the list is the parent of the block after it. When an op containing nested +/// blocks is encountered, the current block within that op is pushed to the stack so that any code +/// generated will be placed inside that block and when the nested block is complete, it is popped. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +#[derive(Debug)] +pub struct BlockContextStack<'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Context for the function entry block. + root: BlockContext<'ctx, 'blk, 'val>, + /// Additional nesting of blocks within the function. Tail is the current insertion point. + other_blocks: Vec>, +} + +impl<'ctx, 'blk, 'val> BlockContextStack<'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Create a new [BlockContextStack] with the given root block. + pub fn new(root: BlockRef<'ctx, 'blk>) -> Self { + Self { root: BlockContext::new(root), other_blocks: Default::default() } + } + + /// Create a new [BlockContextStack] for the given function with an initial name-to-value + /// mapping containing function parameters and a mapping of `var` declaration names to their + /// declared LLZK types. + pub fn from_function( + func: &FuncDefOp<'ctx>, + param_name_to_value: HashMap>, + ) -> Result { + let root_block = + func.region(0)?.first_block().ok_or_else(|| anyhow!("missing function entry block"))?; + Ok(Self { + root: BlockContext::new(root_block).with_params(param_name_to_value), + other_blocks: Default::default(), + }) + } + + /// Return true iff the stack contains only the root block (i.e. stack depth is 1). + pub fn is_only_root(&self) -> bool { + self.other_blocks.is_empty() + } + + /// Get reference to the current block (i.e. the top of the stack). + pub fn top_block(&self) -> &BlockRef<'ctx, 'blk> { + match self.other_blocks.last() { + Some(bc) => &bc.block, + None => &self.root.block, + } + } + + /// Ref to the current block context (i.e. the top of the stack). + fn top(&self) -> &BlockContext<'ctx, 'blk, 'val> { + match self.other_blocks.last() { + Some(bc) => bc, + None => &self.root, + } + } + + /// Mutable ref to the current block context (i.e. the top of the stack). + fn top_mut(&mut self) -> &mut BlockContext<'ctx, 'blk, 'val> { + match self.other_blocks.last_mut() { + Some(bc) => bc, + None => &mut self.root, + } + } + + /// Append an operation to the current block (i.e. the top of the stack). + pub fn append_current_block(&mut self, operation: Operation<'ctx>) -> OperationRef<'ctx, 'val> { + let current = &self.top_mut().block; + // Account for possible terminator in the current block. For example, the `compute_fn()` + // and `constrain_fn()` helpers automatically add a return op at the end of the block + // so new ops must be inserted before that terminator. + match current.terminator() { + Some(terminator) => current.insert_operation_before(terminator, operation), + None => current.append_operation(operation), + } + } + + /// Queues an operation to the given block that will get appended before it goes out of scope. + /// + /// If the block is scoped in multiple contexts picks the deepest one. + /// + /// Fails if the block is not part of the stack. + pub fn enqueue_in_block( + &mut self, + operation: Operation<'ctx>, + block: BlockRef<'ctx, 'blk>, + ) -> Result<()> { + let mut blocks = self.blocks_iter_mut_rev(); + let bc = blocks.find(|bc| bc.block == block).ok_or_else(|| block_not_in_stack(block))?; + bc.op_queue.push(operation); + Ok(()) + } + + /// Check if the given name is already declared in the current scope. + pub fn is_name_present(&self, name: &str) -> bool { + self.top().scope_local_name_to_value.contains_key(name) + } + + /// Ensure the given name is not already declared in the current scope, then declare it by + /// producing an [Operation] via the callback, inserting that into the current block, and + /// using its result. The only scenario where a declaration would already be present is when + /// the same Declaration statements are visited that were used to produce the parameters of + /// the current function. Otherwise, the checks performed earlier in the circom parser + /// pipeline will produce an error if a symbol is declared more than once in the same scope. + pub fn declare_name_ensure_not_present( + &mut self, + name: &str, + op: Operation<'ctx>, + ) -> Result<()> { + ensure!(!self.is_name_present(name), format!("name {name} is already present")); + let value = single_result_as_value(self.append_current_block(op))?; + self.top_mut().scope_local_name_to_value.insert(name.to_string(), value); + Ok(()) + } + + /// If the given name is not already declared in the current scope, declare it by producing an + /// [Operation] via the callback, inserting that into the current block, and using its result. + /// The only scenario where a declaration would already be present is when the same Declaration + /// statements are visited that were used to produce the parameters of the current function. + /// Otherwise, the checks performed earlier in the circom parser pipeline will produce an error + /// if a symbol is declared more than once in the same scope. + pub fn declare_name_if_not_present( + &mut self, + name: &str, + uninit_value: impl FnOnce() -> Result>, + ) -> Result<()> { + if !self.is_name_present(name) { + let op = uninit_value()?; + let value = single_result_as_value(self.append_current_block(op))?; + self.top_mut().scope_local_name_to_value.insert(name.to_string(), value); + } + Ok(()) + } + + /// In the top block context, set the LLZK IR SSA Value for the given circom var name. + pub fn set_named_value(&mut self, name: String, value: Value<'ctx, 'val>) -> Result<()> { + // This is mainly a sanity check on proper usage of `declare_name_if_not_present()` and + // this function to ensure values end up in the correct map in the BlockContext and are + // thus scoped correctly. + if !self.root.declares(&name) && !self.other_blocks.iter().any(|bc| bc.declares(&name)) { + return Err(anyhow!("Variable '{name}' was not declared in any scope")); + } + self.top_mut().insert(name, value); + Ok(()) + } + + /// Set the LLZK IR SSA Value for the given circom var name at the declaration scope. + pub fn set_named_value_at_declaration( + &mut self, + name: String, + value: Value<'ctx, 'val>, + ) -> Result<()> { + let scope = self + .blocks_iter_mut() + .find(|bc| bc.declares(&name)) + .ok_or_else(|| anyhow!("Variable '{name}' was not declared in any scope"))?; + scope.insert(name, value); + Ok(()) + } + + /// Returns an iterator of the blocks in stack order (from the top to the bottom). + fn blocks_iter(&self) -> impl Iterator> { + self.other_blocks.iter().rev().chain(std::iter::once(&self.root)) + } + + /// Returns an iterator of mutable references to the blocks in stack order (from the top to the + /// bottom). + fn blocks_iter_mut(&mut self) -> impl Iterator> { + self.other_blocks.iter_mut().rev().chain(std::iter::once(&mut self.root)) + } + + /// Returns an iterator of mutable references to the blocks in reverse stack order (from the + /// bottom to the top). + fn blocks_iter_mut_rev(&mut self) -> impl Iterator> { + std::iter::once(&mut self.root).chain(self.other_blocks.iter_mut()) + } + + /// In the top block context, set multiple LLZK IR SSA Values for the given circom var names. + pub fn set_named_values( + &mut self, + insert: impl IntoIterator)>, + ) -> Result<()> { + for (name, value) in insert.into_iter() { + self.set_named_value(name, value)?; + } + Ok(()) + } + + /// Get the LLZK IR SSA Value for the given circom var name, checking the top block context + /// and then proceeding down the stack until found (if at all). + pub fn get_named_value(&self, name: &str) -> Result<&Value<'ctx, 'val>> { + self.blocks_iter() + .find_map(|bc| bc.get(name)) + .ok_or_else(|| anyhow!("variable '{name}' not found")) + } + + /// Get the block that declared the given circom var name. + pub fn get_decl_block_of_value(&self, name: &str) -> Result> { + self.blocks_iter() + .find_map(|bc| bc.declares(name).then_some(bc.block)) + .ok_or_else(|| anyhow!("Variable '{name}' was not declared in any scope")) + } + + /// Push a new block onto the stack to make it the current block. + pub fn push(&mut self, block: BlockRef<'ctx, 'blk>) { + self.other_blocks.push(BlockContext::new(block)); + } + + /// Pop the current block off the stack to return to the previous block. The vars declared in + /// the popped frame are dropped and those which are overwrites are returned. + pub fn pop(&mut self) -> HashMap> { + self.append_queue(); + self.other_blocks.pop().expect("There is no block to pop!").overwriting_name_to_value + } + + /// Appends the queued operations in the top of the stack. + pub fn append_queue(&mut self) { + let queue = std::mem::take(&mut self.top_mut().op_queue); + for op in queue { + self.append_current_block(op); + } + } +} + +/// Items pertaining to a region/block created to nest within another LLZK op. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +#[derive(Debug)] +pub struct NestedBlockInfo<'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// The new [Region] to be nested within another LLZK op. + pub(crate) region: Region<'ctx>, + /// Reference to the single [Block] in the [Region]. + pub(crate) block: BlockRef<'ctx, 'blk>, + /// Map from circom variable name to LLZK Value for variables that were + /// overwritten within by code generated within the new [Block]. + pub(crate) var_overwrites: HashMap>, +} + +impl Default for NestedBlockInfo<'_, '_, '_> { + #[inline] + fn default() -> Self { + let region = Region::new(); + let block = region.append_block(Block::new(&[])); + NestedBlockInfo { region, block, var_overwrites: Default::default() } + } +} + +impl<'ctx, 'blk, 'val> NestedBlockInfo<'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Update `self.var_overwrites` to ensure it has all keys from `other.var_overwrites`, using + /// values from the current scope in the [BlockGenContext] for missing keys. + pub fn add_missing_values( + &mut self, + other: &NestedBlockInfo<'ctx, 'blk, 'val>, + fc: &BlockGenContext<'_, 'ctx, 'blk, 'val>, + ) -> Result<()> { + for name in other.var_overwrites.keys() { + if !self.var_overwrites.contains_key(name) { + self.var_overwrites.insert(name.clone(), *fc.block_ctx.get_named_value(name)?); + } + } + Ok(()) + } +} + +/// Provides common functions for generating code that respects circom variable scoping, abstracting +/// access to the [BlockContextStack] so that functions and templates can share these functions. +pub trait GenWithCircomScopeHandling<'ctx, 'func, 'blk, 'val> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + /// LLZK block context type. For functions, this is just [BlockRef], but for templates, + /// this type holds a [BlockRef] for both the `compute` and `constrain` functions. + type BlockType; + + /// Type of the additional data passed to the overwrite handler. + type HandlerDataType: Default; + + /// Retrieve the top `BlockType` from the [BlockContextStack]. + fn stack_top(&self) -> Self::BlockType; + + /// Push new `BlockType` onto the [BlockContextStack]. + fn stack_push(&mut self, block: Self::BlockType); + + /// Pop the top `BlockType` from the [BlockContextStack]. + fn stack_pop( + &mut self, + overwrite_handler: H, + overwrite_data: &mut Self::HandlerDataType, + ) -> Result<()> + where + H: Fn( + &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + &mut NestedBlockInfo<'ctx, 'blk, 'val>, + HashMap>, + ) -> Result<()>; + + /// Use the callback to generate code for a new circom scope/block within the given LLZK + /// `BlockType`. Assignments to circom variables that are newly introduced in this context go + /// out of scope so they are dropped after the callback but overwriting assignments to circom + /// variables that already exist prior to this new scope are passed to the overwrite handler. + fn gen_in_given_block_with_new_circom_scope( + &mut self, + block: Self::BlockType, + generator: impl FnOnce(&mut Self) -> Result, + overwrite_handler: H, + overwrite_data: &mut Self::HandlerDataType, + ) -> Result + where + H: Fn( + &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + &mut NestedBlockInfo<'ctx, 'blk, 'val>, + HashMap>, + ) -> Result<()>, + { + self.stack_push(block); + let res = generator(self); + self.stack_pop(overwrite_handler, overwrite_data)?; + res + } + + /// Use the callback to generate code for a new circom scope/block within the given LLZK + /// `BlockType`. Assignments to circom variables that are newly introduced in this context go + /// out of scope so they are dropped after the callback but overwriting assignments to circom + /// variables that already exist prior to this new scope are cached in the `var_overwrites` + /// field of the `NestedBlockInfo` struct. + #[inline] + fn gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + &mut self, + block: Self::BlockType, + generator: impl FnOnce(&mut Self) -> Result, + overwrite_data: &mut Self::HandlerDataType, + ) -> Result { + self.gen_in_given_block_with_new_circom_scope( + block, + generator, + |_, block_info, overwrites| { + block_info.var_overwrites.extend(overwrites); + Ok(()) + }, + overwrite_data, + ) + } + + /// Use the callback to generate code for a new circom scope/block within the given LLZK + /// `BlockType`. Assignments to circom variables that are newly introduced in this context go + /// out of scope so they are dropped after the callback but overwriting assignments to circom + /// variables that already exist prior to this new scope are preserved and written into the + /// existing block context. + #[inline] + fn gen_in_given_block_with_new_circom_scope_and_merge_overwrites( + &mut self, + block: Self::BlockType, + generator: impl FnOnce(&mut Self) -> Result, + ) -> Result { + self.gen_in_given_block_with_new_circom_scope( + block, + generator, + |fc, _, overwrites| fc.block_ctx.set_named_values(overwrites), + &mut Self::HandlerDataType::default(), // ignored in the handler ^ + ) + } + + /// Use the callback to generate code for a new circom scope/block but within the current LLZK + /// `BlockType`. Assignments to circom variables that are newly introduced in this context go + /// out of scope so they are dropped after the callback but overwriting assignments to circom + /// variables that already exist prior to this new scope are preserved and written into the + /// existing block context. + #[inline] + fn gen_in_current_block_with_new_circom_scope_and_merge_overwrites( + &mut self, + generator: impl FnOnce(&mut Self) -> Result, + ) -> Result { + self.gen_in_given_block_with_new_circom_scope_and_merge_overwrites( + self.stack_top(), + generator, + ) + } +} + +/// Helper function for creating an error reporting that the given block is not part of the stack. +fn block_not_in_stack(block: BlockRef) -> anyhow::Error { + anyhow!("Block {block:?} is not part of the stack") +} + +/// Holds the block context stack for generating LLZK IR within an LLZK Block body. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +#[derive(Debug)] +pub struct BlockGenContext<'decls, 'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Nested block context within the root block. + pub(crate) block_ctx: BlockContextStack<'ctx, 'blk, 'val>, + /// Pre-computed LLZK types for circom `var` declarations, keyed by var name. Populated from + /// [crate::module::DeclarationInfo] when generating `poly.expr` bodies for templates so that + /// dimension expressions referencing other vars do not trigger recursive code generation. + pub(crate) var_decl_types: &'decls HashMap>, + /// Mapping of `poly.param` and `poly.expr` symbol names visible in the current context to + /// their optional type restriction. The `poly.param` generally don't have a type restriction, + /// but the `poly.expr` always produces a specific typed result. + poly_template_binding_names: HashMap>>, +} + +impl<'decls, 'ctx, 'blk, 'val> BlockGenContext<'decls, 'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Create a new [BlockGenContext] with the given block context stack, mapping of `var` name to + /// declared LLZK type, and set of visible `poly.param` and `poly.expr` names. + pub fn new( + block_ctx: BlockContextStack<'ctx, 'blk, 'val>, + var_decl_types: &'decls HashMap>, + poly_template_binding_names: impl IntoIterator>)>, + ) -> Self { + Self { + block_ctx, + var_decl_types, + poly_template_binding_names: poly_template_binding_names.into_iter().collect(), + } + } + + /// For each name in `self.poly_template_binding_names`, generate and append a `read_const` + /// operation and store the resulting Value in the block context under that name. This ensures + /// that template parameters are available as SSA Values in the block context. + pub fn with_poly_template_binding_locals( + mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + ) -> Result { + let mut sorted: Vec<_> = self.poly_template_binding_names.iter().collect(); + if codegen.config.stabilize { + sorted.sort_by(|(a, _), (b, _)| a.cmp(b)); + } + for (name, ty) in sorted { + // If there is no type restriction use `felt` since it's circom's basic type. + self.block_ctx.declare_name_ensure_not_present( + name, + poly::read_const(location, name, ty.unwrap_or_else(|| codegen.felt_type().into())), + )?; + } + Ok(self) + } + + /// Append an operation. + pub fn append_op(&mut self, op: Operation<'ctx>) -> OperationRef<'ctx, 'val> { + self.block_ctx.append_current_block(op) + } + + /// Append an operation that must produce no results. + pub fn append_op_no_result(&mut self, op: Operation<'ctx>) -> Result<()> { + no_results(self.append_op(op)) + } + + /// Append an operation that must produce a single result and is NOT associated with a variable + /// name in the circom code. + pub fn append_op_unnamed_result(&mut self, op: Operation<'ctx>) -> Result> { + single_result_as_value(self.append_op(op)) + } + + /// Append an operation that must produce a single result and store the mapping of the circom + /// variable name to the result Value. + pub fn append_op_named_result( + &mut self, + op: Operation<'ctx>, + name: String, + ) -> Result> { + let v = self.append_op_unnamed_result(op)?; + self.block_ctx.set_named_value(name, v)?; + Ok(v) + } + + /// Insert cast operations as needed to make `lhs` and `rhs` have compatible types for equality + /// constraints. + #[inline] + fn unify_constrain_eq_types( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + lhs: Value<'ctx, 'val>, + rhs: Value<'ctx, 'val>, + ) -> Result<(Value<'ctx, 'val>, Value<'ctx, 'val>)> { + match (lhs.r#type(), rhs.r#type()) { + (t0, t1) if is_felt_type(t0) && !is_felt_type(t1) => { + Ok((lhs, self.cast_to_felt(codegen, location, rhs)?)) + } + (t0, t1) if !is_felt_type(t0) && is_felt_type(t1) => { + Ok((self.cast_to_felt(codegen, location, lhs)?, rhs)) + } + _ => Ok((lhs, rhs)), + } + } + + /// Generate a `constrain.eq` operation for the given values, casting to compatible felt types + /// if necessary. + pub fn append_constrain_eq( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + lhs: Value<'ctx, 'val>, + rhs: Value<'ctx, 'val>, + ) -> Result<()> { + let (lhs, rhs) = self.unify_constrain_eq_types(codegen, location, lhs, rhs)?; + self.append_op_no_result(constrain::eq(location, lhs, rhs).into()) + } + + /// Generate an `array.write` or `array.insert` operation appropriate for the number of indices + /// and the [ArrayType] of the `arr_ref` value. + pub fn append_array_write( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + arr_ref: Value<'ctx, 'val>, + indices: &[Value<'ctx, 'val>], + location: Location<'ctx>, + rvalue: Value<'ctx, 'val>, + var_name: Option<&str>, + ) -> Result<()> { + let arr_ty = ArrayType::try_from(arr_ref.r#type()).with_context(|| { + let v = var_name.map_or(String::from("array"), |s| format!("'{s}'")); + format!("Conflicting types to write {v} at {location}") + })?; + let arr_ty_dims = arr_ty.dims(); + let write_op = match indices.len().cmp(&arr_ty_dims.len()) { + std::cmp::Ordering::Equal => { + // Indexing all dimensions requires an `array.write` + let rvalue = self.cast_to_expected_type_if_needed( + codegen, + location, + rvalue, + arr_ty.element_type(), + )?; + array::write(location, arr_ref, indices, rvalue) + } + std::cmp::Ordering::Less => { + // Indexing a subset of dimensions requires an `array.insert` + array::insert(location, arr_ref, indices, rvalue) + } + std::cmp::Ordering::Greater => { + let v = var_name.map_or(String::from("array"), |s| format!("'{s}'")); + anyhow::bail!("Too many indices to write {v} at {location}"); + } + }; + self.append_op_no_result(write_op) + } + + /// Generate an `array.read` or `array.extract` operation appropriate for the number of indices + /// and the [ArrayType] of the `arr_ref` value. + pub fn append_array_read( + &mut self, + arr_ref: Value<'ctx, 'val>, + indices: &[Value<'ctx, 'val>], + location: Location<'ctx>, + var_name: Option<&str>, + ) -> Result> { + let arr_ty = ArrayType::try_from(arr_ref.r#type()).with_context(|| { + let v = var_name.map_or(String::from("array"), |s| format!("'{s}'")); + format!("Conflicting types to read {v} at {location}") + })?; + let arr_ty_dims = arr_ty.dims(); + let array_get_op = match indices.len().cmp(&arr_ty_dims.len()) { + std::cmp::Ordering::Equal => { + // Indexing all dimensions requires an `array.read` + array::read(location, arr_ty.element_type(), arr_ref, indices) + } + std::cmp::Ordering::Less => { + // Indexing a subset of dimensions requires an `array.extract` + let reduced_dims: Vec<_> = + arr_ty_dims.iter().skip(indices.len()).copied().collect(); + let reduced_type = ArrayType::new(arr_ty.element_type().into(), &reduced_dims); + array::extract(location, reduced_type.into(), arr_ref, indices) + } + std::cmp::Ordering::Greater => { + let v = var_name.map_or(String::from("array"), |s| format!("'{s}'")); + anyhow::bail!("Too many indices to read {v} at {location}"); + } + }; + self.append_op_unnamed_result(array_get_op) + } + + /// Cast `val` to bool and emit a `bool.assert` op. + pub fn append_assert( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result<()> { + let cond = self.cast_to_bool_if_needed(codegen, location, val)?; + let msg = Some("assertion failed"); + self.append_op_no_result(bool::assert(location, cond, msg)?.into()) + } + + /// Append a `unifiable_cast` operation to cast `val` to the `expected` type. + #[inline] + pub fn unifiable_cast( + &mut self, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + expected: Type<'ctx>, + ) -> Result> { + assert!(types_unify(val.r#type(), expected)); // pre-condition + self.append_op_unnamed_result(poly::unifiable_cast(location, val, expected)) + } + + /// Append a cast to felt (field element) type. + #[inline] + pub fn cast_to_felt( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result> { + self.append_op_unnamed_result(cast::tofelt(location, val, Some(codegen.felt_type()))) + } + + /// Append a cast to felt (field element) type if the given value is not already a felt. + #[inline] + pub fn cast_to_felt_if_needed( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result> { + if !is_felt_type(val.r#type()) { + self.cast_to_felt(codegen, location, val) + } else { + Ok(val) + } + } + + /// Append a cast to index type if the given value is not already an index. + #[inline] + pub fn cast_to_index_if_needed( + &mut self, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result> { + if !is_index(val.r#type()) { + self.append_op_unnamed_result(cast::toindex(location, val).into()) + } else { + Ok(val) + } + } + + /// Append a cast to bool type (i1) if the given value is not already a bool. + #[inline] + pub fn cast_to_bool_if_needed( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result> { + // The conversion to bool is simply to check `!=0` which is the same as + // `normalize()` in `modular_arithmetic.rs`. + if is_felt_type(val.r#type()) { + let zero = self + .append_op_unnamed_result(codegen.new_felt_const_op(&BigInt::zero(), location)?)?; + self.append_op_unnamed_result(bool::ne(location, val, zero)?) + } else if is_index(val.r#type()) { + let zero = self.append_op_unnamed_result(codegen.new_index_const_op(0, location))?; + self.append_op_unnamed_result(index::cmp( + codegen.context, + arith::CmpiPredicate::Ne, + val, + zero, + location, + )) + } else { + assert!(is_bool(val.r#type())); + Ok(val) + } + } + + /// Append an op to cast `val` to `expected` type, if it does not already have that type. + #[inline] + pub fn cast_to_expected_type_if_needed( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + expected: Type<'ctx>, + ) -> Result> { + if expected == val.r#type() { + Ok(val) + } else if types_unify(expected, val.r#type()) { + self.unifiable_cast(location, val, expected) + } else if is_felt_type(expected) { + self.cast_to_felt_if_needed(codegen, location, val) + } else if is_index(expected) { + self.cast_to_index_if_needed(location, val) + } else if is_bool(expected) { + self.cast_to_bool_if_needed(codegen, location, val) + } else { + anyhow::bail!( + "Unsupported 'expected' type '{expected}' with value type {}", + val.r#type() + ) + } + } + + /// Copy the values in the source array into the destination array. Assumes both arrays have + /// unifiable element types the same number of dimensions, but each dimension may be wider or + /// narrower than the destination type. + /// + /// General overview: create a N-dimensional nested `for` loop to copy. If the indices are + /// out-of-bounds, then the array is default-filled. Otherwise, copy elements into destination. + fn copy_into_array( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + dst: Value<'ctx, 'val>, + dst_ty: ArrayType<'ctx>, + src: Value<'ctx, 'val>, + src_ty: ArrayType<'ctx>, + ) -> Result<()> + where + 'val: 'blk, + { + if codegen.config.verbose { + println!("[copy_into_array] src: {src}"); + println!("[copy_into_array] src_ty: {src_ty}"); + println!("[copy_into_array] dst: {dst}"); + println!("[copy_into_array] dst_ty: {dst_ty}"); + } + // Check caller pre-conditions to ensure this function is being used correctly. + assert_eq!(src.r#type(), src_ty.into()); + assert_eq!(dst.r#type(), dst_ty.into()); + assert_eq!(dst_ty.num_dims(), src_ty.num_dims()); + let src_elem_ty = src_ty.element_type(); + let dst_elem_ty = dst_ty.element_type(); + assert!(types_unify(src_elem_ty, dst_elem_ty)); + + let location = codegen.location_from_meta(meta); + + let bounds = zip(src_ty.dims(), dst_ty.dims()) + .map(|(src_dim, dest_dim)| { + if codegen.config.verbose { + println!("[copy_into_array] src_dim: {src_dim} -> dest_dim: {dest_dim}"); + } + let src_val = self.array_dim_attr_to_idx_val(codegen, location, src_dim)?; + let dest_val = self.array_dim_attr_to_idx_val(codegen, location, dest_dim)?; + let condition = self.append_op_unnamed_result(arith::cmpi( + codegen.context, + arith::CmpiPredicate::Ult, + src_val, + dest_val, + location, + ))?; + let if_op = self.generate_simple_scf_if( + codegen, + meta, + condition, + |_| Ok(src_val), + |_| Ok(dest_val), + )?; + self.append_op_unnamed_result(if_op) + }) + .collect::>>()?; + + let element_types_not_equal = src_elem_ty != dst_elem_ty; + self.gen_loop_nest(codegen, location, &bounds, move |fc, indices| { + let mut val = + fc.append_op_unnamed_result(array::read(location, src_elem_ty, src, indices))?; + if element_types_not_equal { + val = fc.unifiable_cast(location, val, dst_elem_ty)?; + } + fc.append_op_no_result(array::write(location, dst, indices, val)) + }) + } + + /// Perform a direct assignment of `rvalue` to `var`. In many cases this is handled + /// by updating the variable name map to have `var` -> `rvalue`, but special handling + /// is required during array assignments where `rvalue` is a different width than the + /// destination array (this requires 0-filling or truncation). + pub fn handle_simple_assignment( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + var: &String, + rvalue: Value<'ctx, 'val>, + ) -> Result<()> + where + 'val: 'blk, + { + // Since there's no simple assignment in LLZK, just update the mapped Value which + // essentially propagates the assignment. The exception here is the ArrayType case: + // Circom allows (with a warning) arrays to be assigned to array variables of differing + // widths (wider/narrower) as long as both arrays have the same number of dimensions + // (e.g., var x[2][2] = y, where y is var[1][7], is allowed). If the dimension is + // wider, the values are truncated, and if they are narrower, the array is left in + // its current state. + let Ok(existing) = self.block_ctx.get_named_value(var) else { + // Otherwise, set the var to point to rvalue + return self.block_ctx.set_named_value(var.clone(), rvalue); + }; + if existing.r#type() == rvalue.r#type() { + // Replace existing value reference to rvalue + return self.block_ctx.set_named_value(var.clone(), rvalue); + } + let existing_arr_ty = ArrayType::try_from(existing.r#type()); + let rvalue_arr_ty = ArrayType::try_from(rvalue.r#type()); + if let (Ok(existing_arr_ty), Ok(rvalue_arr_ty)) = (existing_arr_ty, rvalue_arr_ty) { + // If the arrays have the same number of dimensions and unifiable element type, + // then copy values from the `rvalue` array into the existing array. + if existing_arr_ty.num_dims() == rvalue_arr_ty.num_dims() + && types_unify(existing_arr_ty.element_type(), rvalue_arr_ty.element_type()) + { + // Copy values from the rvalue into the existing array. + // No need to update named value here. + return self.copy_into_array( + codegen, + meta, + *existing, + existing_arr_ty, + rvalue, + rvalue_arr_ty, + ); + } + } + if types_unify(existing.r#type(), rvalue.r#type()) { + todo!("'handle_simple_assignment' with unifiable but different types if this happens"); + } + anyhow::bail!( + "could not assign value of type '{}' to '{var}', which has type '{}'", + rvalue.r#type(), + existing.r#type() + ) + } + + /// Handle [program_structure::ast::Statement::Substitution] when the operator is not a signal + /// operator. Note: Do not use directly from `GenerateLLZKInTemplate`. + #[allow(clippy::too_many_arguments)] + pub fn handle_substitution_stmt_nonsignal<'info>( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + info: InfoProviders<'info>, + meta: &Meta, + var: &String, + access: &[Access], + op: &AssignOp, + rhe: &Expression, + ) -> Result<()> + where + 'val: 'blk, + { + assert!(!op.is_signal_operator()); // pre-condition + + let rvalue = rhe.gen_llzk_in_block(codegen, self, info)?; + if access.is_empty() { + self.handle_simple_assignment(codegen, meta, var, rvalue) + } else { + let location = codegen.location_from_meta(meta); + let indices = &access + .iter() + .map(|access| { + let idx = match access { + Access::ArrayAccess(index_expr) => { + index_expr.gen_llzk_in_block(codegen, self, info) + } + Access::ComponentAccess(_) => { + todo!("Handle Substitution component access in BlockGenContext") + } + }?; + self.cast_to_index_if_needed(location, idx) + }) + .collect::>>>()?; + let arr_ref = *self.block_ctx.get_named_value(var)?; + self.append_array_write(codegen, arr_ref, indices, location, rvalue, Some(var)) + } + } + + /// Creates LLZK ops for array indexing from the collection of elements. + pub fn gen_index_ops<'ast, 'info, E>( + &mut self, + indices: impl IntoIterator, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + info: InfoProviders<'info>, + ) -> Result>> + where + E: GenerateLLZKInAnyBlock<'ctx, 'blk, 'val> + 'ast, + { + indices + .into_iter() + .map(|e| { + let val = e.gen_llzk_in_block(codegen, self, info)?; + self.append_op_unnamed_result(cast::toindex(location, val)) + }) + .collect() + } + + /// Generate LLZK code in the current function for a circom prefix operation. + pub fn gen_prefix_op( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + op: &ExpressionPrefixOpcode, + rhs: Value<'ctx, 'val>, + ) -> Result> { + let mut get_negative_idx = || { + let zero = self.append_op_unnamed_result(index::constant( + codegen.context, + IntegerAttribute::new(rhs.r#type(), 0), + codegen.location_from_meta(meta), + ))?; + self.append_op_unnamed_result(index::sub(zero, rhs, codegen.location_from_meta(meta))) + }; + match op { + ExpressionPrefixOpcode::Sub => { + if is_felt_type(rhs.r#type()) { + return self.append_op_unnamed_result(felt::neg( + codegen.location_from_meta(meta), + rhs, + )?); + } + // For index negation, we need to subtract from zero. + if shared::is_index(rhs.r#type()) { + return get_negative_idx(); + } + } + ExpressionPrefixOpcode::BoolNot => { + if shared::is_bool(rhs.r#type()) { + return self.append_op_unnamed_result(bool::not( + codegen.location_from_meta(meta), + rhs, + )?); + } + } + ExpressionPrefixOpcode::Complement => { + if is_felt_type(rhs.r#type()) { + return self.append_op_unnamed_result(felt::bit_not( + codegen.location_from_meta(meta), + rhs, + )?); + } + // For index negation, we need to subtract one from the negative + // value (for the identity that `~x == -x - 1`). + if shared::is_index(rhs.r#type()) { + let negative_idx = get_negative_idx()?; + let one = self.append_op_unnamed_result(index::constant( + codegen.context, + IntegerAttribute::new(rhs.r#type(), 1), + codegen.location_from_meta(meta), + ))?; + return self.append_op_unnamed_result(index::sub( + negative_idx, + one, + codegen.location_from_meta(meta), + )); + } + } + } + let err_msg = format!( + "Cannot generate LLZK for prefix {:?} with operand type '{}'", + self, + rhs.r#type() + ); + codegen.emit_circom_error(meta, err_msg.as_str(), ReportCode::PrefixOperatorWithWrongTypes); + Err(anyhow!(err_msg)) + } + + /// If both operands have types that match the respective filter predicates, generate the + /// operation using the provided generator function and return the result, otherwise None. + #[inline] + fn gen_infix_op_if_types_are( + &mut self, + lhs_type_filter: impl FnOnce(Type) -> bool, + rhs_type_filter: impl FnOnce(Type) -> bool, + lhs: Value<'ctx, 'val>, + rhs: Value<'ctx, 'val>, + op_gen_fn: impl FnOnce(&mut Self) -> Result>, + ) -> Result>> { + if lhs_type_filter(lhs.r#type()) && rhs_type_filter(rhs.r#type()) { + let op_res = op_gen_fn(self)?; + self.append_op_unnamed_result(op_res).map(Option::Some) + } else { + Ok(None) + } + } + + /// Generate LLZK code in the current function for an infix operation. + pub fn gen_infix_op( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + op: &ExpressionInfixOpcode, + lhs: Value<'ctx, 'val>, + rhs: Value<'ctx, 'val>, + ) -> Result> { + // Macro to handle the common pattern for type checks for op selection. + macro_rules! try_callback_for_type { + ($type_check:path, $cb:expr) => {{ + if let Some(result) = + self.gen_infix_op_if_types_are($type_check, $type_check, lhs, rhs, $cb)? + { + return Ok(result); + } + }}; + } + + macro_rules! generic_op_callback { + ($op_path:path) => {{ + |_| { + let loc = codegen.location_from_meta(meta); + $op_path(loc, lhs, rhs).map_err(Into::into) + } + }}; + } + + macro_rules! try_felt_op { + ($op_path:path) => {{ + try_callback_for_type!(is_felt_type, generic_op_callback!($op_path)); + }}; + } + + macro_rules! try_bool_op { + ($op_path:path) => {{ + let loc = codegen.location_from_meta(meta); + let lhs = self.cast_to_bool_if_needed(codegen, loc, lhs)?; + let rhs = self.cast_to_bool_if_needed(codegen, loc, rhs)?; + return self.append_op_unnamed_result($op_path(loc, lhs, rhs)?); + }}; + } + + macro_rules! try_index_op { + ($op_path:path) => {{ + try_callback_for_type!(shared::is_index, |_| { + let loc = codegen.location_from_meta(meta); + Ok($op_path(lhs, rhs, loc)) + }); + }}; + } + + macro_rules! try_math_op { + ($op_path:path) => {{ + try_callback_for_type!(shared::is_index, |_| { + let loc = codegen.location_from_meta(meta); + Ok(Operation::from($op_path(codegen.context, lhs, rhs, loc))) + }); + }}; + } + + // Macro to handle the common pattern for felt and index type checks. + // For index operations that use felt:: module and simple index operations. + macro_rules! try_felt_or_index_op { + ($felt_op:path, $index_op:path) => {{ + try_felt_op!($felt_op); + try_index_op!($index_op); + }}; + } + + macro_rules! try_felt_or_math_op { + ($felt_op:path, $math_op:path) => {{ + try_felt_op!($felt_op); + try_math_op!($math_op); + }}; + } + + // Macro to handle the common pattern for felt and index type checks. + // For comparison operations that use bool:: module and index::cmpi. + macro_rules! try_bool_cmp_op { + ($bool_op:path, $cmp:ident) => {{ + try_felt_op!($bool_op); + try_callback_for_type!(shared::is_index, |_| { + let loc = codegen.location_from_meta(meta); + Ok(index::cmp(codegen.context, arith::CmpiPredicate::$cmp, lhs, rhs, loc)) + }); + }}; + } + + match op { + ExpressionInfixOpcode::Add => { + try_felt_or_index_op!(felt::add, index::add); + } + ExpressionInfixOpcode::Sub => { + try_felt_or_index_op!(felt::sub, index::sub); + } + ExpressionInfixOpcode::Mul => { + try_felt_or_index_op!(felt::mul, index::mul); + } + ExpressionInfixOpcode::Div => { + try_felt_or_index_op!(felt::div, index::divu); + } + ExpressionInfixOpcode::IntDiv => { + try_felt_or_index_op!(felt::uintdiv, index::divu); + } + ExpressionInfixOpcode::Mod => { + try_felt_or_index_op!(felt::umod, index::remu); + } + ExpressionInfixOpcode::Pow => { + try_felt_or_math_op!(felt::pow, math::ipowi); + } + ExpressionInfixOpcode::ShiftL => { + try_felt_or_index_op!(felt::shl, index::shl); + } + ExpressionInfixOpcode::ShiftR => { + try_felt_or_index_op!(felt::shr, index::shru); + } + ExpressionInfixOpcode::LesserEq => { + try_bool_cmp_op!(bool::le, Ule); + } + ExpressionInfixOpcode::GreaterEq => { + try_bool_cmp_op!(bool::ge, Uge); + } + ExpressionInfixOpcode::Lesser => { + try_bool_cmp_op!(bool::lt, Ult); + } + ExpressionInfixOpcode::Greater => { + try_bool_cmp_op!(bool::gt, Ugt); + } + ExpressionInfixOpcode::Eq => { + try_bool_cmp_op!(bool::eq, Eq); + } + ExpressionInfixOpcode::NotEq => { + try_bool_cmp_op!(bool::ne, Ne); + } + ExpressionInfixOpcode::BoolOr => { + try_bool_op!(bool::or); + } + ExpressionInfixOpcode::BoolAnd => { + try_bool_op!(bool::and); + } + ExpressionInfixOpcode::BitOr => { + try_felt_or_index_op!(felt::bit_or, index::or); + } + ExpressionInfixOpcode::BitAnd => { + try_felt_or_index_op!(felt::bit_and, index::and); + } + ExpressionInfixOpcode::BitXor => { + try_felt_or_index_op!(felt::bit_xor, index::xor); + } + } + let err_msg = format!( + "Cannot generate LLZK for infix {:?} with LHS type '{}' and RHS type '{}'", + op, + lhs.r#type(), + rhs.r#type() + ); + codegen.emit_circom_error(meta, err_msg.as_str(), ReportCode::InfixOperatorWithWrongTypes); + Err(anyhow!(err_msg)) + } + + /// Create a new `scf.yield` op, in the given block, that yields multiple values with associated + /// variable names. Create an [Attribute] containing comma-separated list of `value_names` + /// and attach it to the `scf.yield` op using the [OPERAND_VAL_NAMES] attribute key. + fn append_multi_operand_yield_to_block( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + block: BlockRef<'ctx, 'val>, + values: &[Value<'ctx, 'val>], + value_names: &[String], + location: Location<'ctx>, + ) -> Result<()> { + assert_eq!(values.len(), value_names.len(), "requires one name per value"); + let mut op = scf::r#yield(values, location); + op.set_attribute(OPERAND_VAL_NAMES, codegen.list_to_attribute(value_names)); + no_results(block.append_operation(op)) + } + + /// Generate an `scf.if` op based on the given [NestedBlockInfo] for each branch and update the + /// block context with the results of the `scf.if` op mapped to the given names. + pub fn gen_scf_if( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + condition: Value<'ctx, 'val>, + mut then_info: NestedBlockInfo<'ctx, 'blk, 'val>, + else_info: NestedBlockInfo<'ctx, 'blk, 'val>, + ) -> Result<()> { + // Ensure both blocks will yield the same set of variables. + then_info.add_missing_values(&else_info, self)?; + + // Split `then_block_info.var_overwrites` into ordered lists of names and values. The + // ordering of names here defines the ordering of results from the `scf.if` op and + // thus the ordering of operands to `scf.yield` ops in both branches. + let mut overwrites_sorted: Vec<_> = then_info.var_overwrites.into_iter().collect(); + if codegen.config.stabilize { + // Sort by circom variable names to ensure a stable order. + overwrites_sorted.sort_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b)); + } + let (overwrite_names, then_values): (Vec<_>, Vec<_>) = + overwrites_sorted.into_iter().unzip(); + + // Create list of values to yield from the `else` block in the same order + // as `overwrite_names`, again using current-scope values for missing keys. + let else_values = overwrite_names + .iter() + .map(|name| { + else_info + .var_overwrites + .get(name) + .map_or_else(|| self.block_ctx.get_named_value(name).cloned(), |v| Ok(*v)) + }) + .collect::>>()?; + + // Insert `scf.yield` at the end of each block. + Self::append_multi_operand_yield_to_block( + codegen, + then_info.block, + &then_values, + &overwrite_names, + location, + )?; + Self::append_multi_operand_yield_to_block( + codegen, + else_info.block, + &else_values, + &overwrite_names, + location, + )?; + + // Use `overwrite_names` and the current block context to get the types of + // the named values to define the result types of the `scf.if` op. + let result_types = overwrite_names + .iter() + .map(|name| { + self.block_ctx + .get_named_value(name) + .inspect_err(|e| eprintln!("\nERROR: {e:?}")) + .map(|v| v.r#type()) + }) + .collect::>>()?; + + // Cast condition value to bool type if needed. + let condition = self.cast_to_bool_if_needed(codegen, location, condition)?; + // Generate the `scf.if` op for the circom `IfThenElse` statement. + let scf_if_op = self.append_op(scf::r#if( + condition, + &result_types, + then_info.region, + else_info.region, + location, + )); + + // Update the current block context with results from the `scf.if` op. + overwrite_names + .into_iter() + .zip(scf_if_op.results()) + .try_for_each(|(name, result)| self.block_ctx.set_named_value(name, result.into()))?; + Ok(()) + } + + /// Generate one region for either the then-arm or else-arm of a simple scf.if operation. + /// Used by [Self::generate_simple_scf_if]. + /// The `value_gen` function is called to generate the value to be yielded from the arm. + pub fn generate_simple_scf_if_arm( + &mut self, + location: Location<'ctx>, + value_gen: F, + ) -> Result<(Region<'ctx>, Value<'ctx, 'val>)> + where + F: FnOnce(&mut Self) -> Result>, + { + let region = Region::new(); + let block = region.append_block(Block::new(&[])); + self.block_ctx.push(block); + let arm_val = value_gen(self)?; + self.block_ctx.pop(); + no_results(block.append_operation(scf::r#yield(&[arm_val], location)))?; + Ok((region, arm_val)) + } + + /// Generate a simple scf.if operation that yields the given `then_value` or `else_value` + /// depending on the `condition` value. Unlike [Self::gen_scf_if], this assumes that the + /// then and else arms do not modify the current block context and only produce values. + pub fn generate_simple_scf_if( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + condition: Value<'ctx, 'val>, + then_value_gen: F1, + else_value_gen: F2, + ) -> Result> + where + F1: FnOnce(&mut Self) -> Result>, + F2: FnOnce(&mut Self) -> Result>, + { + let location = codegen.location_from_meta(meta); + + let (then_region, then_value) = + self.generate_simple_scf_if_arm(location, then_value_gen)?; + let (else_region, else_value) = + self.generate_simple_scf_if_arm(location, else_value_gen)?; + + // Ensure then_value and else_value have the same types + assert_eq!( + then_value.r#type(), + else_value.r#type(), + "then and else branches of scf.if must have matching value types" + ); + + Ok(scf::r#if(condition, &[then_value.r#type()], then_region, else_region, location)) + } + + /// Generate a simple `scf.for` op that doesn't need to override variables + /// in the block context. + /// The body function (`body_fn`) accepts a `Block` with a single argument representing + /// the for-loop induction variable and is used to fill in the `scf.for` op. + pub fn gen_simple_scf_for( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + start: Value<'ctx, 'val>, + step: Value<'ctx, 'val>, + end: Value<'ctx, 'val>, + body_fn: impl FnOnce(&mut Block<'ctx>) -> Result<()>, + ) -> Result<()> { + let block_arg = (codegen.index_type(), location); + let mut block = Block::new(&[block_arg]); + body_fn(&mut block)?; + let region = Region::new(); + region.append_block(block); + let scf_op = scf::r#for(start, end, step, region, location); + self.append_op_no_result(scf_op) + } + + /// Generate a simple `scf.for` op with normalized start=0 and step=1. + #[inline] + pub fn gen_normalized_scf_for( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + end: Value<'ctx, 'val>, + body_fn: impl FnOnce(&mut Block<'ctx>) -> Result<()>, + ) -> Result<()> { + let start = self.append_op_unnamed_result(codegen.new_index_const_op(0, location))?; + let step = self.append_op_unnamed_result(codegen.new_index_const_op(1, location))?; + self.gen_simple_scf_for(codegen, location, start, step, end, body_fn) + } + + /// Generates IR for decreasing the counter of a subcomponent. + /// + /// Returns a [`Value`] with the updated counter. + pub fn gen_subcmp_decrease_counter( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + subcmp_memory: Value<'ctx, 'val>, + amount: u32, + ) -> Result> { + let counter = self.append_op_unnamed_result(codegen.new_pod_read_op( + subcmp_memory, + COUNT, + location, + )?)?; + let one = self.append_op_unnamed_result(codegen.new_index_const_op(amount, location))?; + let counter = self.append_op_unnamed_result(arith::subi(counter, one, location))?; + self.append_op_no_result(codegen.new_pod_write_op( + location, + subcmp_memory, + COUNT, + counter, + ))?; + Ok(counter) + } + + /// Decomposes the given value of [`PodType`] into a sequence of values in declaration order. + pub fn gen_decompose_pod( + &mut self, + pod: Value<'ctx, 'val>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + ) -> Result>> { + PodType::try_from(pod.r#type())? + .get_records() + .into_iter() + .map(|record| { + let record_name = codegen.flat_sym(record.name().as_string_ref().as_str()?); + self.append_op_unnamed_result(pod::read( + location, + pod, + record_name, + record.r#type(), + )) + }) + .collect() + } + + /// Generates `pod.read` instructions for the template parameters that are required for + /// instantiating a struct type in its `compute` function. + /// + /// At the step where we create the params pod we don't have that + /// information so we conservatively put all parameters in the pod. Hence the need for + /// filtering what template parameters are actually required by the call. + /// + /// For example, consider a subcomponent of type `A(X, Y)`, with instances `A(1, N)` and `A(1, M)`. The type eraser + /// will create the caller struct member with type `A(1, ?)` since `1` is common across all + /// instances. This means that the call to `compute` only expects one affine map operand for + /// the second parameter. + fn read_required_params( + &mut self, + struct_type: StructType<'ctx>, + params_value: Value<'ctx, '_>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + ) -> Result>> { + let params = PodType::try_from(params_value.r#type())?.get_records(); + params_requiring_map_operands(struct_type) + .into_iter() + .map(|idx| { + let record_name = params[idx].name(); + let record_name = record_name.as_string_ref(); + let value = self.append_op_unnamed_result(codegen.new_pod_read_op( + params_value, + record_name.as_str()?, + location, + )?)?; + let value = self.cast_to_index_if_needed(location, value)?; + + Ok(OwningValueRange::from([value].as_slice())) + }) + .collect() + } + + /// Generates a call to `@compute` for the given struct type. + /// + /// The arguments for the call are given as a value of [`PodType`] representing the inputs of + /// the subcomponent. + pub fn gen_compute_call( + &mut self, + struct_type: StructType<'ctx>, + inputs: Value<'ctx, 'val>, + params: Value<'ctx, 'val>, + location: Location<'ctx>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result> { + let input_values = self.gen_decompose_pod(inputs, codegen, location)?; + let func_name = append_tail(&struct_type.name(), FUNC_NAME_COMPUTE.as_ref()); + let params = self.read_required_params(struct_type, params, codegen, location)?; + let mut map_operands = MapOperandsBuilder::new(); + fill_map_operands_for_subcmp_call(¶ms, &mut map_operands)?; + self.append_op_unnamed_result( + function::call_with_map_operands( + codegen.op_builder(), + location, + func_name, + &input_values, + &[struct_type], + map_operands, + )? + .into(), + ) + } + + /// Generates a call to `@constrain` for the given struct type. + /// + /// The arguments for the call are given as a value of [`PodType`] representing the inputs of + /// the subcomponent. + pub fn gen_constrain_call( + &mut self, + subcmp: Value<'ctx, 'val>, + inputs: Value<'ctx, 'val>, + location: Location<'ctx>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result<()> { + let mut call_args = vec![subcmp]; + call_args.extend(self.gen_decompose_pod(inputs, codegen, location)?); + let func_name = append_tail( + &StructType::try_from(subcmp.r#type())?.name(), + FUNC_NAME_CONSTRAIN.as_ref(), + ); + let return_types: [Type; 0] = []; + self.append_op_no_result( + function::call(codegen.op_builder(), location, func_name, &call_args, &return_types)? + .into(), + ) + } + + /// Convert a dimension [Attribute] from an [ArrayType] into a [`Value`] of index type, + /// generating the necessary IR if the attribute is a symbol reference to a struct param. + pub fn array_dim_attr_to_idx_val( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + dim: Attribute<'ctx>, + ) -> Result> { + if IntegerAttribute::try_from(dim).is_ok() { + if !dim.r#type().is_index() { + anyhow::bail!( + "expected index type for array dimension attribute, got '{}'", + dim.r#type() + ); + } + return self.append_op_unnamed_result(arith::constant(codegen.context, dim, location)); + } + if let Ok(sym_ref) = FlatSymbolRefAttribute::try_from(dim) { + // Check if this is a template binding reference + let sym_name = sym_ref.value(); + if let Some(type_restriction) = self.poly_template_binding_names.get(sym_name) { + if let Some(ty) = type_restriction { + // Read as the restricted type and then cast to index. + let op = + self.append_op_unnamed_result(poly::read_const(location, sym_name, *ty))?; + return self.cast_to_index_if_needed(location, op); + } else { + // If there is no type restriction just use `index`. + return self.append_op_unnamed_result(poly::read_const( + location, + sym_name, + codegen.index_type(), + )); + } + } + } + unreachable!("Unhandled attribute in array dimensions {}", dim) + } + + /// Creates a loop nest from a list of array dimension attributes. + /// + /// The body of the inner-most loop is defined by the given closure, which accepts a list of + /// values representing the current value of each loop's induction variable. + pub fn gen_loop_nest_from_attrs( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + dims: &[Attribute<'ctx>], + body: impl FnOnce(&mut Self, &[Value<'ctx, 'val>]) -> Result<()>, + ) -> Result<()> + where + 'val: 'blk, + { + // Create values from the dimensions + let dim_values = dims + .iter() + .map(|dim| self.array_dim_attr_to_idx_val(codegen, location, *dim)) + .collect::>>()?; + + self.gen_loop_nest(codegen, location, &dim_values, body) + } + + /// Creates a loop nest from a list of array dimension values. + /// + /// The body of the inner-most loop is defined by the given closure, which accepts a list of + /// values representing the current value of each loop's induction variable. + pub fn gen_loop_nest( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + dim_values: &[Value<'ctx, 'val>], + body: impl FnOnce(&mut Self, &[Value<'ctx, 'val>]) -> Result<()>, + ) -> Result<()> + where + 'val: 'blk, + { + let zero = self.append_op_unnamed_result(codegen.new_index_const_op(0, location))?; + let one = self.append_op_unnamed_result(codegen.new_index_const_op(1, location))?; + + let loop_block_args = [(codegen.index_type(), location)]; + let top_block = *self.block_ctx.top_block(); + let mut loop_vars: Vec = vec![]; + // Create the loop nest + let mut block: Option> = None; + for dim in dim_values { + let op = scf::r#for(zero, *dim, one, region_with_block(&loop_block_args), location); + let loop_op = match &block { + Some(block_ref) => block_ref.append_operation(op), + None => self.append_op(op), + }; + block = Some( + loop_op + .region(0)? + .first_block() + .ok_or_else(|| anyhow::anyhow!("region is missing first block"))?, + ); + // Accumulate the induction variables for later giving all of them to the callback. + loop_vars.push(block.unwrap().argument(0)?.into()); + } + + // Unwrap the block after creating the loop nest. + let mut block = block.ok_or_else(|| anyhow::anyhow!("no loops created"))?; + // Push the block of the inner-most loop s.t. the user can use `self` and ops will get added + // to the right block. + self.block_ctx.push(block); + body(self, &loop_vars)?; + self.block_ctx.pop(); + + // Traverse the stack of blocks until we reach the block where we inserted the whole nest. + // For each block traversed this way add the scf terminator op. + while block != top_block { + block.append_operation(scf::r#yield(&[], location)); + block = block + .parent_operation() + .and_then(|op| op.block()) + .ok_or_else(|| anyhow::anyhow!("detached block while creating loop nest"))?; + } + + Ok(()) + } + + /// Implementation for [Expression::UniformArray] after conversion of dimension + /// expression. Useful because dimension generation differs between function + /// and template contexts due to template parameters, but the actual array generation + /// is otherwise the same. + pub fn generate_uniform_array( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + value: Value<'ctx, 'val>, + dimension: &ArrayDimension<'ctx, 'val>, + ) -> Result> { + // Ensure all symbols are of index type + let dimension = &self.transform_symbols_to_index(location, dimension)?; + let const_dim = IntegerAttribute::try_from(dimension); + if let Ok(subarr_ty) = ArrayType::try_from(value.r#type()) { + let arr_ty = dimension.new_array_type(&subarr_ty.into()); + let mut symbols_sto = vec![]; + // The array.new constructor doesn't accept arrays as initializer values, + // so we instead create the array empty and use array.insert to insert values. + let ctor = if let Some(symbols) = dimension.value_range()? { + symbols_sto.push(symbols); + ArrayCtor::MapDimSlice(&symbols_sto, &[0]) + } else { + ArrayCtor::Empty + }; + let new_arr = + self.append_op_unnamed_result(codegen.new_array_new_op(location, arr_ty, ctor))?; + if let Ok(const_dim) = const_dim { + for idx in 0..const_dim.value() { + let idx_val = + self.append_op_unnamed_result(codegen.new_index_const_op(idx, location))?; + self.append_op_no_result(array::insert(location, new_arr, &[idx_val], value))?; + } + } else { + let dim = self.append_op_unnamed_result(codegen.new_index_const_op(0, location))?; + let array_len = + self.append_op_unnamed_result(array::len(location, new_arr, dim))?; + self.gen_normalized_scf_for(codegen, location, array_len, |b| { + let induction_var = b.argument(0)?; + b.append_operation(array::insert( + location, + new_arr, + &[induction_var.into()], + value, + )); + b.append_operation(scf::r#yield(&[], location)); + Ok(()) + })?; + }; + // Output value is still the newly created array + Ok(new_arr) + } else { + let arr_ty = dimension.new_array_type(&value.r#type()); + if let Ok(const_dim) = const_dim { + let values = vec![value; usize::try_from(const_dim.value())?]; + self.append_op_unnamed_result(codegen.new_array_new_op( + location, + arr_ty, + ArrayCtor::Values(&values), + )) + } else { + let mut v_sto = vec![]; + let ctor = if let Ok(Some(v)) = dimension.value_range() { + v_sto.push(v); + ArrayCtor::MapDimSlice(&v_sto, &[0]) + } else { + ArrayCtor::Empty + }; + let array_ref = self + .append_op_unnamed_result(codegen.new_array_new_op(location, arr_ty, ctor))?; + let dim = self.append_op_unnamed_result(codegen.new_index_const_op(0, location))?; + let array_len = + self.append_op_unnamed_result(array::len(location, array_ref, dim))?; + self.gen_normalized_scf_for(codegen, location, array_len, |b| { + let induction_var = b.argument(0)?; + b.append_operation(array::write( + location, + array_ref, + &[induction_var.into()], + value, + )); + b.append_operation(scf::r#yield(&[], location)); + Ok(()) + })?; + Ok(array_ref) + } + } + } + + /// Generate an `scf.while` op based on the given [NestedBlockInfo] and update the + /// block context with the results of the `scf.while` op mapped to the given names. + pub fn gen_scf_while( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + condition: Value<'ctx, 'val>, + loop_cond_info: NestedBlockInfo<'ctx, 'blk, 'val>, + loop_body_info: NestedBlockInfo<'ctx, 'blk, 'val>, + loop_bounds: Option>, + ) -> Result { + // ASSERT: loop condition was a single Expression so there are no variable overwrites. + assert!(loop_cond_info.var_overwrites.is_empty()); + + // Split `loop_body_info.var_overwrites` into ordered lists of names and values. The + // ordering of names here defines the ordering of the loop-carried variables for the + // `scf.while` op and thus the ordering of operands for `scf.yield` and `scf.condition`. + let mut overwrites_sorted: Vec<_> = loop_body_info.var_overwrites.into_iter().collect(); + if codegen.config.stabilize { + // Sort by circom variable names to ensure a stable order. + overwrites_sorted.sort_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b)); + } + let (loop_carried_var_names, body_yield_values): (Vec<_>, Vec<_>) = + overwrites_sorted.into_iter().unzip(); + + // Append the loop body block with an `scf.yield` + Self::append_multi_operand_yield_to_block( + codegen, + loop_body_info.block, + &body_yield_values, + &loop_carried_var_names, + location, + )?; + + // Use `loop_carried_var_names` and the current block context to build a list of types of + // the loop-carried variables, add BlockArguments of those types in both blocks, and + // replace uses of the overwritten variables in both blocks with references to the new + // BlockArguments Values. + let mut loop_carried_types = Vec::new(); + // Additionally, track if `VAR_NAME_HAD_RETURN` is among the loop-carried variables + // (indicating there was a return somewhere within the loop body) to later update the + // loop condition to ensure iteration stops when a return occurs. + let mut return_flag: Option = None; + for name in loop_carried_var_names.iter() { + let orig_val = self.block_ctx.get_named_value(name).unwrap(); + loop_carried_types.push(orig_val.r#type()); + replace_uses_with_new_block_argument(loop_body_info.block, orig_val, location); + let f = replace_uses_with_new_block_argument(loop_cond_info.block, orig_val, location); + if name == VAR_NAME_HAD_RETURN { + return_flag = Some(f); + } + } + + // In the loop condition block, ensure the condition has bool type and generate an + // `scf.condition` op with the condition value and the loop-carried variables. + { + let mut condition = self.cast_to_bool_if_needed(codegen, location, condition)?; + // If there is a return within the loop, add prefix "! &&" to the + // loop condition to ensure the loop terminates when the return occurs. + if let Some(flag) = return_flag { + let not_return_flag = single_result_as_value( + loop_cond_info.block.append_operation(bool::r#not(location, flag)?.into()), + )?; + condition = + single_result_as_value(loop_cond_info.block.append_operation( + bool::and(location, not_return_flag, condition)?.into(), + ))?; + } + // Pass the block arguments as the initial values to the condition op. + let block_arg_values = (0..loop_cond_info.block.argument_count()) + .map(|i| loop_cond_info.block.argument(i).map_err(Into::into).map(Value::from)) + .collect::>>()?; + no_results(loop_cond_info.block.append_operation(scf::condition( + condition, + &block_arg_values, + location, + )))?; + } + + // Create list of initial values of the loop-carried variables (i.e. the overwritten + // variables, in the order of `loop_carried_var_names`) to pass into the `scf.while`. + let initial_values = loop_carried_var_names + .iter() + .map(|name| self.block_ctx.get_named_value(name).cloned()) + .collect::>>()?; + + // Generate the `scf.while` op for the circom `While` statement, adding `loopbounds` + // attribute if given, and append it to the current block. + let mut scf_op = scf::r#while( + &initial_values, + &loop_carried_types, + loop_cond_info.region, + loop_body_info.region, + location, + ); + if let Some(loop_bounds) = loop_bounds { + scf_op.set_attribute("llzk.loopbounds", loop_bounds.into()); + } + let scf_op = self.append_op(scf_op); + + // Update the current block context with results from the `scf.while` op. + loop_carried_var_names + .into_iter() + .zip(scf_op.results()) + .try_for_each(|(name, result)| self.block_ctx.set_named_value(name, result.into()))?; + + Ok(return_flag.is_some()) + } + + /// Cast all symbols passed to affine_maps into index types, which is required + /// for affine_map usage. + #[inline] + fn transform_symbols_to_index( + &mut self, + location: Location<'ctx>, + dimension: &ArrayDimension<'ctx, 'val>, + ) -> Result> { + dimension.transform(|val| self.cast_to_index_if_needed(location, val)) + } + + /// Handle a [program_structure::ast::Statement::Declaration] by generating a nondet felt value + /// with the given dimensions and declaring it in the current block context. + pub fn gen_declaration( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + name: &str, + dimensions: &[Expression], + ) -> Result<()> { + if self.block_ctx.is_name_present(name) { + return Ok(()); + } + // If generating from VCP, there are cases where the `sugar_cleaner` added new + // declarations that only have `MemoryKnowledge` but not `dimensions` in the AST. So + // check `MemoryKnowledge` first (if not generating from VCP, this will always be + // empty so `dimensions` will be used). + let mk = meta.get_memory_knowledge(); + let dims = if mk.has_concrete_dimensions() { + (mk.get_concrete_dimensions(), codegen).try_into()? + } else { + self.get_dim_exprs(codegen, dimensions)? + }; + if codegen.config.verbose { + println!("Declaring variable '{name}' with dimensions {dims:?}"); + } + let op = dims.new_nondet_felt_of_dimensions(codegen, meta)?; + self.block_ctx.declare_name_ensure_not_present(name, op) + } +} + +impl<'ctx, 'blk, 'val> DimExprConverter<'ctx, 'val> for BlockGenContext<'_, 'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + fn get_var_decl_types(&self) -> &HashMap> { + self.var_decl_types + } + + fn callback_store_poly_expr( + &self, + name: String, + op: TemplateExprOp<'ctx>, + ) -> StringAttribute<'ctx> { + // TODO: How/where can it be store when using this general BlockGenContext? + // TODO: Also, add new name+type to `self.poly_template_binding_names` so future + // calls to `get_dim_expr` can find it. The field will need RefCell. + todo!("BlockGenContext::callback_store_poly_expr: {name} -> {op:?}"); + } + + fn get_dim_expr( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + expr: &Expression, + ) -> Result> { + // First try to compute statically, falling back to literal computation if all values are + // not compile-time constants or if the final result does not properly convert to i64. + if let Some(integer) = shared::try_compute_as_i64(expr, codegen.prime())? { + ArrayDimensionResult::new(codegen.index_attr(integer).into(), &[]) + } else { + #[allow(unused_variables)] // TODO: TEMP + match expr { + Expression::Number(_, _) => { + unreachable!("handled by try_compute_as_i64") + } + Expression::Variable { meta, name, access } if access.is_empty() => { + // Grab the template symbol binding name if it exists (first try `poly.param` + // name then try `poly.expr` name). Otherwise, use `affine_map` to convert Value + // to Attribute. + if self.poly_template_binding_names.contains_key(name) { + ArrayDimensionResult::new(codegen.flat_sym(name).into(), &[]) + } else { + let expr_name = dim_expr_name(expr); + if self.poly_template_binding_names.contains_key(&expr_name) { + ArrayDimensionResult::new(codegen.flat_sym(expr_name).into(), &[]) + } else if let Ok(v) = self.block_ctx.get_named_value(name) { + ArrayDimensionResult::new(codegen.identity_affine_map_attr()?, &[*v]) + } else { + todo!("Handle dimension Variable expression in BlockGenContext") + } + } + } + // Variable case with non-empty `access` + Expression::Variable { .. } + | Expression::InlineSwitchOp { .. } + | Expression::PrefixOp { .. } + | Expression::InfixOp { .. } + | Expression::Call { .. } => self.gen_template_poly_expr(codegen, expr), + // The remaining cases do not produce a scalar value. + // i.e. ParallelOp, ArrayInLine, UniformArray, BusCall, AnonymousComp, Tuple + // Give the same error that the circom type checker gives. The type checker ran + // earlier so this should technically be unreachable. + _ => { + unreachable!("Array indexes and lengths must produce a scalar value") + } + } + } + } +} + +/// A trait to generate LLZK IR to an arbitrary LLZK block. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +pub trait GenerateLLZKInAnyBlock<'ctx, 'blk, 'val> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Generates LLZK IR in the given block context. + fn gen_llzk_in_block<'info>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + block_gen: &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + info: InfoProviders<'info>, + ) -> Result>; +} + +/// This handles translation of circom [Expression] nodes within functions and templates (i.e. +/// [crate::template::GenerateLLZKInTemplate] implementation for [Expression] directly calls this) +/// and code generation to LLZK `poly.expr` blocks. Therefore, it must handle things that are not +/// legal in functions such as [Expression::BusCall] and [Access::ComponentAccess] but are legal in +/// other contexts such as circom templates. The `type_analysis_user::analyse_project()` pass that +/// runs before the LLZK translation pass ensures that these illegal constructs do not appear in +/// pure functions. +impl<'ctx, 'blk, 'val> GenerateLLZKInAnyBlock<'ctx, 'blk, 'val> for Expression +where + 'ctx: 'blk, + 'blk: 'val, +{ + fn gen_llzk_in_block<'info>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + block_gen: &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + info: InfoProviders<'info>, + ) -> Result> { + match self { + Expression::Number(meta, big_int) => { + // Convert the BigInt to an LLZK `felt.const` op. The user of the Expression is + // responsible for converting this `felt.type` value to another type if needed. + block_gen.append_op_unnamed_result( + codegen.new_felt_const_op(big_int, codegen.location_from_meta(meta))?, + ) + } + Expression::Variable { meta, name, access } => { + let lvalue = Lvalue::new( + name, + if info.subcmp_info.is_subcmp(name) { Root::Signal } else { Root::Var }, + access, + ); + lvalue.get_value( + codegen, + block_gen, + info.subcmp_info, + codegen.location_from_meta(meta), + None, + ) + } + Expression::InfixOp { meta, lhe, infix_op, rhe } => { + let lhs = lhe.gen_llzk_in_block(codegen, block_gen, info)?; + let rhs = rhe.gen_llzk_in_block(codegen, block_gen, info)?; + block_gen.gen_infix_op(codegen, meta, infix_op, lhs, rhs) + } + Expression::PrefixOp { meta, prefix_op, rhe } => { + let rhs = rhe.gen_llzk_in_block(codegen, block_gen, info)?; + block_gen.gen_prefix_op(codegen, meta, prefix_op, rhs) + } + Expression::InlineSwitchOp { meta, cond, if_true, if_false } => { + let location = codegen.location_from_meta(meta); + // Ensure the condition is a bool type. + let cond_val = cond.gen_llzk_in_block(codegen, block_gen, info)?; + let condition = block_gen.cast_to_bool_if_needed(codegen, location, cond_val)?; + + // Then arm: generate in a nested block so `gen_llzk_in_block` can use `block_gen` + let then_region = Region::new(); + let then_block = then_region.append_block(Block::new(&[])); + block_gen.block_ctx.push(then_block); + let then_value = if_true.gen_llzk_in_block(codegen, block_gen, info)?; + block_gen.block_ctx.pop(); + no_results(then_block.append_operation(scf::r#yield(&[then_value], location)))?; + + // Else arm + let else_region = Region::new(); + let else_block = else_region.append_block(Block::new(&[])); + block_gen.block_ctx.push(else_block); + let else_value = if_false.gen_llzk_in_block(codegen, block_gen, info)?; + block_gen.block_ctx.pop(); + no_results(else_block.append_operation(scf::r#yield(&[else_value], location)))?; + + assert_eq!( + then_value.r#type(), + else_value.r#type(), + "then and else branches of scf.if must have matching value types" + ); + block_gen.append_op_unnamed_result(scf::r#if( + condition, + &[then_value.r#type()], + then_region, + else_region, + location, + )) + } + Expression::ArrayInLine { meta, values } => { + let location = codegen.location_from_meta(meta); + // Multi-dimensional arrays are made up of array values as their elements + let values = values + .iter() + .map(|val_expr| val_expr.gen_llzk_in_block(codegen, block_gen, info)) + .collect::>>()?; + let value_ty = + values.first().expect("Array must have at least one element").r#type(); + assert!( + values.iter().all(|&v| v.r#type() == value_ty), + "All array elements must have the same type" + ); + if let Ok(subarr_ty) = ArrayType::try_from(value_ty) { + // For subarrays, we need to create a new array then insert the values + let dim = codegen.index_attr(i64::try_from(values.len())?); + let arr_ty = new_array_type(dim.into(), &subarr_ty); + let new_arr = block_gen.append_op_unnamed_result(codegen.new_array_new_op( + location, + arr_ty, + ArrayCtor::Empty, + ))?; + for (idx, val) in values.iter().enumerate() { + let idx_val = block_gen.append_op_unnamed_result( + codegen.new_index_const_op(i64::try_from(idx)?, location), + )?; + block_gen.append_op_no_result(array::insert( + location, + new_arr, + &[idx_val], + *val, + ))?; + } + + // Output value is still the newly created array + Ok(new_arr) + } else { + let dim = codegen.index_attr(i64::try_from(values.len())?); + let arr_ty = ArrayType::new(value_ty.into(), &[dim.into()]); + block_gen.append_op_unnamed_result(codegen.new_array_new_op( + location, + arr_ty, + ArrayCtor::Values(&values), + )) + } + } + Expression::UniformArray { meta, value, dimension } => { + let location = codegen.location_from_meta(meta); + // Multi-dimensional arrays are made up of array values as their elements + let value = value.gen_llzk_in_block(codegen, block_gen, info)?; + let dim = block_gen + .get_dim_expr(codegen, dimension) + .and_then(ArrayDimension::try_from)?; + block_gen.generate_uniform_array(codegen, location, value, &dim) + } + Expression::Call { meta, id, args } => { + let location = codegen.location_from_meta(meta); + let target_function_data = codegen.program.get_function_data(id); + // Visit each argument and collect the resulting LLZK Values for both functions. + let param_types = target_function_data.get_type_of_params(codegen); + assert_eq!(param_types.len(), args.len(), "Argument-parameter count mismatch"); + let call_operands = args + .iter() + .zip(param_types) + .map(|(arg, expected_type)| { + let operand_val = arg.gen_llzk_in_block(codegen, block_gen, info)?; + block_gen.cast_to_expected_type_if_needed( + codegen, + location, + operand_val, + expected_type, + ) + }) + .collect::>>()?; + // Create the CallOp in each function using the collected args. + block_gen.append_op_unnamed_result( + function::call( + codegen.op_builder(), + location, + codegen.flat_sym(id), + &call_operands, + &[target_function_data.get_type_of_return(codegen)], + )? + .into(), + ) + } + #[allow(unused_variables)] // TODO: TEMP + Expression::BusCall { meta, id, args } => { + todo!("Handle BusCall expression") + } + Expression::AnonymousComp { .. } => unreachable!("removed by 'syntax_sugar_remover'"), + Expression::Tuple { .. } => unreachable!("removed by 'syntax_sugar_remover'"), + Expression::ParallelOp { .. } => { + unreachable!("handled in templates, illegal in pure functions") + } + } + } +} + +/// Returns the indices of the struct parameters that require affine map operands. +fn params_requiring_map_operands(struct_type: StructType) -> Vec { + struct_type + .params() + .into_iter() + .enumerate() + .filter_map(|(n, attr)| attr.is_affine_map().then_some(n)) + .collect() +} + +/// Fills the map operands builder with the subcomponent params that are required for the final +/// struct type. +fn fill_map_operands_for_subcmp_call( + params: &[OwningValueRange<'_, '_>], + map_operands: &mut MapOperandsBuilder, +) -> Result<()> { + for required_param in params { + let range = ValueRange::try_from(required_param)?; + map_operands.append_operands(range); + map_operands.append_dim_count(1); + } + + Ok(()) +} diff --git a/llzk_backend/src/lib.rs b/llzk_backend/src/lib.rs new file mode 100644 index 000000000..5eb182a1d --- /dev/null +++ b/llzk_backend/src/lib.rs @@ -0,0 +1,31 @@ +//! Provides functionality to generate LLZK code from the Circom AST. + +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(unused_must_use)] +#![warn(redundant_imports)] +#![warn(clippy::cast_lossless)] +#![allow(clippy::useless_conversion)] + +mod codegen; +mod function; +mod function_ext; +mod gen_context; +mod module; +mod program_ext; +#[macro_use] +mod shared; +mod affine_map; +mod lvalue; +mod subcmp; +mod template; +mod template_ext; +mod traversal; +mod write_chain; + +pub use codegen::generate_llzk; +pub use codegen::LlzkConfig; +pub use program_ext::CachedParseInfo; +pub use program_ext::VCPPlus; diff --git a/llzk_backend/src/lvalue.rs b/llzk_backend/src/lvalue.rs new file mode 100644 index 000000000..62a19b867 --- /dev/null +++ b/llzk_backend/src/lvalue.rs @@ -0,0 +1,474 @@ +//! Types for handling location values (a.k.a. lvalues). + +use crate::function::InfoProviders; +use crate::gen_context::BlockGenContext; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::LlzkCodegen; +use crate::subcmp::names::COMP; +use crate::subcmp::SubcmpInfo; +use anyhow::Result; +use llzk::dialect::pod; +use llzk::dialect::r#struct; +use llzk::prelude::Location; +use llzk::prelude::PodType; +use llzk::prelude::StructType; +use llzk::prelude::Value; +use llzk::prelude::ValueLike as _; +use program_structure::ast::Access; +use program_structure::ast::AssignOp; +use program_structure::ast::Expression; +use program_structure::ast::ExpressionInfixOpcode; +use program_structure::ast::ExpressionPrefixOpcode; +use std::borrow::Cow; +use std::cmp; +use std::convert::TryFrom as _; +use std::fmt; + +/// Decorator trait that can modify the name of the root variable. +pub trait OverrideVar { + /// May override the var name. Returns `None` if the var name was not overriden. + fn override_var(&self, var: &str, op: Root) -> Option; +} + +impl OverrideVar for Option<&dyn OverrideVar> { + fn override_var(&self, var: &str, op: Root) -> Option { + self.as_ref().and_then(|t| t.override_var(var, op)) + } +} + +/// Type of data at the root of the lvalue +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Root { + /// Write into a signal. + Signal, + /// Write into a felt var. + Var, +} + +/// Helper type that defines a chain of write operations. +#[derive(Debug, Clone)] +pub enum Lvalue<'ast> { + /// Root of the chain. + Root { + /// Name of the variable. + var: &'ast str, + /// Type of the variable written into. + op: Root, + }, + /// Array access. + Array { + /// List of indexing expressions. + indices: Vec<&'ast Expression>, + /// Location accessed from as an array. + prev: Box>, + }, + /// Access via dot-notation. + Subcmp { + /// Name of the field read with dot-notation. + name: &'ast str, + /// Location the field is read from. + prev: Box>, + }, +} + +impl<'ast> Lvalue<'ast> { + /// Creates a new lvalue. + pub fn new(var: &'ast str, op: Root, access: &'ast [Access]) -> Self { + access.iter().fold(Lvalue::Root { var, op }, |wc, access| match (wc, access) { + (Lvalue::Array { mut indices, prev }, Access::ArrayAccess(expression)) => { + indices.push(expression); + Lvalue::Array { indices, prev } + } + (wc, Access::ComponentAccess(name)) => Lvalue::Subcmp { name, prev: Box::new(wc) }, + (wc, Access::ArrayAccess(expression)) => { + Lvalue::Array { indices: vec![expression], prev: Box::new(wc) } + } + }) + } + + /// Returns the root of the lvalue. + pub fn root(&self) -> &Self { + match self { + root @ Lvalue::Root { .. } => root, + Lvalue::Array { prev, .. } | Lvalue::Subcmp { prev, .. } => prev.root(), + } + } + + /// Returns the root var name of the lvalue. + fn root_var(&self) -> &str { + match self { + Lvalue::Root { var, .. } => var, + Lvalue::Array { prev, .. } | Lvalue::Subcmp { prev, .. } => prev.root_var(), + } + } + + /// Handle [Lvalue::Root] with [Root::Signal] case of [`Lvalue::get_value`]. + fn get_root_signal<'ctx, 'val>( + &self, + var: &str, + block_gen: &mut BlockGenContext<'_, 'ctx, '_, 'val>, + ) -> Result> { + // Both compute and constrain functions should have the `var` defined: + // compute from an existing assignment, or constrain from pre-generation + // of the `readm` in `gen_template_llzk`. + block_gen.block_ctx.get_named_value(var).copied() + } + + /// Handle [Lvalue::Root] case of [`Lvalue::get_value`] other than + /// [Root::Signal]. + fn get_root_value<'ctx, 'val>( + &self, + var: &str, + block_gen: &mut BlockGenContext<'_, 'ctx, '_, 'val>, + ) -> Result> { + block_gen.block_ctx.get_named_value(var).copied() + } + + /// Handle [Lvalue::Array] case of [`Lvalue::get_value`]. + fn get_array_value<'ctx, 'val>( + &self, + indices: &[&Expression], + prev: Value<'ctx, 'val>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + block_gen: &mut BlockGenContext<'_, 'ctx, '_, 'val>, + location: Location<'ctx>, + info: InfoProviders<'_>, + ) -> Result> { + let indices = block_gen.gen_index_ops(indices.iter().copied(), codegen, location, info)?; + block_gen.append_array_read(prev, &indices, location, None) + } + + /// Handle [Lvalue::Subcmp] in [`Lvalue::get_value`] when the signal is an output of the + /// subcomponent. + fn get_subcmp_output<'ctx, 'val>( + &self, + signal_name: &str, + subcmp_value: Value<'ctx, 'val>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + block_gen: &mut BlockGenContext<'_, 'ctx, '_, 'val>, + location: Location<'ctx>, + ) -> Result> { + let comp_value = type_switch! { let ty = subcmp_value.r#type(); + PodType => { + block_gen.append_op_unnamed_result(pod::read( + location, + subcmp_value, + codegen.flat_sym(COMP), + ty + .get_type_of_record(COMP) + .ok_or_else(|| { + anyhow::anyhow!( + "record {COMP} not found in subcomponent memory pod: {subcmp_value}" + ) + })?, + ))? + } + StructType as _ => subcmp_value, + }; + let comp_value_type = StructType::try_from(comp_value.r#type())?; + let field_ty = codegen + .get_output_signal_type(shared::get_name_tail(&comp_value_type)?, signal_name)?; + + block_gen.append_op_unnamed_result(r#struct::readm( + codegen.op_builder(), + location, + field_ty, + comp_value, + signal_name, + )?) + } + + /// Handle [Lvalue::Subcmp] in [`Lvalue::get_value`] when the signal is an input of the + /// subcomponent. + fn get_subcmp_input<'ctx, 'val>( + &self, + signal_name: &str, + subcmp_value: Value<'ctx, 'val>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + block_gen: &mut BlockGenContext<'_, 'ctx, '_, 'val>, + location: Location<'ctx>, + ) -> Result> { + block_gen.append_op_unnamed_result(pod::read( + location, + subcmp_value, + codegen.flat_sym(signal_name), + PodType::try_from(subcmp_value.r#type()) + .map_err(|e| anyhow::anyhow!("not a pod type '{e}' coming from {subcmp_value}"))? + .get_type_of_record(signal_name) + .ok_or_else(|| { + anyhow::anyhow!( + "subcomponent input signal {signal_name} not found: {subcmp_value}" + ) + })?, + )) + } + + /// Returns the SSA [`Value`] representing the op. + /// + /// It could be a placeholder operation at this point (usually represented with `llzk.nondet`). + pub fn get_value<'ctx, 'val>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + block_gen: &mut BlockGenContext<'_, 'ctx, '_, 'val>, + subcmp_info: &dyn SubcmpInfo, + location: Location<'ctx>, + ov: Option<&dyn OverrideVar>, + ) -> Result> { + macro_rules! var_name { + ($ov:ident, $var:ident, $op:expr) => { + $ov.override_var(*$var, $op) + .map(Cow::Owned) + .unwrap_or(Cow::Borrowed(*$var)) + .as_ref() + }; + } + + match self { + Lvalue::Root { var, op: Root::Signal } => { + self.get_root_signal(var_name!(ov, var, Root::Signal), block_gen) + } + Lvalue::Root { var, op: Root::Var } => { + self.get_root_value(var_name!(ov, var, Root::Var), block_gen) + } + Lvalue::Array { indices, prev } => self.get_array_value( + indices, + prev.get_value(codegen, block_gen, subcmp_info, location, ov)?, + codegen, + block_gen, + location, + InfoProviders { subcmp_info, ..Default::default() }, + ), + Lvalue::Subcmp { name: signal_name, prev } => { + let root = prev.root_var(); + if !subcmp_info.is_subcmp(root) { + anyhow::bail!( + "variable '{root}' is not a subcomponent but it was accessed like one" + ); + } + + /// Overrides the input if the root is Signal. Only used if no decorator was passed. + /// Replaces "{var}" with "{var}$inputs" in the inner layers of this lvalue. + struct OverrideIfInput { + /// If true, and if the root is Signal, replaces the variable with the + /// `$inputs` version. + do_override: bool, + } + impl OverrideVar for OverrideIfInput { + fn override_var(&self, var: &str, op: Root) -> Option { + let will_override = self.do_override && op == Root::Signal; + (will_override).then(|| crate::subcmp::names::inputs(var)) + } + } + let info = subcmp_info.subcmp_info(root, codegen)?; + let is_input = info.signal_is_input(signal_name); + let ovii = OverrideIfInput { do_override: is_input }; + + let subcmp_value = + prev.get_value(codegen, block_gen, subcmp_info, location, ov.or(Some(&ovii)))?; + + if is_input { + self.get_subcmp_input(signal_name, subcmp_value, codegen, block_gen, location) + } else { + self.get_subcmp_output(signal_name, subcmp_value, codegen, block_gen, location) + } + } + } + } +} + +impl fmt::Display for Lvalue<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn print_expr(expr: &impl AsRef, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let expr = expr.as_ref(); + let d = depth(expr); + if d > 1 { + write!(f, "(")?; + } + display_expr(expr, f)?; + if d > 1 { + write!(f, ")")?; + } + Ok(()) + } + + fn interleave( + args: I, + sep: &S, + f: &mut fmt::Formatter<'_>, + print_fn: impl Fn(A, &mut fmt::Formatter<'_>) -> fmt::Result, + ) -> fmt::Result + where + I: IntoIterator, + I::IntoIter: DoubleEndedIterator + ExactSizeIterator, + S: fmt::Display + ?Sized, + { + args.into_iter().rev().enumerate().rev().try_for_each(|(rev_idx, arg)| { + print_fn(arg, f)?; + if rev_idx > 0 { + write!(f, "{sep}")?; + } + Ok(()) + }) + } + + fn print_args( + args: I, + f: &mut fmt::Formatter<'_>, + print_fn: impl Fn(A, &mut fmt::Formatter<'_>) -> fmt::Result, + ) -> fmt::Result + where + I: IntoIterator, + I::IntoIter: DoubleEndedIterator + ExactSizeIterator, + { + write!(f, "(")?; + interleave(args, ", ", f, print_fn)?; + write!(f, ")") + } + + fn display_expr(expr: &Expression, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match expr { + Expression::InfixOp { meta: _, lhe, infix_op, rhe } => { + print_expr(lhe, f)?; + let op_str = match infix_op { + ExpressionInfixOpcode::Mul => "*", + ExpressionInfixOpcode::Div => "/", + ExpressionInfixOpcode::Add => "+", + ExpressionInfixOpcode::Sub => "-", + ExpressionInfixOpcode::Pow => "**", + ExpressionInfixOpcode::IntDiv => "\\", + ExpressionInfixOpcode::Mod => "%", + ExpressionInfixOpcode::ShiftL => "<<", + ExpressionInfixOpcode::ShiftR => ">>", + ExpressionInfixOpcode::LesserEq => "<=", + ExpressionInfixOpcode::GreaterEq => ">=", + ExpressionInfixOpcode::Lesser => "<", + ExpressionInfixOpcode::Greater => ">", + ExpressionInfixOpcode::Eq => "==", + ExpressionInfixOpcode::NotEq => "!=", + ExpressionInfixOpcode::BoolOr => "||", + ExpressionInfixOpcode::BoolAnd => "&&", + ExpressionInfixOpcode::BitOr => "|", + ExpressionInfixOpcode::BitAnd => "&", + ExpressionInfixOpcode::BitXor => "^", + }; + write!(f, " {op_str} ",)?; + print_expr(rhe, f) + } + Expression::PrefixOp { meta: _, prefix_op, rhe } => { + let op_str = match prefix_op { + ExpressionPrefixOpcode::Sub => "-", + ExpressionPrefixOpcode::BoolNot => "!", + ExpressionPrefixOpcode::Complement => "~", + }; + write!(f, "{op_str}")?; + print_expr(rhe, f) + } + Expression::InlineSwitchOp { meta: _, cond, if_true, if_false } => { + print_expr(cond, f)?; + write!(f, " ? ")?; + print_expr(if_true, f)?; + write!(f, " : ")?; + print_expr(if_false, f) + } + Expression::ParallelOp { meta: _, rhe } => { + write!(f, "parallel ")?; + print_expr(rhe, f) + } + Expression::Variable { meta: _, name, access } => { + write!(f, "{name}")?; + for a in access { + match a { + Access::ComponentAccess(name) => write!(f, ".{name}"), + Access::ArrayAccess(expression) => display_expr(expression, f), + }?; + } + Ok(()) + } + Expression::Number(_, big_int) => write!(f, "{big_int}"), + Expression::BusCall { meta: _, id, args } + | Expression::Call { meta: _, id, args } => { + write!(f, "{id}(")?; + print_args(args, f, display_expr)?; + write!(f, ")") + } + Expression::AnonymousComp { meta: _, id, is_parallel, params, signals, names } => { + if *is_parallel { + write!(f, "parallel ")?; + } + write!(f, "{id}")?; + print_args(params, f, display_expr)?; + match names { + Some(names) => print_args( + std::iter::zip(names, signals), + f, + |((op, name), signal), f| { + let op_str = match op { + AssignOp::AssignSignal => "<==", + AssignOp::AssignConstraintSignal => "<--", + AssignOp::AssignVar => unreachable!(), + }; + write!(f, "{name} {op_str} ")?; + display_expr(signal, f) + }, + ), + None => print_args(signals, f, display_expr), + } + } + Expression::ArrayInLine { meta: _, values } => { + write!(f, "[")?; + interleave(values, ", ", f, display_expr)?; + write!(f, "]") + } + Expression::Tuple { meta: _, values } => print_args(values, f, display_expr), + Expression::UniformArray { meta: _, value, dimension } => { + write!(f, "[")?; + display_expr(value, f)?; + write!(f, "; ")?; + display_expr(dimension, f)?; + write!(f, "]") + } + } + } + + fn depth(expr: &Expression) -> usize { + match expr { + Expression::InfixOp { meta: _, lhe, infix_op: _, rhe } => { + 1 + cmp::max(depth(lhe), depth(rhe)) + } + Expression::PrefixOp { meta: _, prefix_op: _, rhe } => depth(rhe), + Expression::InlineSwitchOp { meta: _, cond, if_true, if_false } => { + 1 + cmp::max(cmp::max(depth(cond), depth(if_true)), depth(if_false)) + } + Expression::ParallelOp { meta: _, rhe } => depth(rhe), + _ => 1, + } + } + + match self { + Lvalue::Root { var, op, .. } => { + write!( + f, + "{}:{var}", + match op { + Root::Signal => "Signal", + Root::Var => "Var", + } + ) + } + Lvalue::Array { indices, prev } => { + fmt::Display::fmt(prev.as_ref(), f)?; + for index in indices { + write!(f, "[")?; + display_expr(index, f)?; + write!(f, "]")?; + } + Ok(()) + } + Lvalue::Subcmp { name, prev } => { + fmt::Display::fmt(prev.as_ref(), f)?; + write!(f, ".{name}") + } + } + } +} diff --git a/llzk_backend/src/module.rs b/llzk_backend/src/module.rs new file mode 100644 index 000000000..b97dfe1d1 --- /dev/null +++ b/llzk_backend/src/module.rs @@ -0,0 +1,907 @@ +//! Handles the top-level constructs (i.e. circom templates and functions), by delegating +//! to the [crate::function] and [crate::template] modules to generate the code for each. + +use crate::affine_map::AffineMapAttribute; +use crate::function::FunctionContext; +use crate::function::GenerateLLZKInFunction as _; +use crate::function_ext::FunctionLike; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::get_poly_expr_name; +use crate::shared::map_name_to_arg_value; +use crate::shared::ArrayDimensionResult; +use crate::shared::DimExprConverter; +use crate::shared::LlzkCodegen; +use crate::shared::TmplParamsInstance; +use crate::subcmp::unique_instance_types; +use crate::subcmp::SubcmpDeclInfo; +use crate::subcmp::SubcmpPrologueData; +use crate::template::GenerateLLZKInTemplate as _; +use crate::template::TemplateContext; +use crate::template_ext::TemplateLike; +use anyhow::bail; +use anyhow::Result; +use llzk::attributes::NamedAttribute; +use llzk::dialect::function; +use llzk::dialect::poly; +use llzk::dialect::r#struct; +use llzk::dialect::r#struct::helpers::compute_fn; +use llzk::dialect::r#struct::helpers::constrain_fn; +use llzk::error::Error; +use llzk::prelude::Block; +use llzk::prelude::BlockLike as _; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::FuncDefOpRef; +use llzk::prelude::FuncDefOpRefMut; +use llzk::prelude::FunctionType; +use llzk::prelude::LlzkError; +use llzk::prelude::Location; +use llzk::prelude::MemberDefOpLike as _; +use llzk::prelude::Operation; +use llzk::prelude::OperationLike as _; +use llzk::prelude::PodRecordAttribute; +use llzk::prelude::PodType; +use llzk::prelude::PublicAttribute; +use llzk::prelude::RegionLike as _; +use llzk::prelude::StringAttribute; +use llzk::prelude::StructDefOpLike as _; +use llzk::prelude::StructDefOpRef; +use llzk::prelude::StructType; +use llzk::prelude::TemplateExprOp; +use llzk::prelude::TemplateExprOpLike as _; +use llzk::prelude::TemplateOpLike; +use llzk::prelude::Type; +use melior::ir::Attribute; +use melior::ir::AttributeLike as _; +use program_structure::ast::AssignOp; +use program_structure::ast::Expression; +use program_structure::ast::Meta; +use program_structure::ast::SignalType; +use program_structure::ast::Statement; +use program_structure::ast::VariableType; +use std::cell::RefCell; +use std::collections::HashMap; +use std::collections::HashSet; +use std::convert::TryFrom; +use std::convert::TryInto as _; + +/// Information needed to create an LLZK struct function parameter collected from the input signal +/// Declaration statements within a circom template. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +#[derive(Debug)] +struct InputSignalInfo<'ctx> { + /// Name of circom input signal that maps to a function parameter. + name: String, + /// Type+Location information for the function parameter. + type_and_loc: (Type<'ctx>, Location<'ctx>), + /// Named Attributes for the function parameter. + attrs: Vec>, +} + +/// Information neeeded to create a struct member representing either an output signal, an internal +/// signal or a subcomponent. +#[derive(Debug)] +struct MemberInfo<'ctx> { + /// Name of the member. + name: String, + /// Type of the member. + decl_type: Type<'ctx>, + /// Location of the member. + location: Location<'ctx>, + /// Whether it's a publicly facing member of the struct + public: bool, +} + +impl<'ctx> TryFrom> for Operation<'ctx> { + type Error = Error; + + fn try_from(value: MemberInfo<'ctx>) -> std::result::Result { + r#struct::member(value.location, &value.name, value.decl_type, false, value.public) + .map(Into::into) + } +} + +/// Information collected from Declaration statements within a template that is used to setup LLZK +/// struct fields and parameters to the functions with the struct. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +#[derive(Debug, Default)] +pub struct DeclarationInfo<'ctx> { + /// Input Signal declarations to use as parameters to the LLZK struct functions. + inputs: Vec>, + /// Output and Intermediate declarations to use as LLZK struct fields. + struct_fields: Vec>, + /// Map var/signal name to its LLZK declaration Operation (usually `llzk.nondet`). + decl_inits: HashMap>, + /// Map `component` name to its declaration information. + subcmp_decls: HashMap>, + /// The template params that may be used to instantiate array dimensions. + /// Note: this is set on construction and not modified. + template_params: HashSet, + /// Dimension expressions mapped to generated `poly.expr` op to be inserted into the template. + poly_exprs: RefCell>>, + /// Pre-computed LLZK types for `var` declarations, keyed by var name. Used by + /// `poly.expr` body generation to avoid recursive dimension-expression recomputation. + var_decl_types: HashMap>, +} + +impl<'ctx> DeclarationInfo<'ctx> { + /// Returns a mapping of input signal names to their types. + pub(crate) fn build_input_name_to_type_map(&self) -> HashMap> { + self.inputs.iter().map(|i| (i.name.clone(), i.type_and_loc.0)).collect() + } + + /// Returns a mapping of output signal names to their types. + pub(crate) fn build_output_name_to_type_map(&self) -> HashMap> { + self.struct_fields + .iter() + .filter(|info| info.public) + .map(|info| (info.name.clone(), info.decl_type)) + .collect() + } + + /// Returns the type of the input signal with the given name, if it exists. + pub(crate) fn get_input_type(&self, signal_name: &str) -> Option> { + self.inputs.iter().find_map(|i| (i.name == signal_name).then_some(i.type_and_loc.0)) + } + + /// Returns the type of the input signal with the given name, if it exists. + pub(crate) fn get_output_type(&self, signal_name: &str) -> Option> { + self.struct_fields.iter().find_map(|member| { + (member.name == signal_name && member.public).then_some(member.decl_type) + }) + } + + /// Completes the declaration information from the information collected from the + /// subcomponents. + /// + /// Returns a vector with an associative list of names to the types of the declaration. + /// + /// Currently handles declaration of scalar subcomponents and array subcomponents of the same + /// type. + fn complete<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + ) -> Result>> { + let mut ops = vec![]; + let mut subcmps: Vec<_> = self.subcmp_decls.keys().cloned().collect(); + if codegen.config.stabilize { + // Sort by circom subcomponent names to ensure a stable order of struct fields. + subcmps.sort_by(Ord::cmp); + } + for name in subcmps { + let info = self.subcmp_decls.get_mut(&name).unwrap(); + let instances = info.instances(); + + let types = unique_instance_types(instances); + let subcmp_type = match &types[..] { + [] => todo!("Handle uninitialized component decl"), + [t] => *t, + types => { + let name = ensure_same_template(types, &name).name(); + // Zip the parameters of all the types and derive a common unification for + // each. If all the instances of parameter N are the same we keep it, + // otherwise we create an identity affine map to replace the parameter. + let params = zip_struct_params(types) + .into_iter() + .map(|attrs| { + if is_singleton(&attrs) { + attrs[0] + } else { + AffineMapAttribute::identity(codegen.context, 1).into() + } + }) + .collect::>(); + StructType::new(name, ¶ms) + } + }; + record_subcmp_decl( + name, + info, + subcmp_type, + codegen, + &mut ops, + &mut self.struct_fields, + )?; + } + Ok(ops) + } + + /// Visit all statements in the body of the template and return a new [DeclarationInfo] + /// with any declarations found. + pub(crate) fn from_template( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &impl TemplateLike, + ) -> Result> { + let mut declarations = DeclarationInfo { + template_params: template.get_name_of_params().iter().cloned().collect(), + subcmp_decls: template.get_init_subcmp_decls(codegen)?, + ..DeclarationInfo::default() + }; + for s in template.get_body() { + declarations.visit(codegen, s)?; + } + Ok(declarations) + } + + /// Visit a statement and populate this `DeclarationInfo` with any declarations found. + /// + /// TODO: This currently visits only top-level statements within the template body. However, + /// since circom 2.1.5, signal declarations are allowed inside of blocks and known-condition if + /// statements. Those nested declarations are not currently processed here. + fn visit( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + stmt: &Statement, + ) -> Result<()> { + let _guard = codegen.trace_statement(stmt); + match stmt { + Statement::Block { stmts, .. } => { + // TemplateInstance (in concrete programs) has Declaration in Block (but only for + // var, not signals). The Block contains no additional information beyond the + // Declarations that appear within it so just process the inner statements. + for init in stmts { + self.visit(codegen, init)?; + } + Ok(()) + } + Statement::InitializationBlock { initializations, .. } => { + // TemplateData (in non-concrete programs) has Declaration in InitializationBlock. + // The InitializationBlock contains no additional information beyond the + // Declarations that appear within it so just process the inner statements. + for init in initializations { + self.visit(codegen, init)?; + } + Ok(()) + } + Statement::Declaration { meta, name, xtype, dimensions, .. } => { + // The Signal and Bus types use SignalType to indicate if they are input, output, or + // intermediate. The others are all intermediate. Intermediates become SSA values + // (which could later be stored as a struct field if used in a constraint), outputs + // become "pub" struct fields, and inputs become arguments to the functions. + match xtype { + VariableType::Signal(signal_type, ..) => self.visit_signal_or_bus( + codegen, + signal_type, + name, + meta, + dimensions, + codegen.felt_type().into(), + ), + VariableType::Bus(bus_name, signal_type, ..) => self.visit_signal_or_bus( + codegen, + signal_type, + name, + meta, + dimensions, + codegen.struct_type(bus_name).into(), + ), + VariableType::Var => { + // Create `llzk.nondet` of the appropriate type. When the actual assignment + // is processed later, this is replaced with the appropriate value. + let dimensions = self.get_dim_exprs(codegen, dimensions)?; + let var_type = + dimensions.type_from_dimension_exprs(codegen.felt_type().into()); + self.var_decl_types.insert(name.clone(), var_type); + self.decl_inits.insert( + name.clone(), + codegen.new_nondet_at_location( + codegen.location_from_meta(meta), + var_type, + )?, + ); + Ok(()) + } + VariableType::Component => { + self.visit_component_decl(codegen, meta, name, dimensions) + } + VariableType::AnonymousComponent => { + // The 'syntax_sugar_remover' will convert anonymous components to normal + // components unless they appear within a loop. In that case, the anonymous + // component is just refactored to the top level, outside the loop. + // See: circom/tests/circom_doc_examples/34.circom + todo!("handle AnonymousComponent declaration") + } + } + } + stmt => self.search_component_instances(codegen, stmt, false), + } + } + + /// Searches in an [`Expression`] for a call to a subcomponent's constructor. + /// + /// In this context, constructor refers to `Foo(n)` in Circom, not `@Foo::@compute` in LLZK. + fn find_subcmp_ctor_call( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + expression: &Expression, + ) -> Result> { + match expression { + Expression::Call { meta, id, args, .. } if meta.get_type_knowledge().is_component() => { + let dims = self.get_dim_exprs(codegen, args)?; + Ok(dims.struct_type_with_concrete_dimensions(codegen, id)) + } + Expression::ParallelOp { rhe, .. } => { + // `parallel` is a tag used to generate parallelized code for the C++ + // witness generator. Since LLZK currently has no such hint, + // we simply generate the underlying expression. + self.find_subcmp_ctor_call(codegen, rhe) + } + _ => bail!("expected call expression for subcomponent substitution rhe"), + } + } + + /// [`visit`](Self::visit) helper for component declarations. + fn visit_component_decl( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + name: &str, + dimensions: &[Expression], + ) -> Result<()> { + let location = codegen.location_from_meta(meta); + let dims = self.get_dim_exprs(codegen, dimensions)?; + if self + .subcmp_decls + .insert(name.to_owned(), SubcmpDeclInfo::new(dims.attrs(), location)) + .is_some() + { + bail!("Subcomponent {name} declared twice"); + } + Ok(()) + } + + /// `visit()` helper for Signal and Bus VariableType. + #[inline] + fn visit_signal_or_bus( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + signal_type: &SignalType, + name: &String, + meta: &Meta, + dimensions: &[Expression], + base_type: Type<'ctx>, + ) -> Result<()> { + let location = codegen.location_from_meta(meta); + let dims = self.get_dim_exprs(codegen, dimensions)?; + let decl_type = dims.type_from_dimension_exprs(base_type); + self.visit_signal_or_bus_impl(codegen, signal_type, name, location, decl_type) + } + + /// `visit()` helper for Signal and Bus VariableType. + pub(crate) fn visit_signal_or_bus_impl( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + signal_type: &SignalType, + name: &String, + location: Location<'ctx>, + decl_type: Type<'ctx>, + ) -> Result<()> { + if SignalType::Input == *signal_type { + let mut attrs: Vec> = Vec::new(); + if codegen.program.get_main_public_inputs().contains(name) { + attrs.push(PublicAttribute::new_named_attr(codegen.context)); + } + self.inputs.push(InputSignalInfo { + name: name.clone(), + type_and_loc: (decl_type, location), + attrs, + }); + } else { + self.struct_fields.push(MemberInfo { + name: name.clone(), + decl_type, + location, + public: SignalType::Output == *signal_type, + }); + } + // Create `llzk.nondet` of the appropriate type. When the actual assignment + // is processed later, this is replaced with the appropriate value. + codegen.new_nondet_at_location(location, decl_type).map(|op| { + self.decl_inits.insert(name.clone(), op); + }) + } + + /// Traverses the AST looking for assigments of subcomponents and collects the instances used + /// for them. + /// + /// Passing `true` to `push_trace` pushes the statement to the trace. Callers of this function + /// should pass `false` to `push_trace` if `stmt` has already been pushed into the statements + /// trace. + fn search_component_instances( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + stmt: &Statement, + push_trace: bool, + ) -> Result<()> { + // The first call to this function comes from visit, which already pushes a trace on stmt. + let _guard = push_trace.then(|| codegen.trace_statement(stmt)); + match stmt { + Statement::Substitution { var, op, rhe, .. } + if matches!(op, AssignOp::AssignVar) && self.subcmp_decls.contains_key(var) => + { + let ctor_call = self.find_subcmp_ctor_call(codegen, rhe)?; + self.subcmp_decls.entry(var.clone()).and_modify(|info| { + info.instances_mut().push(ctor_call); + }); + + Ok(()) + } + Statement::IfThenElse { if_case, else_case, .. } => { + self.search_component_instances(codegen, if_case.as_ref(), true)?; + if let Some(else_case) = else_case.as_deref() { + self.search_component_instances(codegen, else_case, true)?; + } + Ok(()) + } + Statement::While { stmt, .. } => { + self.search_component_instances(codegen, stmt.as_ref(), true) + } + Statement::InitializationBlock { initializations: stmts, .. } + | Statement::Block { stmts, .. } => { + for stmt in stmts { + self.search_component_instances(codegen, stmt, true)?; + } + Ok(()) + } + _ => Ok(()), + } + } +} + +impl<'ctx, 'val> DimExprConverter<'ctx, 'val> for DeclarationInfo<'ctx> +where + 'ctx: 'val, +{ + fn get_var_decl_types(&self) -> &HashMap> { + &self.var_decl_types + } + + fn poly_template_binding_names( + &self, + ) -> impl IntoIterator>)> { + self.template_params.iter().map(|name| (name.clone(), None)) + } + + fn callback_store_poly_expr( + &self, + name: String, + op: TemplateExprOp<'ctx>, + ) -> StringAttribute<'ctx> { + let uniqued_name = get_poly_expr_name(&op); + let old = self.poly_exprs.borrow_mut().insert(name, op); + // ASSERT: In general, `DimExprConverter` has to account for non-unique names but in this + // `DeclarationInfo` implementation, names will always be unique (assuming the `Meta::start` + // from the circom AST that `dim_expr_name()` appends to the names is accurate). + assert!(old.is_none(), "multiple poly.expr generated for the same dimension expression"); + uniqued_name + } + + fn get_dim_expr( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + expr: &Expression, + ) -> Result> { + // First try to compute statically, falling back to literal computation if all values are + // not compile-time constants or if the final result does not properly convert to i64. + if let Some(integer) = shared::try_compute_as_i64(expr, codegen.prime())? { + ArrayDimensionResult::new(codegen.index_attr(integer).into(), &[]) + } else { + match expr { + Expression::Number(_, _) => { + unreachable!("handled by try_compute_as_i64") + } + Expression::Variable { name, access, .. } if access.is_empty() => { + if self.template_params.contains(name) { + ArrayDimensionResult::new(codegen.flat_sym(name).into(), &[]) + } else if self.poly_exprs.borrow().contains_key(name) { + // This is a `Statement::Declaration` with `VariableType::Var` that was + // previously encountered and converted into a `poly.expr`. + ArrayDimensionResult::new(codegen.flat_sym(name).into(), &[]) + } else if self.decl_inits.contains_key(name) { + // This is a `Statement::Declaration` with `VariableType::Var` that is + // encountered for the first time and must have a `poly.expr` generated. + self.gen_template_poly_expr(codegen, expr) + } else { + // TODO: this happens in `circom/tests/circom_doc_examples/04.circom` + // and I think it's related to the "TODO" on `DeclarationInfo::visit`. + todo!("not a known circom template parameter or var declaration: {}", name) + } + } + // Variable case with non-empty `access` + Expression::Variable { .. } + | Expression::InlineSwitchOp { .. } + | Expression::PrefixOp { .. } + | Expression::InfixOp { .. } + | Expression::Call { .. } => self.gen_template_poly_expr(codegen, expr), + // The remaining cases do not produce a scalar value. + // i.e. ParallelOp, ArrayInLine, UniformArray, BusCall, AnonymousComp, Tuple + // Give the same error that the circom type checker gives. The type checker ran + // earlier so this should technically be unreachable. + _ => { + unreachable!("Array indexes and lengths must produce a scalar value") + } + } + } + } +} + +/// Generate LLZK for a function-like construct. Helper to avoid code duplication. +fn gen_function_llzk<'ast, 'ctx, F: FunctionLike>( + func_like: &'ast F, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, +) -> Result<()> { + if codegen.config.verbose { + println!("Generating LLZK for function {}", func_like.get_name()); + } + let location = func_like.get_location(codegen); + let inputs = func_like.get_type_of_params(codegen); + let result = func_like.get_type_of_return(codegen); + let func_type = FunctionType::new(codegen.context, &inputs, &[result]); + let func_def = + function::def(location, func_like.get_name(), func_type, &[], None).and_then(|f| { + let arguments: Vec<(Type, Location)> = + inputs.into_iter().map(|t| (t, location)).collect(); + f.region(0)?.append_block(Block::new(&arguments)); + Ok(f) + })?; + func_def.set_allow_non_native_field_ops_attr(true); + // Store function to the module. + let func: FuncDefOpRefMut = codegen.add_function(func_def)?; + + // Generate mapping from parameter names to SSA Values. + let name_to_value = map_name_to_arg_value(func, func_like.get_name_of_params())?; + + // Visit the body of the function and generate LLZK IR for it. + let var_decl_types = HashMap::new(); + let mut func_context = FunctionContext::new::( + codegen, + func, + name_to_value, + &var_decl_types, + std::iter::empty(), + )?; + func_like.get_body().gen_llzk_in_function(codegen, &mut func_context, Default::default())?; + func_context.finalize(codegen) +} + +/// Generate LLZK for a template-like construct. Helper to avoid code duplication. +fn gen_template_llzk<'ast, 'ctx, T: TemplateLike>( + template_like: &'ast T, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, +) -> Result<()> { + if codegen.config.verbose { + println!("Generating LLZK for template {}", template_like.get_name()); + } + // Collect declarations first to determine struct fields and function parameters. + let mut declarations: DeclarationInfo<'ctx> = + codegen.take_template_decl(template_like.get_name())?; + let subcmps = declarations.complete(codegen)?; + let location = template_like.get_location(codegen); + + // Generate list of template params for the LLZK template body. + let template_region_ops = template_like + .get_name_of_params() + .iter() + .map(|name| poly::param(location, name, None).map(Into::into)) + .collect::>>(); + let llzk_template_def = + poly::template(location, template_like.get_name(), template_region_ops)?; + let new_template = codegen.add_template(llzk_template_def)?; + + // Insert the declarations from `declarations.poly_exprs`. + let mut poly_exprs = declarations.poly_exprs.into_inner(); + let mut poly_expr_names: Vec<_> = + poly_exprs.iter().map(|(name, op)| (name.clone(), op.expr_type())).collect(); + if codegen.config.stabilize { + poly_expr_names.sort_by(|(a, _), (b, _)| a.cmp(b)); + } + for (name, _) in &poly_expr_names { + // Here the template is empty and the names are unique per storage in HashMap so they + // can be directly appended without using `symbol_table::insert` to unique names. + new_template.body().append_operation(poly_exprs.remove(name).unwrap().into()); + } + + // Add the struct definition, prepopulated with fields, to the template body. + let new_struct = StructDefOpRef::try_from( + new_template.body().append_operation( + r#struct::def( + location, + template_like.get_name(), + declarations.struct_fields.into_iter().map(MemberInfo::try_into), + )? + .into(), + ), + )?; + + // Consume and separate 'declarations.inputs' (to avoid cloning 'attrs' and 'name'). + let (inputs, arg_attrs, arg_names) = declarations.inputs.into_iter().fold( + (Vec::new(), Vec::new(), Vec::new()), + |(mut inputs, mut attrs, mut names), v| { + inputs.push(v.type_and_loc); + attrs.push(v.attrs); + names.push(v.name); + (inputs, attrs, names) + }, + ); + // Generate the compute and constrain functions. + let new_struct_type = new_struct.r#type(); + let struct_body = new_struct.body(); + let compute_func = FuncDefOpRef::try_from(struct_body.append_operation( + compute_fn(location, new_struct_type, &inputs, Some(&arg_attrs))?.into(), + ))? + .into(); + let constrain_func = FuncDefOpRef::try_from(struct_body.append_operation( + constrain_fn(location, new_struct_type, &inputs, Some(&arg_attrs))?.into(), + ))? + .into(); + + // Map parameter Values of each LLZK function to the corresponding circom variable names and + // then create the FunctionContext for each function. Before creating the FunctionContext + // for constrain, add a dummy name at index 0 since the first parameter is the struct ref. + let mut compute_ctx = FunctionContext::new::( + codegen, + compute_func, + map_name_to_arg_value(compute_func, arg_names.clone())?, + &declarations.var_decl_types, + // concatenate circom template parameter names with `poly_expr_names` + template_like + .get_name_of_params() + .iter() + .map(|name| (name.clone(), None)) + .chain(poly_expr_names.iter().map(|(name, ty)| (name.clone(), Some(*ty)))), + )?; + let mut arg_names = arg_names; + arg_names.insert(0, "**self**".to_string()); + let mut constrain_ctx = FunctionContext::new::( + codegen, + constrain_func, + map_name_to_arg_value(constrain_func, arg_names)?, + &declarations.var_decl_types, + // concatenate circom template parameter names with `poly_expr_names` + template_like + .get_name_of_params() + .iter() + .map(|name| (name.clone(), None)) + .chain(poly_expr_names.iter().map(|(name, ty)| (name.clone(), Some(*ty)))), + )?; + + // Insert read operations for struct fields into constrain functions. + let location = codegen.location_unknown(); + for member in new_struct.get_member_defs() { + let member_name = member.member_name(); + constrain_ctx.block_ctx.declare_name_if_not_present(member_name, || { + r#struct::readm( + codegen.op_builder(), + location, + member.member_type(), + constrain_func.self_value_of_constrain()?, + member_name, + ) + .map_err(Into::into) + })?; + } + + fn decl_inits<'ctx>( + decl_inits: HashMap>, + sort: bool, + ) -> Vec<(String, Operation<'ctx>)> { + let mut v: Vec<_> = decl_inits.into_iter().collect(); + if sort { + v.sort_by(|(lhs, _), (rhs, _)| lhs.cmp(rhs)); + } + v + } + + // Insert the Operations created from variable Declaration statements and map the circom + // variable name to LLZK op result Value (do this in each function). + for (name, op) in decl_inits(declarations.decl_inits, codegen.config.stabilize) { + // Insert (a clone of) the declaration into the compute function. + compute_ctx.block_ctx.declare_name_if_not_present(&name, || Ok(op.clone()))?; + // Insert the declaration into the constrain function. + constrain_ctx.block_ctx.declare_name_if_not_present(&name, || Ok(op))?; + } + let subcmp_decls = declarations.subcmp_decls; + let subcmp_names = subcmps + .iter() + .map(|subcmp| { + let template_name = subcmp_decls + .get(subcmp.name()) + .and_then(|decl| decl.template()) + .ok_or_else(|| { + anyhow::anyhow!("could not deduce the type of subcomponent '{}'", subcmp.name()) + })?; + + Ok((subcmp.name().to_owned(), (template_name.to_owned(), subcmp.comp_pod(codegen)))) + }) + .collect::>()?; + // Insert the Operations created from subcomponent Declaration statements and map the + // circom variable name to a LLZK op result Value. + gen_subcmps_prologue_in_template( + subcmps, + &mut compute_ctx, + &mut constrain_ctx, + codegen, + &subcmp_decls, + )?; + + // Visit the body of the template and generate LLZK IR for it within the struct functions. + let template_context = TemplateContext::new( + new_template.into(), + new_struct.into(), + compute_ctx, + constrain_ctx, + &subcmp_names, + &declarations.var_decl_types, + ); + template_like.gen_preamble(codegen, &template_context)?; + template_like.get_body().gen_llzk_in_template(codegen, &template_context)?; + template_context.finalize(codegen) +} + +/// Generates the prologue related to subcomponents in a template body. +fn gen_subcmps_prologue_in_template<'ast, 'ctx, 'func, 'blk, 'val>( + subcmps: impl IntoIterator>, + compute_ctx: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + constrain_ctx: &mut FunctionContext<'_, 'ctx, '_, '_, '_>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + subcmp_decls: &HashMap>, +) -> Result<()> +where + 'val: 'blk, +{ + subcmps.into_iter().try_for_each(|subcmp| { + subcmp.generate_constraint_func_prologue( + constrain_ctx, + codegen.op_builder(), + subcmp_decls, + )?; + subcmp.generate_compute_func_prologue( + compute_ctx, + codegen.op_builder(), + codegen, + subcmp_decls, + ) + }) +} + +/// A trait to generate LLZK IR for structural elements of the circom AST: +/// ProgramArchive, TemplateData, and FunctionData. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +pub trait GenerateLLZKInModule<'ctx, P: ProgramLike> { + /// Generates LLZK IR from the circom AST element. + /// + /// 'ast: lifetime of the circom AST element + fn gen_llzk<'ast>(&'ast self, codegen: &LlzkCodegen<'ast, 'ctx, P>) -> Result<()>; +} + +impl<'ctx, P: ProgramLike> GenerateLLZKInModule<'ctx, P> for P { + fn gen_llzk<'ast>(&'ast self, codegen: &LlzkCodegen<'ast, 'ctx, P>) -> Result<()> { + for f in self.get_functions(codegen.config.stabilize) { + codegen.set_current_body(f.get_body()); + gen_function_llzk(f, codegen)?; + } + // Collect declaration information for all templates first to avoid duplicating work. + for t in self.get_templates(false) { + codegen.set_current_body(t.get_body()); + codegen.put_template_decl(t.get_name(), t.get_declarations(codegen)?); + } + for t in self.get_templates(codegen.config.stabilize) { + codegen.set_current_body(t.get_body()); + gen_template_llzk(t, codegen)?; + } + Ok(()) + } +} + +/// Collects the inputs' types of a template in a pod type. +#[inline] +fn collect_inputs<'ctx>( + template_name: &str, + subcmp_type: StructType<'ctx>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, +) -> Result> { + let template = codegen + .program + .get_templates(false) + .into_iter() + .find(|t| t.get_name() == template_name) + .ok_or_else(|| anyhow::anyhow!("template '{template_name}' not found"))?; + let template_params = + TmplParamsInstance::new(template.get_name_of_params(), subcmp_type.params_vec()); + let inputs = template + .get_declaration_inputs() + .iter() + .map(|(signal_name, _)| { + let signal_type = template_params + .map_type(codegen.get_input_signal_type(template_name, signal_name)?)?; + Ok(PodRecordAttribute::new(signal_name, signal_type)) + }) + .collect::>>()?; + Ok(PodType::new(codegen.context, &inputs).into()) +} + +/// Fills out the declaration information for a subcomponent using the given type. +#[inline] +fn record_subcmp_decl<'ctx, 'ast>( + name: String, + info: &mut SubcmpDeclInfo<'ctx>, + subcmp_type: StructType<'ctx>, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + ops: &mut Vec>, + struct_fields: &mut Vec>, +) -> Result<()> { + let template_name = shared::get_name_tail(&subcmp_type)?; + info.set_template(template_name.to_owned()); + + let inputs = info.extend_dims(collect_inputs(template_name, subcmp_type, codegen)?); + let member_type = info.extend_dims(subcmp_type.into()); + struct_fields.push(MemberInfo { + name: name.clone(), + decl_type: member_type, + location: info.location(), + public: false, + }); + struct_fields.push(MemberInfo { + name: crate::subcmp::names::inputs(&name), + decl_type: inputs, + location: info.location(), + public: false, + }); + ops.push(SubcmpPrologueData::new(name, template_name.to_owned(), member_type, inputs)); + Ok(()) +} + +/// Ensures that the given collection of struct types all reference the same base type, +/// panicking otherwise. The typechecker ensures this, so this function is just a fail-safe. +/// Returns the type of the *unique* template. +/// +/// For example, `{ A(1), A(2) }` is OK but `{ A(1), C(2) }` is not. +/// +/// # Panics +/// +/// If the given slice is empty or the types do not match. And only if the frontend was +/// built with debug assertions enabled. +#[inline] +fn ensure_same_template<'ctx>(types: &[StructType<'ctx>], subcmp: &str) -> StructType<'ctx> { + debug_assert!(!types.is_empty()); + let name = types[0].name(); + let params_len = types[0].params().len(); + for t in &types[1..] { + debug_assert!(t.name() == name, "Unexpected type for subcomponent instantation of '{subcmp}'. Was expecting {name} but got {}", t.name()); + debug_assert!(t.params().len() == params_len, "Unexpected parameter count for subcomponent instantation of '{subcmp}'. Was expecting {params_len} but got {}", t.params().len()); + } + + types[0] +} + +/// Groups the template parameters of the given structs by their declaration order. +fn zip_struct_params<'ctx>(types: &[StructType<'ctx>]) -> Vec>> { + let params_len = types[0].params().len(); + let mut outer = Vec::with_capacity(params_len); + for param_idx in 0..params_len { + let mut inner = Vec::with_capacity(types.len()); + for t in types { + inner.push(t.params().get(param_idx).unwrap()); + } + outer.push(inner); + } + outer +} + +/// Returns wether the given list of attributes is a singleton set. +fn is_singleton(attrs: &[Attribute]) -> bool { + #[derive(PartialEq, Eq)] + struct A<'ctx>(Attribute<'ctx>); + + impl std::hash::Hash for A<'_> { + fn hash(&self, state: &mut H) { + self.0.to_raw().ptr.hash(state); + } + } + debug_assert!(!attrs.is_empty()); + let set = attrs.iter().copied().map(A).collect::>(); + set.len() == 1 +} diff --git a/llzk_backend/src/program_ext.rs b/llzk_backend/src/program_ext.rs new file mode 100644 index 000000000..6ed6219de --- /dev/null +++ b/llzk_backend/src/program_ext.rs @@ -0,0 +1,210 @@ +//! Extensions for the [`ProgramArchive`] and [`VCP`] types. + +use crate::function_ext::FunctionLike; +use crate::shared::LlzkCodegen; +use crate::template_ext::SignalDeclarations; +use crate::template_ext::TemplateLike; +use compiler::compiler_interface::VCP; +use program_structure::ast::Expression; +use program_structure::file_definition::FileID; +use program_structure::file_definition::FileLibrary; +use program_structure::file_definition::FileLocation; +use program_structure::program_archive::ProgramArchive; + +/// A dyn-safe trait for obtaining information about the program. +pub trait ProgramInfo { + /// Looks for a template with the given name. + fn find_template(&self, name: &str) -> anyhow::Result<&dyn SignalDeclarations>; +} + +impl ProgramInfo for LlzkCodegen<'_, '_, P> { + fn find_template(&self, name: &str) -> anyhow::Result<&dyn SignalDeclarations> { + self.program + .get_templates(false) + .into_iter() + .find(|t| t.get_name() == name) + .map(|t| -> &dyn SignalDeclarations { t }) + .ok_or_else(|| anyhow::anyhow!("template '{name}' not found")) + } +} + +/// Specification of the main component of a circom program. +#[derive(Debug)] +pub struct MainComponentInfo { + /// Location of the main component declaration. + pub file_location: FileLocation, + /// Name of the main component template. + pub name: String, + /// Parameters of the main component template. + pub params: Vec, +} + +/// A trait that allows common handling of the structs used to represent a circom +/// program at different stages in the compilation process. +pub trait ProgramLike: std::fmt::Debug { + /// Get the file library of the program. + fn get_file_library(&self) -> &FileLibrary; + /// Get the FileID of the file containing the "main" component declaration. + fn get_main_file_id(&self) -> &FileID; + /// Get information specifying the main component. + fn get_main_component_info(&self) -> MainComponentInfo; + /// Get the names of public inputs of the main component. + fn get_main_public_inputs(&self) -> &[String]; + /// Get an iterator over all functions in the program. + fn get_functions(&self, sorted: bool) -> impl IntoIterator; + /// Returns true if the program contains a function with the given name. + fn contains_function(&self, name: &str) -> bool { + self.get_functions(false).into_iter().any(|f| f.get_name() == name) + } + /// Returns the function with the given name. + /// + /// # Panics + /// + /// If the program does not have a function with that name. + fn get_function_data(&self, name: &str) -> &impl FunctionLike { + self.get_functions(false) + .into_iter() + .find(|f| f.get_name() == name) + .unwrap_or_else(|| panic!("Function not found: {}", name)) + } + /// Get an iterator over all templates in the program. + fn get_templates(&self, sorted: bool) -> impl IntoIterator; + /// Returns true if the program contains a template with the given name. + fn contains_template(&self, name: &str) -> bool { + self.get_templates(false).into_iter().any(|t| t.get_name() == name) + } + /// Returns the template with the given name. + /// + /// # Panics + /// + /// If the program does not have a template with that name. + fn get_template_data<'a>(&'a self, name: &str) -> &'a (impl TemplateLike + use<'a, Self>) { + self.get_templates(false) + .into_iter() + .find(|t| t.get_name() == name) + .unwrap_or_else(|| panic!("Template not found: {}", name)) + } +} + +impl ProgramLike for ProgramArchive { + fn get_file_library(&self) -> &FileLibrary { + &self.file_library + } + fn get_main_file_id(&self) -> &FileID { + self.get_file_id_main() + } + fn get_main_component_info(&self) -> MainComponentInfo { + match self.get_main_expression() { + c @ Expression::Call { id, args, .. } => MainComponentInfo { + file_location: c.get_meta().location.clone(), + name: id.clone(), + params: args.clone(), + }, + _ => unreachable!("Main component expression must be `Call`"), + } + } + fn get_main_public_inputs(&self) -> &[String] { + self.get_public_inputs_main_component() + } + fn get_functions(&self, sorted: bool) -> impl IntoIterator { + let mut functions: Vec<_> = self.functions.values().collect(); + if sorted { + sort_functions_by_name(&mut functions); + } + functions + } + fn contains_function(&self, name: &str) -> bool { + self.contains_function(name) + } + fn get_function_data(&self, name: &str) -> &impl FunctionLike { + self.get_function_data(name) + } + fn get_templates(&self, sorted: bool) -> impl IntoIterator { + let mut templates: Vec<_> = self.templates.values().collect(); + if sorted { + sort_templates_by_name(&mut templates); + } + templates + } + fn contains_template(&self, name: &str) -> bool { + self.contains_template(name) + } + fn get_template_data<'a>(&'a self, name: &str) -> &'a (impl TemplateLike + use<'a>) { + self.get_template_data(name) + } +} + +/// Caches info from the [ProgramArchive] that is not present in the [VCP]. +#[derive(Debug)] +pub struct CachedParseInfo { + /// Names of public inputs of the "main" component. + pub public_inputs: Vec, + /// ID of the file containing the "main" component declaration. + pub main_file_id: FileID, + /// Location of the "main" component declaration expression. + pub main_expr_location: FileLocation, +} + +impl From<&ProgramArchive> for CachedParseInfo { + fn from(program: &ProgramArchive) -> Self { + Self { + public_inputs: program.get_public_inputs_main_component().clone(), + main_file_id: *program.get_file_id_main(), + main_expr_location: program.get_main_expression().get_meta().file_location(), + } + } +} + +/// A wrapper around a VCP that also includes the public inputs for the main component. +#[derive(Debug)] +pub struct VCPPlus<'vcp> { + /// Reference to the [VCP]. + pub vcp: &'vcp VCP, + /// Additional information cached from the [ProgramArchive] before it was consumed. + pub parse_info: CachedParseInfo, +} + +impl ProgramLike for VCPPlus<'_> { + fn get_file_library(&self) -> &FileLibrary { + &self.vcp.file_library + } + fn get_main_file_id(&self) -> &FileID { + &self.parse_info.main_file_id + } + fn get_main_component_info(&self) -> MainComponentInfo { + MainComponentInfo { + file_location: self.parse_info.main_expr_location.clone(), + name: self.vcp.templates[self.vcp.main_id].template_header.clone(), + params: vec![], + } + } + fn get_main_public_inputs(&self) -> &[String] { + &self.parse_info.public_inputs + } + fn get_functions(&self, sorted: bool) -> impl IntoIterator { + let mut functions: Vec<_> = self.vcp.functions.iter().collect(); + if sorted { + sort_functions_by_name(&mut functions); + } + functions + } + fn get_templates(&self, sorted: bool) -> impl IntoIterator { + let mut templates: Vec<_> = self.vcp.templates.iter().collect(); + if sorted { + sort_templates_by_name(&mut templates); + } + templates + } +} + +/// Helper function to sort a vector of &FunctionLike by name. +#[inline] +fn sort_functions_by_name(functions: &mut [&F]) { + functions.sort_by(|a, b| a.get_name().cmp(b.get_name())); +} + +/// Helper function to sort a vector of &TemplateLike by name. +#[inline] +fn sort_templates_by_name(templates: &mut [&T]) { + templates.sort_by(|a, b| a.get_name().cmp(b.get_name())); +} diff --git a/llzk_backend/src/shared.rs b/llzk_backend/src/shared.rs new file mode 100644 index 000000000..b110e5814 --- /dev/null +++ b/llzk_backend/src/shared.rs @@ -0,0 +1,2104 @@ +//! Shared code generation utilities. + +use crate::function::InfoProviders; +use crate::gen_context::BlockContextStack; +use crate::gen_context::BlockGenContext; +use crate::gen_context::GenerateLLZKInAnyBlock; +use crate::gen_context::NestedBlockInfo; +use crate::lvalue::Lvalue; +use crate::lvalue::Root; +use crate::module::DeclarationInfo; +use crate::program_ext::ProgramLike; +use crate::subcmp::names::COMP; +use crate::template_ext::TemplateLike; +use crate::template_ext::WireLike as _; +use crate::traversal::walk_from_block; +use crate::traversal::WalkCallbacks; +use ansi_term::Color; +use anyhow::anyhow; +use anyhow::ensure; +use anyhow::Context as _; +use anyhow::Result; +use llzk::builder::OpBuilder; +use llzk::dialect; +use llzk::dialect::array; +use llzk::dialect::array::ArrayCtor; +use llzk::dialect::felt; +use llzk::dialect::pod; +use llzk::dialect::poly; +use llzk::prelude::is_felt_type; +use llzk::prelude::melior_dialects::arith; +use llzk::prelude::melior_dialects::scf; +use llzk::prelude::verify_operation_with_diags; +use llzk::prelude::ArrayType; +use llzk::prelude::Attribute; +use llzk::prelude::AttributeLike as _; +use llzk::prelude::Block; +use llzk::prelude::BlockLike as _; +use llzk::prelude::BlockRef; +use llzk::prelude::BoolAttribute; +use llzk::prelude::FeltConstAttribute; +use llzk::prelude::FeltType; +use llzk::prelude::FlatSymbolRefAttribute; +use llzk::prelude::FuncDefOp; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::FuncDefOpRef; +use llzk::prelude::FuncDefOpRefMut; +use llzk::prelude::IntegerAttribute; +use llzk::prelude::IntegerType; +use llzk::prelude::LlzkContext; +use llzk::prelude::LlzkError; +use llzk::prelude::Location; +use llzk::prelude::Module; +use llzk::prelude::Operation; +use llzk::prelude::OperationLike; +use llzk::prelude::OperationMutLike; +use llzk::prelude::PassManager; +use llzk::prelude::PodRecordAttribute; +use llzk::prelude::PodType; +use llzk::prelude::Region; +use llzk::prelude::RegionLike as _; +use llzk::prelude::StringAttribute; +use llzk::prelude::StructType; +use llzk::prelude::SymbolRefAttribute; +use llzk::prelude::TemplateExprOp; +use llzk::prelude::TemplateExprOpLike; +use llzk::prelude::TemplateOp; +use llzk::prelude::TemplateOpRef; +use llzk::prelude::TemplateOpRefMut; +use llzk::prelude::Type; +use llzk::prelude::TypeLike as _; +use llzk::prelude::Value; +use llzk::prelude::ValueLike; +use llzk::value_ext::replace_all_uses_in_block_with; +use llzk::value_ext::OwningValueRange; +use llzk::value_ext::ValueRange; +use melior::utility; +use num_bigint_dig::BigInt; +use num_bigint_dig::BigUint; +use num_bigint_dig::ModInverse; +use num_bigint_dig::ToBigInt as _; +use num_traits::cast::ToPrimitive; +use num_traits::One; +use num_traits::Zero; +use program_structure::ast::Expression; +use program_structure::ast::ExpressionInfixOpcode; +use program_structure::ast::ExpressionPrefixOpcode; +use program_structure::ast::Meta; +use program_structure::ast::Statement; +use program_structure::ast::VariableType; +use program_structure::error_code::ReportCode; +use program_structure::error_definition::Report; +use program_structure::file_definition::FileID; +use program_structure::file_definition::FileLocation; +use program_structure::wire_data::WireType; +use std::cell::Cell; +use std::cell::RefCell; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::convert::TryInto as _; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::ops::Deref; +use std::os::raw::c_void; +use std::path::Path; + +/// This macro allows writing type switches with a syntax similar to match expressions. +#[macro_export] +macro_rules! type_switch { + // Entry point + { $name:ident = $value:expr, $( $body:tt )+ } => {{ + // Evaluate once + let $name = $value; + + type_switch!(@parse $name, $( $body )+) + }}; + + // Entry point + { $name:ident, $( $body:tt )+ } => { + type_switch!(@parse $name, $( $body )+) + }; + + // Entry point + { let $name:ident = $value:expr ; $( $body:tt )+ } => {{ + // Evaluate once + let $name = $value; + + type_switch!(@parse $name, $( $body )+) + }}; + + // Entry point + { let $name:ident ; $( $body:tt )+ } => { + type_switch!(@parse $name, $( $body )+) + }; + + + // Parsing + + (@parse $name:ident, $ty:ty => $body:block $( $rest:tt )*) => { + type_switch!(@inner $name, ($ty, $name, $body) $( $rest )*) + }; + + (@parse $name:ident, $ty:ty as $bind:ident => $body:block $( $rest:tt )*) => { + type_switch!(@inner $name, ($ty, $bind, $body) $( $rest )*) + }; + + (@parse $name:ident, $ty:ty as => $body:block $( $rest:tt )*) => { + type_switch!(@inner $name, ($ty, _, $body) $( $rest )*) + }; + + (@parse $name:ident, $ty:ty => $body:expr, $( $rest:tt )*) => { + type_switch!(@inner $name, ($ty, $name, {$body}) $( $rest )*) + }; + + (@parse $name:ident, $ty:ty as $bind:ident => $body:expr, $( $rest:tt )*) => { + type_switch!(@inner $name, ($ty, $bind, {$body}) $( $rest )*) + }; + + (@parse $name:ident, $ty:ty as _ => $body:expr, $( $rest:tt )*) => { + type_switch!(@inner $name, ($ty, _, {$body}) $( $rest )*) + }; + + // Inner implementation + + // Last arm without default case + (@inner $name:ident, ( $ty:ty, $bind:ident, $body:block) $(,)?) => { + type_switch!(@inner $name, ( $ty, $bind, $body) else => {panic!("unhandled type {}", $name)}) + }; + (@inner $name:ident, ( $ty:ty, _, $body:block) $(,)?) => { + type_switch!(@inner $name, ( $ty, _, $body) else => {panic!("unhandled type {}", $name)}) + }; + + // Last arm with default case + (@inner $name:ident, ( $ty:ty, $bind:ident, $body:block ) else => $else_body:expr $(,)?) => { + if let Ok($bind) = <$ty>::try_from($name) { + $body + } else { + $else_body + } + }; + + (@inner $name:ident, ( $ty:ty, _, $body:block ) else => $else_body:expr $(,)?) => { + if let Ok(_) = <$ty>::try_from($name) { + $body + } else { + $else_body + } + }; + + // Recursive case + (@inner $name:ident, ( $ty:ty, $bind:ident, $body:block ) $( $rest:tt )+ ) => { + if let Ok($bind) = <$ty>::try_from($name) { + $body + } else { + type_switch!(@parse $name, $( $rest )+) + } + }; + + (@inner $name:ident, ( $ty:ty, _, $body:block ) $( $rest:tt )+ ) => { + if let Ok(_) = <$ty>::try_from($name) { + $body + } else { + type_switch!(@parse $name, $( $rest )+) + } + }; +} + +/// Information about a template's declaration, either full before LLZK IR is generated for the +/// template or, after the template is processed, just the minimal information needed to support +/// queries about input signal types that other templates may need. +#[derive(Debug)] +enum DeclInfo<'ctx> { + /// Complete declaration info computed initially. + Full(DeclarationInfo<'ctx>), + /// Minimal information left behind after generating LLZK for a template. + Remnant { + /// Map of signal name to type for input signals. + inputs: HashMap>, + /// Map of signal name to type for output signals. + outputs: HashMap>, + }, +} + +/// Convert circom location information to MLIR location. +pub fn location<'ctx>( + context: &'ctx LlzkContext, + program: &impl ProgramLike, + file_id: FileID, + file_location: FileLocation, +) -> Location<'ctx> { + let files = program.get_file_library(); + let filename = files.get_filename_or_default(&file_id); + let line = files.get_line(file_location.start, file_id).unwrap_or(0); + let column = files.get_column(file_location.start, file_id).unwrap_or(0); + Location::new(context, &filename, line, column) +} + +/// Configuration for LLZK code generation, derived from user input. +#[derive(Debug)] +pub struct LlzkConfig { + /// Output filename for the generated LLZK IR. + pub filename: String, + /// MLIR pass pipeline to run on the generated module. + pub pass_pipeline: String, + /// Value of the `--prime` flag (e.g. `"bn128"`). + pub prime_str: String, + /// Prime field modulus as an unsigned integer. + pub prime: BigUint, + /// State of the `--verbose` flag. + pub verbose: bool, + /// State of the `--stabilize` flag. + pub stabilize: bool, + /// Emit plaintext (assembly) instead of bytecode. + pub emit_plaintext: bool, +} + +/// Stores necessary context for generating LLZK IR. +/// +/// 'ast: lifetime of the circom AST element +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +#[derive(Debug)] +pub struct LlzkCodegen<'ast, 'ctx, P: ProgramLike> { + /// The circom program AST. + pub program: &'ast P, + /// The LLZK (and MLIR) context. + pub context: &'ctx LlzkContext, + /// The generated LLZK `Module`. + pub module: Module<'ctx>, + /// Code generation configuration. + pub config: LlzkConfig, + /// Declaration info pre-computed for all templates. + template_decls: RefCell>>, + /// Body of the function or template currently being processed. + current_body: Cell>, + /// Current [Statement] (or stack thereof when within an `IfThenElse` or `While`) being visited + /// and/or translated. Used by [`DimExprConverter::gen_template_poly_expr`] to replicate the + /// body into the `poly.expr` initializer up to the current position so all variable + /// assignments that contribute to the target expression will be computed. Raw pointers are + /// used to avoid a lifetime issue from synthetic Statements created on-the-fly and translated. + /// They pointers must only be used for pointer equality comparisons to avoid unsafe behavior. + statement_trace: RefCell>, + /// Operation builder + builder: OpBuilder<'ctx>, +} + +/// RAII guard that pops one entry from the statement trace on drop. +/// +/// Returned by [`LlzkCodegen::trace_statement`] to keep [`LlzkCodegen::statement_trace`] updated. +#[derive(Debug)] +pub struct StatementTraceGuard<'a> { + /// Reference to the statement trace managed by this guard. + trace: &'a RefCell>, +} + +impl Drop for StatementTraceGuard<'_> { + fn drop(&mut self) { + self.trace.borrow_mut().pop(); + } +} + +/// Maps parameter symbols to the attributes assigned to a concrete instances of a template. +#[derive(Debug)] +pub struct TmplParamsInstance<'ast, 'ctx> { + /// Maps a symbol name to an attribute used as template parameter. + map: HashMap<&'ast str, Attribute<'ctx>>, +} + +impl<'ast, 'ctx> TmplParamsInstance<'ast, 'ctx> { + /// Creates a new mapping of template parameter formals to attributes. + pub fn new( + params: impl IntoIterator, + attrs: impl IntoIterator>, + ) -> Self { + Self { map: std::iter::zip(params.into_iter().map(|s| s.as_str()), attrs).collect() } + } + + /// Returns the attribute mapped by the given symbol. + fn get(&self, sym: SymbolRefAttribute<'ctx>) -> Result>> { + Ok(self.map.get(sym.root().as_str()?).copied()) + } + + /// Converts the given attribute if it is a [`SymbolRefAttribute`] and its symbol has a + /// mapping. + /// + /// If the attribute is not of that type returns it as is. + pub fn map_attr(&self, attr: Attribute<'ctx>) -> Result> { + type_switch! { attr, + SymbolRefAttribute => { + self.get(attr)?.ok_or_else(|| anyhow!("symbol {attr} was not found in the mapping")) + } + else => Ok(attr) + } + } + + /// Converts the given type using the mapping, replacing the symbols found in the map with the + /// corresponding attribute. + pub fn map_type(&self, ty: Type<'ctx>) -> Result> { + type_switch! { ty, + ArrayType => self.handle_array_type(ty), + FeltType => self.handle_passthrough(ty), + else => { + todo!("Unhandled type {ty} while mapping through template parameters.") + } + } + } + + /// Handler for array type. + fn handle_array_type(&self, ty: ArrayType<'ctx>) -> Result> { + let dims = + ty.dims().into_iter().map(|attr| self.map_attr(attr)).collect::>>()?; + let inner = self.map_type(ty.element_type())?; + Ok(ArrayType::new(inner, &dims).into()) + } + + /// Handler for mapping types that don't actually require mapping. + fn handle_passthrough(&self, ty: impl Into>) -> Result> { + Ok(ty.into()) + } + + /// Return an interator over references of the key-value pairs. + pub fn iter(&self) -> <&HashMap<&'ast str, Attribute<'ctx>> as IntoIterator>::IntoIter { + self.into_iter() + } +} + +impl<'i, 'ast, 'ctx> IntoIterator for &'i TmplParamsInstance<'ast, 'ctx> { + type Item = <&'i HashMap<&'ast str, Attribute<'ctx>> as IntoIterator>::Item; + + type IntoIter = <&'i HashMap<&'ast str, Attribute<'ctx>> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.map.iter() + } +} + +/// Represents the size of a Type as an expression of its parts. +#[derive(Debug, Clone)] +pub enum TypeSizeExpr<'ctx> { + /// Constant unsigned integer. + Const(usize), + /// Symbol reference. + Sym(SymbolRefAttribute<'ctx>), + /// Addition of two expressions. + Add(Box, Box), + /// Multiplication of two expressions. + Mul(Box, Box), +} + +impl PartialEq for TypeSizeExpr<'_> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (TypeSizeExpr::Const(a), TypeSizeExpr::Const(b)) => a == b, + (TypeSizeExpr::Sym(a), TypeSizeExpr::Sym(b)) => a.to_raw().ptr == b.to_raw().ptr, + (TypeSizeExpr::Add(lhs1, rhs1), TypeSizeExpr::Add(lhs2, rhs2)) => { + lhs1 == lhs2 && rhs1 == rhs2 + } + (TypeSizeExpr::Mul(lhs1, rhs1), TypeSizeExpr::Mul(lhs2, rhs2)) => { + lhs1 == lhs2 && rhs1 == rhs2 + } + _ => false, + } + } +} + +impl<'ctx> TypeSizeExpr<'ctx> { + /// The zero expression. + #[inline] + pub fn zero() -> Self { + TypeSizeExpr::Const(0) + } + /// The one expression. + #[inline] + pub fn one() -> Self { + TypeSizeExpr::Const(1) + } + /// Creates a constant expression. + #[inline] + pub fn const_val(v: usize) -> Self { + TypeSizeExpr::Const(v) + } + /// Creates a symbol reference expression. + #[inline] + pub fn sym(a: SymbolRefAttribute<'ctx>) -> Self { + TypeSizeExpr::Sym(a) + } + /// Creates an addition expression. + #[inline] + pub fn add(self, other: Self) -> Self { + TypeSizeExpr::Add(Box::new(self), Box::new(other)) + } + /// Creates a multiplication expression. + #[inline] + pub fn mul(self, other: Self) -> Self { + TypeSizeExpr::Mul(Box::new(self), Box::new(other)) + } + + /// Returns true if the expression is known to compute to constant zero. + pub fn is_const_zero(&self) -> bool { + match self { + TypeSizeExpr::Const(a) => *a == 0, + TypeSizeExpr::Add(lhs, rhs) => lhs.is_const_zero() && rhs.is_const_zero(), + TypeSizeExpr::Mul(lhs, rhs) => lhs.is_const_zero() || rhs.is_const_zero(), + _ => false, + } + } + + /// Produce a simplified addition expression of the two operands. + fn simplify_add(lhs: &Self, rhs: &Self) -> Self { + match (lhs.simplified(), rhs.simplified()) { + (TypeSizeExpr::Const(0), x) => x.clone(), + (x, TypeSizeExpr::Const(0)) => x.clone(), + (TypeSizeExpr::Const(a), TypeSizeExpr::Const(b)) => { + TypeSizeExpr::Const(a.checked_add(b).expect("type size overflows usize max")) + } + (new_lhs, new_rhs) => { + if new_lhs == *lhs && new_rhs == *rhs { + TypeSizeExpr::Add(Box::new(new_lhs), Box::new(new_rhs)) // unchanged + } else { + Self::simplify_add(&new_lhs, &new_rhs) + } + } + } + } + + /// Produce a simplified multiplication expression of the two operands. + fn simplify_mul(lhs: &Self, rhs: &Self) -> Self { + match (lhs.simplified(), rhs.simplified()) { + (TypeSizeExpr::Const(0), _) => TypeSizeExpr::Const(0), + (_, TypeSizeExpr::Const(0)) => TypeSizeExpr::Const(0), + (TypeSizeExpr::Const(1), x) => x.clone(), + (x, TypeSizeExpr::Const(1)) => x.clone(), + (TypeSizeExpr::Const(a), TypeSizeExpr::Const(b)) => { + TypeSizeExpr::Const(a.checked_mul(b).expect("type size overflows usize max")) + } + (new_lhs, new_rhs) => { + if new_lhs == *lhs && new_rhs == *rhs { + TypeSizeExpr::Mul(Box::new(new_lhs), Box::new(new_rhs)) // unchanged + } else { + Self::simplify_mul(&new_lhs, &new_rhs) + } + } + } + } + + /// Returns a simplified version of the expression by applying arithmetic identities. + pub fn simplified(&self) -> Self { + match self { + s @ TypeSizeExpr::Const(_) => s.clone(), + s @ TypeSizeExpr::Sym(_) => s.clone(), + TypeSizeExpr::Add(lhs, rhs) => Self::simplify_add(lhs, rhs), + TypeSizeExpr::Mul(lhs, rhs) => Self::simplify_mul(lhs, rhs), + } + } + + /// Generate code for the expression as an index value in LLZK IR. + pub fn to_index_value<'ast, 'blk, 'val>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + fc: &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + location: Location<'ctx>, + env: Option<&TmplParamsInstance<'ast, 'ctx>>, + ) -> Result> { + match self.simplified() { + TypeSizeExpr::Const(a) => { + fc.append_op_unnamed_result(codegen.new_index_const_op(i64::try_from(a)?, location)) + } + TypeSizeExpr::Sym(a) => { + if !a.nested().is_empty() { + unreachable!("we don't generate any nested symbols, i.e. reference to globals"); + } + match env { + Some(env) => env + .get(a) + .and_then(|attr| { + attr.ok_or_else(|| { + anyhow!( + "Symbol ref {a} was not found in the list of template parameters" + ) + }) + }) + .and_then(Self::try_from_attr) + .and_then(|e| + // The call to this new expression should happen in the context of + // the caller since we mapped the formal to its parameter. + e.to_index_value(codegen, fc, location, None)), + None => { + let v = fc.block_ctx.get_named_value(a.root().as_str()?)?; + fc.cast_to_index_if_needed(location, *v) + } + } + } + TypeSizeExpr::Add(lhs, rhs) => { + let lhs = lhs.to_index_value(codegen, fc, location, env)?; + let rhs = rhs.to_index_value(codegen, fc, location, env)?; + fc.append_op_unnamed_result(arith::addi(lhs, rhs, location)) + } + TypeSizeExpr::Mul(lhs, rhs) => { + let lhs = lhs.to_index_value(codegen, fc, location, env)?; + let rhs = rhs.to_index_value(codegen, fc, location, env)?; + fc.append_op_unnamed_result(arith::muli(lhs, rhs, location)) + } + } + } + + /// Creates a new expression based on an attribute. + fn try_from_attr(attr: Attribute<'ctx>) -> Result { + type_switch! { attr, + IntegerAttribute => { + let value = attr.value(); + if value < 0 { + anyhow::bail!("Negative value {value} in attribute: {attr}"); + } + Ok(Self::const_val(value.try_into()?)) + } + SymbolRefAttribute => Ok(Self::Sym(attr)), + else => anyhow::bail!("Unsupported attribute: {attr}") + } + } +} + +impl<'ast, 'ctx, P: ProgramLike> LlzkCodegen<'ast, 'ctx, P> { + /// Construct. + pub fn new( + program: &'ast P, + context: &'ctx LlzkContext, + module: Module<'ctx>, + config: LlzkConfig, + ) -> Self { + LlzkCodegen { + program, + context, + module, + config, + template_decls: RefCell::new(Default::default()), + current_body: Cell::new(None), + statement_trace: RefCell::new(Vec::new()), + builder: OpBuilder::new(context), + } + } + + /// Dump the current state of the LLZK module for debugging purposes. + #[allow(unused)] + pub fn dump_module(&self) { + println!("[LlzkCodegen::dump_module] {:?}", self.module.as_operation()); + } + + /// Returns a reference to the operation builder. + pub fn op_builder(&self) -> &OpBuilder<'ctx> { + &self.builder + } + + /// Set the body of the function or template currently being processed. + pub fn set_current_body(&self, body: &'ast [Statement]) { + self.current_body.set(Some(body)); + } + + /// Get the body of the function or template currently being processed. + pub fn current_body(&self) -> Option<&'ast [Statement]> { + self.current_body.get() + } + + /// Push `stmt` onto the statement trace and return a guard that pops it on drop. + /// + /// Call this at the top of every `gen_llzk_in_template`, `gen_llzk_in_function`, etc. + /// that traverses all body [Statement] and may end up calling the [`DimExprConverter`] so + /// that its [`DimExprConverter::gen_template_poly_expr`] can observe the exact path of + /// statements surrounding the target expression. + pub fn trace_statement<'a>(&'a self, stmt: &Statement) -> StatementTraceGuard<'a> { + self.statement_trace.borrow_mut().push(stmt as *const Statement); + StatementTraceGuard { trace: &self.statement_trace } + } + + /// Snapshot the current statement trace (outermost statement first) as raw pointers. + /// + /// The pointers are valid for the duration of the AST (`'ast` lifetime of the codegen). + /// Use pointer equality (`std::ptr::eq`) when comparing against slice elements. + pub fn snapshot_statement_trace(&self) -> Vec<*const Statement> { + self.statement_trace.borrow().clone() + } + + /// Store the full [DeclarationInfo] for the template with the given name. + pub fn put_template_decl(&self, name: &str, decl_info: DeclarationInfo<'ctx>) { + self.template_decls.borrow_mut().insert(name.to_string(), DeclInfo::Full(decl_info)); + } + + /// Remove and return the full [DeclarationInfo] for the template with the given name and leave + /// behind just the mapping of signal names to types. + pub fn take_template_decl(&self, name: &str) -> Result> { + let mut borrow = self.template_decls.borrow_mut(); + if let Some((name, DeclInfo::Full(decl_info))) = borrow.remove_entry(name) { + let inputs = decl_info.build_input_name_to_type_map(); + let outputs = decl_info.build_output_name_to_type_map(); + borrow.insert(name, DeclInfo::Remnant { inputs, outputs }); + return Ok(decl_info); + } + Err(anyhow!("No full declaration info for {name}")) + } + + /// Get the type of the input signal with the given name in the given template, if it exists. + pub fn get_input_signal_type( + &self, + template_name: &str, + signal_name: &str, + ) -> Result> { + let borrow = self.template_decls.borrow(); + match borrow.get(template_name) { + None => anyhow::bail!("No declaration info for {template_name}"), + Some(DeclInfo::Full(info)) => info.get_input_type(signal_name), + Some(DeclInfo::Remnant { inputs, .. }) => inputs.get(signal_name).copied(), + } + .ok_or_else(|| anyhow!("No input signal with name {signal_name}")) + } + + /// Get the type of an output signal with the given name in the given template, if it exists. + pub fn get_output_signal_type( + &self, + template_name: &str, + signal_name: &str, + ) -> Result> { + let borrow = self.template_decls.borrow(); + match borrow.get(template_name) { + None => anyhow::bail!("No declaration info for {template_name}"), + Some(DeclInfo::Full(info)) => info.get_output_type(signal_name), + Some(DeclInfo::Remnant { outputs, .. }) => outputs.get(signal_name).copied(), + } + .ok_or_else(|| anyhow!("No output signal with name {signal_name}")) + } + + /// Get the width of the scalar prime field in bits. + pub fn prime_field_bits(&self) -> usize { + self.prime().bits() + } + + /// Get the prime field modulus as a BigUint + pub fn prime(&self) -> &BigUint { + &self.config.prime + } + + /// Emit a circom-style warning. + pub fn emit_circom_warning(&self, meta: &Meta, message: &str, code: ReportCode) { + let mut report = Report::warning(String::from(message), code); + report.add_primary(meta.file_location(), meta.get_file_id(), String::from("here")); + Report::print_reports(&[report], self.program.get_file_library()); + } + + /// Emit a circom-style error. + pub fn emit_circom_error(&self, meta: &Meta, message: &str, code: ReportCode) { + let mut report = Report::error(String::from(message), code); + report.add_primary(meta.file_location(), meta.get_file_id(), String::from("here")); + Report::print_reports(&[report], self.program.get_file_library()); + } + + /// Get the unknown location. + #[inline] + pub fn location_unknown(&self) -> Location<'ctx> { + Location::unknown(self.context) + } + + /// Convert circom location information to MLIR location. + #[inline] + pub fn location(&self, file_id: FileID, file_location: FileLocation) -> Location<'ctx> { + location(self.context, self.program, file_id, file_location) + } + + /// Convert circom Meta location information to MLIR location. + pub fn location_from_meta(&self, meta: &Meta) -> Location<'ctx> { + if let Some(file) = meta.file_id { + self.location(file, meta.file_location()) + } else { + self.location_unknown() + } + } + + /// Insert the LLZK template into the module and return a reference to it. + pub fn add_template(&self, t: TemplateOp<'ctx>) -> Result> { + let t: TemplateOpRef = self.module.body().append_operation(t.into()).try_into()?; + Ok(t.into()) + } + + /// Insert the LLZK free function into the module and return a reference to it. + pub fn add_function(&self, f: FuncDefOp<'ctx>) -> Result> { + let f: FuncDefOpRef = self.module.body().append_operation(f.into()).try_into()?; + Ok(f.into()) + } + + /// If `dimensions` is empty, return `base_type`. Otherwise, create [ArrayType] by + /// converting the dimension sizes to LLZK Attributes. + pub fn type_from_dimension_consts( + &self, + base_type: Type<'ctx>, + dimensions: &[usize], + ) -> Result> { + if dimensions.is_empty() { + Ok(base_type) + } else { + dimensions + .iter() + .map(|c| { + i64::try_from(*c) + .map_err(Into::into) + .map(|c| Attribute::from(self.index_attr(c))) + }) + .collect::>>() + .map(|dims| ArrayType::new(base_type, &dims).into()) + } + } + + /// Create an LLZK operation that produces a nondeterministic value of the given type. + pub fn new_nondet_at_location( + &self, + location: Location<'ctx>, + result_type: Type<'ctx>, + ) -> Result> { + Ok(dialect::llzk::nondet(location, result_type)) + } + + /// Get the integer type of the given bitwidth. + #[inline] + pub fn int_type(&self, bits: u32) -> IntegerType<'ctx> { + IntegerType::new(self.context, bits) + } + + /// Get the boolean type (`i1`). + #[inline] + pub fn bool_type(&self) -> IntegerType<'ctx> { + self.int_type(1) + } + + /// Get the index type. + #[inline] + pub fn index_type(&self) -> Type<'ctx> { + Type::index(self.context) + } + + /// Get the felt type. + #[inline] + pub fn felt_type(&self) -> FeltType<'ctx> { + FeltType::with_field(self.context, &self.config.prime_str) + } + + /// Get the struct type for the given struct name and parameters. + #[inline] + pub fn struct_type_with_params( + &self, + name: &str, + params: &[Attribute<'ctx>], + ) -> StructType<'ctx> { + // When creating a StructType directly, use the same name for both the template and + // the struct itself to match LLZK code generated by `gen_template_llzk()`. + StructType::new(SymbolRefAttribute::new_from_str(self.context, name, &[name]), params) + } + + /// Get the struct type for the given struct name. + #[inline] + pub fn struct_type(&self, name: &str) -> StructType<'ctx> { + self.struct_type_with_params(name, &[]) + } + + /// Get a pod struct type with the given records. + #[inline] + pub fn pod_type(&self, records: &[(&str, Type<'ctx>)]) -> PodType<'ctx> { + let records = records + .iter() + .map(|(name, r#type)| PodRecordAttribute::new(name, *r#type)) + .collect::>(); + PodType::new(self.context, &records) + } + + /// Create an index attribute. + #[inline] + pub fn index_attr(&self, integer: T) -> IntegerAttribute<'ctx> + where + T: Into, + { + IntegerAttribute::new(self.index_type(), integer.into()) + } + + /// Create an affine_map attribute from a string definition. + pub fn affine_map_attr(&self, definition: &str) -> Result> { + Attribute::parse(self.context, definition) + .ok_or_else(|| anyhow!("could not parse affine_map definition")) + } + + /// Create an identity affine_map attribute. + #[inline] + pub fn identity_affine_map_attr(&self) -> Result> { + self.affine_map_attr("affine_map<()[i] -> (i)>") + } + + /// Creates a [`FlatSymbolRefAttribute`] from the given string. + #[inline] + pub fn flat_sym(&self, sym: impl AsRef) -> FlatSymbolRefAttribute<'ctx> { + FlatSymbolRefAttribute::new(self.context, sym.as_ref()) + } + + /// Creates a `pod.read` operation. + /// + /// Fails if the type of the value is not [`PodType`] or if the given name does not correspond + /// with a record in the pod. + pub fn new_pod_read_op( + &self, + pod: Value<'ctx, '_>, + name: impl AsRef, + location: Location<'ctx>, + ) -> Result> { + let name = name.as_ref(); + let pod_type = PodType::try_from(pod.r#type())?; + let record_type = pod_type + .get_type_of_record(name) + .ok_or_else(|| anyhow!("record '{}' not found for pod {pod_type}", name))?; + Ok(pod::read(location, pod, self.flat_sym(name), record_type)) + } + + /// Creates a `pod.write` operation. + #[inline] + pub fn new_pod_write_op( + &self, + location: Location<'ctx>, + pod: Value<'ctx, '_>, + name: impl AsRef, + src: Value<'ctx, '_>, + ) -> Operation<'ctx> { + pod::write(location, pod, self.flat_sym(name), src) + } + + /// Create an LLZK operation that produces a boolean constant value. + #[inline] + pub fn new_bool_const_op(&self, val: bool, location: Location<'ctx>) -> Operation<'ctx> { + arith::constant(self.context, BoolAttribute::new(self.context, val).into(), location) + } + + /// Create an LLZK operation that produces an integer constant value. + #[inline] + pub fn new_int_const_op( + &self, + ty: Type<'ctx>, + val: i64, + location: Location<'ctx>, + ) -> Operation<'ctx> { + arith::constant(self.context, IntegerAttribute::new(ty, val).into(), location) + } + + /// Create an LLZK operation that produces an index constant value. + #[inline] + pub fn new_index_const_op(&self, val: T, location: Location<'ctx>) -> Operation<'ctx> + where + T: Into, + { + arith::constant(self.context, self.index_attr(val).into(), location) + } + + /// Create an LLZK `array.new` operation with the given element type and dimensions. + #[inline] + pub fn new_array_new_op( + &self, + location: Location<'ctx>, + r#type: ArrayType<'ctx>, + ctor: ArrayCtor<'ctx, '_, '_, '_>, + ) -> Operation<'ctx> { + array::new(&self.builder, location, r#type, ctor) + } + + /// Generate a `felt.const` operation from a BigInt. Returns an `Err` result if unsuccessful + /// or if the number of bits required to represent the BigInt does not fit in 32 bits. + pub fn new_felt_const_op( + &self, + val: &BigInt, + location: Location<'ctx>, + ) -> Result> { + // ASSERT: The circom parser always produces non-negative constants. These can be negated + // via PrefixOp but negative BigInt constants are never created directly. + assert_ne!(val.sign(), num_bigint_dig::Sign::Minus, "Felt constants must be non-negative"); + let attr = FeltConstAttribute::parse( + self.context, + // use required bits +1 to ensure unsigned representation + u32::try_from(val.bits())? + 1, + val.to_string().as_str(), + Some(&self.config.prime_str), + ); + felt::constant(location, attr).map_err(Into::into) + } + + /// Creates a new constant op to create a constant of the given type. + /// Assumes `type` will be a felt or integral/index type. + pub fn new_const_op( + &self, + location: Location<'ctx>, + r#type: Type<'ctx>, + val: i64, + ) -> Result> { + if is_felt_type(r#type) { + self.new_felt_const_op(&BigInt::from(val), location) + } else { + Ok(self.new_int_const_op(r#type, val, location)) + } + } + + /// Run the given pass pipeline on the given operation. + pub fn run_pass_pipeline_on<'c: 'a, 'a>( + &self, + pipeline: &str, + op: &impl OperationLike<'c, 'a>, + ) -> Result<()> { + if pipeline.is_empty() { + return Ok(()); + } + + let manager = PassManager::new(self.context); + // Disable verifier in case the given op is orphaned (i.e. not yet attached to a parent). + manager.enable_verifier(false); + // Print current IR on failure to make debugging easier. + enable_ir_printing( + &manager, + false, + false, + false, + false, + true, + Default::default(), + Default::default(), + ); + + // Setup and run the pass pipeline. + utility::register_all_passes(); + utility::parse_pass_pipeline(manager.as_operation_pass_manager(), pipeline) + .map_err(anyhow::Error::from)?; + + let mlir_result = + unsafe { mlir_sys::mlirPassManagerRunOnOp(manager.to_raw(), op.to_raw()) }; + ensure!(mlir_result.value != 0, "failed to run pass pipeline: {pipeline}"); + + Ok(()) + } + + /// Run the pass pipeline from the command line on the generated `Module`. + pub fn run_user_pass_pipeline(&mut self) -> Result<()> { + if self.config.pass_pipeline.is_empty() { + return Ok(()); + } + let manager = PassManager::new(self.context); + manager.enable_verifier(true); + utility::register_all_passes(); + utility::parse_pass_pipeline( + manager.as_operation_pass_manager(), + &self.config.pass_pipeline, + ) + .map_err(anyhow::Error::from)?; + manager.run(&mut self.module).map_err(Into::into) + } + + /// Verify the generated `Module`. + #[inline] + pub fn verify(&self) -> Result<(), LlzkError> { + verify_operation_with_diags(&self.module.as_operation()) + } + + /// Create file at the given path, ensuring parent directories exist. + fn create_file(filename: &str) -> Result { + let out_path = Path::new(filename); + // Ensure parent directories exist + if let Some(parent) = out_path.parent() { + fs::create_dir_all(parent).map_err(anyhow::Error::from)?; + } + File::create(out_path).map_err(Into::into) + } + + /// Write the generated `Module` to a file in LLZK IR assembly format. + pub fn write_assembly_to_file(self) -> Result<()> { + let filename = self.config.filename.as_str(); + let mut file = Self::create_file(filename)?; + write!(file, "{}", self.module.as_operation())?; + println!("{} {}", Color::Green.paint("Written successfully:"), filename); + Ok(()) + } + + /// Write the generated `Module` to a file in bytecode format. + pub fn write_bytecode_to_file(self) -> Result<()> { + unsafe extern "C" fn callback(string_ref: mlir_sys::MlirStringRef, user_data: *mut c_void) { + let file = &mut *(user_data as *mut File); + let slice = std::slice::from_raw_parts(string_ref.data as *const u8, string_ref.length); + file.write_all(slice).unwrap(); + } + + let filename = self.config.filename.as_str(); + let mut file = Self::create_file(filename)?; + unsafe { + mlir_sys::mlirOperationWriteBytecode( + self.module.as_operation().to_raw(), + Some(callback), + &mut file as *mut File as *mut c_void, + ); + } + println!("{} {}", Color::Green.paint("Written successfully:"), filename); + Ok(()) + } + + /// Returns the data for the template with that name. + pub fn find_template_data( + &self, + name: &str, + ) -> Option<&'ast (impl TemplateLike + use<'ast, P>)> { + if self.program.contains_template(name) { + Some(self.program.get_template_data(name)) + } else { + None + } + } + + /// Returns the types of the inputs for the given template, in declaration order. + pub fn get_template_input_types(&self, name: &str) -> Result>> { + let data = + self.find_template_data(name).ok_or_else(|| anyhow!("Template {name} not found"))?; + Ok(data + .get_declaration_inputs() + .iter() + .map(move |(input, _)| -> Type<'ctx> { + let wire = data + .get_input_info(input) + // The name comes from `get_declaration_inputs`. + // If `get_input_info` fails here with that name, + // something has gone very wrong. + .unwrap_or_else(|| panic!("Input {:?} not found for type {:?}", input, name)); + match wire.get_type() { + WireType::Signal => self.felt_type().into(), + WireType::Bus(name) => self.struct_type(&name).into(), + } + }) + .collect()) + } + + /// Compute the size of the type in signals. A scalar signal has size 1, an array of 2 signals + /// has size 2, a 2x3 matrix of signals has size 6, and so on. Structs have a size equal to the + /// sum of its input sizes. + pub fn count_input_signals(&self, t: Type<'ctx>) -> Result> { + if is_felt_type(t) { + Ok(TypeSizeExpr::one()) + } else if let Ok(at) = ArrayType::try_from(t) { + let init = self.count_input_signals(at.element_type())?; + at.dims().iter().try_fold(init, |acc, d| { + if let Ok(a) = IntegerAttribute::try_from(*d) { + let s = usize::try_from(a.value()).context("negative array size")?; + Ok(acc.mul(TypeSizeExpr::const_val(s))) + } else if let Ok(a) = SymbolRefAttribute::try_from(*d) { + Ok(acc.mul(TypeSizeExpr::sym(a))) + } else { + Err(anyhow!("expected array dimension to be Int or SymRef but found: {d}")) + } + }) + } else if let Ok(pt) = PodType::try_from(t) { + pt.get_records().iter().try_fold(TypeSizeExpr::zero(), |acc, r| { + Ok(acc.add(self.count_input_signals(r.r#type())?)) + }) + } else if let Ok(st) = StructType::try_from(t) { + self.get_template_input_types(get_name_tail(&st)?)? + .iter() + .try_fold(TypeSizeExpr::zero(), |acc, t| Ok(acc.add(self.count_input_signals(*t)?))) + } else { + anyhow::bail!("unexpected type while counting signals: {t}"); + } + } + + /// Convert a `Vec` into a comma-separated [StringAttribute]. + #[inline] + pub fn list_to_attribute(&self, names: &[String]) -> Attribute<'ctx> { + StringAttribute::new(self.context, &names.join(",")).into() + } + + /// Convert a [StringAttribute] containing a comma-separated list into a `Vec`. + #[inline] + pub fn attribute_to_list( + &self, + attr: Attribute<'ctx>, + ) -> Result> { + Ok(StringAttribute::try_from(attr)?.value().split(',')) + } +} + +/// Get the StructDefOp name from the given StructType. +#[inline] +pub fn get_name_tail<'c>(ty: &StructType<'c>) -> Result<&'c str> { + ty.name().leaf().as_str().map_err(Into::into) +} + +/// Return a new SymbolRefAttribute like the given one with another path element appended. +#[inline] +pub fn append_tail<'c>(base: &SymbolRefAttribute<'c>, append: &'c str) -> SymbolRefAttribute<'c> { + let mut tail = base.nested(); + tail.push(FlatSymbolRefAttribute::new(unsafe { base.context().to_ref() }, append)); + SymbolRefAttribute::new(unsafe { base.context().to_ref() }, base.root(), &tail) +} + +/// Try to statically compute the value of a circom [Expression] used as an array dimension. +/// +/// This is computed in a separate function so that nested expressions return BigUint results +/// instead of IntegerAttributes. +/// +/// Returns `Ok(None)` if the expression cannot be computed statically, `Ok(Some(BigUint))` +/// if the computation is successful, or `Err` if a conversion error occurs along the way. +pub fn try_compute_biguint(expr: &Expression, prime: &BigUint) -> Result> { + match expr { + Expression::Number(_, big_int) => { + let v = + big_int.to_biguint().ok_or_else(|| anyhow!("could not convert to signed"))? % prime; + Ok(Some(v)) + } + Expression::InfixOp { lhe, infix_op, rhe, .. } => { + let lhs = try_compute_biguint(lhe, prime)?; + let rhs = try_compute_biguint(rhe, prime)?; + match (lhs, rhs) { + (Some(lhs), Some(rhs)) => { + // Perform the arithmetic + let bool_to_biguint = |b| if b { BigUint::one() } else { BigUint::zero() }; + let res = match infix_op { + ExpressionInfixOpcode::Mul => (lhs * rhs) % prime, + ExpressionInfixOpcode::Div => { + let rhs_inv = rhs + .mod_inverse(prime) + .ok_or_else(|| anyhow!("failed to compute inverse"))? + .to_biguint() + .ok_or_else(|| anyhow!("could not convert to BigUint"))?; + (lhs * rhs_inv) % prime + } + ExpressionInfixOpcode::Add => (lhs + rhs) % prime, + ExpressionInfixOpcode::Sub => (lhs + (prime - rhs)) % prime, + ExpressionInfixOpcode::Pow => lhs.modpow(&rhs, prime), + ExpressionInfixOpcode::IntDiv => (lhs / rhs) % prime, + ExpressionInfixOpcode::Mod => lhs % rhs, + ExpressionInfixOpcode::ShiftL => { + (lhs << rhs + .to_usize() + .ok_or_else(|| anyhow!("could not convert to usize"))?) + % prime + } + ExpressionInfixOpcode::ShiftR => { + (lhs >> rhs + .to_usize() + .ok_or_else(|| anyhow!("could not convert to usize"))?) + % prime + } + // Comparison operators are performed based on a signed interpretation + // of the field elements as defined by the `relational_val` function, + // according to the circom spec. + ExpressionInfixOpcode::LesserEq => { + let res = relational_val(&lhs, prime)? <= relational_val(&rhs, prime)?; + bool_to_biguint(res) + } + ExpressionInfixOpcode::GreaterEq => { + let res = relational_val(&lhs, prime)? >= relational_val(&rhs, prime)?; + bool_to_biguint(res) + } + ExpressionInfixOpcode::Lesser => { + let res = relational_val(&lhs, prime)? < relational_val(&rhs, prime)?; + bool_to_biguint(res) + } + ExpressionInfixOpcode::Greater => { + let res = relational_val(&lhs, prime)? > relational_val(&rhs, prime)?; + bool_to_biguint(res) + } + ExpressionInfixOpcode::Eq => bool_to_biguint(lhs == rhs), + ExpressionInfixOpcode::NotEq => bool_to_biguint(lhs != rhs), + ExpressionInfixOpcode::BoolOr => { + bool_to_biguint(!lhs.is_zero() || !rhs.is_zero()) + } + ExpressionInfixOpcode::BoolAnd => { + bool_to_biguint(!lhs.is_zero() && !rhs.is_zero()) + } + ExpressionInfixOpcode::BitOr => (lhs | rhs) % prime, + ExpressionInfixOpcode::BitAnd => lhs & rhs, + ExpressionInfixOpcode::BitXor => (lhs ^ rhs) % prime, + }; + Ok(Some(res)) + } + _ => Ok(None), + } + } + Expression::PrefixOp { prefix_op, rhe, .. } => { + let rhs = try_compute_biguint(rhe, prime)?; + match rhs { + Some(rhs) => { + // Perform the arithmetic + let res = match prefix_op { + ExpressionPrefixOpcode::Sub => { + if rhs.is_zero() { + rhs + } else { + prime - rhs + } + } + ExpressionPrefixOpcode::BoolNot => { + if rhs.is_zero() { + BigUint::one() + } else { + BigUint::zero() + } + } + ExpressionPrefixOpcode::Complement => { + let mask = (BigUint::one() << prime.bits()) - BigUint::one(); + mask ^ rhs + } + }; + Ok(Some(res)) + } + _ => Ok(None), + } + } + Expression::InlineSwitchOp { cond, if_true, if_false, .. } => { + let cond = try_compute_biguint(cond, prime)?; + let if_true = try_compute_biguint(if_true, prime)?; + let if_false = try_compute_biguint(if_false, prime)?; + match (cond, if_true, if_false) { + (Some(cond), Some(if_true), Some(if_false)) => { + Ok(Some(if !cond.is_zero() { if_true } else { if_false })) + } + _ => Ok(None), + } + } + _ => Ok(None), + } +} + +/// Try to statically compute the value of a circom [Expression] used as an array dimension. +/// +/// Returns `Ok(None)` if the expression cannot be computed statically as `i64`, `Ok(Some(i64))` +/// if the computation is successful, or `Err` if a conversion error occurs along the way. +pub fn try_compute_as_i64(expr: &Expression, prime: &BigUint) -> Result> { + try_compute_biguint(expr, prime).map(|b| b.as_ref().and_then(BigUint::to_i64)) +} + +/// Extract the single result Value from an OperationRef. Returns an `Err` result if the operation +/// does not have exactly one result. +/// +/// 'c: lifetime of the `LlzkContext` and generated `Module` +/// 'a: lifetime of the generated `Value` or `Operation` instances within blocks +#[inline] +pub fn single_result_as_value<'c: 'a, 'a>(op: impl OperationLike<'c, 'a>) -> Result> { + if op.result_count() != 1 { + return Err(anyhow!( + "Expected operation to have a single result, found {}", + op.result_count() + )); + } + op.result(0).map(Value::from).map_err(Into::into) +} + +/// Ensures the given OperationRef has 0 result Values, else returns an `Err` result. +/// +/// 'c: lifetime of the `LlzkContext` and generated `Module` +/// 'a: lifetime of the generated `Value` or `Operation` instances within blocks +#[inline] +pub fn no_results<'c: 'a, 'a>(op: impl OperationLike<'c, 'a>) -> Result<()> { + if op.result_count() != 0 { + return Err(anyhow!("Expected operation to have no results, found {}", op.result_count())); + } + Ok(()) +} + +/// Create a map of circom variable names (either function arguments or template input signals) to +/// LLZK function argument Values. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +#[inline] +pub fn map_name_to_arg_value<'ctx, 'val>( + func: FuncDefOpRefMut<'ctx, 'val>, + arg_names: Vec, +) -> Result>> { + arg_names + .into_iter() + .enumerate() + .map(|(i, name)| { + func.deref().argument(i).map_err(Into::into).map(|a| (name, Value::from(a))) + }) + .collect() +} + +/// Return `true` iff the given Type is an `IndexType`. +#[inline] +pub fn is_index(t: Type) -> bool { + t.is_index() +} + +/// Return `true` iff the given Type is a boolean type, i.e. `i1`. +#[inline] +pub fn is_bool(t: Type) -> bool { + t.is_integer() && IntegerType::try_from(t).is_ok_and(|it| it.width() == 1) +} + +/// Add a new argument to the given [BlockRef] with the same type as `orig` and replace all uses of +/// `orig` within the given [BlockRef] (and within any nested blocks) with the new block argument. +pub fn replace_uses_with_new_block_argument<'ctx, 'val>( + block: BlockRef<'ctx, 'val>, + orig: &Value<'ctx, 'val>, + location: Location<'ctx>, +) -> Value<'ctx, 'val> { + let replacement = block.add_argument(orig.r#type(), location); + walk_from_block( + block, + WalkCallbacks::for_blocks(|b| replace_all_uses_in_block_with(b, *orig, replacement)), + ); + replacement +} + +/// Create new array type that is an array of the given sub-array type. +#[inline] +pub fn new_array_type<'c>(dim: Attribute<'c>, subarr_ty: &ArrayType<'c>) -> ArrayType<'c> { + let dims: Vec<_> = std::iter::once(dim).chain(subarr_ty.dims()).collect(); + ArrayType::new(subarr_ty.element_type(), &dims) +} + +/// Convert unsigned field elements into relational values used for comparisons. +/// +/// relational_val(a) = a-p if m/2 +1 <= a < m +/// relational_val(a) = a, otherwise. +/// +/// see https://docs.circom.io/circom-language/basic-operators/#relational-operators +/// for definition. +pub fn relational_val(a: &BigUint, p: &BigUint) -> Result { + let a = (a % p) + .to_bigint() + .ok_or_else(|| anyhow!("could not convert field element to signed int"))?; + let p = + p.to_bigint().ok_or_else(|| anyhow!("could not convert field modulus to signed int"))?; + let val = if ((&p / 2) + 1) <= a { a - p } else { a }; + Ok(val) +} + +/// Heuristically detect a circom `for` loop (represented by a `Statement::Block` containing a +/// `Statement::InitializationBlock` followed by a `Statement::While`). Since all values in circom +/// are of type `felt`, we cannot (easily) generate `scf.for` which requires index-typed loop +/// variables so this macro generates code for the `Statement::InitializationBlock` followed by an +/// `scf.while` for the loop (and then returns "Ok" so that the caller does not fall through to the +/// normal code generation for a Block). Additionally, if the loop bounds and step can be computed +/// as compile-time constants, then create an LLZK `loopbounds` attribute to attach to the generated +/// `scf.while` loop. +#[macro_export] +macro_rules! try_for_loop_heuristic { + ($codegen:expr, $gen_context:expr, $meta:expr, $stmts:expr $(, $info:expr)? $(,)?) => { + if let [program_structure::ast::Statement::InitializationBlock { + xtype: program_structure::ast::VariableType::Var, + initializations, + .. + }, program_structure::ast::Statement::While { cond, stmt, .. }] = $stmts.as_slice() + { + // TODO: Analyze `initializations` and `While` loop contents to determine if loop bounds + // and step can be computed. The loop `cond` is probably the starting point to find the + // loop iteration variable and then `initializations` has the start value and the loop + // body `stmt` has the step. + // TODO: Once this is implemented, find "for" loops in all `.circom` test files to add + // the `loopbounds` attribute to relevant loops (because existing tests will likely not + // fail when this is added since the lit checks only do line prefix by default). + let loop_bounds = None; + + gen_init_block($codegen, $gen_context, $($info,)* initializations)?; + return gen_while($codegen, $gen_context, $($info,)* $meta, cond, stmt, loop_bounds); + } + }; +} + +/// Returns a reference to a parent operation. +/// +/// This function provides an API that is added in a newer release of melior via +/// [mlir-sys/melior#789](https://github.com/mlir-rs/melior/pull/789). +pub fn parent_operation_mut<'c: 'a, 'a>( + op: &impl OperationLike<'c, 'a>, +) -> Option> { + unsafe { + melior::ir::operation::OperationRefMut::from_option_raw( + mlir_sys::mlirOperationGetParentOperation(op.to_raw()), + ) + } +} + +/// Returns a mutable reference to the next operation in the same block. +/// +/// This function provides an API fix that is added in a newer release of melior via +/// [mlir-sys/melior#790](https://github.com/mlir-rs/melior/pull/790). +pub fn next_in_block_mut<'c: 'a, 'a>( + op: &impl melior::ir::operation::OperationLike<'c, 'a>, +) -> Option> { + unsafe { + melior::ir::operation::OperationRefMut::from_option_raw( + mlir_sys::mlirOperationGetNextInBlock(op.to_raw()), + ) + } +} + +/// Set IR printing options on the given [PassManager]. +/// +/// This function provides an API fix that is added in a newer release of melior via +/// [mlir-sys/melior#802](https://github.com/mlir-rs/melior/pull/802). +#[allow(clippy::too_many_arguments)] +pub fn enable_ir_printing( + _self: &melior::pass::PassManager, + before_all: bool, + after_all: bool, + module_scope: bool, + on_change: bool, + on_failure: bool, + flags: melior::ir::operation::OperationPrintingFlags, + tree_printing_path: std::path::PathBuf, +) { + unsafe { + mlir_sys::mlirPassManagerEnableIRPrinting( + _self.to_raw(), + before_all, + after_all, + module_scope, + on_change, + on_failure, + flags.to_raw(), + melior::StringRef::new(&tree_printing_path.display().to_string()).to_raw(), + ) + } +} + +/// Removes itself from a parent block and returns the owned [Operation]. +pub fn remove_from_parent<'c: 'a, 'a>(op: &mut impl OperationMutLike<'c, 'a>) -> Operation<'c> { + unsafe { + mlir_sys::mlirOperationRemoveFromParent(op.to_raw()); + } + unsafe { Operation::from_raw(op.to_raw()) } +} + +/// Information needed to create a new LLZK array type with the given dimension +/// and to instantiate that array if the dimension attribute is an affine_map +/// with symbols. +#[derive(Debug, Clone)] +pub struct ArrayDimension<'ctx, 'val> { + /// The attribute to use as the dimension; could be a constant, a symbol, or an affine map. + attr: Attribute<'ctx>, + /// The symbols to be passed to the affine map, if attr is an AffineMapAttr + symbols: Option>, +} + +impl<'ctx, 'val> ArrayDimension<'ctx, 'val> { + /// Construct a new ArrayDimension. + /// If attr is not an affine map, then symbol_vals should be empty. + pub fn new(attr: Attribute<'ctx>, symbol_vals: &[Value<'ctx, 'val>]) -> Result { + ensure!( + attr.is_affine_map() || symbol_vals.is_empty(), + "if attribute is not an affine map, no symbols should be provided" + ); + Ok(Self { + attr, + symbols: (!symbol_vals.is_empty()).then(|| OwningValueRange::from(symbol_vals)), + }) + } + /// Access the inner attribute. + pub fn attr(&self) -> &Attribute<'ctx> { + &self.attr + } + /// Access the inner symbols, if present, as a [ValueRange]. + pub fn value_range(&self) -> Result>> { + let range = match &self.symbols { + None => None, + Some(s) => Some(ValueRange::try_from(s)?), + }; + Ok(range) + } + /// Create a new [ArrayType] with the given dimension. + pub fn new_array_type(&self, element_type: &Type<'ctx>) -> ArrayType<'ctx> { + if let Ok(subarr_ty) = ArrayType::try_from(*element_type) { + new_array_type(self.attr, &subarr_ty) + } else { + ArrayType::new(*element_type, &[self.attr]) + } + } + /// Transform the array dimension using the given function that converts + /// values into new values (e.g., casting operations to index). + pub fn transform( + &self, + mut map_fn: impl FnMut(Value<'ctx, 'val>) -> Result>, + ) -> Result { + Ok(Self { + attr: self.attr, + symbols: match &self.symbols { + None => None, + Some(ovr) => { + let translated_vals = ovr + .values() + .iter() + .map(|v| map_fn(unsafe { Value::from_raw(*v) })) + .collect::>>()?; + Some(OwningValueRange::from(translated_vals.as_slice())) + } + }, + }) + } +} + +#[derive(Debug, Clone)] +/// Conveys information about array dimension computation. +pub enum ArrayDimensionResult<'ctx, 'val> { + /// Indicates that the computing context had sufficient information to compute the array and + /// computed it successfully. + Computed(ArrayDimension<'ctx, 'val>), + /// Indicates that the computing context was missing information (e.g., variables defined + /// within the function not accessible at template level). + InsufficientData, +} + +impl<'ctx, 'val> ArrayDimensionResult<'ctx, 'val> { + /// Construct a new [ArrayDimensionResult::Computed]. + pub fn new(attr: Attribute<'ctx>, symbol_vals: &[Value<'ctx, 'val>]) -> Result { + Ok(Self::Computed(ArrayDimension::new(attr, symbol_vals)?)) + } + /// Construct a [ArrayDimensionResult::InsufficientData]. + /// A convenience method for cases needing a return value of [Result] + pub fn insufficient_data_result() -> Result { + Ok(Self::InsufficientData) + } +} + +impl<'ctx, 'val> TryFrom> for ArrayDimension<'ctx, 'val> { + type Error = anyhow::Error; + + fn try_from(value: ArrayDimensionResult<'ctx, 'val>) -> Result { + match value { + ArrayDimensionResult::Computed(array_dimension) => Ok(array_dimension), + ArrayDimensionResult::InsufficientData => { + Err(anyhow!("insufficient data to convert dimension expression")) + } + } + } +} + +impl<'ctx, 'val> TryFrom<&ArrayDimension<'ctx, 'val>> for IntegerAttribute<'ctx> { + type Error = anyhow::Error; + + fn try_from(dim: &ArrayDimension<'ctx, 'val>) -> Result { + ensure!(dim.value_range()?.is_none(), "const dimension should have no symbols"); + if let Ok(d) = IntegerAttribute::try_from(*dim.attr()) { + Ok(d) + } else { + Err(anyhow!("could not convert to IntegerAttribute")) + } + } +} + +/// Information needed to create a new LLZK array type with the given dimensions. +#[derive(Debug, Default)] +pub struct ArrayDimensions<'ctx, 'val>(Vec>); + +impl<'ctx, 'val> ArrayDimensions<'ctx, 'val> { + /// Check if the number of dimensions is non-zero. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + /// Get all contained attributes. + pub fn attrs(&self) -> Vec> { + self.0.iter().map(|d| *d.attr()).collect() + } + /// Create a new [ArrayType] with the given dimensions. + pub fn new_array_type(&self, element_type: &Type<'ctx>) -> ArrayType<'ctx> { + ArrayType::new(*element_type, self.attrs().as_slice()) + } + /// Get the non-empty symbol values for affine map instantiation. + #[allow(dead_code)] // TODO: temporary, pending more support for computing array dimensions + pub fn symbol_vals(&self) -> Result>> { + let optional_vec = self.0.iter().map(|d| d.value_range()).collect::>>()?; + Ok(optional_vec.into_iter().flatten().collect()) + } + + /// If `self` is empty, return `base_type`. Otherwise, create [ArrayType] by + /// converting the dimension circom [Expressions](Expression) to LLZK Attributes. + pub fn type_from_dimension_exprs(&self, base_type: Type<'ctx>) -> Type<'ctx> { + if self.is_empty() { + base_type + } else { + self.new_array_type(&base_type).into() + } + } + + /// Create an LLZK operation that produces a nondeterministic `felt.type` value of the given + /// `dimensions` (non-array scalar if empty). + #[inline] + pub fn new_nondet_felt_of_dimensions_at_location( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + ) -> Result> { + codegen.new_nondet_at_location( + location, + self.type_from_dimension_exprs(codegen.felt_type().into()), + ) + } + + /// Create an LLZK operation that produces a nondeterministic `felt.type` value of the given + /// `dimensions` (non-array scalar if empty). + #[inline] + pub fn new_nondet_felt_of_dimensions( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + meta: &Meta, + ) -> Result> { + self.new_nondet_felt_of_dimensions_at_location(codegen, codegen.location_from_meta(meta)) + } + + /// If `dimensions` is empty, returns a [`StructType`] with just the name. Otherwise, + /// returns a [`StructType`] with parameters by converting the + /// dimension circom Expressions to LLZK Attributes. + pub fn struct_type_with_concrete_dimensions( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + name: &str, + ) -> StructType<'ctx> { + if self.is_empty() { + codegen.struct_type(name) + } else { + codegen.struct_type_with_params(name, &self.attrs()) + } + } +} + +impl<'ctx> IntoIterator for &ArrayDimensions<'ctx, '_> { + type Item = Attribute<'ctx>; + + type IntoIter = > as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.attrs().into_iter() + } +} + +/// Constructs a new [ArrayDimensions] if all input [ArrayDimensionResult] are +/// [ArrayDimensionResult::Computed], returns [Err] otherwise. +impl<'ctx, 'val> TryFrom>> for ArrayDimensions<'ctx, 'val> { + type Error = anyhow::Error; + + fn try_from(dim_results: Vec>) -> Result { + dim_results + .into_iter() + .map(ArrayDimension::try_from) + .collect::>>() + .map(ArrayDimensions) + } +} + +impl<'ctx, 'val, P: ProgramLike> TryFrom<(&[usize], &LlzkCodegen<'_, 'ctx, P>)> + for ArrayDimensions<'ctx, 'val> +{ + type Error = anyhow::Error; + + fn try_from( + (dim_sizes, codegen): (&[usize], &LlzkCodegen<'_, 'ctx, P>), + ) -> Result { + dim_sizes + .iter() + .map(|size| ArrayDimension::new(codegen.index_attr(i64::try_from(*size)?).into(), &[])) + .collect::>>() + .map(ArrayDimensions) + } +} + +/// A trait to generate array dimensions from the given dimension expressions. +pub trait DimExprConverter<'ctx, 'val> +where + 'ctx: 'val, +{ + /// Convert a circom [Expression] used as an array dimension to an LLZK Attribute. + /// + /// Returns an error if there was an error converting a dimension that should be convertible. + /// Returns [ArrayDimensionResult::InsufficientData] if a dimension is not convertible due to + /// lack of information in the implementer. Users can then attempt to resolve the dimension in a + /// different context, or throw an error if all available contexts are unable to convert the + /// dimension. + fn get_dim_expr( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + expr: &Expression, + ) -> Result>; + + /// Computes the [ArrayDimensions] from the given `dimension_exprs`, returning: + /// - An error if one of the underlying [Expression]s generated an error, + /// - [None] if one generates [ArrayDimensionResult::InsufficientData], + /// - [Some] otherwise + fn get_dim_exprs_if_able( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + dimension_exprs: &[Expression], + ) -> Result>> { + let dim_result_vec = dimension_exprs + .iter() + .map(|e| self.get_dim_expr(codegen, e)) + .collect::>>()?; // propagate error + Ok(ArrayDimensions::try_from(dim_result_vec).ok()) // insufficient -> None + } + + /// Same as [DimExprConverter::get_dim_exprs_if_able], but converts [None] into an error. + /// For cases where [ArrayDimensions] are expected to be generated and there are no fallback + /// contexts to try. + fn get_dim_exprs( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + dimension_exprs: &[Expression], + ) -> Result> { + self.get_dim_exprs_if_able(codegen, dimension_exprs)?.ok_or_else(|| { + anyhow!("unexpected lack of data needed to convert dimension expressions") + }) + } + + /// Names of `poly.param` and `poly.expr` defs visible in the current context. + fn poly_template_binding_names( + &self, + ) -> impl IntoIterator>)> { + std::iter::empty() + } + + /// Callback to store a `poly.expr` operation generated on the fly for an array dimension. + fn callback_store_poly_expr( + &self, + name: String, + op: TemplateExprOp<'ctx>, + ) -> StringAttribute<'ctx>; + + /// Get the mapping of `var` name to declared LLZK type. + fn get_var_decl_types(&self) -> &HashMap>; + + /// Generate a new `poly.expr` operation for the given array dimension [Expression]. + fn gen_template_poly_expr( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + target_expr: &Expression, // the result that should yield from the `poly.expr` + ) -> Result> { + /// Fully generate LLZK for a single [Statement] in a `poly.expr` initializer without + /// truncating at any target expression. + fn gen_stmt_fully<'ctx, 'blk, 'val>( + stmt: &Statement, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + gen_ctx: &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + ) -> Result<()> + where + 'ctx: 'blk, + 'blk: 'val, + 'val: 'blk, + { + match stmt { + Statement::Block { stmts, .. } => { + for s in stmts { + gen_stmt_fully(s, codegen, gen_ctx)?; + } + } + Statement::InitializationBlock { initializations, .. } => { + for s in initializations { + gen_stmt_fully(s, codegen, gen_ctx)?; + } + } + Statement::Declaration { meta, xtype, name, dimensions, .. } => { + if VariableType::Var == *xtype { + // Use the pre-computed type from DeclarationInfo (seeded into the + // BlockContextStack root) to avoid triggering recursive + // gen_template_poly_expr calls for non-constant dimension expressions. + if let Some(ty) = gen_ctx.var_decl_types.get(name) { + let op = codegen + .new_nondet_at_location(codegen.location_from_meta(meta), *ty)?; + gen_ctx.block_ctx.declare_name_ensure_not_present(name, op)?; + } else { + gen_ctx.gen_declaration(codegen, meta, name, dimensions)?; + } + } + } + Statement::Substitution { meta, var, access, op, rhe } => { + // Signal assignments don't affect var values so skip those. + if !op.is_signal_operator() { + gen_ctx.handle_substitution_stmt_nonsignal( + codegen, + InfoProviders::default(), + meta, + var, + access, + op, + rhe, + )?; + } + } + Statement::IfThenElse { meta, cond, if_case, else_case } => { + let location = codegen.location_from_meta(meta); + let cond_val = cond.gen_llzk_in_block(codegen, gen_ctx, Default::default())?; + let cond_bool = gen_ctx.cast_to_bool_if_needed(codegen, location, cond_val)?; + // Build then-branch NestedBlockInfo. + let mut then_info = NestedBlockInfo::default(); + gen_ctx.block_ctx.push(then_info.block); + gen_stmt_fully(if_case, codegen, gen_ctx)?; + then_info.var_overwrites = gen_ctx.block_ctx.pop(); + // Build else-branch NestedBlockInfo. + let mut else_info = NestedBlockInfo::default(); + gen_ctx.block_ctx.push(else_info.block); + if let Some(ec) = else_case { + gen_stmt_fully(ec, codegen, gen_ctx)?; + } + else_info.var_overwrites = gen_ctx.block_ctx.pop(); + gen_ctx.gen_scf_if(codegen, location, cond_bool, then_info, else_info)?; + } + Statement::While { .. } => { + anyhow::bail!("poly.expr depending on a while loop is not yet supported") + } + Statement::Assert { meta, arg } => { + let val = arg.gen_llzk_in_block(codegen, gen_ctx, Default::default())?; + gen_ctx.append_assert(codegen, codegen.location_from_meta(meta), val)? + } + Statement::LogCall { meta, .. } => { + codegen.emit_circom_warning( + meta, + "log calls are not currently supported in LLZK", + ReportCode::NotAllowedOperation, + ); + } + Statement::Return { .. } => { + unreachable!("encountered return statement while computing array dimension") + } + _ => {} + } + Ok(()) + } + + /// Generate the `scf.if` for a [Statement::IfThenElse] boundary encountered while + /// walking toward `target_expr`. The branch containing the target recursively calls + /// `gen_up_to_target`; the other branch yields a `nondet` placeholder of the same type. + #[allow(clippy::too_many_arguments)] + fn gen_if_then_else_up_to_target<'ctx, 'blk, 'val>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + gen_ctx: &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + target_expr: &Expression, + trace: &[*const Statement], + meta: &Meta, + cond: &Expression, + if_case: &Statement, + else_case: &Option>, + ) -> Result> + where + 'ctx: 'blk, + 'blk: 'val, + 'val: 'blk, + { + ensure!( + !trace.is_empty(), + "trace ended at IfThenElse; expected continuation into a branch" + ); + let boundary_ptr: *const Statement = trace[0]; + + let location = codegen.location_from_meta(meta); + let cond_val = cond.gen_llzk_in_block(codegen, gen_ctx, Default::default())?; + let cond_bool = gen_ctx.cast_to_bool_if_needed(codegen, location, cond_val)?; + + // Determine which branch contains the target by pointer identity. + let target_in_then = std::ptr::eq(if_case, boundary_ptr); + let containing_stmt = if target_in_then { + if_case + } else { + let opt = else_case.as_deref(); + ensure!( + opt.is_some_and(|ec| std::ptr::eq(ec, boundary_ptr)), + "trace does not point to either branch of IfThenElse" + ); + opt.expect("checked above") + }; + + // Generate the branch that contains the target, then a nondet placeholder for the + // other branch (using the result type from the containing branch). + let (containing_region, target_val) = + gen_ctx.generate_simple_scf_if_arm(location, |gc| { + gen_up_to_target( + codegen, + gc, + target_expr, + std::slice::from_ref(containing_stmt), + trace, + ) + })?; + let result_type = target_val.r#type(); + let (other_region, _) = gen_ctx.generate_simple_scf_if_arm(location, |gc| { + gc.append_op_unnamed_result(codegen.new_nondet_at_location(location, result_type)?) + })?; + + // Assemble the scf.if with regions in the correct order. + let (then_region, else_region) = if target_in_then { + (containing_region, other_region) + } else { + (other_region, containing_region) + }; + gen_ctx.append_op_unnamed_result(scf::r#if( + cond_bool, + &[result_type], + then_region, + else_region, + location, + )) + } + + /// Generate LLZK for all of `stmts` up to the first [Statement] in the `trace` (i.e. the + /// boundary). Then, pop the first [Statement] from the `trace` and, if applicable, descend + /// into its nested body following the same proceedure. When the trace is exhausted, + /// generate LLZK for the `target_expr` and return its result [Value]. + fn gen_up_to_target<'ctx, 'blk, 'val>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + gen_ctx: &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + target_expr: &Expression, + stmts: &[Statement], + trace: &[*const Statement], + ) -> Result> + where + 'ctx: 'blk, + 'blk: 'val, + 'val: 'blk, + { + assert!(!trace.is_empty(), "trace must not be empty in gen_up_to_target"); + let boundary_ptr: *const Statement = trace[0]; + let inner_trace = &trace[1..]; + + // Generate all statements that precede the boundary, then find the boundary itself. + let mut boundary_stmt: Option<&Statement> = None; + for stmt in stmts { + if std::ptr::eq(stmt, boundary_ptr) { + boundary_stmt = Some(stmt); + break; + } + gen_stmt_fully(stmt, codegen, gen_ctx)?; + } + let boundary_stmt = boundary_stmt.ok_or_else(|| { + anyhow!("statement trace boundary not found in current stmts slice") + })?; + + // Process the boundary statement (the one on the trace path). + match boundary_stmt { + Statement::Block { stmts, .. } => { + gen_up_to_target(codegen, gen_ctx, target_expr, stmts, inner_trace) + } + Statement::InitializationBlock { initializations, .. } => { + gen_up_to_target(codegen, gen_ctx, target_expr, initializations, inner_trace) + } + Statement::IfThenElse { meta, cond, if_case, else_case } => { + gen_if_then_else_up_to_target( + codegen, + gen_ctx, + target_expr, + inner_trace, + meta, + cond, + if_case, + else_case, + ) + } + Statement::While { .. } => { + anyhow::bail!("poly.expr depending on a while loop is not yet supported") + } + _ => { + assert!(inner_trace.is_empty(), "trace should end at a leaf statement"); + // Leaf: this boundary statement directly contains `target_expr` in its + // dimensions. Generate the target expression in the current block context. + target_expr.gen_llzk_in_block(codegen, gen_ctx, Default::default()) + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Generate `poly.expr` and fill its initializer region. + let name = dim_expr_name(target_expr); + let location = codegen.location_from_meta(target_expr.get_meta()); + let expr_op = poly::expr(location, &name, std::iter::empty())?; + let mut expr_gen_ctx = BlockGenContext::new( + BlockContextStack::new( + expr_op.initializer_region().first_block().expect("block should have been added"), + ), + self.get_var_decl_types(), + self.poly_template_binding_names(), + ) + .with_poly_template_binding_locals(codegen, location)?; + let body_opt = codegen.current_body(); + assert!(body_opt.is_some(), "should have been set at top level"); + let trace = codegen.snapshot_statement_trace(); + let val = + gen_up_to_target(codegen, &mut expr_gen_ctx, target_expr, body_opt.unwrap(), &trace)?; + expr_gen_ctx.append_op_no_result(poly::r#yield(location, val)?.into())?; + + // Run cleanup passes to simplify and normalize the generated code a bit: + // - remove-dead-values: drop ops whose results are not used + // - sccp: constant fold and propagate values + // - canonicalize: mainly to remove unused `llzk.nondet` (which `remove-dead-values` cannot + // remove due to memory effects) but also folds felt constants, etc. + // This cleanup is not simply an optimization. In some cases, an `llzk.nondet` may be + // generated that references a symbol defined by a different `poly.expr` due to the way + // array declaration and initialization are split up in the circom AST. This is illegal + // in LLZK but that `llzk.nondet` result value is unused so it can be removed. + codegen.run_pass_pipeline_on( + "any(composite-fixed-point-pass{pipeline=\"canonicalize,sccp,remove-dead-values\"})", + &expr_op, + )?; + + let uniqued_name = self.callback_store_poly_expr(name, expr_op); + // Have to convert the StringAttribute to FlatSymbolRefAttribute before returning it. + ArrayDimensionResult::new(codegen.flat_sym(uniqued_name.value()).into(), &[]) + } +} + +/// Maps the inner type of the given type if it is an [`ArrayType`]. Returns the new type +/// otherwise. +/// +/// ```text +/// map([T], O) -> [O] +/// map(T, O) -> O +/// ``` +pub fn map_array_inner_type<'ctx>(t: Type<'ctx>, new_inner: Type<'ctx>) -> Type<'ctx> { + ArrayType::try_from(t).map(|t| ArrayType::new(new_inner, &t.dims()).into()).unwrap_or(new_inner) +} + +/// Returns a region that contains one block with the given arguments. +pub fn region_with_block<'ctx>(arguments: &[(Type<'ctx>, Location<'ctx>)]) -> Region<'ctx> { + let region = Region::new(); + region.append_block(Block::new(arguments)); + region +} + +/// Returns the type of a subcomponent as defined in its memory. +pub fn comp_type<'ctx>(pod: PodType<'ctx>) -> Result> { + pod.get_type_of_record(COMP) + .ok_or_else(|| anyhow::anyhow!("missing {COMP} record in memory struct: {pod:?}")) +} + +/// Generate a stable name for a dimension expression, suitable for use as a `poly.expr` name. +/// +/// The generated name uniquely represents the expression structure so that the same expression +/// always maps to the same name, enabling deduplication of `poly.expr` ops. +pub fn dim_expr_name(expr: &Expression) -> String { + fn visit(expr: &Expression) -> String { + match expr { + Expression::Number(_, n) => n.to_string(), + Expression::Variable { name, access, .. } => { + if access.is_empty() { + name.clone() + } else { + // Reuse `Lvalue` string format but drop the "Var:" prefix. + Lvalue::new(name, Root::Var, access) + .to_string() + .trim_start_matches("Var:") + .to_string() + } + } + Expression::InfixOp { lhe, infix_op, rhe, .. } => { + format!("{}_{infix_op:?}_{}", visit(lhe), visit(rhe)) + } + Expression::PrefixOp { prefix_op, rhe, .. } => { + format!("{prefix_op:?}_{}", visit(rhe)) + } + Expression::InlineSwitchOp { cond, if_true, if_false, .. } => { + format!("{}?{}:{}", visit(cond), visit(if_true), visit(if_false)) + } + Expression::Call { id, args, .. } => { + format!("{id}({})", args.iter().map(visit).collect::>().join(",")) + } + _ => unreachable!("dimension expression must produce a scalar value"), + } + } + // Add starting location of the expression to the name to ensure (**assuming Meta information + // is accurate**) context sensitivity (i.e. expressions at different code locations may have a + // different value store and thus should be treated as different values). + format!("{}@{}", visit(expr), expr.get_meta().start) +} + +/// Get the `sym_name` attribute from a `poly.expr` operation. +pub fn get_poly_expr_name<'c: 'a, 'a>(op: &impl OperationLike<'c, 'a>) -> StringAttribute<'c> { + assert!(poly::is_expr_op(op), "expected a poly.expr operation"); + let a = op.attribute("sym_name").expect("`poly.expr` op has `sym_name` attribute per ODS"); + StringAttribute::try_from(a).expect("`sym_name` attribute is StringAttr per ODS") +} diff --git a/llzk_backend/src/subcmp.rs b/llzk_backend/src/subcmp.rs new file mode 100644 index 000000000..4e2df751e --- /dev/null +++ b/llzk_backend/src/subcmp.rs @@ -0,0 +1,363 @@ +//! Helper types for handling subcomponents. + +use crate::function::FunctionContext; +use crate::program_ext::ProgramInfo; +use crate::program_ext::ProgramLike; +use crate::shared::map_array_inner_type; +use crate::shared::LlzkCodegen; +use crate::template_ext::SignalDeclarations; +use crate::template_ext::TemplateLike as _; +use anyhow::Result; +use llzk::builder::OpBuilder; +use llzk::dialect::array; +use llzk::dialect::array::ArrayCtor; +use llzk::dialect::llzk::nondet; +use llzk::dialect::pod; +use llzk::dialect::r#struct; +use llzk::prelude::ArrayType; +use llzk::prelude::Attribute; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::Location; +use llzk::prelude::PodType; +use llzk::prelude::StructType; +use llzk::prelude::Type; +use llzk::prelude::TypeLike as _; +use std::collections::HashMap; +use std::collections::HashSet; +use std::convert::TryFrom; + +/// Names used for `pod` records. +pub mod names { + /// Counts the number of inputs pending an assignment. When it reaches 0 it's safe + /// to call the corresponding `@compute` function. + pub const COUNT: &str = "count"; + /// Holds the output of calling `@compute`. Before the call, this value is undefined + /// and should not be read from. + pub const COMP: &str = "comp"; + /// Holds the affine map operands of the subcomponents, if any. + pub const PARAMS: &str = "params"; + + /// Returns the inputs field name for a subcomponent by appending "$inputs" suffix. + #[inline] + pub fn inputs(name: &str) -> String { + format!("{name}$inputs") + } +} + +/// Gives information about subcomponents. +pub trait SubcmpInfo: std::fmt::Debug { + /// Returns true if the given variable name is a subcomponent. + fn is_subcmp(&self, var: &str) -> bool; + + /// Returns the template information for the given subcomponent. + fn subcmp_info<'i>( + &self, + var: &str, + info: &'i dyn ProgramInfo, + ) -> Result<&'i dyn SignalDeclarations>; +} + +/// Empty implementation for [`SubcmpInfo`]. +#[derive(Debug)] +pub struct NoSubcmps; + +impl SubcmpInfo for NoSubcmps { + fn is_subcmp(&self, _var: &str) -> bool { + false + } + + fn subcmp_info<'i>( + &self, + _var: &str, + _info: &'i dyn ProgramInfo, + ) -> Result<&'i dyn SignalDeclarations> { + unreachable!() + } +} + +/// Information collected about a subcomponent. +#[derive(Debug)] +pub struct SubcmpDeclInfo<'ctx> { + /// Name of the template type. + template: Option, + /// List of dimensions for arrays of subcomponents of the same type. + dimensions: Vec>, + /// Location of the declaration. + location: Location<'ctx>, + /// Instances of the subcomponent type. + instances: Vec>, +} + +impl<'ctx> SubcmpDeclInfo<'ctx> { + /// Creates a new declaration instance. + pub fn new(dimensions: Vec>, location: Location<'ctx>) -> Self { + Self { template: None, dimensions, location, instances: Default::default() } + } + + /// Sets the name of the subcomponent's template type. + pub fn set_template(&mut self, name: String) { + self.template = Some(name) + } + + /// Gets the name of the subcomponent's template type. + pub fn template(&self) -> Option<&str> { + self.template.as_deref() + } + + /// Returns the dimensions of the declaration. + pub fn dimensions(&self) -> &[Attribute<'ctx>] { + &self.dimensions + } + + /// Returns the location of the declaration. + pub fn location(&self) -> Location<'ctx> { + self.location + } + + /// Returns a mutable reference to the different type instances. + pub fn instances_mut(&mut self) -> &mut Vec> { + &mut self.instances + } + + /// Returns a reference to the different type instances. + pub fn instances(&self) -> &[StructType<'ctx>] { + &self.instances + } + + /// Applies the dimensions to the given type, wrapping it in an array type if necessary. + pub fn extend_dims(&self, t: Type<'ctx>) -> Type<'ctx> { + match self.dimensions() { + [] => t, + dims => ArrayType::new(t, dims).into(), + } + } +} + +/// Returns a list with the unique struct types in the given instances. +pub fn unique_instance_types<'ctx>(instances: &[StructType<'ctx>]) -> Vec> { + instances.iter().copied().map(ST).collect::>().into_iter().map(|s| s.0).collect() +} + +/// Newtype for implementing Hash in StructType. +struct ST<'ctx>(pub StructType<'ctx>); + +impl PartialEq for ST<'_> { + fn eq(&self, other: &Self) -> bool { + self.0.to_raw().ptr == other.0.to_raw().ptr + } +} + +impl Eq for ST<'_> {} + +impl std::hash::Hash for ST<'_> { + fn hash(&self, state: &mut H) { + self.0.to_raw().ptr.hash(state); + } +} + +/// A wrapper around a subcomponent's struct type. +/// +/// Can be used for obtaining information related to the subcomponent +/// like its memory pod. +#[derive(Debug)] +pub struct SubcmpType<'ctx> { + /// Inner type of the subcomponent. + inner: Type<'ctx>, + /// Name of the template the subcomponent's type comes from. + template_name: String, +} + +impl<'ctx> SubcmpType<'ctx> { + /// Creates a new subcomponent type wrapper. + pub fn new(inner: Type<'ctx>, template_name: String) -> Self { + Self { inner, template_name } + } + + /// Returns the element type if the type is an [`ArrayType`]. Returns the type itself otherwise. + fn scalar_or_inner(&self) -> Type<'ctx> { + ArrayType::try_from(self.inner).map(|t| t.element_type()).unwrap_or(self.inner) + } + + /// Type used to represent template parameters. + pub fn param_type(&self, codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>) -> Type<'ctx> { + codegen.felt_type().into() + } + + /// Returns the pod type for the template parameters. + pub fn params_pod_type( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> PodType<'ctx> { + let param_type = self.param_type(codegen); + // These need to be in declaration order. + let params_records = codegen + .program + .get_template_data(self.template_name()) + .get_name_of_params() + .iter() + .map(|name| (name.as_str(), param_type)) + .collect::>(); + + codegen.pod_type(¶ms_records) + } + + /// Returns the list of records of the "memory" comp pod of the subcomponent. + /// + /// That pod holds the inputs for the constructor, the component itself, and the countdown + /// for the number of assigned inputs. + pub fn comp_pod_records( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> [(&'static str, Type<'ctx>); 3] { + [ + // Counts the number of inputs pending an assignment. When it reaches 0 it's safe + // to call the corresponding `@compute` function. + (names::COUNT, codegen.index_type()), + // Holds the output of calling `@compute`. Before the call, this value is undefined + // and should not be read from. + (names::COMP, self.scalar_or_inner()), + // Holds the affine map operands of the subcomponents, if any. + (names::PARAMS, self.params_pod_type(codegen).into()), + ] + } + + /// Returns the type of the subcomponent's memory pod. + /// + /// That pod represents the subcomponent lifecycle during the compute function. + /// Holds the template parameters, necessary for passing map operands if the struct type of the + /// subcomponent requires them. The input countdown, that keeps track of how many inputs have + /// been assigned, signaling that is safe to call the compute function if it reached 0. And the + /// subcomponent itself, as a non-deterministic value before the call to compute and the result + /// of said function after. + pub fn comp_pod(&self, codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>) -> Type<'ctx> { + let records = self.comp_pod_records(codegen); + map_array_inner_type(self.inner, codegen.pod_type(&records).into()) + } + + /// Returns the name of the template the subcomponent's type comes from. + pub fn template_name(&self) -> &str { + &self.template_name + } + + /// Returns the actual MLIR type of the subcomponent. + pub fn r#type(&self) -> Type<'ctx> { + self.inner + } +} + +/// Holds the information required for generating the IR to support subcomponents in the prologue +/// of the template's functions. +#[derive(Debug)] +pub struct SubcmpPrologueData<'ctx> { + /// Name of the subcomponent. + name: String, + /// Name of the subcomponent inputs. + name_inputs: String, + /// Type of the subcomponent. + subcmp: SubcmpType<'ctx>, + /// Type representing the inputs of the subcomponent. + inputs: Type<'ctx>, +} + +impl<'ctx> SubcmpPrologueData<'ctx> { + /// Creates a new instance. + pub fn new( + name: String, + template_name: String, + subcmp: Type<'ctx>, + inputs: Type<'ctx>, + ) -> Self { + let name_inputs = names::inputs(&name); + Self { name, name_inputs, subcmp: SubcmpType::new(subcmp, template_name), inputs } + } + + /// Generates the subcomponents prologue in the constraint function. + /// + /// For each subcomponent reads the corresponding struct member and binds it + /// to the subcomponent's name. + pub fn generate_constraint_func_prologue( + &self, + constrain_ctx: &mut FunctionContext<'_, 'ctx, '_, '_, '_>, + op_builder: &OpBuilder<'ctx>, + subcmp_decls: &HashMap>, + ) -> Result<()> { + let decl = &subcmp_decls[self.name()]; + let self_ref = constrain_ctx.func.self_value_of_constrain()?; + constrain_ctx.block_ctx.declare_name_if_not_present(self.name(), || { + Ok(r#struct::readm( + op_builder, + decl.location(), + self.subcmp.r#type(), + self_ref, + self.name(), + )?) + })?; + constrain_ctx.block_ctx.declare_name_if_not_present(self.name_inputs(), || { + Ok(r#struct::readm( + op_builder, + decl.location(), + self.inputs, + self_ref, + self.name_inputs(), + )?) + })?; + Ok(()) + } + + /// Generates the subcomponents prologue in the compute function. + /// + /// For each subcomponent creates an initializer operation and binds it + /// to the subcomponent's name. For scalar subcomponent the initializer is a + /// `nondet` op with the type of the subcomponent's memory pod. For array subcomponent + /// the initializer is an array of the memory pods. + pub fn generate_compute_func_prologue<'func, 'blk, 'val>( + &self, + compute_ctx: &mut FunctionContext<'_, 'ctx, 'func, 'blk, 'val>, + op_builder: &OpBuilder<'ctx>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + subcmp_decls: &HashMap>, + ) -> Result<()> + where + 'val: 'blk, + { + let comp_pod = self.subcmp.comp_pod(codegen); + let decl = &subcmp_decls[self.name()]; + compute_ctx.block_ctx.declare_name_ensure_not_present( + self.name(), + match ArrayType::try_from(comp_pod) { + Ok(comp_pod) => array::new(op_builder, decl.location(), comp_pod, ArrayCtor::Empty), + Err(_) => nondet(decl.location(), comp_pod), + }, + )?; + compute_ctx.block_ctx.declare_name_ensure_not_present( + self.name_inputs(), + match self.inputs_as::() { + Ok(inputs) => array::new(op_builder, decl.location(), inputs, ArrayCtor::Empty), + Err(_) => pod::new(op_builder, decl.location(), &[], Some(self.inputs_as()?)), + }, + ) + } + + /// Returns the inputs type casted into the given type. + fn inputs_as>>(&self) -> Result>>::Error> { + T::try_from(self.inputs) + } + + /// Returns the name of the subcomponent. + pub fn name(&self) -> &str { + &self.name + } + + /// Returns the name of the inputs member for this subcomponent. + pub fn name_inputs(&self) -> &str { + &self.name_inputs + } +} + +impl<'ctx> std::ops::Deref for SubcmpPrologueData<'ctx> { + type Target = SubcmpType<'ctx>; + + fn deref(&self) -> &Self::Target { + &self.subcmp + } +} diff --git a/llzk_backend/src/template.rs b/llzk_backend/src/template.rs new file mode 100644 index 000000000..f44fec19d --- /dev/null +++ b/llzk_backend/src/template.rs @@ -0,0 +1,1574 @@ +//! Handles template-level LLZK code generation. The [TemplateContext] carries information about the +//! current LLZK struct being generated and some helpers related to generating code within the +//! struct. The [GenerateLLZKInTemplate] trait provides the visitor to generate LLZK IR for all +//! circom [Expression](program_structure::ast::Expression) and +//! [Statement](program_structure::ast::Statement) nodes. There are also a few helper traits like +//! [GenResult] and [Chainable] that implement some boilerplate to make the actual code generation +//! within [GenerateLLZKInTemplate] a lot simpler. + +use crate::function::FunctionContext; +use crate::gen_context::BlockGenContext; +use crate::gen_context::GenWithCircomScopeHandling; +use crate::gen_context::GenerateLLZKInAnyBlock; +use crate::gen_context::NestedBlockInfo; +use crate::lvalue::Lvalue; +use crate::lvalue::Root; +use crate::program_ext::ProgramInfo; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::comp_type; +use crate::shared::dim_expr_name; +use crate::shared::get_poly_expr_name; +use crate::shared::map_array_inner_type; +use crate::shared::ArrayDimension; +use crate::shared::ArrayDimensionResult; +use crate::shared::ArrayDimensions; +use crate::shared::DimExprConverter; +use crate::shared::LlzkCodegen; +use crate::shared::TmplParamsInstance; +use crate::shared::TypeSizeExpr; +use crate::subcmp::names::COMP; +use crate::subcmp::names::COUNT; +use crate::subcmp::names::PARAMS; +use crate::subcmp::SubcmpInfo; +use crate::subcmp::SubcmpType; +use crate::template_ext::SignalDeclarations; +use crate::template_ext::TemplateLike as _; +use crate::write_chain::WriteChain; +use crate::write_chain::WriteTarget; +use anyhow::anyhow; +use anyhow::Result; +use llzk::dialect::array::ArrayCtor; +use llzk::dialect::constrain; +use llzk::dialect::pod; +use llzk::dialect::poly; +use llzk::dialect::r#struct; +use llzk::prelude::ArrayType; +use llzk::prelude::BlockRef; +use llzk::prelude::FlatSymbolRefAttribute; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::IntegerAttribute; +use llzk::prelude::LoopBoundsAttribute; +use llzk::prelude::MemberDefOpLike as _; +use llzk::prelude::PodType; +use llzk::prelude::RecordValue; +use llzk::prelude::StringAttribute; +use llzk::prelude::StringRef; +use llzk::prelude::StructDefOpLike as _; +use llzk::prelude::StructDefOpRefMut; +use llzk::prelude::StructType; +use llzk::prelude::TemplateExprOp; +use llzk::prelude::TemplateOpLike; +use llzk::prelude::TemplateOpRefMut; +use llzk::prelude::Type; +use llzk::prelude::Value; +use llzk::prelude::ValueLike as _; +use llzk::symbol_table; +use melior::ir::Attribute; +use melior::ir::Location; +use num_bigint_dig::BigInt; +use program_structure::ast::AssignOp; +use program_structure::ast::Expression; +use program_structure::ast::Meta; +use program_structure::ast::Statement; +use program_structure::error_code::ReportCode; +use std::cell::RefCell; +use std::collections::HashMap; +use std::collections::HashSet; +use std::convert::TryFrom; +use std::convert::TryInto as _; +use std::rc::Rc; + +/// Alias for `Option` to make it clear what the meaning of the option is within the +/// [TemplateContext] below. +type ShouldGenerate = Option; + +/// A pair of things, one for the "@compute" function and one for the "@constrain" function. +#[derive(Debug, Default)] +pub struct TemplateFuncPair { + /// The value for the "@compute" function. + compute: ShouldGenerate, + /// The value for the "@constrain" function. + constrain: ShouldGenerate, +} + +impl<'ctx, 'blk, 'val> TemplateFuncPair> +where + 'ctx: 'blk, + 'blk: 'val, +{ + /// Returns a [TemplateFuncPair] with a default [NestedBlockInfo] for `compute`/`constrain` + /// according to the respective [ShouldGenerate] value in the given [TemplateContext]. + pub fn new(template: &TemplateContext<'_, '_, '_, '_, '_, '_>) -> Self { + Self { + compute: template.compute.is_some().then(NestedBlockInfo::default), + constrain: template.constrain.is_some().then(NestedBlockInfo::default), + } + } + + /// Returns a [TemplateFuncPair] containing just the `block` fields of `self`. + pub fn block(&self) -> TemplateFuncPair> { + TemplateFuncPair { + compute: self.compute.as_ref().map(|i| i.block), + constrain: self.constrain.as_ref().map(|i| i.block), + } + } +} + +/// Stores refs to the current struct and its associated functions while generating LLZK IR for a +/// template. Implemented as a lightweight wrapper around several mutable references to allow +/// derived versions for witness-only or constraint-only to be created cheaply. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'str: lifetime of the generated `StructDefOp` +/// 'func: lifetime of the generated `FuncDefOp` instances within the struct +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +#[derive(Debug)] +pub struct TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + /// Current LLZK `TemplateOp` + template_def: TemplateOpRefMut<'ctx, 'str>, + /// Current LLZK `StructDefOp` within the `TemplateOp` + struct_def: StructDefOpRefMut<'ctx, 'str>, + /// Codegen refs for the "@compute" function within `struct_def` + compute: ShouldGenerate>>>, + /// Codegen refs for the "@constrain" function within `struct_def` + constrain: ShouldGenerate>>>, + /// Map of subcomponent names to their types. + subcmps: &'str HashMap)>, + /// Tracks for what component signals we have created their `struct.writem` op already. + written_signals: Rc>>, + /// Pre-computed LLZK types for circom `var` declarations, keyed by var name. Populated from + /// [crate::module::DeclarationInfo] when generating `poly.expr` bodies for templates so that + /// dimension expressions referencing other vars do not trigger recursive code generation. + /// + /// Note: this is also stored in `compute` and/or `constrain` but it made several things more + /// straightforward to just store it here as well since it's just a reference. + var_decl_types: &'decls HashMap>, +} + +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val> TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val> { + /// Creates a new [TemplateContext]. + #[inline] + pub fn new( + template_def: TemplateOpRefMut<'ctx, 'str>, + struct_def: StructDefOpRefMut<'ctx, 'str>, + compute: FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + constrain: FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + subcmps: &'str HashMap)>, + var_decl_types: &'decls HashMap>, + ) -> TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val> { + Self { + template_def, + struct_def, + compute: Some(Rc::new(RefCell::new(compute))), + constrain: Some(Rc::new(RefCell::new(constrain))), + subcmps, + written_signals: Default::default(), + var_decl_types, + } + } + + /// Creates a new [TemplateContext] that will only generate within the "@compute" function. + #[inline] + pub fn compute_only(&self) -> TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val> { + Self { + template_def: self.template_def, + struct_def: self.struct_def, + compute: self.compute.as_ref().map(Rc::clone), + constrain: None, + subcmps: self.subcmps, + written_signals: self.written_signals.clone(), + var_decl_types: self.var_decl_types, + } + } + + /// Creates a new [TemplateContext] that will only generate within the "@constrain" function. + #[inline] + pub fn constrain_only(&self) -> TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val> { + Self { + template_def: self.template_def, + struct_def: self.struct_def, + compute: None, + constrain: self.constrain.as_ref().map(Rc::clone), + subcmps: self.subcmps, + written_signals: self.written_signals.clone(), + var_decl_types: self.var_decl_types, + } + } + + /// Returns true if we already generated a `struct.writem` op for the given signal. + pub fn signal_already_written(&self, name: &str) -> bool { + self.written_signals.borrow().contains(name) + } + + /// Marks the given signal as written. + pub fn mark_signal_as_written(&self, name: String) { + self.written_signals.borrow_mut().insert(name); + } + + /// Get the type of the struct field with the given name. + /// Errors if the field does not exist within the struct. + pub fn get_signal_type(&self, name: &str) -> Result> { + Ok(self + .struct_def + .get_member_def(name) + .ok_or_else(|| anyhow!("no field '{name}' in struct"))? + .member_type()) + } + + /// Part of the finalization procedure that emits the pending operations in the queue. + fn finalize_queue(self) -> Result { + self.and_then_same::<_, GenResultUnit>(|fc, _| { + if !fc.block_ctx.is_only_root() { + anyhow::bail!("Template generation reached final step with more than one scope"); + } + fc.block_ctx.append_queue(); + Ok(()) + })?; + Ok(self) + } + + /// Part of the finalization procedure that emits the operations related to the subcomponents. + /// + /// For `@compute` emits the write operations for the results of calling the `@compute` + /// functions of the subcomponents and the values of the inputs. + /// + /// For `@constrain` emits calls to `@constrain` for each subcomponent. + fn finalize_subcmps(self, codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>) -> Result + where + // Required by `loop_nest` + 'val: 'blk, + { + let mut subcmps: Vec<_> = self.subcmps.keys().collect(); + if codegen.config.stabilize { + // Sort by circom subcomponent names to ensure a stable order of operations. + subcmps.sort_by(Ord::cmp); + } + let location = codegen.location_unknown(); + let comp_sym = codegen.flat_sym(COMP); + + self.and_then::<_, _, GenResultUnit>(|fc, _| { + // Write the subcomponent declarations to self. + let self_value = fc.func.self_value_of_compute()?; + subcmps.iter().try_for_each(|name| { + // Write the inputs of the subcomponent. + let name_inputs = crate::subcmp::names::inputs(name); + let name_inputs_val = *fc.block_ctx.get_named_value(&name_inputs)?; + fc.append_op_no_result(r#struct::writem( + location, + self_value, + &name_inputs, + name_inputs_val, + )?)?; + + // This value is the memory SSA value. We need to extract the component from + // it. + let mem = *fc.block_ctx.get_named_value(name)?; + let value = type_switch! { ty = mem.r#type(), + ArrayType => { + // Copy the components into an array of the same dimensions. + let struct_type = comp_type(ty.element_type().try_into()?)?; + + let comp_array = fc.append_op_unnamed_result(codegen.new_array_new_op( + location, + map_array_inner_type(ty.into(), struct_type).try_into()?, + ArrayCtor::Empty + ))?; + + fc.gen_loop_nest_from_attrs(codegen, codegen.location_unknown(), &ty.dims(), |fc, indices| { + let comp_memory = fc.append_array_read( + mem, + indices, + location, + None + )?; + let comp_instance = fc.append_op_unnamed_result(pod::read( + location, + comp_memory, + comp_sym, + struct_type + ))?; + fc.append_array_write( + codegen, + comp_array, + indices, + location, + comp_instance, + None + ) + })?; + + comp_array + } + PodType => { + fc.append_op_unnamed_result(pod::read( + location, + mem, + comp_sym, + comp_type(ty)? + ))? + } + }; + + fc.append_op_no_result(r#struct::writem( + location, + self_value, + name, + value, + )?) + + }) + + }, |fc, _| { + subcmps.iter().try_for_each(|name| { + // Read the subcomponent + let subcmp = *fc.block_ctx.get_named_value(name)?; + + // Read the subcomponent inputs. The pod records are in declaration order, + // which matches the order of the `@constrain` function. + let inputs = *fc.block_ctx.get_named_value(&crate::subcmp::names::inputs(name))?; + // Call `@constrain` + type_switch! { inputs_type = inputs.r#type(), + ArrayType => { + let dims = inputs_type.dims(); + let subcmp_type = ArrayType::try_from(subcmp.r#type())?; + assert_eq!(dims, subcmp_type.dims()); + + fc.gen_loop_nest_from_attrs(codegen, location, &dims, |fc, indices| { + let subcmp_instance = fc.append_array_read( + subcmp, + indices, + location, + None + )?; + let subcmp_inputs = fc.append_array_read( + inputs, + indices, + location, + None + )?; + fc.gen_constrain_call( + subcmp_instance, + subcmp_inputs, + location, + codegen + ) + }) + } + PodType as _ => fc.gen_constrain_call(subcmp, inputs, location, codegen), + } + }) + })?; + Ok(self) + } + + /// Finalizes the context by emitting the final write operations that write subcomponent + /// declarations to the declaring component. + pub fn finalize(self, codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>) -> Result<()> + where + // Required by `finalize_subcmps` + 'val: 'blk, + { + self.finalize_subcmps(codegen)? + .finalize_queue()? + .and_then_same(|fc, _| fc.finalize(codegen)) + } + + /// Returns the real type of the subcomponent, which may be a more general version + /// than the one returned by the IR generation for constructor calls. + fn get_subcmp_type(&self, var: &str) -> Result> { + Ok(self.subcmps.get(var).ok_or_else(|| anyhow!("subcomponent '{var}' not found"))?.1) + } +} + +impl SubcmpInfo for TemplateContext<'_, '_, '_, '_, '_, '_> { + fn is_subcmp(&self, var: &str) -> bool { + self.subcmps.contains_key(var) + } + + fn subcmp_info<'i>( + &self, + var: &str, + info: &'i dyn ProgramInfo, + ) -> Result<&'i dyn SignalDeclarations> { + let (template_name, _) = + self.subcmps.get(var).ok_or_else(|| anyhow!("subcomponent '{var}' not found"))?; + info.find_template(template_name) + } +} + +/// The [TemplateContext] must deal with the block context stack in both functions for circom +/// scope handling. +/// +/// Note: The [GenWithCircomScopeHandling] trait requires mutable references in several places to +/// support `gen_llzk_in_function()` where the [FunctionContext] is passed as a mutable reference. +/// However, the [TemplateContext] instead uses internal mutability and is passed as an immutable +/// reference to the `gen_llzk_in_template()` functions. Thus, this trait cannot be implemented for +/// `TemplateContext` and is instead implemented for `&TemplateContext` which means its functions +/// must be called via a `&mut &TemplateContext` reference. +impl<'ctx, 'str, 'func, 'blk, 'val> GenWithCircomScopeHandling<'ctx, 'func, 'blk, 'val> + for &TemplateContext<'_, 'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type BlockType = TemplateFuncPair>; + type HandlerDataType = TemplateFuncPair>; + + fn stack_top(&self) -> Self::BlockType { + TemplateFuncPair { + compute: self.compute.as_ref().map(|rc| *rc.borrow().block_ctx.top_block()), + constrain: self.constrain.as_ref().map(|rc| *rc.borrow().block_ctx.top_block()), + } + } + + fn stack_push(&mut self, block: Self::BlockType) { + if let Some(rc) = self.compute.as_ref() { + rc.borrow_mut().block_ctx.push(block.compute.unwrap()) + } + if let Some(rc) = self.constrain.as_ref() { + rc.borrow_mut().block_ctx.push(block.constrain.unwrap()) + } + } + + fn stack_pop( + &mut self, + overwrite_handler: H, + overwrite_data: &mut Self::HandlerDataType, + ) -> Result<()> + where + H: Fn( + &mut BlockGenContext<'_, 'ctx, 'blk, 'val>, + &mut NestedBlockInfo<'ctx, 'blk, 'val>, + HashMap>, + ) -> Result<()>, + { + // Note: even when `self.X` is Some, `overwrite_data.X` may be None because of + // `gen_in_given_block_with_new_circom_scope_and_merge_overwrites()` using + // `HandlerDataType::default()`. The overwrite handler in that function ignores the + // `NestedBlockInfo` passed to it so just insert a default `NestedBlockInfo`. + if let Some(rc) = self.compute.as_ref() { + let mut fc = rc.borrow_mut(); + let popped = fc.block_ctx.pop(); + overwrite_handler( + &mut fc, + overwrite_data.compute.get_or_insert_with(NestedBlockInfo::default), + popped, + )?; + } + if let Some(rc) = self.constrain.as_ref() { + let mut fc = rc.borrow_mut(); + let popped = fc.block_ctx.pop(); + overwrite_handler( + &mut fc, + overwrite_data.constrain.get_or_insert_with(NestedBlockInfo::default), + popped, + )?; + } + Ok(()) + } +} + +/// For both the "@compute" and "@constrain" functions, holds the result (SSA Value or list thereof, +/// per the type aliases below) that comes from generating LLZK for a circom Expression within a +/// template. +#[derive(Debug)] +pub struct GenResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, ResultType> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + /// Reference to the template context in which the expression was generated. + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + /// Result for the "@compute" function. + compute_res: ShouldGenerate, + /// Result for the "@constrain" function. + constrain_res: ShouldGenerate, +} + +/// Alias for [GenResult] containing a single SSA Value result. +type GenResultSingleVal<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> = + GenResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, Value<'ctx, 'val>>; + +/// Alias for [GenResult] containing a list of SSA Value results. +type GenResultMultiVal<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> = + GenResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, Vec>>; + +/// Alias for [GenResult] containing the unit type (i.e. nothing). +type GenResultUnit<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> = + GenResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, ()>; + +/// This trait abstracts over the output type of [Chainable::and_then] to allow a single +/// implementation of that function to produce different result types depending on the callback +/// function type provided. +trait ChainResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> { + /// Output type of the generator callback functions. + type HandlerOutput; + + /// Combines results from the "@compute" and "@constrain" generator functions into the final + /// result of [Chainable::and_then]. + fn produce( + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + compute_res: ShouldGenerate, + constrain_res: ShouldGenerate, + ) -> Self; +} + +/// Support [Chainable::and_then] producing a [GenResult]. This allows for chaining another +/// generator function on this result whose input is `ResultType`. +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, ResultType> + ChainResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> + for GenResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, ResultType> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + type HandlerOutput = ResultType; + + fn produce( + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + compute_res: ShouldGenerate, + constrain_res: ShouldGenerate, + ) -> Self { + GenResult::<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, ResultType> { + template, + compute_res, + constrain_res, + } + } +} + +/// Support [Chainable::and_then] producing `()` which can be used when nothing further is generated +/// from the result and there is no [Value] available to return within the generator function. +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> + ChainResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> for () +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + type HandlerOutput = (); + + fn produce( + _: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + _: ShouldGenerate, + _: ShouldGenerate, + ) -> Self { + } +} + +/// This trait provides a clean interface for chaining multiple code generation steps. It abstracts +/// away most of the complexity (unwrapping, is_some assertions, etc.) that result from +/// [GenResult] containing optional results for both "@compute" and "@constrain" functions. +trait Chainable<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + /// Input type of the generator callback functions. + type HandlerInput; + + /// Applies the compute and constrain generator functions to the current result, producing + /// a new [ChainResult]. + fn and_then>( + self, + gen_compute: F1, + gen_constrain: F2, + ) -> Result + where + F1: FnOnce( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + F2: FnOnce( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result; + + /// Delegates to [Self::and_then] with the same handler for both compute and constrain. + #[inline] + fn and_then_same>( + self, + handle: F, + ) -> Result + where + Self: Sized, + F: Fn( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + { + self.and_then::<&F, &F, CR>(&handle, &handle) + } +} + +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> + GenResultMultiVal<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + /// Create an empty [GenResultMultiVal] (i.e. an [GenResult] where the result + /// is a vector of SSA Values). + #[inline] + fn new(template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>) -> Self { + GenResult { + template, + // This construction ensures that the result vectors are only created + // if the corresponding template functions "ShouldGenerate". + compute_res: template.compute.as_ref().map(|_| Vec::new()), + constrain_res: template.constrain.as_ref().map(|_| Vec::new()), + } + } + + /// Create an [GenResultMultiVal] populated by generating LLZK for each [Expression] given. + #[inline] + fn gen_exprs<'ast, I>( + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + exprs: I, + ) -> Result + where + I: IntoIterator, + { + let mut result = Self::new(template); + for e in exprs { + let r = e.gen_llzk_in_template(codegen, template)?; + // Note: `unwrap()` is safe so long as the contract is followed that + // `self.X_res` is None if and only if `self.template.X` is also None + // since these all use the same template instance. + if let Some(v) = result.compute_res.as_mut() { + v.push(r.compute_res.unwrap()) + } + if let Some(v) = result.constrain_res.as_mut() { + v.push(r.constrain_res.unwrap()) + } + } + Ok(result) + } +} + +/// Implementation of [Chainable] for any [GenResult]. +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, T> + Chainable<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> + for GenResult<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r, T> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + type HandlerInput = T; + + fn and_then>( + self, + gen_compute: F1, + gen_constrain: F2, + ) -> Result + where + F1: FnOnce( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + F2: FnOnce( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + { + let template = self.template; + let compute_res: ShouldGenerate = self + .compute_res + .map(|v| { + // Note: `unwrap()` is safe so long as the contract is followed that + // `self.X_res` is None if and only if `self.template.X` is also None. + gen_compute(&mut template.compute.as_ref().unwrap().borrow_mut(), v) + }) + .transpose()?; + let constrain_res: ShouldGenerate = self + .constrain_res + .map(|v| { + // Note: `unwrap()` is safe so long as the contract is followed that + // `self.X_res` is None if and only if `self.template.X` is also None. + gen_constrain(&mut template.constrain.as_ref().unwrap().borrow_mut(), v) + }) + .transpose()?; + Ok(CR::produce(template, compute_res, constrain_res)) + } +} + +/// Implementation of [Chainable] for a [TemplateContext]. Useful when there is no initial +/// [GenResult] to chain onto. +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> Chainable<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> + for &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + type HandlerInput = (); + + fn and_then>( + self, + gen_compute: F1, + gen_constrain: F2, + ) -> Result + where + F1: FnOnce( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + F2: FnOnce( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + { + let compute_res: ShouldGenerate = + self.compute.as_ref().map(|fc| gen_compute(&mut fc.borrow_mut(), ())).transpose()?; + let constrain_res: ShouldGenerate = self + .constrain + .as_ref() + .map(|fc| gen_constrain(&mut fc.borrow_mut(), ())) + .transpose()?; + Ok(CR::produce(self, compute_res, constrain_res)) + } +} + +impl<'decls, 'ctx, 'func, 'blk, 'val, 'str> DimExprConverter<'ctx, 'val> + for TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + fn get_var_decl_types(&self) -> &HashMap> { + self.var_decl_types + } + + fn poly_template_binding_names( + &self, + ) -> impl IntoIterator>)> { + self.template_def + .const_binding_ops() + .into_iter() + .map(|binding| (binding.name().to_owned(), binding.type_opt())) + } + + fn callback_store_poly_expr( + &self, + _: String, + op: TemplateExprOp<'ctx>, + ) -> StringAttribute<'ctx> { + // Use `symbol_table::insert` instead of direct insertion to rename duplicates + get_poly_expr_name(&symbol_table::insert(&self.template_def, op.into())) + } + + fn get_dim_expr( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + expr: &Expression, + ) -> Result> { + // First try to compute statically, falling back to literal computation if all values are + // not compile-time constants or if the final result does not properly convert to i64. + if let Some(integer) = shared::try_compute_as_i64(expr, codegen.prime())? { + ArrayDimensionResult::new(codegen.index_attr(integer).into(), &[]) + } else { + #[allow(unused_variables)] // TODO: TEMP + match expr { + Expression::Number(_, _) => { + unreachable!("handled by try_compute_as_i64") + } + Expression::Variable { meta, name, access } if access.is_empty() => { + // Grab the template symbol binding name if it exists (first try `poly.param` + // name then try `poly.expr` name). Otherwise, defer to `BlockGenContext`. + if self.template_def.has_const_param_named(name) { + ArrayDimensionResult::new(codegen.flat_sym(name).into(), &[]) + } else { + let expr_name = dim_expr_name(expr); + if self.template_def.has_const_expr_named(&expr_name) { + ArrayDimensionResult::new(codegen.flat_sym(expr_name).into(), &[]) + } else { + ArrayDimensionResult::insufficient_data_result() // defer to `BlockGenContext` + } + } + } + // Variable case with non-empty `access` + Expression::Variable { .. } + | Expression::InlineSwitchOp { .. } + | Expression::PrefixOp { .. } + | Expression::InfixOp { .. } + | Expression::Call { .. } => self.gen_template_poly_expr(codegen, expr), + // The remaining cases do not produce a scalar value. + // i.e. ParallelOp, ArrayInLine, UniformArray, BusCall, AnonymousComp, Tuple + // Give the same error that the circom type checker gives. The type checker ran + // earlier so this should technically be unreachable. + _ => { + unreachable!("Array indexes and lengths must produce a scalar value") + } + } + } + } +} + +/// A trait to generate LLZK IR from the body of a circom template. +/// +/// 'ctx: lifetime of the `LlzkContext` and generated `Module` +/// 'str: lifetime of the generated `StructDefOp` +/// 'func: lifetime of the generated `FuncDefOp` instances within the struct +/// 'blk: lifetime of the generated `Block` instances within functions +/// 'val: lifetime of the generated `Value` or `Operation` instances within blocks +pub trait GenerateLLZKInTemplate<'decls, 'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'decls, + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + /// Output type of the generator function. [Statement] nodes do not produce a value so this + /// should be the unit type whereas [Expression] nodes produce a Value. + type Output<'r> + where + 'decls: 'r, + 'val: 'r, + 'val: 'blk; + + /// Generates LLZK IR from [Statement] and [Expression] nodes in a circom template. + fn gen_llzk_in_template<'r>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + ) -> Result> + where + 'val: 'r; +} + +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val> + GenerateLLZKInTemplate<'decls, 'ctx, 'str, 'func, 'blk, 'val> for [Statement] +where + 'ctx: 'decls, + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + type Output<'r> + = () + where + 'decls: 'r, + 'val: 'r; + + fn gen_llzk_in_template<'r>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + ) -> Result> + where + 'val: 'r, + { + for s in self { + s.gen_llzk_in_template(codegen, template)?; + // circom allows unreachable code after a return but it is not processed + // (e.g. `assert(1 == 0)` after a return does not cause an error as it normally + // would) so replicate the same behavior here by stopping processing after a + // return (which is also what MLIR expects, no code after a terminator op). + if matches!(s, Statement::Return { .. }) { + break; + } + } + Ok(()) + } +} + +/// Generate LLZK code for a circom [Statement::IfThenElse]. +fn gen_if_then_else<'ctx, 'str, 'func, 'blk, 'val, 'r>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'_, 'ctx, 'str, 'func, 'blk, 'val>, + meta: &Meta, + cond: &Expression, + if_case: &Statement, + else_case: &Option>, +) -> Result<()> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + let mut template = template; // satisfy the &mut in `GenWithCircomScopeHandling` + + // Initially, generate the blocks for the 'then' and 'else' cases naively. + let mut then_info = TemplateFuncPair::new(template); + template.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + then_info.block(), + |template| if_case.gen_llzk_in_template(codegen, template), + &mut then_info, + )?; + let mut else_info = TemplateFuncPair::new(template); + if let Some(else_case) = else_case { + template.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + else_info.block(), + |template| else_case.gen_llzk_in_template(codegen, template), + &mut else_info, + )?; + } + + // Generate LLZK for the condition and create a GenResult that encapsulates the condition's + // `GenResultSingleVal` along with the `NestedBlockInfo` for both blocks. + let blocks_and_cond = { + let cond_result = cond.gen_llzk_in_template(codegen, template)?; + let t_compute = then_info.compute; + let t_constrain = then_info.constrain; + let e_compute = else_info.compute; + let e_constrain = else_info.constrain; + // The unwraps are safe since `then_info` and `else_info` were created from + // the same `TemplateContext` used for `map()` below. + GenResult { + template, + compute_res: template.compute.as_ref().map(|_| { + (t_compute.unwrap(), e_compute.unwrap(), cond_result.compute_res.unwrap()) + }), + constrain_res: template.constrain.as_ref().map(|_| { + (t_constrain.unwrap(), e_constrain.unwrap(), cond_result.constrain_res.unwrap()) + }), + } + }; + // Generate the actual LLZK `scf.if` operations in both functions. + blocks_and_cond.and_then_same(|fc, (then_info, else_info, condition)| { + fc.gen_scf_if(codegen, codegen.location_from_meta(meta), condition, then_info, else_info) + }) +} + +/// Generate LLZK code for a circom [Statement::While]. +fn gen_while<'ctx, 'str, 'func, 'blk, 'val, 'r>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'_, 'ctx, 'str, 'func, 'blk, 'val>, + meta: &Meta, + cond: &Expression, + body_stmt: &Statement, + loop_bounds: Option>, +) -> Result<()> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + let mut template = template; // satisfy the &mut in `GenWithCircomScopeHandling` + + // Generate the loop condition (i.e. "before") and body (i.e. "after") blocks naively. + let mut loop_cond_info = TemplateFuncPair::new(template); + let cond_result = template.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + loop_cond_info.block(), + |template| cond.gen_llzk_in_template(codegen, template), + &mut loop_cond_info, + )?; + let mut loop_body_info = TemplateFuncPair::new(template); + template.gen_in_given_block_with_new_circom_scope_and_cache_overwrites( + loop_body_info.block(), + |template| body_stmt.gen_llzk_in_template(codegen, template), + &mut loop_body_info, + )?; + + // Create a GenResult that encapsulates both `loop_body_info` and `loop_cond_info` and + // then call the function to generate the `scf.while` loop in both functions. + let cond_and_body = { + let r_compute = cond_result.compute_res; + let r_constrain = cond_result.constrain_res; + let c_compute = loop_cond_info.compute; + let c_constrain = loop_cond_info.constrain; + let b_compute = loop_body_info.compute; + let b_constrain = loop_body_info.constrain; + // The unwraps are safe since these GenResult and TemplateFuncPair instances + // were created from the same `TemplateContext` used for `map()` below. + GenResult { + template, + compute_res: template + .compute + .as_ref() + .map(|_| (r_compute.unwrap(), c_compute.unwrap(), b_compute.unwrap())), + constrain_res: template + .constrain + .as_ref() + .map(|_| (r_constrain.unwrap(), c_constrain.unwrap(), b_constrain.unwrap())), + } + }; + cond_and_body.and_then_same(|fc, (condition, loop_cond_info, loop_body_info)| { + fc.gen_scf_while( + codegen, + codegen.location_from_meta(meta), + condition, + loop_cond_info, + loop_body_info, + loop_bounds, + ) + .map(drop) // ignore the bool result + }) +} + +/// Generate LLZK code for a circom [Statement::InitializationBlock]. +/// This is needed to support the `try_for_loop_heuristic` macro. +#[inline] +fn gen_init_block<'ctx, 'str, 'func, 'blk, 'val, 'r>( + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'_, 'ctx, 'str, 'func, 'blk, 'val>, + initializations: &[Statement], +) -> Result<()> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + initializations.gen_llzk_in_template(codegen, template) +} + +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val> + GenerateLLZKInTemplate<'decls, 'ctx, 'str, 'func, 'blk, 'val> for Statement +where + 'ctx: 'decls, + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + type Output<'r> + = () + where + 'decls: 'r, + 'val: 'r; + + fn gen_llzk_in_template<'r>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + ) -> Result> + where + 'val: 'r, + { + let _guard = codegen.trace_statement(self); + match self { + Statement::InitializationBlock { initializations, .. } => { + gen_init_block(codegen, template, initializations) + } + Statement::Declaration { meta, name, dimensions, .. } => { + template.and_then_same(|fc, _| { + if !template.is_subcmp(name) { + return fc.gen_declaration(codegen, meta, name, dimensions); + } + Ok(()) + }) + } + Statement::Block { meta, stmts } => { + let mut template = template; // satisfy the &mut in `GenWithCircomScopeHandling` + template.gen_in_current_block_with_new_circom_scope_and_merge_overwrites( + |template| { + try_for_loop_heuristic!(codegen, template, meta, stmts); + // Fallback to standard block handling. + stmts.gen_llzk_in_template(codegen, template) + }, + ) + } + Statement::Substitution { meta, var, access, op, rhe } => { + // Since there's no simple assignment in LLZK, just update the mapped Value + // which essentially propagates the assignment. + match op { + AssignOp::AssignVar => { + if access.is_empty() { + rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, mut val| { + // Unify-cast the value if the variable is a subcomponent. + if template.is_subcmp(var) { + let subcmp_type = template.get_subcmp_type(var)?; + + if val.r#type() != subcmp_type { + val = fc.unifiable_cast( + codegen.location_from_meta(meta), + val, + template.get_subcmp_type(var)?, + )?; + } + }; + + fc.handle_simple_assignment(codegen, meta, var, val) + }, + |fc, val| { + // Ignore subcmp vars in the constraint function since they are + // handled separately in the prologue and epilogue. + if !template.is_subcmp(var) { + fc.handle_simple_assignment(codegen, meta, var, val)?; + } + Ok(()) + }, + ) + } else { + let location = codegen.location_from_meta(meta); + rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, val| { + WriteChain::new(var, Root::Var, access).write( + val, + WriteTarget::Compute, + codegen, + fc, + location, + template, + template, + ) + }, + |fc, val| { + WriteChain::new(var, Root::Var, access).write( + val, + WriteTarget::Constrain, + codegen, + fc, + location, + template, + template, + ) + }, + ) + } + } + AssignOp::AssignSignal => { + match &access[..] { + [] => { + // The `<--` operator is witness generation only so code for the RHS + // expression should only be generated in the compute function. + // The constrain function just reads that field from "self" struct. + // However, we already inserted these reads at the beginning of the + // constrain function in `gen_template_llzk`. + let signal_type = template.get_signal_type(var)?; + rhe.gen_llzk_in_template(codegen, &template.compute_only())? + .and_then_same(|fc, val| { + let location = codegen.location_from_meta(meta); + // Cast value to signal type if needed. + let value = fc.cast_to_expected_type_if_needed( + codegen, + location, + val, + signal_type, + )?; + // Write value to field of "self" struct. + let self_val = fc.func.self_value_of_compute()?; + fc.append_op_no_result( + r#struct::writem(location, self_val, var, value)? + .into(), + )?; + fc.block_ctx.set_named_value(var.clone(), value) + }) + } + access => rhe + .gen_llzk_in_template(codegen, &template.compute_only())? + .and_then_same(|fc, rhe| { + WriteChain::new(var, Root::Signal, access).write( + rhe, + WriteTarget::Compute, + codegen, + fc, + codegen.location_from_meta(meta), + template, + template, + ) + }), + } + } + AssignOp::AssignConstraintSignal => { + match &access[..] { + [] => { + let signal_type = template.get_signal_type(var)?; + rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, val| { + let location = codegen.location_from_meta(meta); + // Cast value to field type if needed. + let value = fc.cast_to_expected_type_if_needed( + codegen, + location, + val, + signal_type, + )?; + // Write value to field of "self" struct. + let self_val = fc.func.self_value_of_compute()?; + fc.append_op_no_result( + r#struct::writem(location, self_val, var, value)? + .into(), + )?; + fc.block_ctx.set_named_value(var.clone(), value) + }, + |fc, val| { + // Get value of field from "self" struct (already generated + // at the beginning of the constrain function, see + // `gen_template_llzk`) and generate equality constraint + // with 'val'. + let signal_val = *fc.block_ctx.get_named_value(var)?; + fc.append_constrain_eq( + codegen, + codegen.location_from_meta(meta), + signal_val, + val, + ) + }, + ) + } + access => rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, rhv| { + WriteChain::new(var, Root::Signal, access).write( + rhv, + WriteTarget::Compute, + codegen, + fc, + codegen.location_from_meta(meta), + template, + template, + ) + }, + |fc, rhv| { + let location = codegen.location_from_meta(meta); + let lhv = Lvalue::new(var, Root::Signal, access) + .get_value(codegen, fc, template, location, None)?; + fc.append_op_no_result(constrain::eq(location, lhv, rhv).into()) + }, + ), + } + } + } + } + Statement::UnderscoreSubstitution { op, rhe, .. } => { + // The `<--` operator is witness generation only so this should not + // generate any code in the constrain function. + let template = + if AssignOp::AssignSignal == *op { &template.compute_only() } else { template }; + // Just visit and drop the resulting GenResultSingleVal since the value is unused. + rhe.gen_llzk_in_template(codegen, template).map(drop) + } + Statement::ConstraintEquality { meta, lhe, rhe } => { + // This statement is only relevant to the "@constrain" function. + let template = template.constrain_only(); + // Generate Value for both sides and then generate the constraint op. + GenResultMultiVal::gen_exprs(&template, codegen, [lhe, rhe])?.and_then_same( + |fc, vals| { + fc.append_constrain_eq( + codegen, + codegen.location_from_meta(meta), + vals[0], + vals[1], + ) + }, + ) + } + Statement::IfThenElse { meta, cond, if_case, else_case } => { + gen_if_then_else(codegen, template, meta, cond, if_case, else_case) + } + Statement::While { meta, cond, stmt } => { + gen_while(codegen, template, meta, cond, stmt, None) + } + Statement::Assert { meta, arg } => { + arg.gen_llzk_in_template(codegen, template)?.and_then_same(|fc, val| { + fc.append_assert(codegen, codegen.location_from_meta(meta), val) + }) + } + Statement::LogCall { meta, .. } => { + codegen.emit_circom_warning( + meta, + "log calls are not currently supported in LLZK", + ReportCode::NotAllowedOperation, + ); + Ok(()) + } + Statement::MultSubstitution { .. } => { + unreachable!("removed by 'syntax_sugar_remover'") + } + Statement::Return { .. } => { + // per `type_analysis/src/analyzers/no_returns_in_template.rs` + unreachable!("return statements are not allowed in templates") + } + } + } +} + +/// Counts the size of the inputs. +fn count_input_size<'ctx>( + subcmp_struct_type: StructType<'ctx>, + template_name: &str, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, +) -> Result> { + let template_data = codegen + .program + .get_templates(false) + .into_iter() + .find(|t| t.get_name() == template_name) + .ok_or_else(|| anyhow::anyhow!("template '{template_name}' not found"))?; + let template_params = TmplParamsInstance::new( + template_data.get_name_of_params(), + subcmp_struct_type.params_vec(), + ); + + fn acc_add<'ctx>( + acc: TypeSizeExpr<'ctx>, + signal_size: Result>, + ) -> Result> { + Ok(acc.add(signal_size?)) + } + template_data + .get_declaration_inputs() + .iter() + .map(|(signal_name, _)| -> Result> { + let signal_type = template_params + .map_type(codegen.get_input_signal_type(template_name, signal_name)?)?; + codegen.count_input_signals(signal_type) + }) + .try_fold(TypeSizeExpr::zero(), acc_add) +} + +impl<'decls, 'ctx, 'str, 'func, 'blk, 'val> + GenerateLLZKInTemplate<'decls, 'ctx, 'str, 'func, 'blk, 'val> for Expression +where + 'ctx: 'decls, + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'blk, +{ + type Output<'r> + = GenResultSingleVal<'decls, 'ctx, 'str, 'func, 'blk, 'val, 'r> + where + 'decls: 'r, + 'val: 'r; + + fn gen_llzk_in_template<'r>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'decls, 'ctx, 'str, 'func, 'blk, 'val>, + ) -> Result> + where + 'val: 'r, + { + // This function handles the special cases that happen in templates and any other kind of + // expression is delegated. + match self { + Expression::ParallelOp { rhe, .. } => { + // `parallel` is a tag used to generate parallelized code for the C++ + // witness generator. Since LLZK currently has no such hint, + // we simply generate the underlying expression. + rhe.gen_llzk_in_template(codegen, template) + } + Expression::Call { meta, id, args, .. } + if meta.get_type_knowledge().is_component() + && codegen.program.contains_template(id) => + { + let scope = CtorCallScope::new(meta, id, args, codegen, template)?; + + // Create a `pod.new` operation with the memory for the subcomponent. + template.and_then( + |fc, _| { + scope.emit_ctor_call(codegen, fc, |fc, params_pod| { + // If the count == 0 means that the subcomponent has no inputs. In that + // case we call `@compute` here directly and store it into COMP. + Ok(if scope.count.is_const_zero() { + let empty_inputs = fc.append_op_unnamed_result(pod::new( + codegen.op_builder(), + scope.location, + &[], + Some(codegen.pod_type(&[])), + ))?; + // We can't have this call inside the constraint function. + let instance = fc.gen_compute_call( + scope.subcmp_type.r#type().try_into()?, + empty_inputs, + params_pod, + scope.location, + codegen, + )?; + vec![(COMP, instance)] + } else { + vec![ + ( + COUNT, + scope.count.to_index_value( + codegen, + fc, + scope.location, + Some(&scope.tmpl_params_instance(codegen)), + )?, + ), + (PARAMS, params_pod), + ] + }) + }) + }, + |fc, _| scope.emit_ctor_call(codegen, fc, |_, _| Ok(vec![])), + ) + } + Expression::UniformArray { meta, value, dimension } => { + let location = codegen.location_from_meta(meta); + let template_dim_res = template.get_dim_expr(codegen, dimension)?; + let value = value.gen_llzk_in_template(codegen, template)?; + value.and_then_same(|fc, value| { + // Try to convert in template first, defer to function context if unsuccessful. + let final_dim = match &template_dim_res { + ArrayDimensionResult::Computed(array_dimension) => array_dimension, + ArrayDimensionResult::InsufficientData => &fc + .get_dim_expr(codegen, dimension) + .and_then(ArrayDimension::try_from)?, + }; + fc.generate_uniform_array(codegen, location, value, final_dim) + }) + } + // Delegate any other kind of expression to the implementation in `function.rs`. + expr => { + template.and_then_same(|fc, _| expr.gen_llzk_in_block(codegen, fc, template.into())) + } + } + } +} + +/// Groups the related data for emitting IR for subcomponent constructor calls. +/// +/// Exposes helper functions for emitting the common parts of the IR between the `compute` and +/// `constrain` functions. +struct CtorCallScope<'ast, 'ctx, 'val> { + /// Name of the subcomponent's template + id: &'ast str, + /// Source location. + location: Location<'ctx>, + /// Dimensions derived from the template parameters. + dimensions: ArrayDimensions<'ctx, 'val>, + /// Type of the subcomponent. + subcmp_type: SubcmpType<'ctx>, + /// The input size count of the subcomponent. + count: TypeSizeExpr<'ctx>, + /// Subcomponent memory pod type. + pod_type: PodType<'ctx>, +} + +impl<'ast, 'ctx, 'val> CtorCallScope<'ast, 'ctx, 'val> { + /// Creates a new scope. + fn new( + meta: &'ast Meta, + id: &'ast str, + args: &'ast [Expression], + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &TemplateContext<'_, 'ctx, '_, '_, '_, 'val>, + ) -> Result { + let location = codegen.location_from_meta(meta); + let dimensions = template.get_dim_exprs(codegen, args)?; + let subcmp_struct_type = dimensions.struct_type_with_concrete_dimensions(codegen, id); + let subcmp_type = SubcmpType::new(subcmp_struct_type.into(), id.to_owned()); + + let count = count_input_size(subcmp_struct_type, id, codegen)?; + + let records = subcmp_type.comp_pod_records(codegen); + let pod_type = codegen.pod_type(&records); + Ok(Self { id, location, dimensions, subcmp_type, count, pod_type }) + } + + /// Returns the list of formals associated to the template parameters. + fn params_formals<'f>( + &self, + codegen: &LlzkCodegen<'f, 'ctx, impl ProgramLike>, + ) -> &'f [String] { + codegen.program.get_template_data(self.id).get_name_of_params() + } + + /// Returns an instance of a template parameters instance map. + fn tmpl_params_instance<'f>( + &self, + codegen: &LlzkCodegen<'f, 'ctx, impl ProgramLike>, + ) -> TmplParamsInstance<'f, 'ctx> { + TmplParamsInstance::new(self.params_formals(codegen), &self.dimensions) + } + + /// Emits IR for reading a template parameter, represented by the given attribute. + fn emit_param_op( + &self, + attr: Attribute<'ctx>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'_, 'ctx, '_, '_, 'val>, + ) -> Result> { + let op = type_switch! { attr, + IntegerAttribute as int => codegen.new_felt_const_op(&BigInt::from(int.value()), self.location)?, + FlatSymbolRefAttribute as sym => poly::read_const(self.location, sym.value(), self.subcmp_type.param_type(codegen)), + else => unreachable!("Attribute {}", attr) + }; + fc.append_op_unnamed_result(op) + } + + /// Emits IR representing a read of each template parameter. + fn emit_params<'decls, 'func, 'blk>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + ) -> Result>> { + std::iter::zip(self.params_formals(codegen), self.dimensions.attrs()) + .map(|(formal, attr)| { + let value = self.emit_param_op(attr, codegen, fc)?; + Ok(RecordValue::new(StringRef::new(formal), value)) + }) + .collect() + } + + /// Shared parts between the constraint and compute functions when emitting IR for subcomponent + /// constructor calls. + /// + /// Accepts a callback that returns the list of initialized records in the subcomponent memory + /// pod. + fn emit_ctor_call<'r, 'decls, 'str, 'func, 'blk>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + mut cb: impl FnMut( + &mut FunctionContext<'decls, 'ctx, 'func, 'blk, 'val>, + Value<'ctx, 'val>, + ) -> Result)>>, + ) -> Result> { + let params = self.emit_params(codegen, fc)?; + let params_pod = fc.append_op_unnamed_result(pod::new( + codegen.op_builder(), + self.location, + ¶ms, + Some(self.subcmp_type.params_pod_type(codegen)), + ))?; + + let records = cb(fc, params_pod)? + .into_iter() + .map(|(name, value)| RecordValue::new(StringRef::new(name), value)) + .collect::>(); + + fc.append_op_unnamed_result(pod::new( + codegen.op_builder(), + self.location, + &records, + Some(self.pod_type), + )) + } +} diff --git a/llzk_backend/src/template_ext.rs b/llzk_backend/src/template_ext.rs new file mode 100644 index 000000000..f4e773719 --- /dev/null +++ b/llzk_backend/src/template_ext.rs @@ -0,0 +1,405 @@ +//! Extensions for the [`TemplateData`] and [`TemplateInstance`] types. + +use crate::module::DeclarationInfo; +use crate::program_ext::ProgramLike; +use crate::shared::LlzkCodegen; +use crate::subcmp::SubcmpDeclInfo; +use crate::template::GenerateLLZKInTemplate; +use crate::template::TemplateContext; +use anyhow::anyhow; +use anyhow::bail; +use anyhow::Result; +use compiler::hir::very_concrete_program::ClusterType; +use compiler::hir::very_concrete_program::TemplateInstance; +use compiler::hir::very_concrete_program::Wire; +use llzk::prelude::Attribute; +use llzk::prelude::Location; +use num_bigint_dig::BigInt; +use num_traits::FromPrimitive; +use program_structure::ast::AssignOp::AssignVar; +use program_structure::ast::Expression; +use program_structure::ast::Meta; +use program_structure::ast::SignalType; +use program_structure::ast::Statement; +use program_structure::ast::VariableType; +use program_structure::template_data::TemplateData; +use program_structure::wire_data::WireData; +use program_structure::wire_data::WireType; +use std::borrow::Cow; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::slice; + +/// Trait for IR objects that can have signal declarations on them. +pub trait SignalDeclarations { + /// Returns true if the signal is an input. + /// + /// Returns false if the signal is not an input or is not declared. + fn signal_is_input(&self, name: &str) -> bool; +} + +impl SignalDeclarations for T { + fn signal_is_input(&self, name: &str) -> bool { + self.get_inputs().contains_key(name) + } +} + +/// A trait that allows common handling of structs/enums that represent template +/// inputs or outputs. +pub trait WireLike: Clone + std::fmt::Debug { + /// Type of the wire (signal or bus). + fn get_type(&self) -> WireType; +} + +/// A trait that allows common handling of the structs used to represent a circom +/// template at different stages in the compilation process. +pub trait TemplateLike: std::fmt::Debug { + /// The type used to represent wires. + type WireDataType: WireLike; + + /// Generate the LLZK Location for the template definition. + fn get_location<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Location<'ctx>; + + /// Get the name of the template. + fn get_name(&self) -> &str; + + /// Get the names of the parameters of the template. + fn get_name_of_params(&self) -> &[String]; + + /// Get the initial subcomponent declarations of the template (outside the body), + /// if any. + fn get_init_subcmp_decls<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result>>; + + /// Get the body statements of the template. + fn get_body(&self) -> &[Statement]; + + /// Construct [DeclarationInfo] containing var and signal declarations + /// found in this template body. + fn get_declarations<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result>; + + /// Returns the inputs in declaration order, containing name and number of dimensions. + fn get_declaration_inputs(&'_ self) -> Cow<'_, [(String, usize)]>; + + /// Gets the index of the input signal with the given name from `get_declaration_inputs()`. + fn get_declaration_input_idx(&self, signal_name: &str) -> Result { + self.get_declaration_inputs() + .iter() + .enumerate() + .find_map(|(idx, (s, _))| (signal_name == s).then_some(idx)) + .ok_or_else(|| { + anyhow::anyhow!("no input signal '{signal_name}' in template '{}'", self.get_name()) + }) + } + + /// Returns the inputs of the template. + fn get_inputs(&'_ self) -> Cow<'_, HashMap>; + + /// Returns the outputs of the template. + fn get_outputs(&'_ self) -> Cow<'_, HashMap>; + + /// Returns information about a concrete input. + fn get_input_info(&'_ self, name: &str) -> Option> { + match self.get_inputs() { + Cow::Borrowed(i) => i.get(name).map(Cow::Borrowed), + Cow::Owned(i) => i.get(name).cloned().map(Cow::Owned), + } + } + + /// Generate any LLZK code needed in the beginning of the template. + fn gen_preamble<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &TemplateContext<'_, 'ctx, '_, '_, '_, '_>, + ) -> Result<()>; +} + +impl TemplateLike for TemplateData { + type WireDataType = WireData; + + fn get_location<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Location<'ctx> { + codegen.location(self.get_file_id(), self.get_param_location()) + } + + fn get_name(&self) -> &str { + self.get_name() + } + + fn get_name_of_params(&self) -> &[String] { + self.get_name_of_params() + } + + fn get_body(&self) -> &[Statement] { + self.get_body_as_vec() + } + + fn get_init_subcmp_decls<'ctx>( + &self, + _codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result>> { + Ok(HashMap::new()) + } + + fn get_declarations<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result> { + DeclarationInfo::from_template(codegen, self) + } + + fn get_declaration_inputs(&'_ self) -> Cow<'_, [(String, usize)]> { + Cow::Borrowed(self.get_declaration_inputs()) + } + + fn get_inputs(&'_ self) -> Cow<'_, HashMap> { + Cow::Borrowed(self.get_inputs()) + } + + fn get_outputs(&'_ self) -> Cow<'_, HashMap> { + Cow::Borrowed(self.get_outputs()) + } + + fn get_input_info(&'_ self, name: &str) -> Option> { + self.get_input_info(name).map(Cow::Borrowed) + } + + fn gen_preamble<'ctx>( + &self, + _codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + _template: &TemplateContext<'_, 'ctx, '_, '_, '_, '_>, + ) -> Result<()> { + Ok(()) + } +} + +impl WireLike for WireData { + fn get_type(&self) -> WireType { + self.get_type() + } +} + +impl TemplateLike for TemplateInstance { + type WireDataType = Wire; + + fn get_location<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Location<'ctx> { + codegen.location_unknown() + } + + fn get_name(&self) -> &str { + &self.template_header // this one is unique, but `template_name` is not + } + + fn get_name_of_params(&self) -> &[String] { + &[] + } + + fn get_body(&self) -> &[Statement] { + slice::from_ref(&self.code) + } + + fn get_init_subcmp_decls<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result>> { + let mut subcmp_decls = HashMap::new(); + let location = codegen.location_unknown(); + // Create the declarations, but they currently have no types + for component in &self.components { + let name = &component.name; + let dimensions = component + .lengths + .iter() + .map(|len| -> Result { + let idx = codegen.index_attr(i64::try_from(*len)?); + Ok(idx.into()) + }) + .collect::>>()?; + let subcmp_decl = SubcmpDeclInfo::new(dimensions, location); + + if subcmp_decls.insert(name.clone(), subcmp_decl).is_some() { + bail!("Subcomponent {name} declared twice"); + } + } + // Get the types for the declarations + for cluster in &self.clusters { + if let Some(subcmp_decl) = subcmp_decls.get_mut(&cluster.cmp_name) { + match &cluster.xtype { + // Mixed instantiation is also not supported in [DeclarationInfo::complete] + ClusterType::Mixed { .. } => { + todo!("Support mixed type subcomponent instantiations") + } + ClusterType::Uniform { header, .. } => { + subcmp_decl.instances_mut().push(codegen.struct_type(header)); + } + } + } + } + Ok(subcmp_decls) + } + + fn get_declarations<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result> { + let mut declarations = DeclarationInfo::from_template(codegen, self)?; + let location = self.get_location(codegen); + for w in &self.wires { + match w { + Wire::TSignal(signal) => declarations.visit_signal_or_bus_impl( + codegen, + &signal.xtype, + &signal.name, + location, + codegen + .type_from_dimension_consts(codegen.felt_type().into(), &signal.lengths)?, + )?, + Wire::TBus(bus) => declarations.visit_signal_or_bus_impl( + codegen, + &bus.xtype, + &bus.name, + location, + codegen.type_from_dimension_consts( + codegen.struct_type(&bus.name).into(), + &bus.lengths, + )?, + )?, + } + } + Ok(declarations) + } + + fn get_declaration_inputs(&'_ self) -> Cow<'_, [(String, usize)]> { + Cow::Owned( + self.wires + .iter() + .filter_map(|w| match w { + Wire::TSignal(signal) if signal.xtype == SignalType::Input => { + Some((signal.name.clone(), signal.lengths.len())) + } + Wire::TBus(bus) if bus.xtype == SignalType::Input => { + Some((bus.name.clone(), bus.lengths.len())) + } + _ => None, + }) + .collect(), + ) + } + + fn get_inputs(&'_ self) -> Cow<'_, HashMap> { + Cow::Owned(wires_of_type(&self.wires, SignalType::Input)) + } + + fn get_outputs(&'_ self) -> Cow<'_, HashMap> { + Cow::Owned(wires_of_type(&self.wires, SignalType::Output)) + } + + fn gen_preamble<'ctx>( + &self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + template: &TemplateContext<'_, 'ctx, '_, '_, '_, '_>, + ) -> Result<()> { + fn build_nested( + meta: &Meta, + flat_vals: &[BigInt], + offset: usize, + dims: &[usize], + ) -> Result<(Expression, usize)> { + match dims { + [] => { + if offset >= flat_vals.len() { + Err(anyhow!("offset {} out of bounds (len {})", offset, flat_vals.len())) + } else { + Ok(( + Expression::Number(meta.clone(), flat_vals[offset].clone()), + offset + 1, + )) + } + } + [next, remaining @ ..] => { + let mut values = vec![]; + let mut curr_offset = offset; + for _ in 0..*next { + let (expr, new_offset) = + build_nested(meta, flat_vals, curr_offset, remaining)?; + curr_offset = new_offset; + values.push(expr); + } + let expr = Expression::ArrayInLine { meta: meta.clone(), values }; + Ok((expr, curr_offset)) + } + } + } + + // Insert Argument values (static versions of template parameters) in + // case an assignment references these (happens when assigning an array + // variable to equal an array Argument). + for arg in &self.header { + let meta = Meta::new(0, 0); + let declaration = Statement::Declaration { + meta: meta.clone(), + xtype: VariableType::Var, + name: arg.name.clone(), + dimensions: arg + .lengths + .iter() + .map(|d| { + let bigint = BigInt::from_usize(*d) + .ok_or_else(|| anyhow!("could not convert usize to bigint"))?; + Ok(Expression::Number(meta.clone(), bigint)) + }) + .collect::>>()?, + is_constant: true, + is_anonymous: false, + }; + let assignment = Statement::Substitution { + meta: meta.clone(), + var: arg.name.clone(), + access: vec![], + op: AssignVar, + rhe: build_nested(&meta, &arg.values, 0, &arg.lengths)?.0, + }; + declaration.gen_llzk_in_template(codegen, template)?; + assignment.gen_llzk_in_template(codegen, template)?; + } + Ok(()) + } +} + +/// Filters the wires that match the given type and builds a map with them. +fn wires_of_type(wires: &[Wire], xtype: SignalType) -> HashMap { + wires + .iter() + .filter_map(|w| match w { + Wire::TSignal(signal) if signal.xtype == xtype => { + Some((signal.name.clone(), Wire::TSignal(signal.clone()))) + } + Wire::TBus(bus) if bus.xtype == xtype => { + Some((bus.name.clone(), Wire::TBus(bus.clone()))) + } + _ => None, + }) + .collect() +} + +impl WireLike for Wire { + fn get_type(&self) -> WireType { + match self { + Wire::TSignal(_) => WireType::Signal, + Wire::TBus(bus) => WireType::Bus(bus.name.clone()), + } + } +} diff --git a/llzk_backend/src/traversal.rs b/llzk_backend/src/traversal.rs new file mode 100644 index 000000000..8939f4f19 --- /dev/null +++ b/llzk_backend/src/traversal.rs @@ -0,0 +1,136 @@ +//! Shared LLZK structure traversal utilities. + +use llzk::prelude::BlockLike as _; +use llzk::prelude::BlockRef; +use llzk::prelude::OperationLike as _; +use llzk::prelude::OperationRef; +use llzk::prelude::RegionLike as _; +use llzk::prelude::RegionRef; + +/// Callbacks for walking LLZK structures. +#[derive(Default)] +#[allow(clippy::type_complexity)] +pub struct WalkCallbacks<'a> { + /// Optional callback for each block visited. + pub block_visitor: Option>, + /// Optional callback for each operation visited. + pub op_visitor: Option>, + /// Optional callback for each region visited. + pub region_visitor: Option>, +} + +impl<'a> WalkCallbacks<'a> { + /// Create a new [WalkCallbacks] with a block visitor. + #[allow(unused)] + pub fn for_blocks(visitor: impl FnMut(BlockRef) + 'a) -> Self { + Self { block_visitor: Some(Box::new(visitor)), ..Default::default() } + } + /// Create a new [WalkCallbacks] with an operation visitor. + #[allow(unused)] + pub fn for_ops(visitor: impl FnMut(OperationRef) + 'a) -> Self { + Self { op_visitor: Some(Box::new(visitor)), ..Default::default() } + } + /// Create a new [WalkCallbacks] with a region visitor. + #[allow(unused)] + pub fn for_regions(visitor: impl FnMut(RegionRef) + 'a) -> Self { + Self { region_visitor: Some(Box::new(visitor)), ..Default::default() } + } + /// Add/overwrite the block visitor in an existing [WalkCallbacks]. + #[allow(unused)] + pub fn and_blocks(self, visitor: impl FnMut(BlockRef) + 'a) -> Self { + Self { block_visitor: Some(Box::new(visitor)), ..self } + } + /// Add/overwrite the operation visitor in an existing [WalkCallbacks]. + #[allow(unused)] + pub fn and_ops(self, visitor: impl FnMut(OperationRef) + 'a) -> Self { + Self { op_visitor: Some(Box::new(visitor)), ..self } + } + /// Add/overwrite the region visitor in an existing [WalkCallbacks]. + #[allow(unused)] + pub fn and_regions(self, visitor: impl FnMut(RegionRef) + 'a) -> Self { + Self { region_visitor: Some(Box::new(visitor)), ..self } + } +} + +/// Recursive walk visitor for a block. +fn walk_block(block: BlockRef, cb: &mut WalkCallbacks) { + if let Some(visitor) = cb.block_visitor.as_mut() { + visitor(block); + } + + let mut op = block.first_operation(); + while let Some(operation) = op { + walk_operation(operation, cb); + op = operation.next_in_block(); + } +} + +/// Recursive walk visitor for an operation. +fn walk_operation(op: OperationRef, cb: &mut WalkCallbacks) { + if let Some(visitor) = cb.op_visitor.as_mut() { + visitor(op); + } + + for region in op.regions() { + walk_region(region, cb); + } +} + +/// Recursive walk visitor for a region. +fn walk_region(region: RegionRef, cb: &mut WalkCallbacks) { + if let Some(visitor) = cb.region_visitor.as_mut() { + visitor(region); + } + + let mut block = region.first_block(); + while let Some(b) = block { + walk_block(b, cb); + block = b.next_in_region(); + } +} + +/// Walk starting from a block with the given callbacks. +#[inline] +#[allow(unused)] +pub fn walk_from_block(block: BlockRef, mut callbacks: WalkCallbacks) { + walk_block(block, &mut callbacks); +} + +/// Walk starting from an operation with the given callbacks. +#[inline] +#[allow(unused)] +pub fn walk_from_region(region: RegionRef, mut callbacks: WalkCallbacks) { + walk_region(region, &mut callbacks); +} + +/// Walk starting from a region with the given callbacks. +#[inline] +#[allow(unused)] +pub fn walk_from_operation(operation: OperationRef, mut callbacks: WalkCallbacks) { + walk_operation(operation, &mut callbacks); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn callback_struct_init() { + let _ = WalkCallbacks { + block_visitor: Some(Box::new(|b| println!("Visited: {:?}", b))), + op_visitor: None, + region_visitor: None, + }; + } + + #[test] + fn callback_struct_new_for_ops() { + let _ = WalkCallbacks::for_ops(|op| println!("Visited op {:?}", op)); + } + + #[test] + fn callback_struct_new_chained() { + let _ = WalkCallbacks::for_ops(|op| println!("Visited op {:?}", op)) + .and_blocks(|b| println!("Visited block {:?}", b)); + } +} diff --git a/llzk_backend/src/write_chain.rs b/llzk_backend/src/write_chain.rs new file mode 100644 index 000000000..a3067b6bc --- /dev/null +++ b/llzk_backend/src/write_chain.rs @@ -0,0 +1,328 @@ +//! Helper type for constructing operations that write using [`Access`]. + +use crate::function::FunctionContext; +use crate::function::InfoProviders; +use crate::lvalue::Lvalue; +use crate::lvalue::OverrideVar; +use crate::lvalue::Root; +use crate::program_ext::ProgramLike; +use crate::shared::comp_type; +use crate::shared::LlzkCodegen; +use crate::subcmp::names::COMP; +use crate::subcmp::names::PARAMS; +use crate::subcmp::SubcmpInfo; +use crate::template::TemplateContext; +use anyhow::Result; +use llzk::dialect::r#struct; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::Location; +use llzk::prelude::Value; +use llzk::prelude::ValueLike as _; +use program_structure::ast::Access; +use program_structure::ast::Expression; +use std::convert::TryInto as _; +use std::fmt; + +/// Gives information about signals written into the component. +pub trait SignalWriteInfo: std::fmt::Debug { + /// Returns true if we already generated a `struct.writem` op for the given signal. + fn signal_already_written(&self, name: &str) -> bool; + + /// Marks the given signal as written. + fn mark_signal_as_written(&self, name: String); +} + +impl SignalWriteInfo for TemplateContext<'_, '_, '_, '_, '_, '_> { + fn signal_already_written(&self, name: &str) -> bool { + self.signal_already_written(name) + } + + fn mark_signal_as_written(&self, name: String) { + self.mark_signal_as_written(name) + } +} + +/// Implementation of [`SignalWriteInfo`] for when no signals are allowed. +#[derive(Debug)] +pub struct NoSignalsInfo; + +impl SignalWriteInfo for NoSignalsInfo { + fn signal_already_written(&self, _name: &str) -> bool { + unreachable!() + } + + fn mark_signal_as_written(&self, _name: String) { + unreachable!() + } +} + +/// Indicates the target function the write operation is happening on. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum WriteTarget { + /// The `@compute` function of a struct. + Compute, + /// The `@constrain` function of a struct. + Constrain, + /// A free function. + #[allow(dead_code)] + Free, +} + +impl WriteTarget { + /// Returns true if the target is `@compute`. + #[inline] + fn is_compute(&self) -> bool { + matches!(self, WriteTarget::Compute) + } + /// Returns true if the target is `@constrain`. + #[inline] + fn is_constrain(&self) -> bool { + matches!(self, WriteTarget::Constrain) + } +} + +/// Overrides a subcomponent's variable name. +#[derive(Clone, Copy)] +struct Override<'info> { + /// If true, means that the write chain is storing the result of a call to compute. + compute_result: bool, + /// Reference to a subcomponent information provider. + subcmp_info: &'info dyn SubcmpInfo, +} + +impl OverrideVar for Override<'_> { + fn override_var(&self, var: &str, op: Root) -> Option { + (!self.compute_result && self.subcmp_info.is_subcmp(var) && op == Root::Signal) + .then(|| crate::subcmp::names::inputs(var)) + } +} + +/// Helper type that defines a chain of write operations. +#[derive(Debug)] +pub struct WriteChain<'ast> { + /// Location value this write action takes place on. + lvalue: Lvalue<'ast>, + /// If true, means that the write chain is storing the result of a call to compute. + compute_result: bool, +} + +impl<'ast> WriteChain<'ast> { + /// Creates a new write chain. + pub fn new(var: &'ast str, op: Root, access: &'ast [Access]) -> Self { + Self::from_lvalue(Lvalue::new(var, op, access), false) + } + + /// Creates a write chain from a lvalue. + fn from_lvalue(lvalue: Lvalue<'ast>, compute_result: bool) -> Self { + Self { lvalue, compute_result } + } + + /// Creates a copy of the chain with the `compute_result` flag set to true. + pub fn clone_for_compute_result(&self) -> Self { + Self { lvalue: self.lvalue.clone(), compute_result: true } + } + + /// Handle [Lvalue::Array] case of [`WriteChain::write`]. + #[allow(clippy::too_many_arguments)] + fn write_array<'ctx, 'val>( + indices: Vec<&Expression>, + prev: Self, + val: Value<'ctx, 'val>, + target: WriteTarget, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'_, 'ctx, '_, '_, 'val>, + location: Location<'ctx>, + signal_write_info: &dyn SignalWriteInfo, + subcmp_info: &dyn SubcmpInfo, + ) -> Result<()> { + let arr_ref = prev.lvalue.get_value( + codegen, + fc, + subcmp_info, + location, + Some(&prev.ov(subcmp_info)), + )?; + let indices = fc.gen_index_ops( + indices, + codegen, + location, + InfoProviders { subcmp_info, signal_write_info }, + )?; + fc.append_array_write(codegen, arr_ref, &indices, location, val, None)?; + prev.write(arr_ref, target, codegen, fc, location, signal_write_info, subcmp_info) + } + + /// Handle [Lvalue::Subcmp] case of [`WriteChain::write`]. + #[allow(clippy::too_many_arguments)] + fn write_subcmp<'ctx, 'val>( + name: &str, + prev: Self, + val: Value<'ctx, 'val>, + target: WriteTarget, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'_, 'ctx, '_, '_, 'val>, + location: Location<'ctx>, + signal_write_info: &dyn SignalWriteInfo, + subcmp_info: &dyn SubcmpInfo, + ) -> Result<()> { + let subcmp_value_inputs = prev.lvalue.get_value( + codegen, + fc, + subcmp_info, + location, + Some(&prev.ov(subcmp_info)), + )?; + fc.append_op_no_result(codegen.new_pod_write_op(location, subcmp_value_inputs, name, val))?; + let prev_for_compute = prev.clone_for_compute_result(); + prev.write( + subcmp_value_inputs, + target, + codegen, + fc, + location, + signal_write_info, + subcmp_info, + )?; + // Read the subcomponent's memory, which should be the memory pod. + let subcmp_value = prev_for_compute.lvalue.get_value( + codegen, + fc, + subcmp_info, + location, + Some(&prev_for_compute.ov(subcmp_info)), + )?; + + let counter = fc.gen_subcmp_decrease_counter(codegen, location, subcmp_value, 1)?; + fc.gen_scf_if_is_zero(counter, location, codegen, |fc| { + let params = fc.append_op_unnamed_result(codegen.new_pod_read_op( + subcmp_value, + PARAMS, + location, + )?)?; + let struct_type = comp_type(subcmp_value.r#type().try_into()?)?.try_into()?; + + let subcmp_instance = + fc.gen_compute_call(struct_type, subcmp_value_inputs, params, location, codegen)?; + fc.append_op_no_result(codegen.new_pod_write_op( + location, + subcmp_value, + COMP, + subcmp_instance, + ))?; + prev_for_compute.write( + subcmp_value, + target, + codegen, + fc, + location, + signal_write_info, + subcmp_info, + ) + }) + } + + #[allow(clippy::too_many_arguments)] + /// Emits the write operations. + pub fn write<'ctx, 'val>( + self, + val: Value<'ctx, 'val>, + target: WriteTarget, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'_, 'ctx, '_, '_, 'val>, + location: Location<'ctx>, + signal_write_info: &dyn SignalWriteInfo, + subcmp_info: &dyn SubcmpInfo, + ) -> Result<()> { + /// Handle [Lvalue::Root] with [Root::Signal] case. + fn write_root_signal<'ctx, 'val>( + var: String, + val: Value<'ctx, 'val>, + target: WriteTarget, + fc: &mut FunctionContext<'_, 'ctx, '_, '_, 'val>, + location: Location<'ctx>, + signal_write_info: &dyn SignalWriteInfo, + ) -> Result<()> { + if target.is_compute() && !signal_write_info.signal_already_written(&var) { + let block = fc.block_ctx.get_decl_block_of_value(&var)?; + + let self_value = fc.func.self_value_of_compute()?; + // Write value to field of "self" struct. + fc.block_ctx.enqueue_in_block( + r#struct::writem(location, self_value, &var, val)?.into(), + block, + )?; + fc.block_ctx.set_named_value_at_declaration(var.clone(), val)?; + signal_write_info.mark_signal_as_written(var) + } + Ok(()) + } + + // If we are writing into a variable annotated as a subcomponent and we are + // writing in the constraint function skip the whole chain. + if let Lvalue::Root { var, op: Root::Var, .. } = self.lvalue.root() { + if target.is_constrain() && subcmp_info.is_subcmp(var) { + return Ok(()); + } + } + + match self.lvalue { + Lvalue::Root { var, op: Root::Signal } => { + let final_var = self + .ov(subcmp_info) + .override_var(var, Root::Signal) + .unwrap_or_else(|| var.to_owned()); + // We know we are assigning a subcomponent signal if the root var is the name + // of the var listed as a subcomponent. + if subcmp_info.is_subcmp(var) { + return fc.block_ctx.set_named_value(final_var, val); + } + write_root_signal(final_var, val, target, fc, location, signal_write_info) + } + Lvalue::Root { var, op: Root::Var } => { + if subcmp_info.is_subcmp(var) { + return Ok(()); // Do nothing (for now) + } + fc.block_ctx.set_named_value( + self.ov(subcmp_info) + .override_var(var, Root::Var) + .unwrap_or_else(|| var.to_owned()), + val, + ) + } + Lvalue::Array { indices, prev } => Self::write_array( + indices, + Self::from_lvalue(*prev, self.compute_result), + val, + target, + codegen, + fc, + location, + signal_write_info, + subcmp_info, + ), + Lvalue::Subcmp { name, prev } => Self::write_subcmp( + name, + Self::from_lvalue(*prev, self.compute_result), + val, + target, + codegen, + fc, + location, + signal_write_info, + subcmp_info, + ), + } + } + + /// Creates an override configuration for the given template and the internal `compute_result` + /// state. + fn ov<'info>(&self, template: &'info dyn SubcmpInfo) -> Override<'info> { + Override { compute_result: self.compute_result, subcmp_info: template } + } +} + +impl fmt::Display for WriteChain<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.lvalue, f) + } +} diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 7b89e8bd9..5b8e90305 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -20,7 +20,7 @@ pub fn build_main_component(public: Vec, call: Expression) -> MainCompon pub type Version = (usize, usize, usize); -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Meta { pub elem_id: usize, pub start: usize, @@ -197,7 +197,7 @@ pub fn build_bus( Definition::Bus { meta, name, args, arg_location, body } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Statement { IfThenElse { meta: Meta, @@ -264,7 +264,7 @@ pub enum Statement { }, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub enum SignalType { Output, Input, @@ -274,7 +274,7 @@ pub enum SignalType { pub type TagList = Vec; -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum VariableType { Var, Signal(SignalType, TagList), @@ -283,7 +283,7 @@ pub enum VariableType { Bus(String, SignalType, TagList), } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Expression { InfixOp { meta: Meta, @@ -345,7 +345,7 @@ pub enum Expression { }, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Access { ComponentAccess(String), ArrayAccess(Expression), @@ -357,14 +357,14 @@ pub fn build_array_access(expr: Expression) -> Access { Access::ArrayAccess(expr) } -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum AssignOp { AssignVar, AssignSignal, AssignConstraintSignal, } -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum ExpressionInfixOpcode { Mul, Div, @@ -388,7 +388,7 @@ pub enum ExpressionInfixOpcode { BitXor, } -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum ExpressionPrefixOpcode { Sub, BoolNot, @@ -397,7 +397,7 @@ pub enum ExpressionPrefixOpcode { // Knowledge buckets -#[derive(Clone, PartialOrd, PartialEq, Ord, Eq)] +#[derive(Clone, Debug, PartialOrd, PartialEq, Ord, Eq)] pub enum TypeReduction { Variable, Component(Option), @@ -406,7 +406,7 @@ pub enum TypeReduction { Tag, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum LogArgument { LogStr(String), LogExp(Expression), @@ -419,7 +419,7 @@ pub fn build_log_expression(expr: Expression) -> LogArgument { } -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct TypeKnowledge { reduces_to: Option, } @@ -468,7 +468,7 @@ impl TypeKnowledge { } } -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct MemoryKnowledge { concrete_dimensions: Option>, full_length: Option, @@ -485,6 +485,12 @@ impl MemoryKnowledge { pub fn set_abstract_memory_address(&mut self, value: usize) { self.abstract_memory_address = Option::Some(value); } + pub fn has_concrete_dimensions(&self) -> bool { + self.concrete_dimensions.is_some() + } + pub fn has_abstract_memory_address(&self) -> bool { + self.abstract_memory_address.is_some() + } pub fn get_concrete_dimensions(&self) -> &[usize] { if let Option::Some(v) = &self.concrete_dimensions { v diff --git a/program_structure/src/program_library/bus_data.rs b/program_structure/src/program_library/bus_data.rs index 712497854..1ae15abb7 100644 --- a/program_structure/src/program_library/bus_data.rs +++ b/program_structure/src/program_library/bus_data.rs @@ -5,7 +5,7 @@ use super::wire_data::*; pub type BusInfo = HashMap; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BusData { file_id: FileID, name: String, diff --git a/program_structure/src/program_library/file_definition.rs b/program_structure/src/program_library/file_definition.rs index e372b3034..074fbe544 100644 --- a/program_structure/src/program_library/file_definition.rs +++ b/program_structure/src/program_library/file_definition.rs @@ -1,4 +1,4 @@ -use codespan_reporting::files::{Files, SimpleFiles}; +use codespan_reporting::files::{Files, SimpleFile, SimpleFiles}; use std::ops::Range; pub type FileSource = String; @@ -7,14 +7,16 @@ pub type FileID = usize; pub type FileLocation = Range; type FileStorage = SimpleFiles; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FileLibrary { files: FileStorage, } impl Default for FileLibrary { fn default() -> Self { - FileLibrary { files: FileStorage::new() } + let mut files = FileStorage::new(); + files.add(String::from(""), String::from("")); + FileLibrary { files } } } @@ -26,11 +28,25 @@ impl FileLibrary { self.get_mut_files().add(file_name, file_source) } pub fn get_line(&self, start: usize, file_id: FileID) -> Option { - match self.files.line_index(file_id, start) { - Some(lines) => Some(lines + 1), - None => None, + self.files.location(file_id, start).map(|loc| loc.line_number) + } + pub fn get_column(&self, start: usize, file_id: FileID) -> Option { + self.files.location(file_id, start).map(|loc| loc.column_number) + } + pub fn get_file(&self, file_id: &FileID) -> Option<&SimpleFile> { + self.files.get(*file_id) + } + pub fn get_filename_or(&self, file_id: &FileID, default: &FilePath) -> FilePath { + let sf_opt = self.get_file(file_id); + match sf_opt { + Some(sf) => sf.name().replace("\"", ""), + None => default.clone(), } } + + pub fn get_filename_or_default(&self, file_id: &FileID) -> FilePath { + self.get_filename_or(file_id, &FilePath::from("")) + } pub fn to_storage(&self) -> &FileStorage { &self.get_files() } diff --git a/program_structure/src/program_library/function_data.rs b/program_structure/src/program_library/function_data.rs index c1e022d21..c3dde4a53 100644 --- a/program_structure/src/program_library/function_data.rs +++ b/program_structure/src/program_library/function_data.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; pub type FunctionInfo = HashMap; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FunctionData { name: String, file_id: FileID, diff --git a/program_structure/src/program_library/program_archive.rs b/program_structure/src/program_library/program_archive.rs index 3a55d95bf..ca6b48ff4 100644 --- a/program_structure/src/program_library/program_archive.rs +++ b/program_structure/src/program_library/program_archive.rs @@ -10,7 +10,7 @@ use crate::error_definition::Report; type Contents = Vec<(FileID, Vec)>; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ProgramArchive { pub id_max: usize, pub file_id_main: FileID, diff --git a/program_structure/src/program_library/template_data.rs b/program_structure/src/program_library/template_data.rs index 142e1afc0..3c75baa6c 100644 --- a/program_structure/src/program_library/template_data.rs +++ b/program_structure/src/program_library/template_data.rs @@ -6,7 +6,7 @@ use std::collections::{HashMap}; pub type TemplateInfo = HashMap; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct TemplateData { file_id: FileID, name: String, diff --git a/program_structure/src/program_library/wire_data.rs b/program_structure/src/program_library/wire_data.rs index 8e539a89f..d1d5805ec 100644 --- a/program_structure/src/program_library/wire_data.rs +++ b/program_structure/src/program_library/wire_data.rs @@ -6,14 +6,14 @@ pub type WireInfo = HashMap; pub type WireDeclarationOrder = Vec<(String, usize)>; -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum WireType { Signal, Bus(String), } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct WireData { wire_type: WireType, dimension: usize, diff --git a/scripts/update_llzk_lib.sh b/scripts/update_llzk_lib.sh new file mode 100755 index 000000000..779e46510 --- /dev/null +++ b/scripts/update_llzk_lib.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# Program: update_llzk_lib.sh +# Description: This script updates the llzk-lib dependency +# in the cargo lock file and the nix flake lock file. +# +# Required Programs: +# - cargo: For updating the rust dependencies +# - nix: For updating the nix flake +# +# Usage: ./scripts/update_llzk_lib.sh + +set -e + +cargo update -p llzk +nix flake update llzk-rs-pkgs diff --git a/scripts/update_test_cases.sh b/scripts/update_test_cases.sh new file mode 100755 index 000000000..f57495e74 --- /dev/null +++ b/scripts/update_test_cases.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +# Program: update_test_cases.sh [--concrete] +# Description: This script updates test cases with +# the autogenerated output from generate-test-checks.py. +# Uses '--llzk templated' by default unless the '--concrete' flag is specified, +# in which case '--llzk concrete' is passed to circom. +# +# Required Programs: +# - cargo: For building and running the frontend +# - python3: For running the script +# +# Usage: LLZKLIB_HOME=path/to/llzk-lib ./scripts/update_test_cases.sh test1.circom [test2.circom ...] + +set -e + +llzk_type=templated + +# Parse flags without touching positional args. +positional="" +while [ $# -gt 0 ]; do + case "$1" in + --concrete) + llzk_type=concrete + shift + ;; + --) + shift + break + ;; + *) + positional="$positional $1" + shift + ;; + esac +done +positional="$positional $@" + +if [ -z $LLZKLIB_HOME ]; then + echo "Set environment variable LLZKLIB_HOME with the path to llzk-lib" + exit 1 +fi + +if ! test -z "$(git status --porcelain)"; then + echo "Git index is dirty. Run this script on a clean index" + exit 1 +fi + +# Build the frontend once +cargo build -p circom + +for f in $positional; do + o=$(mktemp -d) + name=$(basename "$f" .circom) + target/debug/circom --stabilize --llzk_plaintext --llzk "$llzk_type" -o $o "$f" + sed '/\/\/ CHECK-*/d' "$f" > "$f".updated + python3 $LLZKLIB_HOME/scripts/generate-test-checks.py "$o/${name}_llzk/$name.llzk" | grep '^// CHECK-' >> "$f".updated + mv "$f".updated "$f" +done